github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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  
    31  #include <libudev.h>
    32  
    33  #include "../libsnap-confine-private/cgroup-support.h"
    34  #include "../libsnap-confine-private/cleanup-funcs.h"
    35  #include "../libsnap-confine-private/device-cgroup-support.h"
    36  #include "../libsnap-confine-private/snap.h"
    37  #include "../libsnap-confine-private/string-utils.h"
    38  #include "../libsnap-confine-private/utils.h"
    39  #include "udev-support.h"
    40  
    41  /* Allow access to common devices. */
    42  static void sc_udev_allow_common(sc_device_cgroup * cgroup)
    43  {
    44  	/* The devices we add here have static number allocation.
    45  	 * https://www.kernel.org/doc/html/v4.11/admin-guide/devices.html */
    46  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 3);	// /dev/null
    47  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 5);	// /dev/zero
    48  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 7);	// /dev/full
    49  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 8);	// /dev/random
    50  	sc_device_cgroup_allow(cgroup, S_IFCHR, 1, 9);	// /dev/urandom
    51  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 0);	// /dev/tty
    52  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 1);	// /dev/console
    53  	sc_device_cgroup_allow(cgroup, S_IFCHR, 5, 2);	// /dev/ptmx
    54  }
    55  
    56  /** Allow access to current and future PTY slaves.
    57   *
    58   * We unconditionally add them since we use a devpts newinstance. Unix98 PTY
    59   * slaves major are 136-143.
    60   *
    61   * See also:
    62   * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
    63   **/
    64  static void sc_udev_allow_pty_slaves(sc_device_cgroup * cgroup)
    65  {
    66  	for (unsigned pty_major = 136; pty_major <= 143; pty_major++) {
    67  		sc_device_cgroup_allow(cgroup, S_IFCHR, pty_major,
    68  				       SC_DEVICE_MINOR_ANY);
    69  	}
    70  }
    71  
    72  /** Allow access to Nvidia devices.
    73   *
    74   * Nvidia modules are proprietary and therefore aren't in sysfs and can't be
    75   * udev tagged. For now, just add existing nvidia devices to the cgroup
    76   * unconditionally (AppArmor will still mediate the access).  We'll want to
    77   * rethink this if snapd needs to mediate access to other proprietary devices.
    78   *
    79   * Device major and minor numbers are described in (though nvidia-uvm currently
    80   * isn't listed):
    81   *
    82   * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
    83   **/
    84  static void sc_udev_allow_nvidia(sc_device_cgroup * cgroup)
    85  {
    86  	struct stat sbuf;
    87  
    88  	/* Allow access to /dev/nvidia0 through /dev/nvidia254 */
    89  	for (unsigned nv_minor = 0; nv_minor < 255; nv_minor++) {
    90  		char nv_path[15] = { 0 };	// /dev/nvidiaXXX
    91  		sc_must_snprintf(nv_path, sizeof(nv_path), "/dev/nvidia%u",
    92  				 nv_minor);
    93  
    94  		/* Stop trying to find devices after one is not found. In this manner,
    95  		 * we'll add /dev/nvidia0 and /dev/nvidia1 but stop trying to find
    96  		 * nvidia3 - nvidia254 if nvidia2 is not found. */
    97  		if (stat(nv_path, &sbuf) < 0) {
    98  			break;
    99  		}
   100  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   101  				       minor(sbuf.st_rdev));
   102  	}
   103  
   104  	if (stat("/dev/nvidiactl", &sbuf) == 0) {
   105  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   106  				       minor(sbuf.st_rdev));
   107  	}
   108  	if (stat("/dev/nvidia-uvm", &sbuf) == 0) {
   109  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   110  				       minor(sbuf.st_rdev));
   111  	}
   112  	if (stat("/dev/nvidia-modeset", &sbuf) == 0) {
   113  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   114  				       minor(sbuf.st_rdev));
   115  	}
   116  }
   117  
   118  /**
   119   * Allow access to /dev/uhid.
   120   *
   121   * Currently /dev/uhid isn't represented in sysfs, so add it to the device
   122   * cgroup if it exists and let AppArmor handle the mediation.
   123   **/
   124  static void sc_udev_allow_uhid(sc_device_cgroup * cgroup)
   125  {
   126  	struct stat sbuf;
   127  
   128  	if (stat("/dev/uhid", &sbuf) == 0) {
   129  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   130  				       minor(sbuf.st_rdev));
   131  	}
   132  }
   133  
   134  /**
   135   * Allow access to /dev/net/tun
   136   *
   137   * When CONFIG_TUN=m, /dev/net/tun will exist but using it doesn't
   138   * autoload the tun module but also /dev/net/tun isn't udev tagged
   139   * until it is loaded. To work around this, if /dev/net/tun exists, add
   140   * it unconditionally to the cgroup and rely on AppArmor to mediate the
   141   * access. LP: #1859084
   142   **/
   143  static void sc_udev_allow_dev_net_tun(sc_device_cgroup * cgroup)
   144  {
   145  	struct stat sbuf;
   146  
   147  	if (stat("/dev/net/tun", &sbuf) == 0) {
   148  		sc_device_cgroup_allow(cgroup, S_IFCHR, major(sbuf.st_rdev),
   149  				       minor(sbuf.st_rdev));
   150  	}
   151  }
   152  
   153  /**
   154   * Allow access to assigned devices.
   155   *
   156   * The snapd udev security backend uses udev rules to tag matching devices with
   157   * tags corresponding to snap applications. Here we interrogate udev and allow
   158   * access to all assigned devices.
   159   **/
   160  static void sc_udev_allow_assigned_device(sc_device_cgroup * cgroup,
   161  					  struct udev_device *device)
   162  {
   163  	const char *path = udev_device_get_syspath(device);
   164  	dev_t devnum = udev_device_get_devnum(device);
   165  	unsigned int major = major(devnum);
   166  	unsigned int minor = minor(devnum);
   167  	/* The manual page of udev_device_get_devnum says:
   168  	 * > On success, udev_device_get_devnum() returns the device type of
   169  	 * > the passed device. On failure, a device type with minor and major
   170  	 * > number set to 0 is returned. */
   171  	if (major == 0 && minor == 0) {
   172  		debug("cannot get major/minor numbers for syspath %s", path);
   173  		return;
   174  	}
   175  	/* devnode is bound to the lifetime of the device and we cannot release
   176  	 * it separately. */
   177  	const char *devnode = udev_device_get_devnode(device);
   178  	if (devnode == NULL) {
   179  		debug("cannot find /dev node from udev device");
   180  		return;
   181  	}
   182  	debug("inspecting type of device: %s", devnode);
   183  	struct stat file_info;
   184  	if (stat(devnode, &file_info) < 0) {
   185  		debug("cannot stat %s", devnode);
   186  		return;
   187  	}
   188  	int devtype = file_info.st_mode & S_IFMT;
   189  	if (devtype == S_IFBLK || devtype == S_IFCHR) {
   190  		sc_device_cgroup_allow(cgroup, devtype, major, minor);
   191  	}
   192  }
   193  
   194  static void sc_udev_setup_acls_common(sc_device_cgroup * cgroup)
   195  {
   196  
   197  	/* Allow access to various devices. */
   198  	sc_udev_allow_common(cgroup);
   199  	sc_udev_allow_pty_slaves(cgroup);
   200  	sc_udev_allow_nvidia(cgroup);
   201  	sc_udev_allow_uhid(cgroup);
   202  	sc_udev_allow_dev_net_tun(cgroup);
   203  }
   204  
   205  static char *sc_security_to_udev_tag(const char *security_tag)
   206  {
   207  	char *udev_tag = sc_strdup(security_tag);
   208  	for (char *c = strchr(udev_tag, '.'); c != NULL; c = strchr(c, '.')) {
   209  		*c = '_';
   210  	}
   211  	return udev_tag;
   212  }
   213  
   214  static void sc_cleanup_udev(struct udev **udev)
   215  {
   216  	if (udev != NULL && *udev != NULL) {
   217  		udev_unref(*udev);
   218  		*udev = NULL;
   219  	}
   220  }
   221  
   222  static void sc_cleanup_udev_enumerate(struct udev_enumerate **enumerate)
   223  {
   224  	if (enumerate != NULL && *enumerate != NULL) {
   225  		udev_enumerate_unref(*enumerate);
   226  		*enumerate = NULL;
   227  	}
   228  }
   229  
   230  void sc_setup_device_cgroup(const char *security_tag)
   231  {
   232  	debug("setting up device cgroup");
   233  
   234  	/* Derive the udev tag from the snap security tag.
   235  	 *
   236  	 * Because udev does not allow for dots in tag names, those are replaced by
   237  	 * underscores in snapd. We just match that behavior. */
   238  	char *udev_tag SC_CLEANUP(sc_cleanup_string) = NULL;
   239  	udev_tag = sc_security_to_udev_tag(security_tag);
   240  
   241  	/* Use udev APIs to talk to udev-the-daemon to determine the list of
   242  	 * "devices" with that tag assigned. The list may be empty, in which case
   243  	 * there's no udev tagging in effect and we must refrain from constructing
   244  	 * the cgroup as it would interfere with the execution of a program. */
   245  	struct udev SC_CLEANUP(sc_cleanup_udev) * udev = NULL;
   246  	udev = udev_new();
   247  	if (udev == NULL) {
   248  		die("cannot connect to udev");
   249  	}
   250  	struct udev_enumerate SC_CLEANUP(sc_cleanup_udev_enumerate) * devices =
   251  	    NULL;
   252  	devices = udev_enumerate_new(udev);
   253  	if (devices == NULL) {
   254  		die("cannot create udev device enumeration");
   255  	}
   256  	if (udev_enumerate_add_match_tag(devices, udev_tag) < 0) {
   257  		die("cannot add tag match to udev device enumeration");
   258  	}
   259  	if (udev_enumerate_scan_devices(devices) < 0) {
   260  		die("cannot enumerate udev devices");
   261  	}
   262  	/* NOTE: udev_list_entry is bound to life-cycle of the used udev_enumerate */
   263  	struct udev_list_entry *assigned;
   264  	assigned = udev_enumerate_get_list_entry(devices);
   265  	if (assigned == NULL) {
   266  		/* NOTE: Nothing is assigned, don't create or use the device cgroup. */
   267  		debug("no devices tagged with %s, skipping device cgroup setup",
   268  		      udev_tag);
   269  		return;
   270  	}
   271  
   272  	/* Note that -1 is the neutral value for a file descriptor.
   273  	 * The cleanup function associated with this variable closes
   274  	 * descriptors other than -1. */
   275  	sc_device_cgroup *cgroup SC_CLEANUP(sc_device_cgroup_cleanup) =
   276  	    sc_device_cgroup_new(security_tag, 0);
   277  	/* Setup the device group access control list */
   278  	sc_udev_setup_acls_common(cgroup);
   279  	for (struct udev_list_entry * entry = assigned; entry != NULL;
   280  	     entry = udev_list_entry_get_next(entry)) {
   281  		const char *path = udev_list_entry_get_name(entry);
   282  		if (path == NULL) {
   283  			die("udev_list_entry_get_name failed");
   284  		}
   285  		struct udev_device *device =
   286  		    udev_device_new_from_syspath(udev, path);
   287  		/** This is a non-fatal error as devices can disappear asynchronously
   288  		 * and on slow devices we may indeed observe a device that no longer
   289  		 * exists.
   290  		 *
   291  		 * Similar debug + continue pattern repeats in all the udev calls in
   292  		 * this function. Related to LP: #1881209 */
   293  		if (device == NULL) {
   294  			debug("cannot find device from syspath %s", path);
   295  			continue;
   296  		}
   297  
   298  		sc_udev_allow_assigned_device(cgroup, device);
   299  		udev_device_unref(device);
   300  	}
   301  	/* Move ourselves to the device cgroup */
   302  	sc_device_cgroup_attach_pid(cgroup, getpid());
   303  	debug("associated snap application process %i with device cgroup %s",
   304  	      getpid(), security_tag);
   305  }