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 }