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 }