gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap-confine/udev-support.c (about)

     1  /*
     2   * Copyright (C) 2015-2020 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  #include "config.h"
    18  
    19  #include <ctype.h>
    20  #include <errno.h>
    21  #include <fcntl.h>
    22  #include <stdlib.h>
    23  #include <string.h>
    24  #include <sys/stat.h>
    25  #include <sys/sysmacros.h>
    26  #include <sys/types.h>
    27  #include <unistd.h>
    28  #include <limits.h>
    29  #include <stdint.h>
    30  #include <dlfcn.h>
    31  
    32  #include <libudev.h>
    33  
    34  #include "../libsnap-confine-private/cgroup-support.h"
    35  #include "../libsnap-confine-private/cleanup-funcs.h"
    36  #include "../libsnap-confine-private/device-cgroup-support.h"
    37  #include "../libsnap-confine-private/snap.h"
    38  #include "../libsnap-confine-private/string-utils.h"
    39  #include "../libsnap-confine-private/utils.h"
    40  #include "udev-support.h"
    41  
    42  /* Allow access to common devices. */
    43  static void sc_udev_allow_common(sc_device_cgroup * cgroup)
    44  {
    45  	/* The devices we add here have static number allocation.
    46  	 * https://www.kernel.org/doc/html/v4.11/admin-guide/devices.html */
    47  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 3);	// /dev/null
    48  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 5);	// /dev/zero
    49  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 7);	// /dev/full
    50  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 8);	// /dev/random
    51  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 9);	// /dev/urandom
    52  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 0);	// /dev/tty
    53  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 1);	// /dev/console
    54  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 2);	// /dev/ptmx
    55  }
    56  
    57  /** Allow access to current and future PTY slaves.
    58   *
    59   * We unconditionally add them since we use a devpts newinstance. Unix98 PTY
    60   * slaves major are 136-143.
    61   *
    62   * See also:
    63   * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
    64   **/
    65  static void sc_udev_allow_pty_slaves(sc_device_cgroup * cgroup)
    66  {
    67  	for (unsigned pty_major = 136; pty_major <= 143; pty_major++) {
    68  		sc_device_cgroup_allow(cgroup, S_IFCHR, pty_major,
    69  				       SC_DEVICE_MINOR_ANY);
    70  	}
    71  }
    72  
    73  /** Allow access to Nvidia devices.
    74   *
    75   * Nvidia modules are proprietary and therefore aren't in sysfs and can't be
    76   * udev tagged. For now, just add existing nvidia devices to the cgroup
    77   * unconditionally (AppArmor will still mediate the access).  We'll want to
    78   * rethink this if snapd needs to mediate access to other proprietary devices.
    79   *
    80   * Device major and minor numbers are described in (though nvidia-uvm currently
    81   * isn't listed):
    82   *
    83   * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
    84   **/
    85  static void sc_udev_allow_nvidia(sc_device_cgroup * cgroup)
    86  {
    87  	struct stat sbuf;
    88  
    89  	/* Allow access to /dev/nvidia0 through /dev/nvidia254 */
    90  	for (unsigned nv_minor = 0; nv_minor < 255; nv_minor++) {
    91  		char nv_path[15] = { 0 };	// /dev/nvidiaXXX
    92  		sc_must_snprintf(nv_path, sizeof(nv_path), "/dev/nvidia%u",
    93  				 nv_minor);
    94  
    95  		/* Stop trying to find devices after one is not found. In this manner,
    96  		 * we'll add /dev/nvidia0 and /dev/nvidia1 but stop trying to find
    97  		 * nvidia3 - nvidia254 if nvidia2 is not found. */
    98  		if (stat(nv_path, &sbuf) < 0) {
    99  			break;
   100  		}
   101  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   102  				       minor(sbuf.st_rdev));
   103  	}
   104  
   105  	if (stat("/dev/nvidiactl", &sbuf) == 0) {
   106  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   107  				       minor(sbuf.st_rdev));
   108  	}
   109  	if (stat("/dev/nvidia-uvm", &sbuf) == 0) {
   110  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   111  				       minor(sbuf.st_rdev));
   112  	}
   113  	if (stat("/dev/nvidia-modeset", &sbuf) == 0) {
   114  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   115  				       minor(sbuf.st_rdev));
   116  	}
   117  }
   118  
   119  /**
   120   * Allow access to /dev/uhid.
   121   *
   122   * Currently /dev/uhid isn't represented in sysfs, so add it to the device
   123   * cgroup if it exists and let AppArmor handle the mediation.
   124   **/
   125  static void sc_udev_allow_uhid(sc_device_cgroup * cgroup)
   126  {
   127  	struct stat sbuf;
   128  
   129  	if (stat("/dev/uhid", &sbuf) == 0) {
   130  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   131  				       minor(sbuf.st_rdev));
   132  	}
   133  }
   134  
   135  /**
   136   * Allow access to /dev/net/tun
   137   *
   138   * When CONFIG_TUN=m, /dev/net/tun will exist but using it doesn't
   139   * autoload the tun module but also /dev/net/tun isn't udev tagged
   140   * until it is loaded. To work around this, if /dev/net/tun exists, add
   141   * it unconditionally to the cgroup and rely on AppArmor to mediate the
   142   * access. LP: #1859084
   143   **/
   144  static void sc_udev_allow_dev_net_tun(sc_device_cgroup * cgroup)
   145  {
   146  	struct stat sbuf;
   147  
   148  	if (stat("/dev/net/tun", &sbuf) == 0) {
   149  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   150  				       minor(sbuf.st_rdev));
   151  	}
   152  }
   153  
   154  /**
   155   * Allow access to assigned devices.
   156   *
   157   * The snapd udev security backend uses udev rules to tag matching devices with
   158   * tags corresponding to snap applications. Here we interrogate udev and allow
   159   * access to all assigned devices.
   160   **/
   161  static void sc_udev_allow_assigned_device(sc_device_cgroup * cgroup,
   162  					  struct udev_device *device)
   163  {
   164  	const char *path = udev_device_get_syspath(device);
   165  	dev_t devnum = udev_device_get_devnum(device);
   166  	unsigned int major = major(devnum);
   167  	unsigned int minor = minor(devnum);
   168  	/* The manual page of udev_device_get_devnum says:
   169  	 * > On success, udev_device_get_devnum() returns the device type of
   170  	 * > the passed device. On failure, a device type with minor and major
   171  	 * > number set to 0 is returned. */
   172  	if (major == 0 && minor == 0) {
   173  		debug("cannot get major/minor numbers for syspath %s", path);
   174  		return;
   175  	}
   176  	/* devnode is bound to the lifetime of the device and we cannot release
   177  	 * it separately. */
   178  	const char *devnode = udev_device_get_devnode(device);
   179  	if (devnode == NULL) {
   180  		debug("cannot find /dev node from udev device");
   181  		return;
   182  	}
   183  	debug("inspecting type of device: %s", devnode);
   184  	struct stat file_info;
   185  	if (stat(devnode, &file_info) < 0) {
   186  		debug("cannot stat %s", devnode);
   187  		return;
   188  	}
   189  	int devtype = file_info.st_mode & S_IFMT;
   190  	if (devtype == S_IFBLK || devtype == S_IFCHR) {
   191  		sc_device_cgroup_allow(cgroup, devtype, major, minor);
   192  	}
   193  }
   194  
   195  static void sc_udev_setup_acls_common(sc_device_cgroup * cgroup)
   196  {
   197  
   198  	/* Allow access to various devices. */
   199  	sc_udev_allow_common(cgroup);
   200  	sc_udev_allow_pty_slaves(cgroup);
   201  	sc_udev_allow_nvidia(cgroup);
   202  	sc_udev_allow_uhid(cgroup);
   203  	sc_udev_allow_dev_net_tun(cgroup);
   204  }
   205  
   206  static char *sc_security_to_udev_tag(const char *security_tag)
   207  {
   208  	char *udev_tag = sc_strdup(security_tag);
   209  	for (char *c = strchr(udev_tag, '.'); c != NULL; c = strchr(c, '.')) {
   210  		*c = '_';
   211  	}
   212  	return udev_tag;
   213  }
   214  
   215  static void sc_cleanup_udev(struct udev **udev)
   216  {
   217  	if (udev != NULL && *udev != NULL) {
   218  		udev_unref(*udev);
   219  		*udev = NULL;
   220  	}
   221  }
   222  
   223  static void sc_cleanup_udev_enumerate(struct udev_enumerate **enumerate)
   224  {
   225  	if (enumerate != NULL && *enumerate != NULL) {
   226  		udev_enumerate_unref(*enumerate);
   227  		*enumerate = NULL;
   228  	}
   229  }
   230  
   231  /* __sc_udev_device_has_current_tag will be filled at runtime if the libudev has
   232   * this symbol.
   233   *
   234   * Note that we could try to define udev_device_has_current_tag with a weak
   235   * attribute, which should in the normal case be the filled by ld.so when
   236   * loading snap-confined. However this was observed to work in practice only
   237   * when the binary itself is build with recent enough toolchain (eg. gcc &
   238   * binutils on Ubuntu 20.04)
   239   */
   240  static int (*__sc_udev_device_has_current_tag)(struct udev_device * udev_device,
   241  					       const char *tag) = NULL;
   242  static void setup_current_tags_support(void)
   243  {
   244  	void *lib = dlopen("libudev.so.1", RTLD_NOW);
   245  	if (lib == NULL) {
   246  		debug("cannot load libudev.so.1: %s", dlerror());
   247  		/* bit unexpected as we use the library from the host and it's stable */
   248  		return;
   249  	}
   250  	/* check whether we have the symbol introduced in systemd v247 to inspect
   251  	 * the CURRENT_TAGS property */
   252  	void *sym = dlsym(lib, "udev_device_has_current_tag");
   253  	if (sym == NULL) {
   254  		debug("cannot find current tags symbol: %s", dlerror());
   255  		/* symbol is not found in the library version */
   256  		(void)dlclose(lib);
   257  		return;
   258  	}
   259  	debug("libudev has current tags support");
   260  	__sc_udev_device_has_current_tag = sym;
   261  	/* lib goes out of scope and is leaked but we need sym and hence
   262  	 * lib to be valid for the entire lifetime of the application
   263  	 * lifecycle so this is fine. */
   264  	/* coverity[leaked_storage] */
   265  }
   266  
   267  void sc_setup_device_cgroup(const char *security_tag)
   268  {
   269  	debug("setting up device cgroup");
   270  
   271  	setup_current_tags_support();
   272  	if (__sc_udev_device_has_current_tag == NULL) {
   273  		debug("no current tags support present");
   274  	}
   275  
   276  	/* Derive the udev tag from the snap security tag.
   277  	 *
   278  	 * Because udev does not allow for dots in tag names, those are replaced by
   279  	 * underscores in snapd. We just match that behavior. */
   280  	char *udev_tag SC_CLEANUP(sc_cleanup_string) = NULL;
   281  	udev_tag = sc_security_to_udev_tag(security_tag);
   282  
   283  	/* Use udev APIs to talk to udev-the-daemon to determine the list of
   284  	 * "devices" with that tag assigned. The list may be empty, in which case
   285  	 * there's no udev tagging in effect and we must refrain from constructing
   286  	 * the cgroup as it would interfere with the execution of a program. */
   287  	struct udev SC_CLEANUP(sc_cleanup_udev) * udev = NULL;
   288  	udev = udev_new();
   289  	if (udev == NULL) {
   290  		die("cannot connect to udev");
   291  	}
   292  	struct udev_enumerate SC_CLEANUP(sc_cleanup_udev_enumerate) * devices =
   293  	    NULL;
   294  	devices = udev_enumerate_new(udev);
   295  	if (devices == NULL) {
   296  		die("cannot create udev device enumeration");
   297  	}
   298  	if (udev_enumerate_add_match_tag(devices, udev_tag) < 0) {
   299  		die("cannot add tag match to udev device enumeration");
   300  	}
   301  	if (udev_enumerate_scan_devices(devices) < 0) {
   302  		die("cannot enumerate udev devices");
   303  	}
   304  	/* NOTE: udev_list_entry is bound to life-cycle of the used udev_enumerate */
   305  	struct udev_list_entry *assigned;
   306  	assigned = udev_enumerate_get_list_entry(devices);
   307  	if (assigned == NULL) {
   308  		/* NOTE: Nothing is assigned, don't create or use the device cgroup. */
   309  		debug("no devices tagged with %s, skipping device cgroup setup",
   310  		      udev_tag);
   311  		return;
   312  	}
   313  
   314  	/* cgroup wrapper is lazily initialized when devices are actually
   315  	 * assigned */
   316  	sc_device_cgroup *cgroup SC_CLEANUP(sc_device_cgroup_cleanup) = NULL;
   317  	for (struct udev_list_entry * entry = assigned; entry != NULL;
   318  	     entry = udev_list_entry_get_next(entry)) {
   319  		const char *path = udev_list_entry_get_name(entry);
   320  		if (path == NULL) {
   321  			die("udev_list_entry_get_name failed");
   322  		}
   323  		struct udev_device *device =
   324  		    udev_device_new_from_syspath(udev, path);
   325  		/** This is a non-fatal error as devices can disappear asynchronously
   326  		 * and on slow devices we may indeed observe a device that no longer
   327  		 * exists.
   328  		 *
   329  		 * Similar debug + continue pattern repeats in all the udev calls in
   330  		 * this function. Related to LP: #1881209 */
   331  		if (device == NULL) {
   332  			debug("cannot find device from syspath %s", path);
   333  			continue;
   334  		}
   335  		/* If we are able to query if the device has a current tag,
   336  		 * do so and if there are no current tags, continue to prevent
   337  		 * allowing assigned devices to the cgroup - this has the net
   338  		 * desired effect of not re-creating device cgroups that were
   339  		 * previously created/setup but should no longer be setup due
   340  		 * to interface disconnection, etc. */
   341  		if (__sc_udev_device_has_current_tag != NULL) {
   342  			if (__sc_udev_device_has_current_tag(device, udev_tag)
   343  			    <= 0) {
   344  				debug("device %s has no matching current tag",
   345  				      path);
   346  				udev_device_unref(device);
   347  				continue;
   348  			}
   349  			debug("device %s has matching current tag", path);
   350  		}
   351  
   352  		if (cgroup == NULL) {
   353  			/* initialize cgroup wrapper only when we are sure that there are
   354  			 * devices assigned to this snap */
   355  			cgroup = sc_device_cgroup_new(security_tag, 0);
   356  			/* Setup the device group access control list */
   357  			sc_udev_setup_acls_common(cgroup);
   358  		}
   359  		sc_udev_allow_assigned_device(cgroup, device);
   360  		udev_device_unref(device);
   361  	}
   362  	if (cgroup != NULL) {
   363  		/* Move ourselves to the device cgroup */
   364  		sc_device_cgroup_attach_pid(cgroup, getpid());
   365  		debug
   366  		    ("associated snap application process %i with device cgroup %s",
   367  		     getpid(), security_tag);
   368  	} else {
   369  		debug("no devices tagged with %s, skipping device cgroup setup",
   370  		      udev_tag);
   371  	}
   372  }