|
25 | 25 | #include <errno.h>
|
26 | 26 | #include <fcntl.h>
|
27 | 27 | #include <glob.h>
|
| 28 | +#include <libgen.h> |
28 | 29 | #include <pwd.h>
|
29 | 30 | #ifndef _BSD_SOURCE
|
30 | 31 | #define _BSD_SOURCE
|
@@ -67,56 +68,24 @@ static DevTypeStr dev_type_str[] = {
|
67 | 68 | };
|
68 | 69 |
|
69 | 70 | typedef struct {
|
70 |
| - const char *dev_fname; |
71 |
| - const char *run_fname; |
| 71 | + const char *dev_pattern; |
| 72 | + const char *run_pattern; |
72 | 73 | DEV_TYPE type;
|
73 | 74 | } DevEntry;
|
74 | 75 |
|
75 | 76 | static DevEntry dev[] = {
|
76 | 77 | {"/dev/snd", RUN_DEV_DIR "/snd", DEV_SOUND}, // sound device
|
77 | 78 | {"/dev/dri", RUN_DEV_DIR "/dri", DEV_3D}, // 3d devices
|
78 | 79 | {"/dev/kfd", RUN_DEV_DIR "/kfd", DEV_3D},
|
79 |
| - {"/dev/nvidia0", RUN_DEV_DIR "/nvidia0", DEV_3D}, |
80 |
| - {"/dev/nvidia1", RUN_DEV_DIR "/nvidia1", DEV_3D}, |
81 |
| - {"/dev/nvidia2", RUN_DEV_DIR "/nvidia2", DEV_3D}, |
82 |
| - {"/dev/nvidia3", RUN_DEV_DIR "/nvidia3", DEV_3D}, |
83 |
| - {"/dev/nvidia4", RUN_DEV_DIR "/nvidia4", DEV_3D}, |
84 |
| - {"/dev/nvidia5", RUN_DEV_DIR "/nvidia5", DEV_3D}, |
85 |
| - {"/dev/nvidia6", RUN_DEV_DIR "/nvidia6", DEV_3D}, |
86 |
| - {"/dev/nvidia7", RUN_DEV_DIR "/nvidia7", DEV_3D}, |
87 |
| - {"/dev/nvidia8", RUN_DEV_DIR "/nvidia8", DEV_3D}, |
88 |
| - {"/dev/nvidia9", RUN_DEV_DIR "/nvidia9", DEV_3D}, |
| 80 | + {"/dev/nvidia[0-9]*", RUN_DEV_DIR "/nvidia[0-9]*", DEV_3D}, |
89 | 81 | {"/dev/nvidiactl", RUN_DEV_DIR "/nvidiactl", DEV_3D},
|
90 | 82 | {"/dev/nvidia-modeset", RUN_DEV_DIR "/nvidia-modeset", DEV_3D},
|
91 | 83 | {"/dev/nvidia-uvm", RUN_DEV_DIR "/nvidia-uvm", DEV_3D},
|
92 |
| - {"/dev/video0", RUN_DEV_DIR "/video0", DEV_VIDEO}, // video camera devices |
93 |
| - {"/dev/video1", RUN_DEV_DIR "/video1", DEV_VIDEO}, |
94 |
| - {"/dev/video2", RUN_DEV_DIR "/video2", DEV_VIDEO}, |
95 |
| - {"/dev/video3", RUN_DEV_DIR "/video3", DEV_VIDEO}, |
96 |
| - {"/dev/video4", RUN_DEV_DIR "/video4", DEV_VIDEO}, |
97 |
| - {"/dev/video5", RUN_DEV_DIR "/video5", DEV_VIDEO}, |
98 |
| - {"/dev/video6", RUN_DEV_DIR "/video6", DEV_VIDEO}, |
99 |
| - {"/dev/video7", RUN_DEV_DIR "/video7", DEV_VIDEO}, |
100 |
| - {"/dev/video8", RUN_DEV_DIR "/video8", DEV_VIDEO}, |
101 |
| - {"/dev/video9", RUN_DEV_DIR "/video9", DEV_VIDEO}, |
| 84 | + {"/dev/video[0-9]*", RUN_DEV_DIR "/video[0-9]*", DEV_VIDEO}, // video camera devices |
102 | 85 | {"/dev/dvb", RUN_DEV_DIR "/dvb", DEV_TV}, // DVB (Digital Video Broadcasting) - TV device
|
103 |
| - {"/dev/sr0", RUN_DEV_DIR "/sr0", DEV_DVD}, // for DVD and audio CD players |
104 |
| - {"/dev/tpm0", RUN_DEV_DIR "/tpm0", DEV_TPM}, // TPM (Trusted Platform Module) devices |
105 |
| - {"/dev/tpm1", RUN_DEV_DIR "/tpm1", DEV_TPM}, |
106 |
| - {"/dev/tpm2", RUN_DEV_DIR "/tpm2", DEV_TPM}, |
107 |
| - {"/dev/tpm3", RUN_DEV_DIR "/tpm3", DEV_TPM}, |
108 |
| - {"/dev/tpm4", RUN_DEV_DIR "/tpm4", DEV_TPM}, |
109 |
| - {"/dev/tpm5", RUN_DEV_DIR "/tpm5", DEV_TPM}, |
110 |
| - {"/dev/hidraw0", RUN_DEV_DIR "/hidraw0", DEV_U2F}, |
111 |
| - {"/dev/hidraw1", RUN_DEV_DIR "/hidraw1", DEV_U2F}, |
112 |
| - {"/dev/hidraw2", RUN_DEV_DIR "/hidraw2", DEV_U2F}, |
113 |
| - {"/dev/hidraw3", RUN_DEV_DIR "/hidraw3", DEV_U2F}, |
114 |
| - {"/dev/hidraw4", RUN_DEV_DIR "/hidraw4", DEV_U2F}, |
115 |
| - {"/dev/hidraw5", RUN_DEV_DIR "/hidraw5", DEV_U2F}, |
116 |
| - {"/dev/hidraw6", RUN_DEV_DIR "/hidraw6", DEV_U2F}, |
117 |
| - {"/dev/hidraw7", RUN_DEV_DIR "/hidraw7", DEV_U2F}, |
118 |
| - {"/dev/hidraw8", RUN_DEV_DIR "/hidraw8", DEV_U2F}, |
119 |
| - {"/dev/hidraw9", RUN_DEV_DIR "/hidraw9", DEV_U2F}, |
| 86 | + {"/dev/sr[0-9]*", RUN_DEV_DIR "/sr[0-9]*", DEV_DVD}, // for DVD and audio CD players |
| 87 | + {"/dev/tpm[0-9]*", RUN_DEV_DIR "/tpm[0-9]*", DEV_TPM}, // TPM (Trusted Platform Module) devices |
| 88 | + {"/dev/hidraw[0-9]*", RUN_DEV_DIR "/hidraw[0-9]*", DEV_U2F}, |
120 | 89 | {"/dev/usb", RUN_DEV_DIR "/usb", DEV_U2F}, // USB devices such as Yubikey, U2F
|
121 | 90 | {"/dev/input", RUN_DEV_DIR "/input", DEV_INPUT},
|
122 | 91 | {"/dev/ntsync", RUN_DEV_DIR "/ntsync", DEV_NTSYNC},
|
@@ -196,11 +165,97 @@ static void deventry_mount(const char *source,
|
196 | 165 | fs_logger2("whitelist", target);
|
197 | 166 | }
|
198 | 167 |
|
| 168 | +// For every path in source_pattern, mount it on the dirname of target_pattern. |
| 169 | +// |
| 170 | +// Example: |
| 171 | +// |
| 172 | +// deventry_mount_glob("/run/foo*", "/dev/foo*") -> |
| 173 | +// mount("/run/foo1", "/dev/foo1") |
| 174 | +// mount("/run/foo2", "/dev/foo2") |
| 175 | +// ... |
| 176 | +static void deventry_mount_glob(const char *source_pattern, |
| 177 | + const char *target_pattern, DEV_TYPE type) { |
| 178 | + assert(source_pattern); |
| 179 | + assert(target_pattern); |
| 180 | + assert(type >= DEV_NONE && type < DEV_MAX); |
| 181 | + |
| 182 | + const char *typestr = dev_type_str[type].str; |
| 183 | + if (arg_debug) { |
| 184 | + printf("Globbing %s on %s (type=%s)\n", source_pattern, |
| 185 | + target_pattern, typestr); |
| 186 | + } |
| 187 | + |
| 188 | + // sanity check to avoid arguments like ("/run/foo*", "/dev/bar*") |
| 189 | + const char *source_pattern_base = gnu_basename(source_pattern); |
| 190 | + const char *target_pattern_base = gnu_basename(target_pattern); |
| 191 | + if (strcmp(source_pattern_base, target_pattern_base) != 0) { |
| 192 | + fprintf(stderr, "Error: patterns do not match: %s / %s (type=%s)\n", |
| 193 | + source_pattern_base, target_pattern_base, typestr); |
| 194 | + exit(1); |
| 195 | + } |
| 196 | + |
| 197 | + EUID_USER(); |
| 198 | + glob_t globbuf; |
| 199 | + int globerr = glob(source_pattern, 0, NULL, &globbuf); |
| 200 | + EUID_ROOT(); |
| 201 | + if (globerr == GLOB_NOMATCH) { |
| 202 | + if (arg_debug) { |
| 203 | + printf("No match %s (type=%s)\n", source_pattern, |
| 204 | + typestr); |
| 205 | + } |
| 206 | + return; |
| 207 | + } else if (globerr) { |
| 208 | + fwarning("failed to glob pattern %s (type=%s): %s\n", |
| 209 | + source_pattern, typestr, strerror(errno)); |
| 210 | + return; |
| 211 | + } |
| 212 | + |
| 213 | + // strdup for dirname |
| 214 | + char *tmp = strdup(target_pattern); |
| 215 | + if (!tmp) |
| 216 | + errExit("strdup"); |
| 217 | + const char *target_dir = dirname(tmp); |
| 218 | + if (strcmp(target_dir, ".") == 0 || |
| 219 | + strcmp(target_dir, "..") == 0) { |
| 220 | + fprintf(stderr, "Error: invalid target_dir: %s -> %s (type=%s)\n", |
| 221 | + target_pattern, target_dir, typestr); |
| 222 | + exit(1); |
| 223 | + } |
| 224 | + |
| 225 | + size_t i; |
| 226 | + for (i = 0; i < globbuf.gl_pathc; i++) { |
| 227 | + const char *source = globbuf.gl_pathv[i]; |
| 228 | + assert(source); |
| 229 | + |
| 230 | + const char *source_base = gnu_basename(source); |
| 231 | + if (strcmp(source_base, ".") == 0 || |
| 232 | + strcmp(source_base, "..") == 0) { |
| 233 | + fprintf(stderr, "Error: invalid source_base: %s -> %s -> %s (type=%s)\n", |
| 234 | + source_pattern, source, source_base, typestr); |
| 235 | + exit(1); |
| 236 | + } |
| 237 | + |
| 238 | + char *target = NULL; |
| 239 | + if (asprintf(&target, "%s/%s", target_dir, source_base) == -1) |
| 240 | + errExit("asprintf"); |
| 241 | + |
| 242 | + deventry_mount(source, target, type); |
| 243 | + free(target); |
| 244 | + } |
| 245 | + |
| 246 | + free(tmp); |
| 247 | + globfree(&globbuf); |
| 248 | +} |
| 249 | + |
| 250 | +// Note: By the time that this function is called for private-dev, RUN_DEV_DIR |
| 251 | +// points to the real /dev directory and tmpfs is mounted on top of /dev, so |
| 252 | +// run_pattern is the source path and dev_pattern is the target path when |
| 253 | +// bind-mounting. |
199 | 254 | static void deventry_mount_all(void) {
|
200 | 255 | int i = 0;
|
201 |
| - while (dev[i].dev_fname != NULL) { |
202 |
| - deventry_mount(dev[i].run_fname, dev[i].dev_fname, |
203 |
| - dev[i].type); |
| 256 | + while (dev[i].dev_pattern != NULL) { |
| 257 | + deventry_mount_glob(dev[i].run_pattern, dev[i].dev_pattern, |
| 258 | + dev[i].type); |
204 | 259 | i++;
|
205 | 260 | }
|
206 | 261 | }
|
@@ -382,102 +437,127 @@ void fs_private_dev(void) {
|
382 | 437 | }
|
383 | 438 | }
|
384 | 439 |
|
385 |
| -void fs_dev_disable_sound(void) { |
386 |
| - unsigned i = 0; |
387 |
| - while (dev[i].dev_fname != NULL) { |
388 |
| - if (dev[i].type == DEV_SOUND) |
389 |
| - disable_file_or_dir(dev[i].dev_fname); |
390 |
| - i++; |
| 440 | +void fs_dev_disable_glob(const char *pattern, DEV_TYPE type, |
| 441 | + int skip_symlinks) { |
| 442 | + assert(pattern); |
| 443 | + assert(type >= DEV_NONE && type < DEV_MAX); |
| 444 | + |
| 445 | + const char *typestr = dev_type_str[type].str; |
| 446 | + if (arg_debug) { |
| 447 | + printf("Globbing %s (type=%s skip_symlinks=%d)\n", pattern, |
| 448 | + typestr, skip_symlinks); |
391 | 449 | }
|
392 | 450 |
|
393 |
| - // disable all jack sockets in /dev/shm |
394 | 451 | EUID_USER();
|
395 | 452 | glob_t globbuf;
|
396 |
| - int globerr = glob("/dev/shm/jack*", GLOB_NOSORT, NULL, &globbuf); |
| 453 | + int globerr = glob(pattern, 0, NULL, &globbuf); |
397 | 454 | EUID_ROOT();
|
398 |
| - if (globerr) |
| 455 | + if (globerr == GLOB_NOMATCH) { |
| 456 | + if (arg_debug) |
| 457 | + printf("No match %s (type=%s)\n", pattern, typestr); |
| 458 | + return; |
| 459 | + } else if (globerr) { |
| 460 | + fwarning("failed to glob pattern %s (type=%s): %s\n", pattern, |
| 461 | + typestr, strerror(errno)); |
399 | 462 | return;
|
| 463 | + } |
400 | 464 |
|
| 465 | + size_t i; |
401 | 466 | for (i = 0; i < globbuf.gl_pathc; i++) {
|
402 | 467 | char *path = globbuf.gl_pathv[i];
|
403 | 468 | assert(path);
|
404 |
| - if (is_link(path)) { |
405 |
| - fwarning("skipping nosound for %s because it is a symbolic link\n", path); |
| 469 | + |
| 470 | + if (skip_symlinks && is_link(path)) { |
| 471 | + fwarning("skipping %s because it is a symbolic link (type=%s)\n", |
| 472 | + pattern, path); |
406 | 473 | continue;
|
407 | 474 | }
|
408 | 475 | disable_file_or_dir(path);
|
409 | 476 | }
|
| 477 | + |
410 | 478 | globfree(&globbuf);
|
411 | 479 | }
|
412 | 480 |
|
| 481 | +void fs_dev_disable_sound(void) { |
| 482 | + int i = 0; |
| 483 | + while (dev[i].dev_pattern != NULL) { |
| 484 | + if (dev[i].type == DEV_SOUND) |
| 485 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_SOUND, 0); |
| 486 | + i++; |
| 487 | + } |
| 488 | + |
| 489 | + // disable all jack sockets in /dev/shm |
| 490 | + fs_dev_disable_glob("/dev/shm/jack*", DEV_SOUND, 1); |
| 491 | +} |
| 492 | + |
413 | 493 | void fs_dev_disable_video(void) {
|
414 | 494 | int i = 0;
|
415 |
| - while (dev[i].dev_fname != NULL) { |
| 495 | + while (dev[i].dev_pattern != NULL) { |
416 | 496 | if (dev[i].type == DEV_VIDEO)
|
417 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 497 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_VIDEO, 0); |
418 | 498 | i++;
|
419 | 499 | }
|
420 | 500 | }
|
421 | 501 |
|
422 | 502 | void fs_dev_disable_3d(void) {
|
423 | 503 | int i = 0;
|
424 |
| - while (dev[i].dev_fname != NULL) { |
| 504 | + while (dev[i].dev_pattern != NULL) { |
425 | 505 | if (dev[i].type == DEV_3D)
|
426 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 506 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_3D, 0); |
427 | 507 | i++;
|
428 | 508 | }
|
429 | 509 | }
|
430 | 510 |
|
431 | 511 | void fs_dev_disable_tv(void) {
|
432 | 512 | int i = 0;
|
433 |
| - while (dev[i].dev_fname != NULL) { |
| 513 | + while (dev[i].dev_pattern != NULL) { |
434 | 514 | if (dev[i].type == DEV_TV)
|
435 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 515 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_TV, 0); |
436 | 516 | i++;
|
437 | 517 | }
|
438 | 518 | }
|
439 | 519 |
|
440 | 520 | void fs_dev_disable_dvd(void) {
|
441 | 521 | int i = 0;
|
442 |
| - while (dev[i].dev_fname != NULL) { |
| 522 | + while (dev[i].dev_pattern != NULL) { |
443 | 523 | if (dev[i].type == DEV_DVD)
|
444 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 524 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_DVD, 0); |
445 | 525 | i++;
|
446 | 526 | }
|
447 | 527 | }
|
448 | 528 |
|
449 | 529 | void fs_dev_disable_tpm(void) {
|
450 | 530 | int i = 0;
|
451 |
| - while (dev[i].dev_fname != NULL) { |
| 531 | + while (dev[i].dev_pattern != NULL) { |
452 | 532 | if (dev[i].type == DEV_TPM)
|
453 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 533 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_TPM, 0); |
454 | 534 | i++;
|
455 | 535 | }
|
456 | 536 | }
|
457 | 537 |
|
458 | 538 | void fs_dev_disable_u2f(void) {
|
459 | 539 | int i = 0;
|
460 |
| - while (dev[i].dev_fname != NULL) { |
| 540 | + while (dev[i].dev_pattern != NULL) { |
461 | 541 | if (dev[i].type == DEV_U2F)
|
462 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 542 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_U2F, 0); |
463 | 543 | i++;
|
464 | 544 | }
|
465 | 545 | }
|
466 | 546 |
|
467 | 547 | void fs_dev_disable_input(void) {
|
468 | 548 | int i = 0;
|
469 |
| - while (dev[i].dev_fname != NULL) { |
| 549 | + while (dev[i].dev_pattern != NULL) { |
470 | 550 | if (dev[i].type == DEV_INPUT)
|
471 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 551 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_INPUT, 0); |
472 | 552 | i++;
|
473 | 553 | }
|
474 | 554 | }
|
475 | 555 |
|
476 | 556 | void fs_dev_disable_ntsync(void) {
|
477 | 557 | int i = 0;
|
478 |
| - while (dev[i].dev_fname != NULL) { |
| 558 | + while (dev[i].dev_pattern != NULL) { |
479 | 559 | if (dev[i].type == DEV_NTSYNC)
|
480 |
| - disable_file_or_dir(dev[i].dev_fname); |
| 560 | + fs_dev_disable_glob(dev[i].dev_pattern, DEV_NTSYNC, 0); |
481 | 561 | i++;
|
482 | 562 | }
|
483 | 563 | }
|
0 commit comments