aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/profile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/profile.c')
-rw-r--r--src/firejail/profile.c380
1 files changed, 353 insertions, 27 deletions
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 6ded0ca2f..0fd45d1ef 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -105,7 +105,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
105 // mkdir 105 // mkdir
106 if (strncmp(ptr, "mkdir ", 6) == 0) { 106 if (strncmp(ptr, "mkdir ", 6) == 0) {
107 fs_mkdir(ptr + 6); 107 fs_mkdir(ptr + 6);
108 return 0; 108 return 1; // process mkdir again while applying blacklists
109 }
110 // mkfile
111 if (strncmp(ptr, "mkfile ", 7) == 0) {
112 fs_mkfile(ptr + 7);
113 return 1; // process mkfile again while applying blacklists
109 } 114 }
110 // sandbox name 115 // sandbox name
111 else if (strncmp(ptr, "name ", 5) == 0) { 116 else if (strncmp(ptr, "name ", 5) == 0) {
@@ -131,6 +136,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
131 136
132 return 0; 137 return 0;
133 } 138 }
139 else if (strcmp(ptr, "nonewprivs") == 0) {
140 arg_nonewprivs = 1;
141 return 0;
142 }
134 else if (strcmp(ptr, "seccomp") == 0) { 143 else if (strcmp(ptr, "seccomp") == 0) {
135#ifdef HAVE_SECCOMP 144#ifdef HAVE_SECCOMP
136 if (checkcfg(CFG_SECCOMP)) 145 if (checkcfg(CFG_SECCOMP))
@@ -160,6 +169,22 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
160 arg_private = 1; 169 arg_private = 1;
161 return 0; 170 return 0;
162 } 171 }
172 if (strncmp(ptr, "private-home ", 13) == 0) {
173#ifdef HAVE_PRIVATE_HOME
174 if (checkcfg(CFG_PRIVATE_HOME)) {
175 cfg.home_private_keep = ptr + 13;
176 fs_check_home_list();
177 arg_private = 1;
178 }
179 else
180 fprintf(stderr, "Warning: private-home is disabled in Firejail configuration file\n");
181#endif
182 return 0;
183 }
184 else if (strcmp(ptr, "allusers") == 0) {
185 arg_allusers = 1;
186 return 0;
187 }
163 else if (strcmp(ptr, "private-dev") == 0) { 188 else if (strcmp(ptr, "private-dev") == 0) {
164 arg_private_dev = 1; 189 arg_private_dev = 1;
165 return 0; 190 return 0;
@@ -174,7 +199,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
174 } 199 }
175 else if (strcmp(ptr, "nosound") == 0) { 200 else if (strcmp(ptr, "nosound") == 0) {
176 arg_nosound = 1; 201 arg_nosound = 1;
177 arg_private_dev = 1; 202 return 0;
203 }
204 else if (strcmp(ptr, "no3d") == 0) {
205 arg_no3d = 1;
178 return 0; 206 return 0;
179 } 207 }
180 else if (strcmp(ptr, "netfilter") == 0) { 208 else if (strcmp(ptr, "netfilter") == 0) {
@@ -274,6 +302,29 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
274 return 0; 302 return 0;
275 } 303 }
276 304
305 else if (strncmp(ptr, "veth-name ", 10) == 0) {
306#ifdef HAVE_NETWORK
307 if (checkcfg(CFG_NETWORK)) {
308 Bridge *br = last_bridge_configured();
309 if (br == NULL) {
310 fprintf(stderr, "Error: no network device configured\n");
311 exit(1);
312 }
313
314 br->veth_name = strdup(ptr + 10);
315 if (br->veth_name == NULL)
316 errExit("strdup");
317 if (*br->veth_name == '\0') {
318 fprintf(stderr, "Error: no veth-name configured\n");
319 exit(1);
320 }
321 }
322 else
323 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
324#endif
325 return 0;
326 }
327
277 else if (strncmp(ptr, "iprange ", 8) == 0) { 328 else if (strncmp(ptr, "iprange ", 8) == 0) {
278#ifdef HAVE_NETWORK 329#ifdef HAVE_NETWORK
279 if (checkcfg(CFG_NETWORK)) { 330 if (checkcfg(CFG_NETWORK)) {
@@ -319,11 +370,145 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
319 return 0; 370 return 0;
320 } 371 }
321 372
322 373
374 else if (strncmp(ptr, "mac ", 4) == 0) {
375#ifdef HAVE_NETWORK
376 if (checkcfg(CFG_NETWORK)) {
377 Bridge *br = last_bridge_configured();
378 if (br == NULL) {
379 fprintf(stderr, "Error: no network device configured\n");
380 exit(1);
381 }
382
383 if (mac_not_zero(br->macsandbox)) {
384 fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n");
385 exit(1);
386 }
387
388 // read the address
389 if (atomac(ptr + 4, br->macsandbox)) {
390 fprintf(stderr, "Error: invalid MAC address\n");
391 exit(1);
392 }
393 }
394 else
395 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
396#endif
397 return 0;
398 }
399
400 else if (strncmp(ptr, "mtu ", 4) == 0) {
401#ifdef HAVE_NETWORK
402 if (checkcfg(CFG_NETWORK)) {
403 Bridge *br = last_bridge_configured();
404 if (br == NULL) {
405 fprintf(stderr, "Error: no network device configured\n");
406 exit(1);
407 }
408
409 if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) {
410 fprintf(stderr, "Error: invalid mtu value\n");
411 exit(1);
412 }
413 }
414 else
415 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
416#endif
417 return 0;
418 }
419
420 else if (strncmp(ptr, "ip ", 3) == 0) {
421#ifdef HAVE_NETWORK
422 if (checkcfg(CFG_NETWORK)) {
423 Bridge *br = last_bridge_configured();
424 if (br == NULL) {
425 fprintf(stderr, "Error: no network device configured\n");
426 exit(1);
427 }
428 if (br->arg_ip_none || br->ipsandbox) {
429 fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
430 exit(1);
431 }
432
433 // configure this IP address for the last bridge defined
434 if (strcmp(ptr + 3, "none") == 0)
435 br->arg_ip_none = 1;
436 else {
437 if (atoip(ptr + 3, &br->ipsandbox)) {
438 fprintf(stderr, "Error: invalid IP address\n");
439 exit(1);
440 }
441 }
442 }
443 else
444 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
445#endif
446 return 0;
447 }
448
449 else if (strncmp(ptr, "ip6 ", 4) == 0) {
450#ifdef HAVE_NETWORK
451 if (checkcfg(CFG_NETWORK)) {
452 Bridge *br = last_bridge_configured();
453 if (br == NULL) {
454 fprintf(stderr, "Error: no network device configured\n");
455 exit(1);
456 }
457 if (br->arg_ip_none || br->ip6sandbox) {
458 fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
459 exit(1);
460 }
461
462 // configure this IP address for the last bridge defined
463 // todo: verify ipv6 syntax
464 br->ip6sandbox = ptr + 4;
465// if (atoip(argv[i] + 5, &br->ipsandbox)) {
466// fprintf(stderr, "Error: invalid IP address\n");
467// exit(1);
468// }
469
470 }
471 else
472 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
473#endif
474 return 0;
475 }
476
477 else if (strncmp(ptr, "defaultgw ", 10) == 0) {
478#ifdef HAVE_NETWORK
479 if (checkcfg(CFG_NETWORK)) {
480 if (atoip(ptr + 10, &cfg.defaultgw)) {
481 fprintf(stderr, "Error: invalid IP address\n");
482 exit(1);
483 }
484 }
485 else
486 fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
487#endif
488 return 0;
489 }
490
491 if (strcmp(ptr, "apparmor") == 0) {
492#ifdef HAVE_APPARMOR
493 arg_apparmor = 1;
494#endif
495 return 0;
496 }
497
323 if (strncmp(ptr, "protocol ", 9) == 0) { 498 if (strncmp(ptr, "protocol ", 9) == 0) {
324#ifdef HAVE_SECCOMP 499#ifdef HAVE_SECCOMP
325 if (checkcfg(CFG_SECCOMP)) 500 if (checkcfg(CFG_SECCOMP)) {
326 protocol_store(ptr + 9); 501 if (cfg.protocol) {
502 if (!arg_quiet)
503 fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", ptr + 9);
504 return 0;
505 }
506
507 // store list
508 cfg.protocol = strdup(ptr + 9);
509 if (!cfg.protocol)
510 errExit("strdup");
511 }
327 else 512 else
328 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); 513 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
329#endif 514#endif
@@ -331,7 +516,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
331 } 516 }
332 517
333 if (strncmp(ptr, "env ", 4) == 0) { 518 if (strncmp(ptr, "env ", 4) == 0) {
334 env_store(ptr + 4); 519 env_store(ptr + 4, SETENV);
520 return 0;
521 }
522 if (strncmp(ptr, "rmenv ", 6) == 0) {
523 env_store(ptr + 6, RMENV);
335 return 0; 524 return 0;
336 } 525 }
337 526
@@ -340,9 +529,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
340#ifdef HAVE_SECCOMP 529#ifdef HAVE_SECCOMP
341 if (checkcfg(CFG_SECCOMP)) { 530 if (checkcfg(CFG_SECCOMP)) {
342 arg_seccomp = 1; 531 arg_seccomp = 1;
343 cfg.seccomp_list = strdup(ptr + 8); 532 cfg.seccomp_list = seccomp_check_list(ptr + 8);
344 if (!cfg.seccomp_list)
345 errExit("strdup");
346 } 533 }
347 else 534 else
348 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); 535 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@@ -356,9 +543,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
356#ifdef HAVE_SECCOMP 543#ifdef HAVE_SECCOMP
357 if (checkcfg(CFG_SECCOMP)) { 544 if (checkcfg(CFG_SECCOMP)) {
358 arg_seccomp = 1; 545 arg_seccomp = 1;
359 cfg.seccomp_list_drop = strdup(ptr + 13); 546 cfg.seccomp_list_drop = seccomp_check_list(ptr + 13);
360 if (!cfg.seccomp_list_drop)
361 errExit("strdup");
362 } 547 }
363 else 548 else
364 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); 549 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@@ -371,9 +556,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
371#ifdef HAVE_SECCOMP 556#ifdef HAVE_SECCOMP
372 if (checkcfg(CFG_SECCOMP)) { 557 if (checkcfg(CFG_SECCOMP)) {
373 arg_seccomp = 1; 558 arg_seccomp = 1;
374 cfg.seccomp_list_keep= strdup(ptr + 13); 559 cfg.seccomp_list_keep= seccomp_check_list(ptr + 13);
375 if (!cfg.seccomp_list_keep)
376 errExit("strdup");
377 } 560 }
378 else 561 else
379 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); 562 fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@@ -387,7 +570,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
387 arg_caps_list = strdup(ptr + 10); 570 arg_caps_list = strdup(ptr + 10);
388 if (!arg_caps_list) 571 if (!arg_caps_list)
389 errExit("strdup"); 572 errExit("strdup");
390 // verify seccomp list and exit if problems 573 // verify caps list and exit if problems
391 if (caps_check_list(arg_caps_list, NULL)) 574 if (caps_check_list(arg_caps_list, NULL))
392 exit(1); 575 exit(1);
393 return 0; 576 return 0;
@@ -399,7 +582,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
399 arg_caps_list = strdup(ptr + 10); 582 arg_caps_list = strdup(ptr + 10);
400 if (!arg_caps_list) 583 if (!arg_caps_list)
401 errExit("strdup"); 584 errExit("strdup");
402 // verify seccomp list and exit if problems 585 // verify caps list and exit if problems
403 if (caps_check_list(arg_caps_list, NULL)) 586 if (caps_check_list(arg_caps_list, NULL))
404 exit(1); 587 exit(1);
405 return 0; 588 return 0;
@@ -441,6 +624,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
441 // nice value 624 // nice value
442 if (strncmp(ptr, "nice ", 4) == 0) { 625 if (strncmp(ptr, "nice ", 4) == 0) {
443 cfg.nice = atoi(ptr + 5); 626 cfg.nice = atoi(ptr + 5);
627 if (getuid() != 0 &&cfg.nice < 0)
628 cfg.nice = 0;
444 arg_nice = 1; 629 arg_nice = 1;
445 return 0; 630 return 0;
446 } 631 }
@@ -451,6 +636,22 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
451 return 0; 636 return 0;
452 } 637 }
453 638
639 // writable-etc
640 if (strcmp(ptr, "writable-etc") == 0) {
641 if (cfg.etc_private_keep) {
642 fprintf(stderr, "Error: private-etc and writable-etc are mutually exclusive\n");
643 exit(1);
644 }
645 arg_writable_etc = 1;
646 return 0;
647 }
648
649 // writable-var
650 if (strcmp(ptr, "writable-var") == 0) {
651 arg_writable_var = 1;
652 return 0;
653 }
654
454 // private directory 655 // private directory
455 if (strncmp(ptr, "private ", 8) == 0) { 656 if (strncmp(ptr, "private ", 8) == 0) {
456 cfg.home_private = ptr + 8; 657 cfg.home_private = ptr + 8;
@@ -459,16 +660,85 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
459 return 0; 660 return 0;
460 } 661 }
461 662
663 if (strcmp(ptr, "x11 none") == 0) {
664 arg_x11_block = 1;
665 return 0;
666 }
667
668 if (strcmp(ptr, "x11 xephyr") == 0) {
669#ifdef HAVE_X11
670 if (checkcfg(CFG_X11)) {
671 char *x11env = getenv("FIREJAIL_X11");
672 if (x11env && strcmp(x11env, "yes") == 0) {
673 mask_x11_abstract_socket = 1;
674 return 0;
675 }
676 else {
677 // start x11
678 x11_start_xephyr(cfg.original_argc, cfg.original_argv);
679 exit(0);
680 }
681 }
682#endif
683 return 0;
684 }
685
686 if (strcmp(ptr, "x11 xorg") == 0) {
687#ifdef HAVE_X11
688 if (checkcfg(CFG_X11))
689 arg_x11_xorg = 1;
690 else {
691 fprintf(stderr, "Error: --x11 feature is disabled in Firejail configuration file\n");
692 return 0;
693 }
694#endif
695 return 0;
696 }
697 if (strcmp(ptr, "x11 xpra") == 0) {
698#ifdef HAVE_X11
699 if (checkcfg(CFG_X11)) {
700 char *x11env = getenv("FIREJAIL_X11");
701 if (x11env && strcmp(x11env, "yes") == 0) {
702 mask_x11_abstract_socket = 1;
703 return 0;
704 }
705 else {
706 // start x11
707 x11_start_xpra(cfg.original_argc, cfg.original_argv);
708 exit(0);
709 }
710 }
711#endif
712 return 0;
713 }
714
715 if (strcmp(ptr, "x11") == 0) {
716#ifdef HAVE_X11
717 if (checkcfg(CFG_X11)) {
718 char *x11env = getenv("FIREJAIL_X11");
719 if (x11env && strcmp(x11env, "yes") == 0) {
720 mask_x11_abstract_socket = 1;
721 return 0;
722 }
723 else {
724 // start x11
725 x11_start(cfg.original_argc, cfg.original_argv);
726 exit(0);
727 }
728 }
729#endif
730 return 0;
731 }
732
462 // private /etc list of files and directories 733 // private /etc list of files and directories
463 if (strncmp(ptr, "private-etc ", 12) == 0) { 734 if (strncmp(ptr, "private-etc ", 12) == 0) {
735 if (arg_writable_etc) {
736 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
737 exit(1);
738 }
464 cfg.etc_private_keep = ptr + 12; 739 cfg.etc_private_keep = ptr + 12;
465 fs_check_etc_list(); 740 fs_check_etc_list();
466 if (*cfg.etc_private_keep != '\0') 741 arg_private_etc = 1;
467 arg_private_etc = 1;
468 else {
469 arg_private_etc = 0;
470 fprintf(stderr, "Warning: private-etc disabled, no file found\n");
471 }
472 742
473 return 0; 743 return 0;
474 } 744 }
@@ -569,6 +839,30 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
569 return 0; 839 return 0;
570 } 840 }
571 841
842 if (strncmp(ptr, "join-or-start ", 14) == 0) {
843 // try to join by name only
844 pid_t pid;
845 if (!name2pid(ptr + 14, &pid)) {
846 if (!cfg.shell && !arg_shell_none)
847 cfg.shell = guess_shell();
848
849 // find first non-option arg
850 int i;
851 for (i = 1; i < cfg.original_argc && strncmp(cfg.original_argv[i], "--", 2) != 0; i++);
852
853 join(pid, cfg.original_argc,cfg.original_argv, i + 1);
854 exit(0);
855 }
856
857 // set sandbox name and start normally
858 cfg.name = ptr + 14;
859 if (strlen(cfg.name) == 0) {
860 fprintf(stderr, "Error: invalid sandbox name\n");
861 exit(1);
862 }
863 return 0;
864 }
865
572 // rest of filesystem 866 // rest of filesystem
573 if (strncmp(ptr, "blacklist ", 10) == 0) 867 if (strncmp(ptr, "blacklist ", 10) == 0)
574 ptr += 10; 868 ptr += 10;
@@ -577,11 +871,23 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
577 else if (strncmp(ptr, "noblacklist ", 12) == 0) 871 else if (strncmp(ptr, "noblacklist ", 12) == 0)
578 ptr += 12; 872 ptr += 12;
579 else if (strncmp(ptr, "whitelist ", 10) == 0) { 873 else if (strncmp(ptr, "whitelist ", 10) == 0) {
580 arg_whitelist = 1; 874#ifdef HAVE_WHITELIST
581 ptr += 10; 875 if (checkcfg(CFG_WHITELIST)) {
876 arg_whitelist = 1;
877 ptr += 10;
878 }
879 else
880 return 0;
881#else
882 return 0;
883#endif
582 } 884 }
583 else if (strncmp(ptr, "read-only ", 10) == 0) 885 else if (strncmp(ptr, "read-only ", 10) == 0)
584 ptr += 10; 886 ptr += 10;
887 else if (strncmp(ptr, "read-write ", 11) == 0)
888 ptr += 11;
889 else if (strncmp(ptr, "noexec ", 7) == 0)
890 ptr += 7;
585 else if (strncmp(ptr, "tmpfs ", 6) == 0) { 891 else if (strncmp(ptr, "tmpfs ", 6) == 0) {
586 if (getuid() != 0) { 892 if (getuid() != 0) {
587 fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n"); 893 fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n");
@@ -651,6 +957,16 @@ void profile_read(const char *fname) {
651 exit(1); 957 exit(1);
652 } 958 }
653 959
960 // allow debuggers
961 if (arg_allow_debuggers) {
962 char *tmp = strrchr(fname, '/');
963 if (tmp && *(tmp + 1) != '\0') {
964 tmp++;
965 if (strcmp(tmp, "disable-devel.inc") == 0)
966 return;
967 }
968 }
969
654 // open profile file: 970 // open profile file:
655 FILE *fp = fopen(fname, "r"); 971 FILE *fp = fopen(fname, "r");
656 if (fp == NULL) { 972 if (fp == NULL) {
@@ -658,8 +974,7 @@ void profile_read(const char *fname) {
658 exit(1); 974 exit(1);
659 } 975 }
660 976
661 if (!arg_quiet) 977 int msg_printed = 0;
662 fprintf(stderr, "Reading profile %s\n", fname);
663 978
664 // read the file line by line 979 // read the file line by line
665 char buf[MAX_READ + 1]; 980 char buf[MAX_READ + 1];
@@ -677,6 +992,17 @@ void profile_read(const char *fname) {
677 continue; 992 continue;
678 } 993 }
679 994
995 // process quiet
996 if (strcmp(ptr, "quiet") == 0) {
997 arg_quiet = 1;
998 continue;
999 }
1000 if (!msg_printed) {
1001 if (!arg_quiet)
1002 fprintf(stderr, "Reading profile %s\n", fname);
1003 msg_printed = 1;
1004 }
1005
680 // process include 1006 // process include
681 if (strncmp(ptr, "include ", 8) == 0) { 1007 if (strncmp(ptr, "include ", 8) == 0) {
682 include_level++; 1008 include_level++;