github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/libsnap-confine-private/mount-opt.c (about)

     1  /*
     2   * Copyright (C) 2016 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "mount-opt.h"
    19  
    20  #include <errno.h>
    21  #include <limits.h>
    22  #include <stdio.h>
    23  #include <stdlib.h>
    24  #include <string.h>
    25  #include <sys/mount.h>
    26  
    27  #include "fault-injection.h"
    28  #include "privs.h"
    29  #include "string-utils.h"
    30  #include "utils.h"
    31  
    32  const char *sc_mount_opt2str(char *buf, size_t buf_size, unsigned long flags)
    33  {
    34  	unsigned long used = 0;
    35  	sc_string_init(buf, buf_size);
    36  
    37  #define F(FLAG, TEXT) do {                                         \
    38      if (flags & (FLAG)) {                                          \
    39        sc_string_append(buf, buf_size, #TEXT ","); flags ^= (FLAG); \
    40      }                                                              \
    41    } while (0)
    42  
    43  	F(MS_RDONLY, ro);
    44  	F(MS_NOSUID, nosuid);
    45  	F(MS_NODEV, nodev);
    46  	F(MS_NOEXEC, noexec);
    47  	F(MS_SYNCHRONOUS, sync);
    48  	F(MS_REMOUNT, remount);
    49  	F(MS_MANDLOCK, mand);
    50  	F(MS_DIRSYNC, dirsync);
    51  	F(MS_NOATIME, noatime);
    52  	F(MS_NODIRATIME, nodiratime);
    53  	if (flags & MS_BIND) {
    54  		if (flags & MS_REC) {
    55  			sc_string_append(buf, buf_size, "rbind,");
    56  			used |= MS_REC;
    57  		} else {
    58  			sc_string_append(buf, buf_size, "bind,");
    59  		}
    60  		flags ^= MS_BIND;
    61  	}
    62  	F(MS_MOVE, move);
    63  	// The MS_REC flag handled separately by affected flags (MS_BIND,
    64  	// MS_PRIVATE, MS_SLAVE, MS_SHARED)
    65  	// XXX: kernel has MS_VERBOSE, glibc has MS_SILENT, both use the same constant
    66  	F(MS_SILENT, silent);
    67  	F(MS_POSIXACL, acl);
    68  	F(MS_UNBINDABLE, unbindable);
    69  	if (flags & MS_PRIVATE) {
    70  		if (flags & MS_REC) {
    71  			sc_string_append(buf, buf_size, "rprivate,");
    72  			used |= MS_REC;
    73  		} else {
    74  			sc_string_append(buf, buf_size, "private,");
    75  		}
    76  		flags ^= MS_PRIVATE;
    77  	}
    78  	if (flags & MS_SLAVE) {
    79  		if (flags & MS_REC) {
    80  			sc_string_append(buf, buf_size, "rslave,");
    81  			used |= MS_REC;
    82  		} else {
    83  			sc_string_append(buf, buf_size, "slave,");
    84  		}
    85  		flags ^= MS_SLAVE;
    86  	}
    87  	if (flags & MS_SHARED) {
    88  		if (flags & MS_REC) {
    89  			sc_string_append(buf, buf_size, "rshared,");
    90  			used |= MS_REC;
    91  		} else {
    92  			sc_string_append(buf, buf_size, "shared,");
    93  		}
    94  		flags ^= MS_SHARED;
    95  	}
    96  	flags ^= used;		// this is just for MS_REC
    97  	F(MS_RELATIME, relatime);
    98  	F(MS_KERNMOUNT, kernmount);
    99  	F(MS_I_VERSION, iversion);
   100  	F(MS_STRICTATIME, strictatime);
   101  #ifndef MS_LAZYTIME
   102  #define MS_LAZYTIME (1<<25)
   103  #endif
   104  	F(MS_LAZYTIME, lazytime);
   105  #ifndef MS_NOSEC
   106  #define MS_NOSEC (1 << 28)
   107  #endif
   108  	F(MS_NOSEC, nosec);
   109  #ifndef MS_BORN
   110  #define MS_BORN (1 << 29)
   111  #endif
   112  	F(MS_BORN, born);
   113  	F(MS_ACTIVE, active);
   114  	F(MS_NOUSER, nouser);
   115  #undef F
   116  	// Render any flags that are unaccounted for.
   117  	if (flags) {
   118  		char of[128] = { 0 };
   119  		sc_must_snprintf(of, sizeof of, "%#lx", flags);
   120  		sc_string_append(buf, buf_size, of);
   121  	}
   122  	// Chop the excess comma from the end.
   123  	size_t len = strnlen(buf, buf_size);
   124  	if (len > 0 && buf[len - 1] == ',') {
   125  		buf[len - 1] = 0;
   126  	}
   127  	return buf;
   128  }
   129  
   130  const char *sc_mount_cmd(char *buf, size_t buf_size, const char *source, const char
   131  			 *target, const char *fs_type, unsigned long mountflags, const
   132  			 void *data)
   133  {
   134  	sc_string_init(buf, buf_size);
   135  	sc_string_append(buf, buf_size, "mount");
   136  
   137  	// Add filesysystem type if it's there and doesn't have the special value "none"
   138  	if (fs_type != NULL && strncmp(fs_type, "none", 5) != 0) {
   139  		sc_string_append(buf, buf_size, " -t ");
   140  		sc_string_append(buf, buf_size, fs_type);
   141  	}
   142  	// Check for some special, dedicated options, that aren't represented with
   143  	// the generic mount option argument (mount -o ...), by collecting those
   144  	// options that we will display as command line arguments in
   145  	// used_special_flags. This is used below to filter out these arguments
   146  	// from mount_flags when calling sc_mount_opt2str().
   147  	int used_special_flags = 0;
   148  
   149  	// Bind-ounts (bind)
   150  	if (mountflags & MS_BIND) {
   151  		if (mountflags & MS_REC) {
   152  			sc_string_append(buf, buf_size, " --rbind");
   153  			used_special_flags |= MS_REC;
   154  		} else {
   155  			sc_string_append(buf, buf_size, " --bind");
   156  		}
   157  		used_special_flags |= MS_BIND;
   158  	}
   159  	// Moving mount point location (move)
   160  	if (mountflags & MS_MOVE) {
   161  		sc_string_append(buf, buf_size, " --move");
   162  		used_special_flags |= MS_MOVE;
   163  	}
   164  	// Shared subtree operations (shared, slave, private, unbindable).
   165  	if (MS_SHARED & mountflags) {
   166  		if (mountflags & MS_REC) {
   167  			sc_string_append(buf, buf_size, " --make-rshared");
   168  			used_special_flags |= MS_REC;
   169  		} else {
   170  			sc_string_append(buf, buf_size, " --make-shared");
   171  		}
   172  		used_special_flags |= MS_SHARED;
   173  	}
   174  
   175  	if (MS_SLAVE & mountflags) {
   176  		if (mountflags & MS_REC) {
   177  			sc_string_append(buf, buf_size, " --make-rslave");
   178  			used_special_flags |= MS_REC;
   179  		} else {
   180  			sc_string_append(buf, buf_size, " --make-slave");
   181  		}
   182  		used_special_flags |= MS_SLAVE;
   183  	}
   184  
   185  	if (MS_PRIVATE & mountflags) {
   186  		if (mountflags & MS_REC) {
   187  			sc_string_append(buf, buf_size, " --make-rprivate");
   188  			used_special_flags |= MS_REC;
   189  		} else {
   190  			sc_string_append(buf, buf_size, " --make-private");
   191  		}
   192  		used_special_flags |= MS_PRIVATE;
   193  	}
   194  
   195  	if (MS_UNBINDABLE & mountflags) {
   196  		if (mountflags & MS_REC) {
   197  			sc_string_append(buf, buf_size, " --make-runbindable");
   198  			used_special_flags |= MS_REC;
   199  		} else {
   200  			sc_string_append(buf, buf_size, " --make-unbindable");
   201  		}
   202  		used_special_flags |= MS_UNBINDABLE;
   203  	}
   204  	// If regular option syntax exists then use it.
   205  	if (mountflags & ~used_special_flags) {
   206  		char opts_buf[1000] = { 0 };
   207  		sc_mount_opt2str(opts_buf, sizeof opts_buf, mountflags &
   208  				 ~used_special_flags);
   209  		sc_string_append(buf, buf_size, " -o ");
   210  		sc_string_append(buf, buf_size, opts_buf);
   211  	}
   212  	// Add source and target locations
   213  	if (source != NULL && strncmp(source, "none", 5) != 0) {
   214  		sc_string_append(buf, buf_size, " ");
   215  		sc_string_append(buf, buf_size, source);
   216  	}
   217  	if (target != NULL && strncmp(target, "none", 5) != 0) {
   218  		sc_string_append(buf, buf_size, " ");
   219  		sc_string_append(buf, buf_size, target);
   220  	}
   221  
   222  	return buf;
   223  }
   224  
   225  const char *sc_umount_cmd(char *buf, size_t buf_size, const char *target,
   226  			  int flags)
   227  {
   228  	sc_string_init(buf, buf_size);
   229  	sc_string_append(buf, buf_size, "umount");
   230  
   231  	if (flags & MNT_FORCE) {
   232  		sc_string_append(buf, buf_size, " --force");
   233  	}
   234  
   235  	if (flags & MNT_DETACH) {
   236  		sc_string_append(buf, buf_size, " --lazy");
   237  	}
   238  	if (flags & MNT_EXPIRE) {
   239  		// NOTE: there's no real command line option for MNT_EXPIRE
   240  		sc_string_append(buf, buf_size, " --expire");
   241  	}
   242  	if (flags & UMOUNT_NOFOLLOW) {
   243  		// NOTE: there's no real command line option for UMOUNT_NOFOLLOW
   244  		sc_string_append(buf, buf_size, " --no-follow");
   245  	}
   246  	if (target != NULL) {
   247  		sc_string_append(buf, buf_size, " ");
   248  		sc_string_append(buf, buf_size, target);
   249  	}
   250  
   251  	return buf;
   252  }
   253  
   254  #ifndef SNAP_CONFINE_DEBUG_BUILD
   255  static const char *use_debug_build =
   256      "(disabled) use debug build to see details";
   257  #endif
   258  
   259  static bool sc_do_mount_ex(const char *source, const char *target,
   260  			   const char *fs_type,
   261  			   unsigned long mountflags, const void *data,
   262  			   bool optional)
   263  {
   264  	char buf[10000] = { 0 };
   265  	const char *mount_cmd = NULL;
   266  
   267  	if (sc_is_debug_enabled()) {
   268  #ifdef SNAP_CONFINE_DEBUG_BUILD
   269  		mount_cmd = sc_mount_cmd(buf, sizeof(buf), source,
   270  					 target, fs_type, mountflags, data);
   271  #else
   272  		mount_cmd = use_debug_build;
   273  #endif
   274  		debug("performing operation: %s", mount_cmd);
   275  	}
   276  	if (sc_faulty("mount", NULL)
   277  	    || mount(source, target, fs_type, mountflags, data) < 0) {
   278  		int saved_errno = errno;
   279  		if (optional && saved_errno == ENOENT) {
   280  			// The special-cased value that is allowed to fail.
   281  			return false;
   282  		}
   283  		// Drop privileges so that we can compute our nice error message
   284  		// without risking an attack on one of the string functions there.
   285  		sc_privs_drop();
   286  
   287  		// Compute the equivalent mount command.
   288  		mount_cmd = sc_mount_cmd(buf, sizeof(buf), source,
   289  					 target, fs_type, mountflags, data);
   290  		// Restore errno and die.
   291  		errno = saved_errno;
   292  		die("cannot perform operation: %s", mount_cmd);
   293  	}
   294  	return true;
   295  }
   296  
   297  void sc_do_mount(const char *source, const char *target,
   298  		 const char *fs_type, unsigned long mountflags,
   299  		 const void *data)
   300  {
   301  	(void)sc_do_mount_ex(source, target, fs_type, mountflags, data, false);
   302  }
   303  
   304  bool sc_do_optional_mount(const char *source, const char *target,
   305  			  const char *fs_type, unsigned long mountflags,
   306  			  const void *data)
   307  {
   308  	return sc_do_mount_ex(source, target, fs_type, mountflags, data, true);
   309  }
   310  
   311  void sc_do_umount(const char *target, int flags)
   312  {
   313  	char buf[10000] = { 0 };
   314  	const char *umount_cmd = NULL;
   315  
   316  	if (sc_is_debug_enabled()) {
   317  #ifdef SNAP_CONFINE_DEBUG_BUILD
   318  		umount_cmd = sc_umount_cmd(buf, sizeof(buf), target, flags);
   319  #else
   320  		umount_cmd = use_debug_build;
   321  #endif
   322  		debug("performing operation: %s", umount_cmd);
   323  	}
   324  	if (sc_faulty("umount", NULL) || umount2(target, flags) < 0) {
   325  		// Save errno as ensure can clobber it.
   326  		int saved_errno = errno;
   327  
   328  		// Drop privileges so that we can compute our nice error message
   329  		// without risking an attack on one of the string functions there.
   330  		sc_privs_drop();
   331  
   332  		// Compute the equivalent umount command.
   333  		umount_cmd = sc_umount_cmd(buf, sizeof(buf), target, flags);
   334  		// Restore errno and die.
   335  		errno = saved_errno;
   336  		die("cannot perform operation: %s", umount_cmd);
   337  	}
   338  }