diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/etc-cleanup/Makefile | 9 | ||||
-rw-r--r-- | src/etc-cleanup/main.c | 255 | ||||
-rw-r--r-- | src/firecfg/firecfg.config | 9 | ||||
-rw-r--r-- | src/firejail/Makefile | 4 | ||||
-rw-r--r-- | src/firejail/firejail.h | 4 | ||||
-rw-r--r-- | src/firejail/fs.c | 5 | ||||
-rw-r--r-- | src/firejail/fs_etc.c | 293 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 9 | ||||
-rw-r--r-- | src/firejail/main.c | 28 | ||||
-rw-r--r-- | src/firejail/profile.c | 32 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 12 | ||||
-rw-r--r-- | src/firejail/usage.c | 3 | ||||
-rw-r--r-- | src/firejail/util.c | 1 | ||||
-rw-r--r-- | src/include/etc_groups.h | 103 | ||||
-rw-r--r-- | src/include/rundefs.h | 2 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 3 | ||||
-rw-r--r-- | src/man/firejail.txt | 49 | ||||
-rw-r--r-- | src/zsh_completion/_firejail.in | 1 |
18 files changed, 653 insertions, 169 deletions
diff --git a/src/etc-cleanup/Makefile b/src/etc-cleanup/Makefile new file mode 100644 index 000000000..349da8821 --- /dev/null +++ b/src/etc-cleanup/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | ROOT = ../.. | ||
2 | -include $(ROOT)/config.mk | ||
3 | |||
4 | PROG = etc-cleanup | ||
5 | TARGET = $(PROG) | ||
6 | |||
7 | MOD_HDRS = ../include/etc-groups.h | ||
8 | |||
9 | include $(ROOT)/src/prog.mk | ||
diff --git a/src/etc-cleanup/main.c b/src/etc-cleanup/main.c new file mode 100644 index 000000000..47fe1556b --- /dev/null +++ b/src/etc-cleanup/main.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2022 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include "../include/etc_groups.h" | ||
22 | #include "../include/common.h" | ||
23 | #include <stdarg.h> | ||
24 | |||
25 | #define MAX_BUF 4098 | ||
26 | #define MAX_ARR 1024 | ||
27 | char *arr[MAX_ARR] = {NULL}; | ||
28 | int arr_cnt = 0; | ||
29 | |||
30 | static int arr_tls_ca = 0; | ||
31 | static int arr_x11 = 0; | ||
32 | static int arr_games = 0; | ||
33 | static char outbuf[256 * 1024]; | ||
34 | static char *outptr; | ||
35 | static int arg_replace = 0; | ||
36 | static int arg_debug = 0; | ||
37 | |||
38 | void outprintf(char* fmt, ...) { | ||
39 | va_list args; | ||
40 | va_start(args,fmt); | ||
41 | outptr += vsprintf(outptr, fmt, args); | ||
42 | va_end(args); | ||
43 | } | ||
44 | |||
45 | |||
46 | |||
47 | static int arr_check(const char *fname, char **pptr) { | ||
48 | assert(fname); | ||
49 | assert(pptr); | ||
50 | |||
51 | while (*pptr != NULL) { | ||
52 | if (strcmp(fname, *pptr) == 0) | ||
53 | return 1; | ||
54 | pptr++; | ||
55 | } | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | |||
61 | |||
62 | static void arr_add(const char *fname) { | ||
63 | assert(fname); | ||
64 | assert(arr_cnt < MAX_ARR); | ||
65 | |||
66 | int i; | ||
67 | for (i = 0; i < arr_cnt; i++) | ||
68 | if (strcmp(arr[i], fname) == 0) | ||
69 | return; | ||
70 | |||
71 | arr[arr_cnt] = strdup(fname); | ||
72 | if (!arr[arr_cnt]) | ||
73 | errExit("strdup"); | ||
74 | arr_cnt++; | ||
75 | } | ||
76 | |||
77 | int arr_cmp(const void *p1, const void *p2) { | ||
78 | char **ptr1 = (char **) p1; | ||
79 | char **ptr2 = (char **) p2; | ||
80 | |||
81 | return strcmp(*ptr1, *ptr2); | ||
82 | } | ||
83 | |||
84 | static void arr_sort(void) { | ||
85 | qsort(&arr[0], arr_cnt, sizeof(char *), arr_cmp); | ||
86 | } | ||
87 | |||
88 | static void arr_clean(void) { | ||
89 | int i; | ||
90 | for (i = 0; i < arr_cnt; i++) { | ||
91 | free(arr[i]); | ||
92 | arr[i] = NULL; | ||
93 | } | ||
94 | |||
95 | arr_cnt = 0; | ||
96 | arr_games = 0; | ||
97 | arr_tls_ca = 0; | ||
98 | arr_x11 = 0; | ||
99 | } | ||
100 | |||
101 | static char *arr_print(void) { | ||
102 | char *last_line = outptr; | ||
103 | outprintf("private-etc "); | ||
104 | |||
105 | if (arr_games) | ||
106 | outprintf("@games,"); | ||
107 | if (arr_tls_ca) | ||
108 | outprintf("@tls-ca,"); | ||
109 | if (arr_x11) | ||
110 | outprintf("@x11,"); | ||
111 | |||
112 | int i; | ||
113 | for (i = 0; i < arr_cnt; i++) | ||
114 | outprintf("%s,", arr[i]); | ||
115 | if (*(outptr - 1) == ' ' || *(outptr - 1) == ',') { | ||
116 | outptr--; | ||
117 | *outptr = '\0'; | ||
118 | } | ||
119 | outprintf("\n"); | ||
120 | |||
121 | return last_line; | ||
122 | } | ||
123 | |||
124 | static void process_file(const char *fname) { | ||
125 | assert(fname); | ||
126 | |||
127 | FILE *fp = fopen(fname, "r"); | ||
128 | if (!fp) { | ||
129 | fprintf(stderr, "Error: cannot open %s file\n", fname); | ||
130 | exit(1); | ||
131 | } | ||
132 | |||
133 | outptr = outbuf; | ||
134 | *outptr = '\0'; | ||
135 | arr_clean(); | ||
136 | |||
137 | char line[MAX_BUF]; | ||
138 | char orig_line[MAX_BUF]; | ||
139 | int cnt = 0; | ||
140 | int print = 0; | ||
141 | while (fgets(line, MAX_BUF, fp)) { | ||
142 | cnt++; | ||
143 | if (strncmp(line, "private-etc", 11) != 0) { | ||
144 | outprintf("%s", line); | ||
145 | continue; | ||
146 | } | ||
147 | |||
148 | strcpy(orig_line,line); | ||
149 | char *ptr = strchr(line, '\n'); | ||
150 | if (ptr) | ||
151 | *ptr = '\0'; | ||
152 | |||
153 | ptr = line + 12; | ||
154 | while (*ptr == ' ' || *ptr == '\t') | ||
155 | ptr++; | ||
156 | |||
157 | // check for blanks and tabs | ||
158 | char *ptr2 = ptr; | ||
159 | while (*ptr2 != '\0') { | ||
160 | if (*ptr2 == ' ' || *ptr2 == '\t') { | ||
161 | fprintf(stderr, "Error: invalid private-etc line %s:%d\n", fname, cnt); | ||
162 | exit(1); | ||
163 | } | ||
164 | ptr2++; | ||
165 | } | ||
166 | |||
167 | ptr = strtok(ptr, ","); | ||
168 | while (ptr) { | ||
169 | if (arg_debug) | ||
170 | printf("%s\n", ptr); | ||
171 | if (arr_check(ptr, &etc_list[0])); | ||
172 | else if (arr_check(ptr, &etc_group_sound[0])); | ||
173 | else if (arr_check(ptr, &etc_group_network[0])); | ||
174 | else if (strcmp(ptr, "@games") == 0) | ||
175 | arr_games = 1; | ||
176 | else if (strcmp(ptr, "@tls-ca") == 0) | ||
177 | arr_tls_ca = 1; | ||
178 | else if (strcmp(ptr, "@x11") == 0) | ||
179 | arr_x11 = 1; | ||
180 | else if (arr_check(ptr, &etc_group_games[0])) | ||
181 | arr_games = 1; | ||
182 | else if (arr_check(ptr, &etc_group_tls_ca[0])) | ||
183 | arr_tls_ca = 1; | ||
184 | else if (arr_check(ptr, &etc_group_x11[0])) | ||
185 | arr_x11 = 1; | ||
186 | else | ||
187 | arr_add(ptr); | ||
188 | |||
189 | ptr = strtok(NULL, ","); | ||
190 | } | ||
191 | |||
192 | arr_sort(); | ||
193 | char *last_line = arr_print(); | ||
194 | if (strcmp(last_line, orig_line) == 0) { | ||
195 | fclose(fp); | ||
196 | return; | ||
197 | } | ||
198 | printf("\n********************\nfile: %s\n\nold: %s\nnew: %s\n", fname, orig_line, last_line); | ||
199 | print = 1; | ||
200 | } | ||
201 | |||
202 | fclose(fp); | ||
203 | |||
204 | if (print && arg_replace) { | ||
205 | fp = fopen(fname, "w"); | ||
206 | if (!fp) { | ||
207 | fprintf(stderr, "Error: cannot open profile file\n"); | ||
208 | exit(1); | ||
209 | } | ||
210 | fprintf(fp, "%s", outbuf); | ||
211 | fclose(fp); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | static void usage(void) { | ||
216 | printf("usage: cleanup-etc [options] file.profile [file.profile]\n"); | ||
217 | printf("Group and clean private-etc entries in one or more profile files.\n"); | ||
218 | printf("Options:\n"); | ||
219 | printf(" --debug - print debug messages\n"); | ||
220 | printf(" -h, -?, --help - this help screen\n"); | ||
221 | printf(" --replace - replace profile file\n"); | ||
222 | } | ||
223 | |||
224 | int main(int argc, char **argv) { | ||
225 | if (argc < 2) { | ||
226 | fprintf(stderr, "Error: invalid number of parameters\n"); | ||
227 | usage(); | ||
228 | return 1; | ||
229 | } | ||
230 | |||
231 | int i; | ||
232 | for (i = 1; i < argc; i++) { | ||
233 | if (strcmp(argv[i], "-h") == 0 || | ||
234 | strcmp(argv[i], "-?") == 0 || | ||
235 | strcmp(argv[i], "--help") == 0) { | ||
236 | usage(); | ||
237 | return 0; | ||
238 | } | ||
239 | else if (strcmp(argv[i], "--debug") == 0) | ||
240 | arg_debug = 1; | ||
241 | else if (strcmp(argv[i], "--replace") == 0) | ||
242 | arg_replace = 1; | ||
243 | else if (*argv[i] == '-') { | ||
244 | fprintf(stderr, "Error: invalid program option %s\n", argv[i]); | ||
245 | return 1; | ||
246 | } | ||
247 | else | ||
248 | break; | ||
249 | } | ||
250 | |||
251 | for (; i < argc; i++) | ||
252 | process_file(argv[i]); | ||
253 | |||
254 | return 0; | ||
255 | } \ No newline at end of file | ||
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index 15169f983..db73dd1f6 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -479,6 +479,7 @@ lincity-ng | |||
479 | links | 479 | links |
480 | links2 | 480 | links2 |
481 | linphone | 481 | linphone |
482 | linuxqq | ||
482 | lmms | 483 | lmms |
483 | lobase | 484 | lobase |
484 | localc | 485 | localc |
@@ -518,6 +519,7 @@ matrix-mirage | |||
518 | mattermost-desktop | 519 | mattermost-desktop |
519 | mcabber | 520 | mcabber |
520 | mcomix | 521 | mcomix |
522 | md5sum | ||
521 | mediainfo | 523 | mediainfo |
522 | mediathekview | 524 | mediathekview |
523 | megaglest | 525 | megaglest |
@@ -693,6 +695,7 @@ qlipper | |||
693 | qmmp | 695 | qmmp |
694 | qnapi | 696 | qnapi |
695 | qpdfview | 697 | qpdfview |
698 | |||
696 | qt-faststart | 699 | qt-faststart |
697 | qtox | 700 | qtox |
698 | quadrapassel | 701 | quadrapassel |
@@ -734,6 +737,11 @@ seahorse-tool | |||
734 | seamonkey | 737 | seamonkey |
735 | seamonkey-bin | 738 | seamonkey-bin |
736 | secret-tool | 739 | secret-tool |
740 | sha1sum | ||
741 | sha224sum | ||
742 | sha256sum | ||
743 | sha348sum | ||
744 | sha512sum | ||
737 | shellcheck | 745 | shellcheck |
738 | shortwave | 746 | shortwave |
739 | shotcut | 747 | shotcut |
@@ -773,6 +781,7 @@ straw-viewer | |||
773 | strings | 781 | strings |
774 | studio.sh | 782 | studio.sh |
775 | subdownloader | 783 | subdownloader |
784 | sum | ||
776 | supertux2 | 785 | supertux2 |
777 | supertuxkart | 786 | supertuxkart |
778 | surf | 787 | surf |
diff --git a/src/firejail/Makefile b/src/firejail/Makefile index 4e241af7e..47edc5ac6 100644 --- a/src/firejail/Makefile +++ b/src/firejail/Makefile | |||
@@ -13,7 +13,9 @@ MOD_HDRS = \ | |||
13 | ../include/seccomp.h \ | 13 | ../include/seccomp.h \ |
14 | ../include/syscall_i386.h \ | 14 | ../include/syscall_i386.h \ |
15 | ../include/syscall_x86_64.h \ | 15 | ../include/syscall_x86_64.h \ |
16 | ../include/firejail_user.h | 16 | ../include/firejail_user.h \ |
17 | ../include/etc_groups.h | ||
18 | |||
17 | 19 | ||
18 | MOD_OBJS = \ | 20 | MOD_OBJS = \ |
19 | ../lib/common.o \ | 21 | ../lib/common.o \ |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 4fe3a5974..a09158e9e 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -332,6 +332,7 @@ extern int arg_nice; // nice value configured | |||
332 | extern int arg_ipc; // enable ipc namespace | 332 | extern int arg_ipc; // enable ipc namespace |
333 | extern int arg_writable_etc; // writable etc | 333 | extern int arg_writable_etc; // writable etc |
334 | extern int arg_keep_config_pulse; // disable automatic ~/.config/pulse init | 334 | extern int arg_keep_config_pulse; // disable automatic ~/.config/pulse init |
335 | extern int arg_keep_shell_rc; // do not copy shell configuration from /etc/skel | ||
335 | extern int arg_writable_var; // writable var | 336 | extern int arg_writable_var; // writable var |
336 | extern int arg_keep_var_tmp; // don't overwrite /var/tmp | 337 | extern int arg_keep_var_tmp; // don't overwrite /var/tmp |
337 | extern int arg_writable_run_user; // writable /run/user | 338 | extern int arg_writable_run_user; // writable /run/user |
@@ -693,11 +694,12 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in | |||
693 | void network_set_run_file(pid_t pid); | 694 | void network_set_run_file(pid_t pid); |
694 | 695 | ||
695 | // fs_etc.c | 696 | // fs_etc.c |
697 | char *fs_etc_build(char *str); | ||
698 | void fs_resolvconf(void); | ||
696 | void fs_machineid(void); | 699 | void fs_machineid(void); |
697 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); | 700 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); |
698 | void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); | 701 | void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); |
699 | void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); | 702 | void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); |
700 | void fs_rebuild_etc(void); | ||
701 | 703 | ||
702 | // no_sandbox.c | 704 | // no_sandbox.c |
703 | int check_namespace_virt(void); | 705 | int check_namespace_virt(void); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index b44eb65ee..89a67f686 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -67,11 +67,8 @@ static void disable_file(OPERATION op, const char *filename) { | |||
67 | // they don't seem to like a uid of 0 | 67 | // they don't seem to like a uid of 0 |
68 | // force mounting | 68 | // force mounting |
69 | int fd = open(filename, O_PATH|O_CLOEXEC); | 69 | int fd = open(filename, O_PATH|O_CLOEXEC); |
70 | if (fd < 0) { | 70 | if (fd < 0) |
71 | if (arg_debug) | ||
72 | printf("Warning (blacklisting): cannot open %s: %s\n", filename, strerror(errno)); | ||
73 | return; | 71 | return; |
74 | } | ||
75 | 72 | ||
76 | EUID_ROOT(); | 73 | EUID_ROOT(); |
77 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); | 74 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); |
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 77fa00d6b..83f140d80 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -24,7 +24,126 @@ | |||
24 | #include <sys/types.h> | 24 | #include <sys/types.h> |
25 | #include <time.h> | 25 | #include <time.h> |
26 | #include <unistd.h> | 26 | #include <unistd.h> |
27 | #include <dirent.h> | 27 | #include <glob.h> |
28 | #include "../include/etc_groups.h" | ||
29 | |||
30 | static int etc_cnt = 0; | ||
31 | |||
32 | static void etc_copy_group(char **pptr) { | ||
33 | assert(pptr); | ||
34 | |||
35 | while (*pptr != NULL) { | ||
36 | etc_list[etc_cnt++] = *pptr; | ||
37 | etc_list[etc_cnt] = NULL; | ||
38 | pptr++; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | static void etc_add(const char *file) { | ||
43 | assert(file); | ||
44 | if (etc_cnt >= ETC_MAX) { | ||
45 | fprintf(stderr, "Error: size of private_etc list exceeded (%d maximum)\n", ETC_MAX); | ||
46 | exit(1); | ||
47 | } | ||
48 | |||
49 | // look for file in the current list | ||
50 | int i; | ||
51 | for (i = 0; i < etc_cnt; i++) { | ||
52 | if (strcmp(file, etc_list[i]) == 0) { | ||
53 | if (arg_debug) | ||
54 | printf("private-etc arguments: skip %s\n", file); | ||
55 | return; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | char *ptr = strdup(file); | ||
60 | if (!ptr) | ||
61 | errExit("strdup"); | ||
62 | etc_list[etc_cnt++] = ptr; | ||
63 | etc_list[etc_cnt] = NULL; | ||
64 | } | ||
65 | |||
66 | // str can be NULL | ||
67 | char *fs_etc_build(char *str) { | ||
68 | while (etc_list[etc_cnt++]); | ||
69 | etc_cnt--; | ||
70 | if (!arg_nonetwork) | ||
71 | etc_copy_group(&etc_group_network[0]); | ||
72 | if (!arg_nosound) | ||
73 | etc_copy_group(&etc_group_sound[0]); | ||
74 | |||
75 | // parsing | ||
76 | if (str) { | ||
77 | char* ptr = strtok(str, ","); | ||
78 | while (ptr) { | ||
79 | // look for standard groups | ||
80 | if (strcmp(ptr, "@tls-ca") == 0) | ||
81 | etc_copy_group(&etc_group_tls_ca[0]); | ||
82 | if (strcmp(ptr, "@x11") == 0) | ||
83 | etc_copy_group(&etc_group_x11[0]); | ||
84 | if (strcmp(ptr, "@sound") == 0) | ||
85 | etc_copy_group(&etc_group_sound[0]); | ||
86 | if (strcmp(ptr, "@network") == 0) | ||
87 | etc_copy_group(&etc_group_network[0]); | ||
88 | if (strcmp(ptr, "@games") == 0) | ||
89 | etc_copy_group(&etc_group_games[0]); | ||
90 | else | ||
91 | etc_add(ptr); | ||
92 | ptr = strtok(NULL, ","); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | // manufacture the new string | ||
97 | int len = 0; | ||
98 | int i; | ||
99 | for (i = 0; i < etc_cnt; i++) | ||
100 | len += strlen(etc_list[i]) + 1; // plus 1 for the trailing ',' | ||
101 | char *rv = malloc(len + 1); | ||
102 | if (!rv) | ||
103 | errExit("malloc"); | ||
104 | char *ptr = rv; | ||
105 | for (i = 0; i < etc_cnt; i++) { | ||
106 | sprintf(ptr, "%s,", etc_list[i]); | ||
107 | ptr += strlen(etc_list[i]) + 1; | ||
108 | } | ||
109 | |||
110 | return rv; | ||
111 | } | ||
112 | |||
113 | void fs_resolvconf(void) { | ||
114 | if (arg_debug) | ||
115 | printf("Creating a new /etc/resolv.conf file\n"); | ||
116 | FILE *fp = fopen(RUN_RESOLVCONF_FILE, "wxe"); | ||
117 | if (!fp) { | ||
118 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); | ||
119 | exit(1); | ||
120 | } | ||
121 | |||
122 | if (cfg.dns1) { | ||
123 | if (any_dhcp()) | ||
124 | fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); | ||
125 | fprintf(fp, "nameserver %s\n", cfg.dns1); | ||
126 | } | ||
127 | if (cfg.dns2) | ||
128 | fprintf(fp, "nameserver %s\n", cfg.dns2); | ||
129 | if (cfg.dns3) | ||
130 | fprintf(fp, "nameserver %s\n", cfg.dns3); | ||
131 | if (cfg.dns4) | ||
132 | fprintf(fp, "nameserver %s\n", cfg.dns4); | ||
133 | |||
134 | // mode and owner | ||
135 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
136 | |||
137 | fclose(fp); | ||
138 | selinux_relabel_path(RUN_RESOLVCONF_FILE, "/etc/resolv.conf"); | ||
139 | |||
140 | |||
141 | if (mount(RUN_RESOLVCONF_FILE, "/etc/resolv.conf", "none", MS_BIND, "mode=644,gid=0") < 0) | ||
142 | errExit("mount"); | ||
143 | |||
144 | fs_logger("create /etc/resolv.conf"); | ||
145 | } | ||
146 | |||
28 | 147 | ||
29 | // spoof /etc/machine_id | 148 | // spoof /etc/machine_id |
30 | void fs_machineid(void) { | 149 | void fs_machineid(void) { |
@@ -143,19 +262,11 @@ errexit: | |||
143 | } | 262 | } |
144 | 263 | ||
145 | static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { | 264 | static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { |
146 | assert(fname); | ||
147 | |||
148 | if (*fname == '~' || *fname == '/' || strstr(fname, "..")) { | ||
149 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); | ||
150 | exit(1); | ||
151 | } | ||
152 | invalid_filename(fname, 0); // no globbing | ||
153 | |||
154 | char *src; | 265 | char *src; |
155 | if (asprintf(&src, "%s/%s", private_dir, fname) == -1) | 266 | if (asprintf(&src, "%s/%s", private_dir, fname) == -1) |
156 | errExit("asprintf"); | 267 | errExit("asprintf"); |
268 | |||
157 | if (check_dir_or_file(src) == 0) { | 269 | if (check_dir_or_file(src) == 0) { |
158 | fwarning("skipping %s for private %s\n", fname, private_dir); | ||
159 | free(src); | 270 | free(src); |
160 | return; | 271 | return; |
161 | } | 272 | } |
@@ -169,8 +280,9 @@ static void duplicate(const char *fname, const char *private_dir, const char *pr | |||
169 | 280 | ||
170 | build_dirs(src, dst, strlen(private_dir), strlen(private_run_dir)); | 281 | build_dirs(src, dst, strlen(private_dir), strlen(private_run_dir)); |
171 | 282 | ||
172 | // follow links! this will make a copy of the file or directory pointed by the symlink | 283 | // follow links by default, thus making a copy of the file or directory pointed by the symlink |
173 | // this will solve problems such as NixOS #4887 | 284 | // this will solve problems such as NixOS #4887 |
285 | // | ||
174 | // don't follow links to dynamic directories such as /proc | 286 | // don't follow links to dynamic directories such as /proc |
175 | if (strcmp(src, "/etc/mtab") == 0) | 287 | if (strcmp(src, "/etc/mtab") == 0) |
176 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FCOPY, src, dst); | 288 | sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FCOPY, src, dst); |
@@ -179,9 +291,38 @@ static void duplicate(const char *fname, const char *private_dir, const char *pr | |||
179 | 291 | ||
180 | free(dst); | 292 | free(dst); |
181 | fs_logger2("clone", src); | 293 | fs_logger2("clone", src); |
182 | free(src); | ||
183 | } | 294 | } |
184 | 295 | ||
296 | static void duplicate_globbing(const char *fname, const char *private_dir, const char *private_run_dir) { | ||
297 | assert(fname); | ||
298 | |||
299 | if (*fname == '~' || *fname == '/' || strstr(fname, "..")) { | ||
300 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); | ||
301 | exit(1); | ||
302 | } | ||
303 | invalid_filename(fname, 1); // no globbing | ||
304 | |||
305 | char *pattern; | ||
306 | if (asprintf(&pattern, "%s/%s", private_dir, fname) == -1) | ||
307 | errExit("asprintf"); | ||
308 | |||
309 | glob_t globbuf; | ||
310 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); | ||
311 | if (globerr) { | ||
312 | fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); | ||
313 | exit(1); | ||
314 | } | ||
315 | |||
316 | size_t i; | ||
317 | int len = strlen(private_dir); | ||
318 | for (i = 0; i < globbuf.gl_pathc; i++) { | ||
319 | char *path = globbuf.gl_pathv[i]; | ||
320 | duplicate(path + len + 1, private_dir, private_run_dir); | ||
321 | } | ||
322 | |||
323 | globfree(&globbuf); | ||
324 | free(pattern); | ||
325 | } | ||
185 | 326 | ||
186 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list) { | 327 | void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list) { |
187 | assert(private_dir); | 328 | assert(private_dir); |
@@ -221,10 +362,10 @@ void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, c | |||
221 | fprintf(stderr, "Error: invalid private %s argument\n", private_dir); | 362 | fprintf(stderr, "Error: invalid private %s argument\n", private_dir); |
222 | exit(1); | 363 | exit(1); |
223 | } | 364 | } |
224 | duplicate(ptr, private_dir, private_run_dir); | 365 | duplicate_globbing(ptr, private_dir, private_run_dir); |
225 | 366 | ||
226 | while ((ptr = strtok(NULL, ",")) != NULL) | 367 | while ((ptr = strtok(NULL, ",")) != NULL) |
227 | duplicate(ptr, private_dir, private_run_dir); | 368 | duplicate_globbing(ptr, private_dir, private_run_dir); |
228 | free(dlist); | 369 | free(dlist); |
229 | fs_logger_print(); | 370 | fs_logger_print(); |
230 | } | 371 | } |
@@ -262,127 +403,3 @@ void fs_private_dir_list(const char *private_dir, const char *private_run_dir, c | |||
262 | fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); | 403 | fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); |
263 | } | 404 | } |
264 | 405 | ||
265 | void fs_rebuild_etc(void) { | ||
266 | int have_dhcp = 1; | ||
267 | if (cfg.dns1 == NULL && !any_dhcp()) | ||
268 | have_dhcp = 0; | ||
269 | |||
270 | if (arg_debug) | ||
271 | printf("rebuilding /etc directory\n"); | ||
272 | if (mkdir(RUN_DNS_ETC, 0755)) | ||
273 | errExit("mkdir"); | ||
274 | selinux_relabel_path(RUN_DNS_ETC, "/etc"); | ||
275 | fs_logger("tmpfs /etc"); | ||
276 | |||
277 | DIR *dir = opendir("/etc"); | ||
278 | if (!dir) | ||
279 | errExit("opendir"); | ||
280 | |||
281 | struct stat s; | ||
282 | struct dirent *entry; | ||
283 | while ((entry = readdir(dir))) { | ||
284 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
285 | continue; | ||
286 | |||
287 | // skip files in cfg.profile_rebuild_etc list | ||
288 | // these files are already blacklisted | ||
289 | { | ||
290 | ProfileEntry *prf = cfg.profile_rebuild_etc; | ||
291 | int found = 0; | ||
292 | while (prf) { | ||
293 | if (strcmp(entry->d_name, prf->data + 5) == 0) { // 5 is strlen("/etc/") | ||
294 | found = 1; | ||
295 | break; | ||
296 | } | ||
297 | prf = prf->next; | ||
298 | } | ||
299 | if (found) | ||
300 | continue; | ||
301 | } | ||
302 | |||
303 | // for resolv.conf we might have to create a brand new file later | ||
304 | if (have_dhcp && | ||
305 | (strcmp(entry->d_name, "resolv.conf") == 0 || | ||
306 | strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0)) | ||
307 | continue; | ||
308 | // printf("linking %s\n", entry->d_name); | ||
309 | |||
310 | char *src; | ||
311 | if (asprintf(&src, "/etc/%s", entry->d_name) == -1) | ||
312 | errExit("asprintf"); | ||
313 | if (stat(src, &s) != 0) { | ||
314 | free(src); | ||
315 | continue; | ||
316 | } | ||
317 | |||
318 | char *dest; | ||
319 | if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1) | ||
320 | errExit("asprintf"); | ||
321 | |||
322 | int symlink_done = 0; | ||
323 | if (is_link(src)) { | ||
324 | char *rp =realpath(src, NULL); | ||
325 | if (rp == NULL) { | ||
326 | free(src); | ||
327 | free(dest); | ||
328 | continue; | ||
329 | } | ||
330 | if (symlink(rp, dest)) | ||
331 | errExit("symlink"); | ||
332 | else | ||
333 | symlink_done = 1; | ||
334 | } | ||
335 | else if (S_ISDIR(s.st_mode)) | ||
336 | create_empty_dir_as_root(dest, S_IRWXU); | ||
337 | else | ||
338 | create_empty_file_as_root(dest, S_IRUSR | S_IWUSR); | ||
339 | |||
340 | // bind-mount src on top of dest | ||
341 | if (!symlink_done) { | ||
342 | if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
343 | errExit("mount bind mirroring /etc"); | ||
344 | } | ||
345 | fs_logger2("clone", src); | ||
346 | |||
347 | free(src); | ||
348 | free(dest); | ||
349 | } | ||
350 | closedir(dir); | ||
351 | |||
352 | // mount bind our private etc directory on top of /etc | ||
353 | if (arg_debug) | ||
354 | printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC); | ||
355 | if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) | ||
356 | errExit("mount bind mirroring /etc"); | ||
357 | fs_logger("mount /etc"); | ||
358 | |||
359 | if (have_dhcp == 0) | ||
360 | return; | ||
361 | |||
362 | if (arg_debug) | ||
363 | printf("Creating a new /etc/resolv.conf file\n"); | ||
364 | FILE *fp = fopen("/etc/resolv.conf", "wxe"); | ||
365 | if (!fp) { | ||
366 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); | ||
367 | exit(1); | ||
368 | } | ||
369 | |||
370 | if (cfg.dns1) { | ||
371 | if (any_dhcp()) | ||
372 | fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); | ||
373 | fprintf(fp, "nameserver %s\n", cfg.dns1); | ||
374 | } | ||
375 | if (cfg.dns2) | ||
376 | fprintf(fp, "nameserver %s\n", cfg.dns2); | ||
377 | if (cfg.dns3) | ||
378 | fprintf(fp, "nameserver %s\n", cfg.dns3); | ||
379 | if (cfg.dns4) | ||
380 | fprintf(fp, "nameserver %s\n", cfg.dns4); | ||
381 | |||
382 | // mode and owner | ||
383 | SET_PERMS_STREAM(fp, 0, 0, 0644); | ||
384 | |||
385 | fclose(fp); | ||
386 | |||
387 | fs_logger("create /etc/resolv.conf"); | ||
388 | } | ||
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 8c4cb3d4f..8e72f8687 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -361,7 +361,8 @@ void fs_private_homedir(void) { | |||
361 | } | 361 | } |
362 | EUID_USER(); | 362 | EUID_USER(); |
363 | 363 | ||
364 | skel(homedir); | 364 | if (!arg_keep_shell_rc) |
365 | skel(homedir); | ||
365 | if (xflag) | 366 | if (xflag) |
366 | copy_xauthority(); | 367 | copy_xauthority(); |
367 | if (aflag) | 368 | if (aflag) |
@@ -430,7 +431,8 @@ void fs_private(void) { | |||
430 | selinux_relabel_path(homedir, homedir); | 431 | selinux_relabel_path(homedir, homedir); |
431 | } | 432 | } |
432 | 433 | ||
433 | skel(homedir); | 434 | if (!arg_keep_shell_rc) |
435 | skel(homedir); | ||
434 | if (xflag) | 436 | if (xflag) |
435 | copy_xauthority(); | 437 | copy_xauthority(); |
436 | if (aflag) | 438 | if (aflag) |
@@ -682,7 +684,8 @@ void fs_private_home_list(void) { | |||
682 | errExit("mounting tmpfs"); | 684 | errExit("mounting tmpfs"); |
683 | EUID_USER(); | 685 | EUID_USER(); |
684 | 686 | ||
685 | skel(homedir); | 687 | if (!arg_keep_shell_rc) |
688 | skel(homedir); | ||
686 | if (xflag) | 689 | if (xflag) |
687 | copy_xauthority(); | 690 | copy_xauthority(); |
688 | if (aflag) | 691 | if (aflag) |
diff --git a/src/firejail/main.c b/src/firejail/main.c index 18e9ae651..8df6926ee 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -127,6 +127,7 @@ int arg_nice = 0; // nice value configured | |||
127 | int arg_ipc = 0; // enable ipc namespace | 127 | int arg_ipc = 0; // enable ipc namespace |
128 | int arg_writable_etc = 0; // writable etc | 128 | int arg_writable_etc = 0; // writable etc |
129 | int arg_keep_config_pulse = 0; // disable automatic ~/.config/pulse init | 129 | int arg_keep_config_pulse = 0; // disable automatic ~/.config/pulse init |
130 | int arg_keep_shell_rc = 0; // do not copy shell configuration from /etc/skel | ||
130 | int arg_writable_var = 0; // writable var | 131 | int arg_writable_var = 0; // writable var |
131 | int arg_keep_var_tmp = 0; // don't overwrite /var/tmp | 132 | int arg_keep_var_tmp = 0; // don't overwrite /var/tmp |
132 | int arg_writable_run_user = 0; // writable /run/user | 133 | int arg_writable_run_user = 0; // writable /run/user |
@@ -1975,6 +1976,9 @@ int main(int argc, char **argv, char **envp) { | |||
1975 | else if (strcmp(argv[i], "--keep-config-pulse") == 0) { | 1976 | else if (strcmp(argv[i], "--keep-config-pulse") == 0) { |
1976 | arg_keep_config_pulse = 1; | 1977 | arg_keep_config_pulse = 1; |
1977 | } | 1978 | } |
1979 | else if (strcmp(argv[i], "--keep-shell-rc") == 0) { | ||
1980 | arg_keep_shell_rc = 1; | ||
1981 | } | ||
1978 | else if (strcmp(argv[i], "--writable-var") == 0) { | 1982 | else if (strcmp(argv[i], "--writable-var") == 0) { |
1979 | arg_writable_var = 1; | 1983 | arg_writable_var = 1; |
1980 | } | 1984 | } |
@@ -2044,6 +2048,17 @@ int main(int argc, char **argv, char **envp) { | |||
2044 | else if (strcmp(argv[i], "--keep-dev-shm") == 0) { | 2048 | else if (strcmp(argv[i], "--keep-dev-shm") == 0) { |
2045 | arg_keep_dev_shm = 1; | 2049 | arg_keep_dev_shm = 1; |
2046 | } | 2050 | } |
2051 | else if (strcmp(argv[i], "--private-etc") == 0) { | ||
2052 | if (checkcfg(CFG_PRIVATE_ETC)) { | ||
2053 | if (arg_writable_etc) { | ||
2054 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
2055 | exit(1); | ||
2056 | } | ||
2057 | arg_private_etc = 1; | ||
2058 | } | ||
2059 | else | ||
2060 | exit_err_feature("private-etc"); | ||
2061 | } | ||
2047 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { | 2062 | else if (strncmp(argv[i], "--private-etc=", 14) == 0) { |
2048 | if (checkcfg(CFG_PRIVATE_ETC)) { | 2063 | if (checkcfg(CFG_PRIVATE_ETC)) { |
2049 | if (arg_writable_etc) { | 2064 | if (arg_writable_etc) { |
@@ -2161,11 +2176,24 @@ int main(int argc, char **argv, char **envp) { | |||
2161 | // hostname, etc | 2176 | // hostname, etc |
2162 | //************************************* | 2177 | //************************************* |
2163 | else if (strncmp(argv[i], "--name=", 7) == 0) { | 2178 | else if (strncmp(argv[i], "--name=", 7) == 0) { |
2179 | int only_numbers = 1; | ||
2164 | cfg.name = argv[i] + 7; | 2180 | cfg.name = argv[i] + 7; |
2165 | if (strlen(cfg.name) == 0) { | 2181 | if (strlen(cfg.name) == 0) { |
2166 | fprintf(stderr, "Error: please provide a name for sandbox\n"); | 2182 | fprintf(stderr, "Error: please provide a name for sandbox\n"); |
2167 | return 1; | 2183 | return 1; |
2168 | } | 2184 | } |
2185 | const char *c = cfg.name; | ||
2186 | while (*c) { | ||
2187 | if (!isdigit(*c)) { | ||
2188 | only_numbers = 0; | ||
2189 | break; | ||
2190 | } | ||
2191 | ++c; | ||
2192 | } | ||
2193 | if (only_numbers) { | ||
2194 | fprintf(stderr, "Error: invalid sandbox name: it only contains digits\n"); | ||
2195 | return 1; | ||
2196 | } | ||
2169 | } | 2197 | } |
2170 | else if (strncmp(argv[i], "--hostname=", 11) == 0) { | 2198 | else if (strncmp(argv[i], "--hostname=", 11) == 0) { |
2171 | cfg.hostname = argv[i] + 11; | 2199 | cfg.hostname = argv[i] + 11; |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index acf206da6..3924465e4 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -326,11 +326,24 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
326 | } | 326 | } |
327 | // sandbox name | 327 | // sandbox name |
328 | else if (strncmp(ptr, "name ", 5) == 0) { | 328 | else if (strncmp(ptr, "name ", 5) == 0) { |
329 | int only_numbers = 1; | ||
329 | cfg.name = ptr + 5; | 330 | cfg.name = ptr + 5; |
330 | if (strlen(cfg.name) == 0) { | 331 | if (strlen(cfg.name) == 0) { |
331 | fprintf(stderr, "Error: invalid sandbox name\n"); | 332 | fprintf(stderr, "Error: invalid sandbox name\n"); |
332 | exit(1); | 333 | exit(1); |
333 | } | 334 | } |
335 | const char *c = cfg.name; | ||
336 | while (*c) { | ||
337 | if (!isdigit(*c)) { | ||
338 | only_numbers = 0; | ||
339 | break; | ||
340 | } | ||
341 | ++c; | ||
342 | } | ||
343 | if (only_numbers) { | ||
344 | fprintf(stderr, "Error: invalid sandbox name: it only contains digits\n"); | ||
345 | exit(1); | ||
346 | } | ||
334 | return 0; | 347 | return 0; |
335 | } | 348 | } |
336 | else if (strcmp(ptr, "ipc-namespace") == 0) { | 349 | else if (strcmp(ptr, "ipc-namespace") == 0) { |
@@ -1222,6 +1235,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1222 | return 0; | 1235 | return 0; |
1223 | } | 1236 | } |
1224 | 1237 | ||
1238 | if (strcmp(ptr, "keep-shell-rc") == 0) { | ||
1239 | arg_keep_shell_rc = 1; | ||
1240 | return 0; | ||
1241 | } | ||
1242 | |||
1225 | // writable-var | 1243 | // writable-var |
1226 | if (strcmp(ptr, "writable-var") == 0) { | 1244 | if (strcmp(ptr, "writable-var") == 0) { |
1227 | arg_writable_var = 1; | 1245 | arg_writable_var = 1; |
@@ -1366,6 +1384,20 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1366 | return 0; | 1384 | return 0; |
1367 | } | 1385 | } |
1368 | 1386 | ||
1387 | // private /etc without a list of files and directories | ||
1388 | if (strcmp(ptr, "private-etc") == 0) { | ||
1389 | if (checkcfg(CFG_PRIVATE_ETC)) { | ||
1390 | if (arg_writable_etc) { | ||
1391 | fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); | ||
1392 | exit(1); | ||
1393 | } | ||
1394 | arg_private_etc = 1; | ||
1395 | } | ||
1396 | else | ||
1397 | warning_feature_disabled("private-etc"); | ||
1398 | return 0; | ||
1399 | } | ||
1400 | |||
1369 | // private /opt list of files and directories | 1401 | // private /opt list of files and directories |
1370 | if (strncmp(ptr, "private-opt ", 12) == 0) { | 1402 | if (strncmp(ptr, "private-opt ", 12) == 0) { |
1371 | if (checkcfg(CFG_PRIVATE_OPT)) { | 1403 | if (checkcfg(CFG_PRIVATE_OPT)) { |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 77fe73174..ec3bc250e 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -580,8 +580,6 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) { | |||
580 | if (arg_debug) | 580 | if (arg_debug) |
581 | printf("Running %s command through %s\n", cfg.command_line, cfg.usershell); | 581 | printf("Running %s command through %s\n", cfg.command_line, cfg.usershell); |
582 | arg[index++] = "-c"; | 582 | arg[index++] = "-c"; |
583 | if (arg_doubledash) | ||
584 | arg[index++] = "--"; | ||
585 | arg[index++] = cfg.command_line; | 583 | arg[index++] = cfg.command_line; |
586 | } | 584 | } |
587 | else if (login_shell) { | 585 | else if (login_shell) { |
@@ -1032,6 +1030,7 @@ int sandbox(void* sandbox_arg) { | |||
1032 | * 3. mount RUN_ETC_DIR at /etc | 1030 | * 3. mount RUN_ETC_DIR at /etc |
1033 | */ | 1031 | */ |
1034 | timetrace_start(); | 1032 | timetrace_start(); |
1033 | cfg.etc_private_keep = fs_etc_build(cfg.etc_private_keep); | ||
1035 | fs_private_dir_copy("/etc", RUN_ETC_DIR, cfg.etc_private_keep); | 1034 | fs_private_dir_copy("/etc", RUN_ETC_DIR, cfg.etc_private_keep); |
1036 | 1035 | ||
1037 | if (umount2("/etc/group", MNT_DETACH) == -1) | 1036 | if (umount2("/etc/group", MNT_DETACH) == -1) |
@@ -1048,7 +1047,8 @@ int sandbox(void* sandbox_arg) { | |||
1048 | 1047 | ||
1049 | // openSUSE configuration is split between /etc and /usr/etc | 1048 | // openSUSE configuration is split between /etc and /usr/etc |
1050 | // process private-etc a second time | 1049 | // process private-etc a second time |
1051 | fs_private_dir_list("/usr/etc", RUN_USR_ETC_DIR, cfg.etc_private_keep); | 1050 | if (access("/usr/etc", F_OK) == 0) |
1051 | fs_private_dir_list("/usr/etc", RUN_USR_ETC_DIR, cfg.etc_private_keep); | ||
1052 | } | 1052 | } |
1053 | } | 1053 | } |
1054 | 1054 | ||
@@ -1098,10 +1098,10 @@ int sandbox(void* sandbox_arg) { | |||
1098 | fs_dev_disable_input(); | 1098 | fs_dev_disable_input(); |
1099 | 1099 | ||
1100 | //**************************** | 1100 | //**************************** |
1101 | // rebuild etc directory, set dns | 1101 | // set DNS |
1102 | //**************************** | 1102 | //**************************** |
1103 | if (!arg_writable_etc) | 1103 | if (cfg.dns1 != NULL || any_dhcp()) |
1104 | fs_rebuild_etc(); | 1104 | fs_resolvconf(); |
1105 | 1105 | ||
1106 | //**************************** | 1106 | //**************************** |
1107 | // start dhcp client | 1107 | // start dhcp client |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 0a4c8a483..e31293c66 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -30,10 +30,12 @@ static char *usage_str = | |||
30 | " -- - signal the end of options and disables further option processing.\n" | 30 | " -- - signal the end of options and disables further option processing.\n" |
31 | " --allow-debuggers - allow tools such as strace and gdb inside the sandbox.\n" | 31 | " --allow-debuggers - allow tools such as strace and gdb inside the sandbox.\n" |
32 | " --allusers - all user home directories are visible inside the sandbox.\n" | 32 | " --allusers - all user home directories are visible inside the sandbox.\n" |
33 | #ifdef HAVE_APPARMOR | ||
33 | " --apparmor - enable AppArmor confinement with the default profile.\n" | 34 | " --apparmor - enable AppArmor confinement with the default profile.\n" |
34 | " --apparmor=profile_name - enable AppArmor confinement with a\n" | 35 | " --apparmor=profile_name - enable AppArmor confinement with a\n" |
35 | "\tcustom profile.\n" | 36 | "\tcustom profile.\n" |
36 | " --apparmor.print=name|pid - print apparmor status.\n" | 37 | " --apparmor.print=name|pid - print apparmor status.\n" |
38 | #endif | ||
37 | " --appimage - sandbox an AppImage application.\n" | 39 | " --appimage - sandbox an AppImage application.\n" |
38 | #ifdef HAVE_NETWORK | 40 | #ifdef HAVE_NETWORK |
39 | " --bandwidth=name|pid - set bandwidth limits.\n" | 41 | " --bandwidth=name|pid - set bandwidth limits.\n" |
@@ -127,6 +129,7 @@ static char *usage_str = | |||
127 | " --keep-config-pulse - disable automatic ~/.config/pulse init.\n" | 129 | " --keep-config-pulse - disable automatic ~/.config/pulse init.\n" |
128 | " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" | 130 | " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" |
129 | " --keep-fd - inherit open file descriptors to sandbox.\n" | 131 | " --keep-fd - inherit open file descriptors to sandbox.\n" |
132 | " --keep-shell-rc - do not copy shell rc files from /etc/skel\n" | ||
130 | " --keep-var-tmp - /var/tmp directory is untouched.\n" | 133 | " --keep-var-tmp - /var/tmp directory is untouched.\n" |
131 | " --list - list all sandboxes.\n" | 134 | " --list - list all sandboxes.\n" |
132 | #ifdef HAVE_FILE_TRANSFER | 135 | #ifdef HAVE_FILE_TRANSFER |
diff --git a/src/firejail/util.c b/src/firejail/util.c index a01290cf2..b35225620 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -255,7 +255,6 @@ static void clean_supplementary_groups(gid_t gid) { | |||
255 | return; | 255 | return; |
256 | 256 | ||
257 | clean_all: | 257 | clean_all: |
258 | fwarning("cleaning all supplementary groups\n"); | ||
259 | if (setgroups(0, NULL) < 0) | 258 | if (setgroups(0, NULL) < 0) |
260 | errExit("setgroups"); | 259 | errExit("setgroups"); |
261 | } | 260 | } |
diff --git a/src/include/etc_groups.h b/src/include/etc_groups.h new file mode 100644 index 000000000..dca767934 --- /dev/null +++ b/src/include/etc_groups.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2022 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef ETC_GROUPS_H | ||
22 | #define ETC_GROUPS_H | ||
23 | #include <stddef.h> | ||
24 | |||
25 | #define ETC_MAX 256 | ||
26 | |||
27 | // @default | ||
28 | static char *etc_list[ETC_MAX + 1] = { // plus 1 for ending NULL pointer | ||
29 | "alternatives", | ||
30 | "fonts", | ||
31 | "group", | ||
32 | "ld.so.cache", | ||
33 | "ld.so.conf", | ||
34 | "ld.so.conf.d", | ||
35 | "ld.so.preload", | ||
36 | "locale", | ||
37 | "locale.alias", | ||
38 | "locale.conf", | ||
39 | "localtime", | ||
40 | "login.defs", // firejail reading UID/GID MIN and MAX at startup | ||
41 | "nsswitch.conf", | ||
42 | "passwd", | ||
43 | "selinux", | ||
44 | NULL | ||
45 | }; | ||
46 | |||
47 | // @games | ||
48 | static char *etc_group_games[] = { | ||
49 | "openal", // 3D sound | ||
50 | "timidity", // MIDI | ||
51 | "timidity.cfg", | ||
52 | "vulkan", // next generation OpenGL stack | ||
53 | NULL | ||
54 | }; | ||
55 | |||
56 | // @network | ||
57 | static char*etc_group_network[] = { | ||
58 | "hostname", | ||
59 | "hosts", | ||
60 | "protocols", | ||
61 | "resolv.conf", | ||
62 | NULL | ||
63 | }; | ||
64 | |||
65 | // @sound | ||
66 | static char *etc_group_sound[] = { | ||
67 | "alsa", | ||
68 | "asound.conf", | ||
69 | "machine-id", // required by PulseAudio | ||
70 | "pulse", | ||
71 | NULL | ||
72 | }; | ||
73 | |||
74 | // @tls-ca | ||
75 | static char *etc_group_tls_ca[] = { | ||
76 | "ca-certificates", | ||
77 | "crypto-policies", | ||
78 | "gcrypt", // GNU crypto library - contains hardware config for various encryption schemes | ||
79 | // and random number generators. The file is not installed by Debian. | ||
80 | "pki", | ||
81 | "ssl", | ||
82 | NULL | ||
83 | }; | ||
84 | |||
85 | // @x11 | ||
86 | static char *etc_group_x11[] = { | ||
87 | "ati", // 3D | ||
88 | "dconf", | ||
89 | "drirc", | ||
90 | "gtk-2.0", | ||
91 | "gtk-3.0", | ||
92 | "kde4rc", | ||
93 | "kde5rc", | ||
94 | "machine-id", // QT dbus lib is crashing without it! | ||
95 | "nvidia", // 3D | ||
96 | "pango", // text rendering/internationalization | ||
97 | "Trolltech.conf", // old QT config file | ||
98 | "X11", | ||
99 | "xdg", | ||
100 | NULL | ||
101 | }; | ||
102 | |||
103 | #endif | ||
diff --git a/src/include/rundefs.h b/src/include/rundefs.h index 079670f10..b3ad564ac 100644 --- a/src/include/rundefs.h +++ b/src/include/rundefs.h | |||
@@ -99,5 +99,7 @@ | |||
99 | #define RUN_UMASK_FILE RUN_MNT_DIR "/umask" | 99 | #define RUN_UMASK_FILE RUN_MNT_DIR "/umask" |
100 | #define RUN_JOIN_FILE RUN_MNT_DIR "/join" | 100 | #define RUN_JOIN_FILE RUN_MNT_DIR "/join" |
101 | #define RUN_OVERLAY_ROOT RUN_MNT_DIR "/oroot" | 101 | #define RUN_OVERLAY_ROOT RUN_MNT_DIR "/oroot" |
102 | #define RUN_RESOLVCONF_FILE RUN_MNT_DIR "/resolv.conf" | ||
103 | |||
102 | 104 | ||
103 | #endif | 105 | #endif |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 5b16179ac..3fa07d1ee 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -288,6 +288,9 @@ pulse servers or non-standard socket paths. | |||
288 | \fBkeep-dev-shm | 288 | \fBkeep-dev-shm |
289 | /dev/shm directory is untouched (even with private-dev). | 289 | /dev/shm directory is untouched (even with private-dev). |
290 | .TP | 290 | .TP |
291 | \fBkeep-shell-rc | ||
292 | Do not copy shell rc files (such as ~/.bashrc and ~/.zshrc) from /etc/skel. | ||
293 | .TP | ||
291 | \fBkeep-var-tmp | 294 | \fBkeep-var-tmp |
292 | /var/tmp directory is untouched. | 295 | /var/tmp directory is untouched. |
293 | .TP | 296 | .TP |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index e5020e37e..6068c9ff4 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -1224,6 +1224,14 @@ Example: | |||
1224 | $ firejail --keep-fd=3,4,5 | 1224 | $ firejail --keep-fd=3,4,5 |
1225 | 1225 | ||
1226 | .TP | 1226 | .TP |
1227 | \fB\-\-keep-shell-rc | ||
1228 | By default, when using a private home directory, firejail copies files from the | ||
1229 | system's user home template (/etc/skel) into it, which overrides attempts to | ||
1230 | whitelist the original files (such as ~/.bashrc and ~/.zshrc). | ||
1231 | This option disables this feature, and enables the user to whitelist the | ||
1232 | original files. | ||
1233 | |||
1234 | .TP | ||
1227 | \fB\-\-keep-var-tmp | 1235 | \fB\-\-keep-var-tmp |
1228 | /var/tmp directory is untouched. | 1236 | /var/tmp directory is untouched. |
1229 | .br | 1237 | .br |
@@ -1330,6 +1338,7 @@ $ firejail \-\-net=eth0 \-\-mtu=1492 | |||
1330 | \fB\-\-name=name | 1338 | \fB\-\-name=name |
1331 | Set sandbox name. Several options, such as \-\-join and \-\-shutdown, can use | 1339 | Set sandbox name. Several options, such as \-\-join and \-\-shutdown, can use |
1332 | this name to identify a sandbox. | 1340 | this name to identify a sandbox. |
1341 | The name cannot contain only digits, as that is treated as a PID in the other options, such as in \-\-join. | ||
1333 | 1342 | ||
1334 | In case the name supplied by the user is already in use by another sandbox, Firejail will assign a | 1343 | In case the name supplied by the user is already in use by another sandbox, Firejail will assign a |
1335 | new name as "name-PID", where PID is the process ID of the sandbox. This functionality | 1344 | new name as "name-PID", where PID is the process ID of the sandbox. This functionality |
@@ -2127,22 +2136,32 @@ cdrom cdrw dri dvd dvdrw full log null ptmx pts random shm snd sr0 | |||
2127 | .br | 2136 | .br |
2128 | $ | 2137 | $ |
2129 | .TP | 2138 | .TP |
2130 | \fB\-\-private-etc=file,directory | 2139 | \fB\-\-private-etc, \-\-private-etc=file,directory,@group |
2131 | Build a new /etc in a temporary | 2140 | The files installed by \-\-private-etc are copies of the original system files from /etc directory. |
2132 | filesystem, and copy the files and directories in the list. | 2141 | By default, the command brings in a skeleton of files and directories used by most console tools: |
2133 | The files and directories in the list must be expressed as relative to | ||
2134 | the /etc directory (e.g., /etc/foo must be expressed as foo). | ||
2135 | If no listed file is found, /etc directory will be empty. | ||
2136 | All modifications are discarded when the sandbox is closed. | ||
2137 | Multiple private-etc commands are allowed and they accumulate. | ||
2138 | .br | ||
2139 | 2142 | ||
2140 | .br | 2143 | $ firejail --private-etc dig debian.org |
2141 | Example: | 2144 | |
2142 | .br | 2145 | For X11/GTK/QT/Gnome/KDE programs add @x11 group as a parameter. Example: |
2143 | $ firejail --private-etc=group,hostname,localtime, \\ | 2146 | |
2144 | .br | 2147 | $ firejail --private-etc=@x11,gcrypt,python* gimp |
2145 | nsswitch.conf,passwd,resolv.conf | 2148 | |
2149 | gcrypt and /etc/python* directories are not part of the generic @x11 group. | ||
2150 | File globbing is supported. | ||
2151 | |||
2152 | For games, add @games group: | ||
2153 | |||
2154 | $ firejail --private-etc=@games,@x11 warzone2100 | ||
2155 | |||
2156 | Sound and networking files are included automatically, unless \-\-nosound or \-\-net=none are specified. | ||
2157 | Files for encrypted TLS/SSL protocol are in @tls-ca group. | ||
2158 | |||
2159 | $ firejail --private-etc=@tls-ca,wgetrc wget https://debian.org | ||
2160 | |||
2161 | |||
2162 | Note: The easiest way to extract the list of /etc files accessed by your program is using strace utility: | ||
2163 | |||
2164 | $ strace /usr/bin/transmission-qt 2>&1 | grep open | grep etc | ||
2146 | #ifdef HAVE_PRIVATE_HOME | 2165 | #ifdef HAVE_PRIVATE_HOME |
2147 | .TP | 2166 | .TP |
2148 | \fB\-\-private-home=file,directory | 2167 | \fB\-\-private-home=file,directory |
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in index 2b67c2a00..37ce7055b 100644 --- a/src/zsh_completion/_firejail.in +++ b/src/zsh_completion/_firejail.in | |||
@@ -104,6 +104,7 @@ _firejail_args=( | |||
104 | '--keep-config-pulse[disable automatic ~/.config/pulse init]' | 104 | '--keep-config-pulse[disable automatic ~/.config/pulse init]' |
105 | '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' | 105 | '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' |
106 | '--keep-fd[inherit open file descriptors to sandbox]: :' | 106 | '--keep-fd[inherit open file descriptors to sandbox]: :' |
107 | '--keep-shell-rc[do not copy shell rc files from /etc/skel]' | ||
107 | '--keep-var-tmp[/var/tmp directory is untouched]' | 108 | '--keep-var-tmp[/var/tmp directory is untouched]' |
108 | '--machine-id[spoof /etc/machine-id with a random id]' | 109 | '--machine-id[spoof /etc/machine-id with a random id]' |
109 | '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' | 110 | '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' |