diff options
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | RELNOTES | 12 | ||||
-rw-r--r-- | src/firecfg/firecfg.config | 2 | ||||
-rw-r--r-- | src/firecfg/main.c | 2 | ||||
-rw-r--r-- | src/firejail/appimage.c | 2 | ||||
-rw-r--r-- | src/firejail/firejail.h | 12 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 2 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 509 | ||||
-rw-r--r-- | src/firejail/join.c | 4 | ||||
-rw-r--r-- | src/firejail/ls.c | 5 | ||||
-rw-r--r-- | src/firejail/macros.c | 284 | ||||
-rw-r--r-- | src/firejail/main.c | 25 | ||||
-rw-r--r-- | src/firejail/profile.c | 46 | ||||
-rw-r--r-- | src/firejail/run_symlink.c | 1 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 4 | ||||
-rw-r--r-- | src/firejail/usage.c | 2 | ||||
-rw-r--r-- | src/firejail/util.c | 129 | ||||
-rw-r--r-- | src/firemon/arp.c | 2 | ||||
-rw-r--r-- | src/firemon/route.c | 6 | ||||
-rw-r--r-- | src/firemon/usage.c | 2 | ||||
-rw-r--r-- | src/fnet/veth.c | 4 | ||||
-rw-r--r-- | src/fnetfilter/main.c | 8 | ||||
-rw-r--r-- | src/fsec-print/print.c | 2 | ||||
-rw-r--r-- | src/include/libnetlink.h | 4 | ||||
-rw-r--r-- | src/lib/libnetlink.c | 4 | ||||
-rw-r--r-- | src/libtracelog/libtracelog.c | 15 |
26 files changed, 574 insertions, 519 deletions
@@ -243,6 +243,8 @@ Fred-Barclay (https://github.com/Fred-Barclay) | |||
243 | - added BibleTime profile | 243 | - added BibleTime profile |
244 | - added caja and galculator profiles | 244 | - added caja and galculator profiles |
245 | - added Catfish profile | 245 | - added Catfish profile |
246 | Frederik Olesen (https://github.com/Freso) | ||
247 | - added many vim profiles | ||
246 | g3ngr33n (https://github.com/g3ngr33n) | 248 | g3ngr33n (https://github.com/g3ngr33n) |
247 | - fix musl compilation | 249 | - fix musl compilation |
248 | G4JC (https://sourceforge.net/u/gaming4jc/profile/) | 250 | G4JC (https://sourceforge.net/u/gaming4jc/profile/) |
@@ -318,6 +320,7 @@ Jean Lucas (https://github.com/flacks) | |||
318 | - alias for riot-desktop | 320 | - alias for riot-desktop |
319 | - add gnome-mpv profile | 321 | - add gnome-mpv profile |
320 | - fix wire profile | 322 | - fix wire profile |
323 | - add Beaker profile | ||
321 | Jericho (https://github.com/attritionorg) | 324 | Jericho (https://github.com/attritionorg) |
322 | - spelling | 325 | - spelling |
323 | Jesse Smith (https://github.com/slicer69) | 326 | Jesse Smith (https://github.com/slicer69) |
@@ -487,6 +490,8 @@ rogshdo (https://github.com/rogshdo) | |||
487 | - BitlBee profile | 490 | - BitlBee profile |
488 | Ruan (https://github.com/ruany) | 491 | Ruan (https://github.com/ruany) |
489 | - fixed hexchat profile | 492 | - fixed hexchat profile |
493 | Salvo 'LtWorf' Tomaselli (https://github.com/ltworf) | ||
494 | - fixed ktorrent profile | ||
490 | sarneaud (https://github.com/sarneaud) | 495 | sarneaud (https://github.com/sarneaud) |
491 | - rewrite globbing code to fix various minor issues | 496 | - rewrite globbing code to fix various minor issues |
492 | - added noblacklist command for profile files | 497 | - added noblacklist command for profile files |
@@ -1,4 +1,4 @@ | |||
1 | firejail (0.9.55) baseline; urgency=low | 1 | firejail (0.9.56~rc1) baseline; urgency=low |
2 | * work in progress | 2 | * work in progress |
3 | * modif: removed CFG_CHROOT_DESKTOP configuration option | 3 | * modif: removed CFG_CHROOT_DESKTOP configuration option |
4 | * modif: removed compile time --enable-network=restricted | 4 | * modif: removed compile time --enable-network=restricted |
@@ -12,10 +12,12 @@ firejail (0.9.55) baseline; urgency=low | |||
12 | * add --private-cache to support private ~/.cache | 12 | * add --private-cache to support private ~/.cache |
13 | * support full paths in private-lib | 13 | * support full paths in private-lib |
14 | * globbing support in private-lib | 14 | * globbing support in private-lib |
15 | * new profiles: ms-excel, ms-office, ms-onenote, ms-outlook, ms-powerpoint | 15 | * new profiles: ms-excel, ms-office, ms-onenote, ms-outlook, ms-powerpoint, |
16 | * new profiles: ms-skype, ms-word, riot-desktop, gnome-mpv, snox, gradio | 16 | * new profiles: ms-skype, ms-word, riot-desktop, gnome-mpv, snox, gradio, |
17 | * new profiles: standardnotes-desktop, shellcheck, patch, flameshot | 17 | * new profiles: standardnotes-desktop, shellcheck, patch, flameshot, |
18 | -- netblue30 <netblue30@yahoo.com> Fri, 25 May 2018 08:00:00 -0500 | 18 | * new profiles: rview, rvim, vimcat, vimdiff, vimpager, vimtutor, xxd, |
19 | * new profiles: Beaker, electrum | ||
20 | -- netblue30 <netblue30@yahoo.com> Sat, 11 Aug 2018 08:00:00 -0500 | ||
19 | 21 | ||
20 | firejail (0.9.54) baseline; urgency=low | 22 | firejail (0.9.54) baseline; urgency=low |
21 | * modif: --force removed | 23 | * modif: --force removed |
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config index 2585d204a..a33aaeb49 100644 --- a/src/firecfg/firecfg.config +++ b/src/firecfg/firecfg.config | |||
@@ -46,6 +46,7 @@ baloo_file | |||
46 | baloo_filemetadata_temp_extractor | 46 | baloo_filemetadata_temp_extractor |
47 | baobab | 47 | baobab |
48 | basilisk | 48 | basilisk |
49 | beaker | ||
49 | bibletime | 50 | bibletime |
50 | bitlbee | 51 | bitlbee |
51 | bleachbit | 52 | bleachbit |
@@ -108,6 +109,7 @@ dosbox | |||
108 | dragon | 109 | dragon |
109 | dropbox | 110 | dropbox |
110 | ebook-viewer | 111 | ebook-viewer |
112 | electrum | ||
111 | elinks | 113 | elinks |
112 | empathy | 114 | empathy |
113 | enchant | 115 | enchant |
diff --git a/src/firecfg/main.c b/src/firecfg/main.c index b79053d3e..6fe220d35 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c | |||
@@ -53,7 +53,7 @@ static char *usage_str = | |||
53 | " [...]\n" | 53 | " [...]\n" |
54 | "\n" | 54 | "\n" |
55 | "License GPL version 2 or later\n" | 55 | "License GPL version 2 or later\n" |
56 | "Homepage: http://firejail.wordpress.com\n\n"; | 56 | "Homepage: https://firejail.wordpress.com\n\n"; |
57 | 57 | ||
58 | static void usage(void) { | 58 | static void usage(void) { |
59 | printf("firecfg - version %s\n\n", VERSION); | 59 | printf("firecfg - version %s\n\n", VERSION); |
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 631276c0b..e8db91958 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c | |||
@@ -17,7 +17,7 @@ | |||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | // http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=770fe30a46a12b6fb6b63fbe1737654d28e84844 | 20 | // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=770fe30a46a12b6fb6b63fbe1737654d28e84844 |
21 | // sudo mount -o loop krita-3.0-x86_64.appimage mnt | 21 | // sudo mount -o loop krita-3.0-x86_64.appimage mnt |
22 | 22 | ||
23 | #include "firejail.h" | 23 | #include "firejail.h" |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 0faf10340..f31d6a2bc 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -451,6 +451,7 @@ void fs_chroot(const char *rootdir); | |||
451 | void fs_check_chroot_dir(const char *rootdir); | 451 | void fs_check_chroot_dir(const char *rootdir); |
452 | void fs_private_tmp(void); | 452 | void fs_private_tmp(void); |
453 | void fs_private_cache(void); | 453 | void fs_private_cache(void); |
454 | void fs_mnt(void); | ||
454 | 455 | ||
455 | // profile.c | 456 | // profile.c |
456 | // find and read the profile specified by name from dir directory | 457 | // find and read the profile specified by name from dir directory |
@@ -463,7 +464,7 @@ void profile_read(const char *fname); | |||
463 | int profile_check_line(char *ptr, int lineno, const char *fname); | 464 | int profile_check_line(char *ptr, int lineno, const char *fname); |
464 | // add a profile entry in cfg.profile list; use str to populate the list | 465 | // add a profile entry in cfg.profile list; use str to populate the list |
465 | void profile_add(char *str); | 466 | void profile_add(char *str); |
466 | void fs_mnt(void); | 467 | void profile_add_ignore(const char *str); |
467 | 468 | ||
468 | // list.c | 469 | // list.c |
469 | void list(void); | 470 | void list(void); |
@@ -490,6 +491,13 @@ int arp_check(const char *dev, uint32_t destaddr); | |||
490 | // assign an IP address using arp scanning | 491 | // assign an IP address using arp scanning |
491 | uint32_t arp_assign(const char *dev, Bridge *br); | 492 | uint32_t arp_assign(const char *dev, Bridge *br); |
492 | 493 | ||
494 | // macros.c | ||
495 | char *expand_home(const char *path, const char *homedir); | ||
496 | char *resolve_macro(const char *name); | ||
497 | void invalid_filename(const char *fname, int globbing); | ||
498 | int is_macro(const char *name); | ||
499 | |||
500 | |||
493 | // util.c | 501 | // util.c |
494 | void errLogExit(char* fmt, ...); | 502 | void errLogExit(char* fmt, ...); |
495 | void fwarning(char* fmt, ...); | 503 | void fwarning(char* fmt, ...); |
@@ -515,10 +523,8 @@ void check_private_dir(void); | |||
515 | void update_map(char *mapping, char *map_file); | 523 | void update_map(char *mapping, char *map_file); |
516 | void wait_for_other(int fd); | 524 | void wait_for_other(int fd); |
517 | void notify_other(int fd); | 525 | void notify_other(int fd); |
518 | char *expand_home(const char *path, const char* homedir); | ||
519 | const char *gnu_basename(const char *path); | 526 | const char *gnu_basename(const char *path); |
520 | uid_t pid_get_uid(pid_t pid); | 527 | uid_t pid_get_uid(pid_t pid); |
521 | void invalid_filename(const char *fname, int globbing); | ||
522 | uid_t get_group_id(const char *group); | 528 | uid_t get_group_id(const char *group); |
523 | int remove_overlay_directory(void); | 529 | int remove_overlay_directory(void); |
524 | void flush_stdin(void); | 530 | void flush_stdin(void); |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index f8e7e6e74..3a332f7ff 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -290,6 +290,8 @@ void fs_private(void) { | |||
290 | if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root | 290 | if (u == 0 && arg_allusers) // allow --allusers when starting the sandbox as root |
291 | ; | 291 | ; |
292 | else { | 292 | else { |
293 | if (arg_allusers) | ||
294 | fwarning("--allusers disabled by --private or --whitelist\n"); | ||
293 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) | 295 | if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) |
294 | errExit("mounting home directory"); | 296 | errExit("mounting home directory"); |
295 | fs_logger("tmpfs /home"); | 297 | fs_logger("tmpfs /home"); |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index bf839b524..c3d34e259 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -29,207 +29,95 @@ | |||
29 | 29 | ||
30 | // mountinfo functionality test; | 30 | // mountinfo functionality test; |
31 | // 1. enable TEST_MOUNTINFO definition | 31 | // 1. enable TEST_MOUNTINFO definition |
32 | // 2. set a symlink in /tmp: ln -s /etc /tmp/etc | 32 | // 2. run firejail --whitelist=/any/directory |
33 | // 3. run firejail --debug --whitelist=/tmp/etc | ||
34 | //#define TEST_MOUNTINFO | 33 | //#define TEST_MOUNTINFO |
35 | 34 | ||
36 | static char *dentry[] = { | ||
37 | "Downloads", | ||
38 | "Загрузки", | ||
39 | "Téléchargement", | ||
40 | NULL | ||
41 | }; | ||
42 | |||
43 | static char *mentry[] = { | ||
44 | "Music", | ||
45 | "Музыка", | ||
46 | "Musique", | ||
47 | NULL | ||
48 | }; | ||
49 | |||
50 | static char *ventry[] = { | ||
51 | "Videos", | ||
52 | "Видео", | ||
53 | "Vidéos", | ||
54 | NULL | ||
55 | }; | ||
56 | |||
57 | static char *pentry[] = { | ||
58 | "Pictures", | ||
59 | "Изображения", | ||
60 | "Photos", | ||
61 | NULL | ||
62 | }; | ||
63 | |||
64 | static char *deentry[] = { | ||
65 | "Desktop", | ||
66 | "Рабочий стол", | ||
67 | "Bureau", | ||
68 | NULL | ||
69 | }; | ||
70 | |||
71 | static char *doentry[] = { | ||
72 | "Documents", | ||
73 | "Документы", | ||
74 | "Documents", | ||
75 | NULL | ||
76 | }; | ||
77 | |||
78 | #define EMPTY_STRING ("") | 35 | #define EMPTY_STRING ("") |
79 | #define MAXBUF 4098 | 36 | #define MAXBUF 4098 |
80 | 37 | ||
81 | static char *resolve_xdg(int nowhitelist_flag, const char *var, size_t length, const char *prnt) { | 38 | // returns mallocated memory |
82 | EUID_ASSERT(); | 39 | char *parse_nowhitelist(int nowhitelist_flag, char *ptr1) { |
83 | char *fname; | 40 | char *rv; |
84 | struct stat s; | 41 | if (nowhitelist_flag) { |
85 | 42 | if (asprintf(&rv, "nowhitelist ~/%s", ptr1) == -1) | |
86 | if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1) | 43 | errExit("asprintf"); |
87 | errExit("asprintf"); | ||
88 | FILE *fp = fopen(fname, "r"); | ||
89 | if (!fp) { | ||
90 | free(fname); | ||
91 | return NULL; | ||
92 | } | ||
93 | free(fname); | ||
94 | |||
95 | char buf[MAXBUF]; | ||
96 | while (fgets(buf, MAXBUF, fp)) { | ||
97 | char *ptr = buf; | ||
98 | |||
99 | // skip blanks | ||
100 | while (*ptr == ' ' || *ptr == '\t') | ||
101 | ptr++; | ||
102 | if (*ptr == '\0' || *ptr == '\n' || *ptr == '#') | ||
103 | continue; | ||
104 | |||
105 | if (strncmp(ptr, var, length) == 0) { | ||
106 | char *ptr1 = ptr + length; | ||
107 | char *ptr2 = strchr(ptr1, '"'); | ||
108 | if (ptr2) { | ||
109 | fclose(fp); | ||
110 | *ptr2 = '\0'; | ||
111 | if (arg_debug || arg_debug_whitelists) | ||
112 | printf("extracted %s from ~/.config/user-dirs.dirs\n", ptr1); | ||
113 | if (strlen(ptr1) != 0) { | ||
114 | if (arg_debug || arg_debug_whitelists) | ||
115 | printf("%s ",prnt); | ||
116 | printf("directory resolved as \"%s\"\n", ptr1); | ||
117 | |||
118 | if (asprintf(&fname, "%s/%s", cfg.homedir, ptr1) == -1) | ||
119 | errExit("asprintf"); | ||
120 | |||
121 | if (stat(fname, &s) == -1) { | ||
122 | free(fname); | ||
123 | goto errout; | ||
124 | } | ||
125 | |||
126 | char *rv; | ||
127 | if (nowhitelist_flag) { | ||
128 | if (asprintf(&rv, "nowhitelist ~/%s", ptr + length) == -1) | ||
129 | errExit("asprintf"); | ||
130 | } | ||
131 | else { | ||
132 | if (asprintf(&rv, "whitelist ~/%s", ptr + length) == -1) | ||
133 | errExit("asprintf"); | ||
134 | } | ||
135 | return rv; | ||
136 | } | ||
137 | else | ||
138 | goto errout; | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | fclose(fp); | ||
144 | return NULL; | ||
145 | |||
146 | errout: | ||
147 | if (!arg_private) { | ||
148 | fprintf(stderr, "***\n"); | ||
149 | fprintf(stderr, "*** Error: %s directory was not found in user home.\n",prnt); | ||
150 | fprintf(stderr, "*** \tAny files saved by the program, will be lost when the sandbox is closed.\n"); | ||
151 | fprintf(stderr, "***\n"); | ||
152 | } | 44 | } |
153 | return NULL; | 45 | else { |
154 | } | 46 | if (asprintf(&rv, "whitelist ~/%s", ptr1) == -1) |
155 | |||
156 | static char *resolve_hardcoded(int nowhitelist_flag, char *entries[], const char *prnt) { | ||
157 | EUID_ASSERT(); | ||
158 | char *fname; | ||
159 | struct stat s; | ||
160 | |||
161 | int i = 0; | ||
162 | while (entries[i] != NULL) { | ||
163 | if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1) | ||
164 | errExit("asprintf"); | 47 | errExit("asprintf"); |
165 | |||
166 | if (stat(fname, &s) == 0) { | ||
167 | if (arg_debug || arg_debug_whitelists) { | ||
168 | printf("%s ", prnt); | ||
169 | printf("directory resolved as \"%s\"\n", fname); | ||
170 | } | ||
171 | |||
172 | char *rv; | ||
173 | if (nowhitelist_flag) { | ||
174 | if (asprintf(&rv, "nowhitelist ~/%s", entries[i]) == -1) | ||
175 | errExit("asprintf"); | ||
176 | } | ||
177 | else { | ||
178 | if (asprintf(&rv, "whitelist ~/%s", entries[i]) == -1) | ||
179 | errExit("asprintf"); | ||
180 | } | ||
181 | free(fname); | ||
182 | return rv; | ||
183 | } | ||
184 | free(fname); | ||
185 | i++; | ||
186 | } | 48 | } |
187 | 49 | return rv; | |
188 | return NULL; | ||
189 | } | 50 | } |
190 | 51 | ||
191 | static int mkpath(const char* path, mode_t mode) { | 52 | static int mkpath(const char* path, mode_t mode) { |
192 | assert(path && *path); | 53 | assert(path && *path); |
193 | |||
194 | mode |= 0111; | 54 | mode |= 0111; |
195 | 55 | ||
196 | // create directories with uid/gid as root or as current user if inside home directory | 56 | // create directories with uid/gid as root or as current user if inside home directory |
197 | uid_t uid = getuid(); | 57 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { |
198 | gid_t gid = getgid(); | 58 | EUID_USER(); |
199 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0) { | ||
200 | uid = 0; | ||
201 | gid = 0; | ||
202 | } | 59 | } |
203 | 60 | ||
204 | // work on a copy of the path | 61 | // work on a copy of the path |
205 | char *file_path = strdup(path); | 62 | char *dup = strdup(path); |
206 | if (!file_path) | 63 | if (!dup) |
207 | errExit("strdup"); | 64 | errExit("strdup"); |
208 | 65 | ||
209 | char* p; | 66 | // don't create the last path element |
67 | char *p = strrchr(dup, '/'); | ||
68 | assert(p); | ||
69 | *p = '\0'; | ||
70 | |||
71 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); | ||
72 | if (parentfd == -1) | ||
73 | errExit("open"); | ||
74 | |||
75 | // traverse the path, return -1 if a symlink is encountered | ||
210 | int done = 0; | 76 | int done = 0; |
211 | for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { | 77 | int fd = -1; |
212 | *p='\0'; | 78 | char *tok = strtok(dup, "/"); |
213 | if (mkdir(file_path, mode)==-1) { | 79 | assert(tok); // path is no top level directory |
80 | while (tok) { | ||
81 | // skip all instances of "/./" | ||
82 | if (strcmp(tok, ".") == 0) { | ||
83 | tok = strtok(NULL, "/"); | ||
84 | continue; | ||
85 | } | ||
86 | // create the directory if necessary | ||
87 | if (mkdirat(parentfd, tok, mode) == -1) { | ||
214 | if (errno != EEXIST) { | 88 | if (errno != EEXIST) { |
215 | *p='/'; | 89 | if (arg_debug || arg_debug_whitelists) |
216 | free(file_path); | 90 | perror("mkdir"); |
91 | close(parentfd); | ||
92 | free(dup); | ||
93 | EUID_ROOT(); | ||
217 | return -1; | 94 | return -1; |
218 | } | 95 | } |
219 | } | 96 | } |
220 | else { | 97 | else |
221 | if (set_perms(file_path, uid, gid, mode)) | ||
222 | errExit("set_perms"); | ||
223 | done = 1; | 98 | done = 1; |
99 | // open the directory | ||
100 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
101 | if (fd == -1) { | ||
102 | if (arg_debug || arg_debug_whitelists) | ||
103 | perror("open"); | ||
104 | close(parentfd); | ||
105 | free(dup); | ||
106 | EUID_ROOT(); | ||
107 | return -1; | ||
224 | } | 108 | } |
225 | 109 | // move on to next path segment | |
226 | *p='/'; | 110 | close(parentfd); |
111 | parentfd = fd; | ||
112 | tok = strtok(NULL, "/"); | ||
227 | } | 113 | } |
114 | |||
228 | if (done) | 115 | if (done) |
229 | fs_logger2("mkpath", path); | 116 | fs_logger2("mkpath", path); |
230 | 117 | ||
231 | free(file_path); | 118 | free(dup); |
232 | return 0; | 119 | EUID_ROOT(); |
120 | return fd; | ||
233 | } | 121 | } |
234 | 122 | ||
235 | static void whitelist_path(ProfileEntry *entry) { | 123 | static void whitelist_path(ProfileEntry *entry) { |
@@ -323,13 +211,17 @@ static void whitelist_path(ProfileEntry *entry) { | |||
323 | } | 211 | } |
324 | assert(wfile); | 212 | assert(wfile); |
325 | 213 | ||
326 | // check if the file exists, confirm again there is no symlink | 214 | if (arg_debug || arg_debug_whitelists) |
215 | printf("Whitelisting %s\n", path); | ||
216 | |||
217 | // confirm again the mount source exists and there is no symlink | ||
327 | struct stat wfilestat; | 218 | struct stat wfilestat; |
328 | #ifndef TEST_MOUNTINFO | ||
329 | EUID_USER(); | 219 | EUID_USER(); |
330 | int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 220 | int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
331 | EUID_ROOT(); | 221 | EUID_ROOT(); |
332 | if (fd == -1) { | 222 | if (fd == -1) { |
223 | if (arg_debug || arg_debug_whitelists) | ||
224 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | ||
333 | free(wfile); | 225 | free(wfile); |
334 | return; | 226 | return; |
335 | } | 227 | } |
@@ -337,57 +229,68 @@ static void whitelist_path(ProfileEntry *entry) { | |||
337 | errExit("fstat"); | 229 | errExit("fstat"); |
338 | close(fd); | 230 | close(fd); |
339 | if (S_ISLNK(wfilestat.st_mode)) { | 231 | if (S_ISLNK(wfilestat.st_mode)) { |
232 | if (arg_debug || arg_debug_whitelists) | ||
233 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | ||
340 | free(wfile); | 234 | free(wfile); |
341 | return; | 235 | return; |
342 | } | 236 | } |
237 | |||
238 | #ifdef TEST_MOUNTINFO | ||
239 | printf("TEST_MOUNTINFO\n"); | ||
240 | path = "/etc/."; | ||
343 | #endif | 241 | #endif |
344 | 242 | ||
345 | if (arg_debug || arg_debug_whitelists) | 243 | // create path of the mount target if necessary |
346 | printf("Whitelisting %s\n", path); | 244 | int fd2 = mkpath(path, 0755); |
245 | if (fd2 == -1) { | ||
246 | // something went wrong during path creation or a symlink was found; | ||
247 | // if there is a symlink somewhere in the path of the mount target, | ||
248 | // assume the file is whitelisted already | ||
249 | if (arg_debug || arg_debug_whitelists) | ||
250 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | ||
251 | free(wfile); | ||
252 | return; | ||
253 | } | ||
347 | 254 | ||
348 | // create the path if necessary | 255 | // get file name of the mount target |
349 | struct stat s; | 256 | const char *file = gnu_basename(path); |
350 | if (stat(path, &s) == -1) { | 257 | |
351 | mkpath(path, 0755); | 258 | // create the mount target if necessary and open it, a symlink is rejected |
352 | if (S_ISDIR(wfilestat.st_mode)) { | 259 | int fd3 = -1; |
353 | int rv = mkdir(path, 0755); | 260 | if (S_ISDIR(wfilestat.st_mode)) { |
354 | if (rv) { | 261 | // directory foo can exist already: |
355 | fprintf(stderr, "Error: cannot create directory %s\n", path); | 262 | // firejail --whitelist=/foo/bar --whitelist=/foo |
356 | exit(1); | 263 | if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { |
357 | } | 264 | if (arg_debug || arg_debug_whitelists) { |
358 | } | 265 | perror("mkdir"); |
359 | else { | 266 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
360 | int fd2 = open(path, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); | ||
361 | if (fd2 == -1) { | ||
362 | fprintf(stderr, "Error: cannot create empty file %s\n", path); | ||
363 | exit(1); | ||
364 | } | 267 | } |
365 | close(fd2); | 268 | close(fd2); |
269 | free(wfile); | ||
270 | return; | ||
366 | } | 271 | } |
272 | fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
367 | } | 273 | } |
368 | else { | 274 | else { |
369 | if (!S_ISDIR(s.st_mode)) { | 275 | // create an empty file, fails with EEXIST if it is whitelisted already: |
370 | free(wfile); | 276 | // firejail --whitelist=/foo --whitelist=/foo/bar |
371 | return; // the file is already present | 277 | fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); |
372 | } | ||
373 | } | 278 | } |
374 | 279 | ||
375 | fs_logger2("whitelist", path); | ||
376 | |||
377 | // get a file descriptor for path; if path contains anything other than directories | ||
378 | // or a regular file, assume it is whitelisted already | ||
379 | int fd3 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
380 | if (fd3 == -1) { | 280 | if (fd3 == -1) { |
281 | if (arg_debug || arg_debug_whitelists) { | ||
282 | if (errno != EEXIST) { | ||
283 | perror("open"); | ||
284 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | ||
285 | } | ||
286 | } | ||
287 | close(fd2); | ||
381 | free(wfile); | 288 | free(wfile); |
382 | return; | 289 | return; |
383 | } | 290 | } |
384 | if (fstat(fd3, &s) == -1) | 291 | close(fd2); |
385 | errExit("fstat"); | 292 | |
386 | if (!(S_ISDIR(s.st_mode) || S_ISREG(s.st_mode))) { | 293 | fs_logger2("whitelist", path); |
387 | free(wfile); | ||
388 | close(fd3); | ||
389 | return; | ||
390 | } | ||
391 | 294 | ||
392 | // mount via the link in /proc/self/fd | 295 | // mount via the link in /proc/self/fd |
393 | char *proc; | 296 | char *proc; |
@@ -411,6 +314,7 @@ static void whitelist_path(ProfileEntry *entry) { | |||
411 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 314 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
412 | if (fd4 == -1) | 315 | if (fd4 == -1) |
413 | errExit("safe_fd"); | 316 | errExit("safe_fd"); |
317 | struct stat s; | ||
414 | if (fstat(fd4, &s) == -1) | 318 | if (fstat(fd4, &s) == -1) |
415 | errExit("fstat"); | 319 | errExit("fstat"); |
416 | if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino) | 320 | if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino) |
@@ -465,99 +369,21 @@ void fs_whitelist(void) { | |||
465 | } | 369 | } |
466 | char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | 370 | char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; |
467 | 371 | ||
468 | // resolve ${DOWNLOADS} | 372 | // resolve macros |
469 | if (strcmp(dataptr, "${DOWNLOADS}") == 0) { | 373 | if (is_macro(dataptr)) { |
470 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_DOWNLOAD_DIR=\"$HOME/", 24, "Downloads"); | 374 | char *tmp = resolve_macro(dataptr); |
471 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, dentry, "Downloads"); | 375 | if (tmp != NULL) |
472 | if (tmp) { | 376 | tmp = parse_nowhitelist(nowhitelist_flag, tmp); |
473 | entry->data = tmp; | ||
474 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
475 | } | ||
476 | else if (tmp2) { | ||
477 | entry->data = tmp2; | ||
478 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
479 | } | ||
480 | else { | ||
481 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | ||
482 | fprintf(stderr, "***\n"); | ||
483 | fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n"); | ||
484 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | ||
485 | fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n"); | ||
486 | fprintf(stderr, "***\n"); | ||
487 | } | ||
488 | entry->data = EMPTY_STRING; | ||
489 | continue; | ||
490 | } | ||
491 | } | ||
492 | 377 | ||
493 | // resolve ${MUSIC} | ||
494 | if (strcmp(dataptr, "${MUSIC}") == 0) { | ||
495 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_MUSIC_DIR=\"$HOME/", 21, "Music"); | ||
496 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, mentry, "Music"); | ||
497 | if (tmp) { | 378 | if (tmp) { |
498 | entry->data = tmp; | 379 | entry->data = tmp; |
499 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | 380 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; |
500 | } | 381 | } |
501 | else if (tmp2) { | ||
502 | entry->data = tmp2; | ||
503 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
504 | } | ||
505 | else { | ||
506 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | ||
507 | fprintf(stderr, "***\n"); | ||
508 | fprintf(stderr, "*** Warning: cannot whitelist Music directory\n"); | ||
509 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | ||
510 | fprintf(stderr, "*** \tPlease create a proper Music directory for your application.\n"); | ||
511 | fprintf(stderr, "***\n"); | ||
512 | } | ||
513 | entry->data = EMPTY_STRING; | ||
514 | continue; | ||
515 | } | ||
516 | } | ||
517 | |||
518 | // resolve ${VIDEOS} | ||
519 | if (strcmp(dataptr, "${VIDEOS}") == 0) { | ||
520 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_VIDEOS_DIR=\"$HOME/", 22, "Videos"); | ||
521 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, ventry, "Videos"); | ||
522 | if (tmp) { | ||
523 | entry->data = tmp; | ||
524 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
525 | } | ||
526 | else if (tmp2) { | ||
527 | entry->data = tmp2; | ||
528 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
529 | } | ||
530 | else { | ||
531 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | ||
532 | fprintf(stderr, "***\n"); | ||
533 | fprintf(stderr, "*** Warning: cannot whitelist Videos directory\n"); | ||
534 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | ||
535 | fprintf(stderr, "*** \tPlease create a proper Videos directory for your application.\n"); | ||
536 | fprintf(stderr, "***\n"); | ||
537 | } | ||
538 | entry->data = EMPTY_STRING; | ||
539 | continue; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | // resolve ${PICTURES} | ||
544 | if (strcmp(dataptr, "${PICTURES}") == 0) { | ||
545 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_PICTURES_DIR=\"$HOME/", 24, "Pictures"); | ||
546 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, pentry, "Pictures"); | ||
547 | if (tmp) { | ||
548 | entry->data = tmp; | ||
549 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
550 | } | ||
551 | else if (tmp2) { | ||
552 | entry->data = tmp2; | ||
553 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
554 | } | ||
555 | else { | 382 | else { |
556 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | 383 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { |
557 | fprintf(stderr, "***\n"); | 384 | fprintf(stderr, "***\n"); |
558 | fprintf(stderr, "*** Warning: cannot whitelist Pictures directory\n"); | 385 | fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", dataptr); |
559 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | 386 | fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n"); |
560 | fprintf(stderr, "*** \tPlease create a proper Pictures directory for your application.\n"); | ||
561 | fprintf(stderr, "***\n"); | 387 | fprintf(stderr, "***\n"); |
562 | } | 388 | } |
563 | entry->data = EMPTY_STRING; | 389 | entry->data = EMPTY_STRING; |
@@ -565,59 +391,24 @@ void fs_whitelist(void) { | |||
565 | } | 391 | } |
566 | } | 392 | } |
567 | 393 | ||
568 | // resolve ${DESKTOP} | 394 | // replace ~/ or ${HOME} into /home/username |
569 | if (strcmp(dataptr, "${DESKTOP}") == 0) { | 395 | new_name = expand_home(dataptr, cfg.homedir); |
570 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_DESKTOP_DIR=\"$HOME/", 24, "Desktop"); | 396 | assert(new_name); |
571 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, deentry, "Desktop"); | ||
572 | if (tmp) { | ||
573 | entry->data = tmp; | ||
574 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
575 | } | ||
576 | else if (tmp2) { | ||
577 | entry->data = tmp2; | ||
578 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | ||
579 | } | ||
580 | else { | ||
581 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | ||
582 | fprintf(stderr, "***\n"); | ||
583 | fprintf(stderr, "*** Warning: cannot whitelist Desktop directory\n"); | ||
584 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | ||
585 | fprintf(stderr, "*** \tPlease create a proper Desktop directory for your application.\n"); | ||
586 | fprintf(stderr, "***\n"); | ||
587 | } | ||
588 | entry->data = EMPTY_STRING; | ||
589 | continue; | ||
590 | } | ||
591 | } | ||
592 | 397 | ||
593 | // resolve ${DOCUMENTS} | 398 | // trim trailing slashes or dots |
594 | if (strcmp(dataptr, "${DOCUMENTS}") == 0) { | 399 | char *end = strchr(new_name, '\0'); |
595 | char *tmp = resolve_xdg(nowhitelist_flag, "XDG_DOCUMENTS_DIR=\"$HOME/", 25, "Documents"); | 400 | assert(end); |
596 | char *tmp2 = resolve_hardcoded(nowhitelist_flag, doentry, "Documents"); | 401 | if ((end - new_name) > 1) { |
597 | if (tmp) { | 402 | end--; |
598 | entry->data = tmp; | 403 | while (*end == '/' || |
599 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | 404 | (*end == '.' && *(end - 1) == '/')) { |
600 | } | 405 | *end = '\0'; |
601 | else if (tmp2) { | 406 | end--; |
602 | entry->data = tmp2; | 407 | if (end == new_name) |
603 | dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | 408 | break; |
604 | } | ||
605 | else { | ||
606 | if (!nowhitelist_flag && !arg_quiet && !arg_private) { | ||
607 | fprintf(stderr, "***\n"); | ||
608 | fprintf(stderr, "*** Warning: cannot whitelist Documents directory\n"); | ||
609 | fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); | ||
610 | fprintf(stderr, "*** \tPlease create a proper Documents directory for your application.\n"); | ||
611 | fprintf(stderr, "***\n"); | ||
612 | } | ||
613 | entry->data = EMPTY_STRING; | ||
614 | continue; | ||
615 | } | 409 | } |
616 | } | 410 | } |
617 | 411 | ||
618 | // replace ~/ or ${HOME} into /home/username | ||
619 | new_name = expand_home(dataptr, cfg.homedir); | ||
620 | assert(new_name); | ||
621 | if (arg_debug || arg_debug_whitelists) | 412 | if (arg_debug || arg_debug_whitelists) |
622 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); | 413 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); |
623 | 414 | ||
@@ -628,7 +419,6 @@ void fs_whitelist(void) { | |||
628 | goto errexit; | 419 | goto errexit; |
629 | } | 420 | } |
630 | 421 | ||
631 | |||
632 | // extract the absolute path of the file | 422 | // extract the absolute path of the file |
633 | // realpath function will fail with ENOENT if the file is not found | 423 | // realpath function will fail with ENOENT if the file is not found |
634 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr | 424 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr |
@@ -704,7 +494,6 @@ void fs_whitelist(void) { | |||
704 | continue; | 494 | continue; |
705 | } | 495 | } |
706 | 496 | ||
707 | |||
708 | // check for supported directories | 497 | // check for supported directories |
709 | if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { | 498 | if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { |
710 | // whitelisting home directory is disabled if --private option is present | 499 | // whitelisting home directory is disabled if --private option is present |
@@ -724,7 +513,7 @@ void fs_whitelist(void) { | |||
724 | 513 | ||
725 | // both path and absolute path are under /home | 514 | // both path and absolute path are under /home |
726 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { | 515 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { |
727 | // avoid naming issues, also entire home dirs are not allowed | 516 | // entire home directory is not allowed |
728 | if (*(fname + strlen(cfg.homedir)) != '/') | 517 | if (*(fname + strlen(cfg.homedir)) != '/') |
729 | goto errexit; | 518 | goto errexit; |
730 | } | 519 | } |
@@ -740,12 +529,10 @@ void fs_whitelist(void) { | |||
740 | entry->tmp_dir = 1; | 529 | entry->tmp_dir = 1; |
741 | tmp_dir = 1; | 530 | tmp_dir = 1; |
742 | 531 | ||
743 | #ifndef TEST_MOUNTINFO | ||
744 | // both path and absolute path are under /tmp | 532 | // both path and absolute path are under /tmp |
745 | if (strncmp(fname, "/tmp/", 5) != 0) { | 533 | if (strncmp(fname, "/tmp/", 5) != 0) { |
746 | goto errexit; | 534 | goto errexit; |
747 | } | 535 | } |
748 | #endif | ||
749 | } | 536 | } |
750 | else if (strncmp(new_name, "/media/", 7) == 0) { | 537 | else if (strncmp(new_name, "/media/", 7) == 0) { |
751 | entry->media_dir = 1; | 538 | entry->media_dir = 1; |
@@ -871,7 +658,7 @@ void fs_whitelist(void) { | |||
871 | entry->link = new_name; | 658 | entry->link = new_name; |
872 | else { | 659 | else { |
873 | free(new_name); | 660 | free(new_name); |
874 | new_name = NULL; | 661 | entry->link = NULL; |
875 | } | 662 | } |
876 | 663 | ||
877 | // change file name in entry->data | 664 | // change file name in entry->data |
@@ -901,7 +688,7 @@ void fs_whitelist(void) { | |||
901 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | 688 | if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) |
902 | errExit("mount bind"); | 689 | errExit("mount bind"); |
903 | 690 | ||
904 | // mount a tmpfs and initialize /home/user | 691 | // mount a tmpfs and initialize /home/user, overrides --allusers |
905 | fs_private(); | 692 | fs_private(); |
906 | } | 693 | } |
907 | else | 694 | else |
@@ -1113,14 +900,29 @@ void fs_whitelist(void) { | |||
1113 | // if the link is already there, do not bother | 900 | // if the link is already there, do not bother |
1114 | if (lstat(entry->link, &s) != 0) { | 901 | if (lstat(entry->link, &s) != 0) { |
1115 | // create the path if necessary | 902 | // create the path if necessary |
1116 | mkpath(entry->link, 0755); | 903 | int fd = mkpath(entry->link, 0755); |
1117 | 904 | if (fd == -1) { | |
1118 | int rv = symlink(entry->data + 10, entry->link); | 905 | if (arg_debug || arg_debug_whitelists) |
1119 | if (rv) | 906 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); |
1120 | fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); | 907 | free(entry->link); |
908 | entry = entry->next; | ||
909 | continue; | ||
910 | } | ||
911 | // get file name of symlink | ||
912 | const char *file = gnu_basename(entry->link); | ||
913 | // create the link | ||
914 | int rv = symlinkat(entry->data + 10, fd, file); | ||
915 | if (rv) { | ||
916 | if (arg_debug || arg_debug_whitelists) { | ||
917 | perror("symlink"); | ||
918 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); | ||
919 | } | ||
920 | } | ||
1121 | else if (arg_debug || arg_debug_whitelists) | 921 | else if (arg_debug || arg_debug_whitelists) |
1122 | printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); | 922 | printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); |
923 | close(fd); | ||
1123 | } | 924 | } |
925 | free(entry->link); | ||
1124 | } | 926 | } |
1125 | 927 | ||
1126 | entry = entry->next; | 928 | entry = entry->next; |
@@ -1203,9 +1005,6 @@ void fs_whitelist(void) { | |||
1203 | fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); | 1005 | fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); |
1204 | } | 1006 | } |
1205 | 1007 | ||
1206 | if (new_name) | ||
1207 | free(new_name); | ||
1208 | |||
1209 | return; | 1008 | return; |
1210 | 1009 | ||
1211 | errexit: | 1010 | errexit: |
diff --git a/src/firejail/join.c b/src/firejail/join.c index e6da4c248..729c7f797 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -49,7 +49,7 @@ static void extract_x11_display(pid_t pid) { | |||
49 | if (!fp) | 49 | if (!fp) |
50 | return; | 50 | return; |
51 | 51 | ||
52 | if (1 != fscanf(fp, "%d", &display)) { | 52 | if (1 != fscanf(fp, "%u", &display)) { |
53 | fprintf(stderr, "Error: cannot read X11 display file\n"); | 53 | fprintf(stderr, "Error: cannot read X11 display file\n"); |
54 | fclose(fp); | 54 | fclose(fp); |
55 | return; | 55 | return; |
@@ -214,7 +214,7 @@ static void extract_umask(pid_t pid) { | |||
214 | free(fname); | 214 | free(fname); |
215 | if (!fp) | 215 | if (!fp) |
216 | return; | 216 | return; |
217 | if (fscanf(fp, "%4o", &orig_umask) < 1) { | 217 | if (fscanf(fp, "%3o", &orig_umask) < 1) { |
218 | fprintf(stderr, "Error: cannot read umask\n"); | 218 | fprintf(stderr, "Error: cannot read umask\n"); |
219 | exit(1); | 219 | exit(1); |
220 | } | 220 | } |
diff --git a/src/firejail/ls.c b/src/firejail/ls.c index 79e4b679b..601cab4f8 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c | |||
@@ -198,6 +198,10 @@ char *expand_path(const char *path) { | |||
198 | } | 198 | } |
199 | else { | 199 | else { |
200 | // assume the file is in current working directory | 200 | // assume the file is in current working directory |
201 | if (!cfg.cwd) { | ||
202 | fprintf(stderr, "Error: current working directory has been deleted\n"); | ||
203 | exit(1); | ||
204 | } | ||
201 | if (asprintf(&fname, "%s/%s", cfg.cwd, path) == -1) | 205 | if (asprintf(&fname, "%s/%s", cfg.cwd, path) == -1) |
202 | errExit("asprintf"); | 206 | errExit("asprintf"); |
203 | } | 207 | } |
@@ -206,6 +210,7 @@ char *expand_path(const char *path) { | |||
206 | 210 | ||
207 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { | 211 | void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { |
208 | EUID_ASSERT(); | 212 | EUID_ASSERT(); |
213 | assert(path1); | ||
209 | 214 | ||
210 | // if the pid is that of a firejail process, use the pid of the first child process | 215 | // if the pid is that of a firejail process, use the pid of the first child process |
211 | EUID_ROOT(); | 216 | EUID_ROOT(); |
diff --git a/src/firejail/macros.c b/src/firejail/macros.c new file mode 100644 index 000000000..283de57f2 --- /dev/null +++ b/src/firejail/macros.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2018 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 | #include "firejail.h" | ||
21 | #include <sys/stat.h> | ||
22 | #define MAXBUF 4098 | ||
23 | |||
24 | typedef struct macro_t { | ||
25 | char *name; // macro name | ||
26 | char *xdg; // xdg line in ~/.config/user-dirs.dirs | ||
27 | #define MAX_TRANSLATIONS 3 // several translations in case ~/.config/user-dirs.dirs not found | ||
28 | char *translation[MAX_TRANSLATIONS]; | ||
29 | } Macro; | ||
30 | |||
31 | Macro macro[] = { | ||
32 | { | ||
33 | "${DOWNLOADS}", | ||
34 | "XDG_DOWNLOAD_DIR=\"$HOME/", | ||
35 | { "Downloads", "Загрузки", "Téléchargement" } | ||
36 | }, | ||
37 | |||
38 | { | ||
39 | "${MUSIC}", | ||
40 | "XDG_MUSIC_DIR=\"$HOME/", | ||
41 | {"Music", "Музыка", "Musique"} | ||
42 | }, | ||
43 | |||
44 | { | ||
45 | "${VIDEOS}", | ||
46 | "XDG_VIDEOS_DIR=\"$HOME/", | ||
47 | {"Videos", "Видео", "Vidéos"} | ||
48 | }, | ||
49 | |||
50 | { | ||
51 | "${PICTURES}", | ||
52 | "XDG_PICTURES_DIR=\"$HOME/", | ||
53 | {"Pictures", "Изображения", "Photos"} | ||
54 | }, | ||
55 | |||
56 | { | ||
57 | "${DESKTOP}", | ||
58 | "XDG_DESKTOP_DIR=\"$HOME/", | ||
59 | {"Desktop", "Рабочий стол", "Bureau"} | ||
60 | }, | ||
61 | |||
62 | { | ||
63 | "${DOCUMENTS}", | ||
64 | "XDG_DOCUMENTS_DIR=\"$HOME/", | ||
65 | {"Documents", "Документы", "Documents"} | ||
66 | }, | ||
67 | |||
68 | { 0 } | ||
69 | }; | ||
70 | |||
71 | // return -1 if not found | ||
72 | static int macro_id(const char *name) { | ||
73 | int i = 0; | ||
74 | while (macro[i].name != NULL) { | ||
75 | if (strcmp(name, macro[i].name) == 0) | ||
76 | return i; | ||
77 | i++; | ||
78 | } | ||
79 | |||
80 | return -1; | ||
81 | } | ||
82 | |||
83 | int is_macro(const char *name) { | ||
84 | assert(name); | ||
85 | int len = strlen(name); | ||
86 | if (len <= 4) | ||
87 | return 0; | ||
88 | if (*name == '$' && name[1] == '{' && name[len - 1] == '}') | ||
89 | return 1; | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | // returns mallocated memory | ||
94 | static char *resolve_xdg(const char *var) { | ||
95 | char *fname; | ||
96 | struct stat s; | ||
97 | size_t length = strlen(var); | ||
98 | |||
99 | if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1) | ||
100 | errExit("asprintf"); | ||
101 | FILE *fp = fopen(fname, "r"); | ||
102 | if (!fp) { | ||
103 | free(fname); | ||
104 | return NULL; | ||
105 | } | ||
106 | free(fname); | ||
107 | |||
108 | char buf[MAXBUF]; | ||
109 | while (fgets(buf, MAXBUF, fp)) { | ||
110 | char *ptr = buf; | ||
111 | |||
112 | // skip blanks | ||
113 | while (*ptr == ' ' || *ptr == '\t') | ||
114 | ptr++; | ||
115 | if (*ptr == '\0' || *ptr == '\n' || *ptr == '#') | ||
116 | continue; | ||
117 | |||
118 | if (strncmp(ptr, var, length) == 0) { | ||
119 | char *ptr1 = ptr + length; | ||
120 | char *ptr2 = strchr(ptr1, '"'); | ||
121 | if (ptr2) { | ||
122 | fclose(fp); | ||
123 | *ptr2 = '\0'; | ||
124 | if (strlen(ptr1) != 0) { | ||
125 | if (asprintf(&fname, "%s/%s", cfg.homedir, ptr1) == -1) | ||
126 | errExit("asprintf"); | ||
127 | |||
128 | if (stat(fname, &s) == -1) { | ||
129 | free(fname); | ||
130 | return NULL; | ||
131 | } | ||
132 | free(fname); | ||
133 | |||
134 | char *rv = strdup(ptr1); | ||
135 | if (!rv) | ||
136 | errExit(ptr1); | ||
137 | return rv; | ||
138 | } | ||
139 | else | ||
140 | return NULL; | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | fclose(fp); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | // returns mallocated memory | ||
150 | static char *resolve_hardcoded(char *entries[]) { | ||
151 | char *fname; | ||
152 | struct stat s; | ||
153 | |||
154 | int i = 0; | ||
155 | while (entries[i] != NULL) { | ||
156 | if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1) | ||
157 | errExit("asprintf"); | ||
158 | |||
159 | if (stat(fname, &s) == 0) { | ||
160 | free(fname); | ||
161 | char *rv = strdup(entries[i]); | ||
162 | if (!rv) | ||
163 | errExit("strdup"); | ||
164 | return rv; | ||
165 | } | ||
166 | free(fname); | ||
167 | i++; | ||
168 | } | ||
169 | |||
170 | return NULL; | ||
171 | } | ||
172 | |||
173 | // returns mallocated memory | ||
174 | char *resolve_macro(const char *name) { | ||
175 | char *rv = NULL; | ||
176 | int id = macro_id(name); | ||
177 | if (id == -1) | ||
178 | return NULL; | ||
179 | |||
180 | rv = resolve_xdg(macro[id].xdg); | ||
181 | if (rv == NULL) | ||
182 | rv = resolve_hardcoded(macro[id].translation); | ||
183 | if (rv && arg_debug) | ||
184 | printf("Directory %s resolved as %s\n", name, rv); | ||
185 | |||
186 | return rv; | ||
187 | } | ||
188 | |||
189 | // This function takes a pathname supplied by the user and expands '~' and | ||
190 | // '${HOME}' at the start, to refer to a path relative to the user's home | ||
191 | // directory (supplied). | ||
192 | // The return value is allocated using malloc and must be freed by the caller. | ||
193 | // The function returns NULL if there are any errors. | ||
194 | char *expand_home(const char *path, const char *homedir) { | ||
195 | assert(path); | ||
196 | assert(homedir); | ||
197 | |||
198 | int called_as_root = 0; | ||
199 | |||
200 | if(geteuid() == 0) | ||
201 | called_as_root = 1; | ||
202 | |||
203 | if(called_as_root) { | ||
204 | EUID_USER(); | ||
205 | } | ||
206 | |||
207 | EUID_ASSERT(); | ||
208 | |||
209 | // Replace home macro | ||
210 | char *new_name = NULL; | ||
211 | if (strncmp(path, "${HOME}", 7) == 0) { | ||
212 | if (asprintf(&new_name, "%s%s", homedir, path + 7) == -1) | ||
213 | errExit("asprintf"); | ||
214 | if(called_as_root) | ||
215 | EUID_ROOT(); | ||
216 | return new_name; | ||
217 | } | ||
218 | else if (*path == '~') { | ||
219 | if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1) | ||
220 | errExit("asprintf"); | ||
221 | if(called_as_root) | ||
222 | EUID_ROOT(); | ||
223 | return new_name; | ||
224 | } | ||
225 | else if (strncmp(path, "${CFG}", 6) == 0) { | ||
226 | if (asprintf(&new_name, "%s%s", SYSCONFDIR, path + 6) == -1) | ||
227 | errExit("asprintf"); | ||
228 | if(called_as_root) | ||
229 | EUID_ROOT(); | ||
230 | return new_name; | ||
231 | } | ||
232 | else { | ||
233 | char *directory = resolve_macro(path); | ||
234 | if (directory) { | ||
235 | if (asprintf(&new_name, "%s/%s", cfg.homedir, directory) == -1) | ||
236 | errExit("asprintf"); | ||
237 | if(called_as_root) | ||
238 | EUID_ROOT(); | ||
239 | free(directory); | ||
240 | return new_name; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | char *rv = strdup(path); | ||
245 | if (!rv) | ||
246 | errExit("strdup"); | ||
247 | |||
248 | if(called_as_root) | ||
249 | EUID_ROOT(); | ||
250 | |||
251 | return rv; | ||
252 | } | ||
253 | |||
254 | void invalid_filename(const char *fname, int globbing) { | ||
255 | // EUID_ASSERT(); | ||
256 | assert(fname); | ||
257 | const char *ptr = fname; | ||
258 | |||
259 | if (strncmp(ptr, "${HOME}", 7) == 0) | ||
260 | ptr = fname + 7; | ||
261 | else if (strncmp(ptr, "${PATH}", 7) == 0) | ||
262 | ptr = fname + 7; | ||
263 | else { | ||
264 | int id = macro_id(fname); | ||
265 | if (id != -1) | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | int len = strlen(ptr); | ||
270 | |||
271 | if (globbing) { | ||
272 | // file globbing ('*?[]') is allowed | ||
273 | if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) { | ||
274 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); | ||
275 | exit(1); | ||
276 | } | ||
277 | } | ||
278 | else { | ||
279 | if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) { | ||
280 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); | ||
281 | exit(1); | ||
282 | } | ||
283 | } | ||
284 | } | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index 891e73eda..d1f3c47c2 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -241,7 +241,10 @@ static void init_cfg(int argc, char **argv) { | |||
241 | fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); | 241 | fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); |
242 | exit(1); | 242 | exit(1); |
243 | } | 243 | } |
244 | |||
244 | cfg.cwd = getcwd(NULL, 0); | 245 | cfg.cwd = getcwd(NULL, 0); |
246 | if (!cfg.cwd && errno != ENOENT) | ||
247 | errExit("getcwd"); | ||
245 | 248 | ||
246 | // check user database | 249 | // check user database |
247 | if (!firejail_user_check(cfg.username)) { | 250 | if (!firejail_user_check(cfg.username)) { |
@@ -833,6 +836,7 @@ static void run_builder(int argc, char **argv) { | |||
833 | (void) argc; | 836 | (void) argc; |
834 | 837 | ||
835 | // drop privileges | 838 | // drop privileges |
839 | EUID_ROOT(); | ||
836 | if (setgid(getgid()) < 0) | 840 | if (setgid(getgid()) < 0) |
837 | errExit("setgid/getgid"); | 841 | errExit("setgid/getgid"); |
838 | if (setuid(getuid()) < 0) | 842 | if (setuid(getuid()) < 0) |
@@ -1477,25 +1481,7 @@ int main(int argc, char **argv) { | |||
1477 | fprintf(stderr, "Error: please use --profile after --ignore\n"); | 1481 | fprintf(stderr, "Error: please use --profile after --ignore\n"); |
1478 | exit(1); | 1482 | exit(1); |
1479 | } | 1483 | } |
1480 | 1484 | profile_add_ignore(argv[i] + 9); | |
1481 | if (*(argv[i] + 9) == '\0') { | ||
1482 | fprintf(stderr, "Error: invalid ignore option\n"); | ||
1483 | exit(1); | ||
1484 | } | ||
1485 | |||
1486 | // find an empty entry in profile_ignore array | ||
1487 | int j; | ||
1488 | for (j = 0; j < MAX_PROFILE_IGNORE; j++) { | ||
1489 | if (cfg.profile_ignore[j] == NULL) | ||
1490 | break; | ||
1491 | } | ||
1492 | if (j >= MAX_PROFILE_IGNORE) { | ||
1493 | fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE); | ||
1494 | exit(1); | ||
1495 | } | ||
1496 | // ... and configure it | ||
1497 | else | ||
1498 | cfg.profile_ignore[j] = argv[i] + 9; | ||
1499 | } | 1485 | } |
1500 | #ifndef LTS | 1486 | #ifndef LTS |
1501 | #ifdef HAVE_CHROOT | 1487 | #ifdef HAVE_CHROOT |
@@ -1670,7 +1656,6 @@ int main(int argc, char **argv) { | |||
1670 | cfg.srv_private_keep = argv[i] + 14; | 1656 | cfg.srv_private_keep = argv[i] + 14; |
1671 | arg_private_srv = 1; | 1657 | arg_private_srv = 1; |
1672 | } | 1658 | } |
1673 | |||
1674 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { | 1659 | else if (strncmp(argv[i], "--private-bin=", 14) == 0) { |
1675 | // extract private bin list | 1660 | // extract private bin list |
1676 | if (*(argv[i] + 14) == '\0') { | 1661 | if (*(argv[i] + 14) == '\0') { |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index ab27c29a8..fb1beacd5 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -87,6 +87,31 @@ static int is_in_ignore_list(char *ptr) { | |||
87 | return 0; | 87 | return 0; |
88 | } | 88 | } |
89 | 89 | ||
90 | void profile_add_ignore(const char *str) { | ||
91 | assert(str); | ||
92 | if (*str == '\0') { | ||
93 | fprintf(stderr, "Error: invalid ignore option\n"); | ||
94 | exit(1); | ||
95 | } | ||
96 | |||
97 | // find an empty entry in profile_ignore array | ||
98 | int i; | ||
99 | for (i = 0; i < MAX_PROFILE_IGNORE; i++) { | ||
100 | if (cfg.profile_ignore[i] == NULL) | ||
101 | break; | ||
102 | } | ||
103 | if (i >= MAX_PROFILE_IGNORE) { | ||
104 | fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE); | ||
105 | exit(1); | ||
106 | } | ||
107 | // ... and configure it | ||
108 | else { | ||
109 | cfg.profile_ignore[i] = strdup(str); | ||
110 | if (!cfg.profile_ignore[i]) | ||
111 | errExit("strdup"); | ||
112 | } | ||
113 | } | ||
114 | |||
90 | 115 | ||
91 | // check profile line; if line == 0, this was generated from a command line option | 116 | // check profile line; if line == 0, this was generated from a command line option |
92 | // return 1 if the command is to be added to the linked list of profile commands | 117 | // return 1 if the command is to be added to the linked list of profile commands |
@@ -99,25 +124,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
99 | return 0; | 124 | return 0; |
100 | 125 | ||
101 | if (strncmp(ptr, "ignore ", 7) == 0) { | 126 | if (strncmp(ptr, "ignore ", 7) == 0) { |
102 | char *str = strdup(ptr + 7); | 127 | profile_add_ignore(ptr + 7); |
103 | if (*str == '\0') { | ||
104 | fprintf(stderr, "Error: invalid ignore option\n"); | ||
105 | exit(1); | ||
106 | } | ||
107 | // find an empty entry in profile_ignore array | ||
108 | int j; | ||
109 | for (j = 0; j < MAX_PROFILE_IGNORE; j++) { | ||
110 | if (cfg.profile_ignore[j] == NULL) | ||
111 | break; | ||
112 | } | ||
113 | if (j >= MAX_PROFILE_IGNORE) { | ||
114 | fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE); | ||
115 | exit(1); | ||
116 | } | ||
117 | // ... and configure it | ||
118 | else | ||
119 | cfg.profile_ignore[j] = str; | ||
120 | |||
121 | return 0; | 128 | return 0; |
122 | } | 129 | } |
123 | 130 | ||
@@ -952,6 +959,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
952 | return 0; | 959 | return 0; |
953 | } | 960 | } |
954 | 961 | ||
962 | |||
955 | #ifdef HAVE_OVERLAYFS | 963 | #ifdef HAVE_OVERLAYFS |
956 | if (strncmp(ptr, "overlay-named ", 14) == 0) { | 964 | if (strncmp(ptr, "overlay-named ", 14) == 0) { |
957 | if (checkcfg(CFG_OVERLAYFS)) { | 965 | if (checkcfg(CFG_OVERLAYFS)) { |
diff --git a/src/firejail/run_symlink.c b/src/firejail/run_symlink.c index 5714206d4..ec8e0f1e5 100644 --- a/src/firejail/run_symlink.c +++ b/src/firejail/run_symlink.c | |||
@@ -34,6 +34,7 @@ void run_symlink(int argc, char **argv, int run_as_is) { | |||
34 | return; | 34 | return; |
35 | 35 | ||
36 | // drop privileges | 36 | // drop privileges |
37 | EUID_ROOT(); | ||
37 | if (setgid(getgid()) < 0) | 38 | if (setgid(getgid()) < 0) |
38 | errExit("setgid/getgid"); | 39 | errExit("setgid/getgid"); |
39 | if (setuid(getuid()) < 0) | 40 | if (setuid(getuid()) < 0) |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 2335e9ed2..deb37d700 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -250,7 +250,7 @@ static int monitor_application(pid_t app_pid) { | |||
250 | } | 250 | } |
251 | while(rv != monitored_pid); | 251 | while(rv != monitored_pid); |
252 | if (arg_debug) | 252 | if (arg_debug) |
253 | printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); | 253 | printf("Sandbox monitor: waitpid %d retval %d status %d\n", monitored_pid, rv, status); |
254 | if (rv == -1) { // we can get here if we have processes joining the sandbox (ECHILD) | 254 | if (rv == -1) { // we can get here if we have processes joining the sandbox (ECHILD) |
255 | if (arg_debug) | 255 | if (arg_debug) |
256 | perror("waitpid"); | 256 | perror("waitpid"); |
@@ -294,7 +294,7 @@ static int monitor_application(pid_t app_pid) { | |||
294 | closedir(dir); | 294 | closedir(dir); |
295 | 295 | ||
296 | if (monitored_pid != 0 && arg_debug) | 296 | if (monitored_pid != 0 && arg_debug) |
297 | printf("Sandbox monitor: monitoring %u\n", monitored_pid); | 297 | printf("Sandbox monitor: monitoring %d\n", monitored_pid); |
298 | } | 298 | } |
299 | 299 | ||
300 | // return the latest exit status. | 300 | // return the latest exit status. |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 804bfb179..78cd30926 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -255,7 +255,7 @@ static char *usage_str = | |||
255 | "\tlist all running sandboxes\n" | 255 | "\tlist all running sandboxes\n" |
256 | "\n" | 256 | "\n" |
257 | "License GPL version 2 or later\n" | 257 | "License GPL version 2 or later\n" |
258 | "Homepage: http://firejail.wordpress.com\n" | 258 | "Homepage: https://firejail.wordpress.com\n" |
259 | "\n"; | 259 | "\n"; |
260 | 260 | ||
261 | 261 | ||
diff --git a/src/firejail/util.c b/src/firejail/util.c index fa32ffcc8..329ae141b 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -32,6 +32,9 @@ | |||
32 | #include <fcntl.h> | 32 | #include <fcntl.h> |
33 | 33 | ||
34 | #define MAX_GROUPS 1024 | 34 | #define MAX_GROUPS 1024 |
35 | #define MAXBUF 4098 | ||
36 | |||
37 | |||
35 | 38 | ||
36 | // send the error to /var/log/auth.log and exit after a small delay | 39 | // send the error to /var/log/auth.log and exit after a small delay |
37 | void errLogExit(char* fmt, ...) { | 40 | void errLogExit(char* fmt, ...) { |
@@ -723,38 +726,6 @@ void notify_other(int fd) { | |||
723 | } | 726 | } |
724 | 727 | ||
725 | 728 | ||
726 | // This function takes a pathname supplied by the user and expands '~' and | ||
727 | // '${HOME}' at the start, to refer to a path relative to the user's home | ||
728 | // directory (supplied). | ||
729 | // The return value is allocated using malloc and must be freed by the caller. | ||
730 | // The function returns NULL if there are any errors. | ||
731 | char *expand_home(const char *path, const char* homedir) { | ||
732 | assert(path); | ||
733 | assert(homedir); | ||
734 | |||
735 | // Replace home macro | ||
736 | char *new_name = NULL; | ||
737 | if (strncmp(path, "${HOME}", 7) == 0) { | ||
738 | if (asprintf(&new_name, "%s%s", homedir, path + 7) == -1) | ||
739 | errExit("asprintf"); | ||
740 | return new_name; | ||
741 | } | ||
742 | else if (*path == '~') { | ||
743 | if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1) | ||
744 | errExit("asprintf"); | ||
745 | return new_name; | ||
746 | } | ||
747 | else if (strncmp(path, "${CFG}", 6) == 0) { | ||
748 | if (asprintf(&new_name, "%s%s", SYSCONFDIR, path + 6) == -1) | ||
749 | errExit("asprintf"); | ||
750 | return new_name; | ||
751 | } | ||
752 | |||
753 | char *rv = strdup(path); | ||
754 | if (!rv) | ||
755 | errExit("strdup"); | ||
756 | return rv; | ||
757 | } | ||
758 | 729 | ||
759 | 730 | ||
760 | // Equivalent to the GNU version of basename, which is incompatible with | 731 | // Equivalent to the GNU version of basename, which is incompatible with |
@@ -815,44 +786,6 @@ uid_t pid_get_uid(pid_t pid) { | |||
815 | } | 786 | } |
816 | 787 | ||
817 | 788 | ||
818 | void invalid_filename(const char *fname, int globbing) { | ||
819 | // EUID_ASSERT(); | ||
820 | assert(fname); | ||
821 | const char *ptr = fname; | ||
822 | |||
823 | if (strncmp(ptr, "${HOME}", 7) == 0) | ||
824 | ptr = fname + 7; | ||
825 | else if (strncmp(ptr, "${PATH}", 7) == 0) | ||
826 | ptr = fname + 7; | ||
827 | else if (strcmp(fname, "${DOWNLOADS}") == 0) | ||
828 | return; | ||
829 | else if (strcmp(fname, "${MUSIC}") == 0) | ||
830 | return; | ||
831 | else if (strcmp(fname, "${VIDEOS}") == 0) | ||
832 | return; | ||
833 | else if (strcmp(fname, "${PICTURES}") == 0) | ||
834 | return; | ||
835 | else if (strcmp(fname, "${DESKTOP}") == 0) | ||
836 | return; | ||
837 | else if (strcmp(fname, "${DOCUMENTS}") == 0) | ||
838 | return; | ||
839 | |||
840 | int len = strlen(ptr); | ||
841 | |||
842 | if (globbing) { | ||
843 | // file globbing ('*?[]') is allowed | ||
844 | if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) { | ||
845 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); | ||
846 | exit(1); | ||
847 | } | ||
848 | } | ||
849 | else { | ||
850 | if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) { | ||
851 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); | ||
852 | exit(1); | ||
853 | } | ||
854 | } | ||
855 | } | ||
856 | 789 | ||
857 | 790 | ||
858 | uid_t get_group_id(const char *group) { | 791 | uid_t get_group_id(const char *group) { |
@@ -1059,54 +992,70 @@ void disable_file_path(const char *path, const char *file) { | |||
1059 | // user controlled paths. Passed flags are ignored if path is a top level directory. | 992 | // user controlled paths. Passed flags are ignored if path is a top level directory. |
1060 | int safe_fd(const char *path, int flags) { | 993 | int safe_fd(const char *path, int flags) { |
1061 | assert(path); | 994 | assert(path); |
1062 | int fd = -1; | 995 | |
996 | // reject empty string, relative path | ||
997 | if (*path != '/') | ||
998 | goto errexit; | ||
999 | // reject ".." | ||
1000 | if (strstr(path, "..")) | ||
1001 | goto errexit; | ||
1063 | 1002 | ||
1064 | // work with a copy of path | 1003 | // work with a copy of path |
1065 | char *dup = strdup(path); | 1004 | char *dup = strdup(path); |
1066 | if (dup == NULL) | 1005 | if (dup == NULL) |
1067 | errExit("strdup"); | 1006 | errExit("strdup"); |
1068 | // reject relative path and empty string | ||
1069 | if (*dup != '/') { | ||
1070 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | ||
1071 | exit(1); | ||
1072 | } | ||
1073 | 1007 | ||
1074 | char *p = strrchr(dup, '/'); | 1008 | char *p = strrchr(dup, '/'); |
1075 | if (p == NULL) | 1009 | assert(p); |
1076 | errExit("strrchr"); | 1010 | // reject trailing slash, root directory |
1077 | // reject trailing slash and root dir | 1011 | if (*(p + 1) == '\0') |
1078 | if (*(p + 1) == '\0') { | 1012 | goto errexit; |
1079 | fprintf(stderr, "Error: invalid pathname: %s\n", path); | 1013 | // reject trailing dot |
1080 | exit(1); | 1014 | if (*(p + 1) == '.' && *(p + 2) == '\0') |
1081 | } | 1015 | goto errexit; |
1016 | // if there is more than one path segment, keep the last one for later | ||
1017 | if (p != dup) | ||
1018 | *p = '\0'; | ||
1082 | 1019 | ||
1083 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); | 1020 | int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC); |
1084 | if (parentfd == -1) | 1021 | if (parentfd == -1) |
1085 | errExit("open"); | 1022 | errExit("open"); |
1086 | 1023 | ||
1087 | // if there is more than one path segment, keep the last one for later | 1024 | // traverse the path and return -1 if a symlink is encountered |
1088 | if (p != dup) | 1025 | int entered = 0; |
1089 | *p = '\0'; | 1026 | int fd = -1; |
1090 | |||
1091 | // traverse the path, return -1 if a symlink is encountered | ||
1092 | char *tok = strtok(dup, "/"); | 1027 | char *tok = strtok(dup, "/"); |
1093 | if (tok == NULL) | ||
1094 | errExit("strtok"); | ||
1095 | while (tok) { | 1028 | while (tok) { |
1029 | // skip all "/./" | ||
1030 | if (strcmp(tok, ".") == 0) { | ||
1031 | tok = strtok(NULL, "/"); | ||
1032 | continue; | ||
1033 | } | ||
1034 | entered = 1; | ||
1035 | |||
1036 | // open the directory | ||
1096 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1037 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1097 | close(parentfd); | 1038 | close(parentfd); |
1098 | if (fd == -1) { | 1039 | if (fd == -1) { |
1099 | free(dup); | 1040 | free(dup); |
1100 | return -1; | 1041 | return -1; |
1101 | } | 1042 | } |
1043 | |||
1102 | parentfd = fd; | 1044 | parentfd = fd; |
1103 | tok = strtok(NULL, "/"); | 1045 | tok = strtok(NULL, "/"); |
1104 | } | 1046 | } |
1105 | if (p != dup) { | 1047 | if (p != dup) { |
1048 | // consistent flags for top level directories (////foo, /.///foo) | ||
1049 | if (!entered) | ||
1050 | flags = O_PATH|O_DIRECTORY|O_CLOEXEC; | ||
1106 | // open last path segment | 1051 | // open last path segment |
1107 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); | 1052 | fd = openat(parentfd, p + 1, flags|O_NOFOLLOW); |
1108 | close(parentfd); | 1053 | close(parentfd); |
1109 | } | 1054 | } |
1110 | free(dup); | 1055 | free(dup); |
1111 | return fd; // -1 if open failed | 1056 | return fd; // -1 if open failed |
1057 | |||
1058 | errexit: | ||
1059 | fprintf(stderr, "Error: cannot open \"%s\", invalid filename\n", path); | ||
1060 | exit(1); | ||
1112 | } | 1061 | } |
diff --git a/src/firemon/arp.c b/src/firemon/arp.c index aac1aeba3..9f3e50e94 100644 --- a/src/firemon/arp.c +++ b/src/firemon/arp.c | |||
@@ -51,7 +51,7 @@ static void print_arp(const char *fname) { | |||
51 | char mac[64]; | 51 | char mac[64]; |
52 | char mask[64]; | 52 | char mask[64]; |
53 | char device[64]; | 53 | char device[64]; |
54 | int rv = sscanf(start, "%s %s %s %s %s %s\n", ip, type, flags, mac, mask, device); | 54 | int rv = sscanf(start, "%63s %63s %63s %63s %63s %63s\n", ip, type, flags, mac, mask, device); |
55 | if (rv != 6) | 55 | if (rv != 6) |
56 | continue; | 56 | continue; |
57 | 57 | ||
diff --git a/src/firemon/route.c b/src/firemon/route.c index 7426637df..6db212831 100644 --- a/src/firemon/route.c +++ b/src/firemon/route.c | |||
@@ -144,7 +144,7 @@ static void print_route(const char *fname) { | |||
144 | char use[64]; | 144 | char use[64]; |
145 | char metric[64]; | 145 | char metric[64]; |
146 | char mask[64]; | 146 | char mask[64]; |
147 | int rv = sscanf(start, "%s %s %s %s %s %s %s %s\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); | 147 | int rv = sscanf(start, "%63s %63s %63s %63s %63s %63s %63s %63s\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); |
148 | if (rv != 8) | 148 | if (rv != 8) |
149 | continue; | 149 | continue; |
150 | 150 | ||
@@ -161,7 +161,7 @@ static void print_route(const char *fname) { | |||
161 | 161 | ||
162 | // printf("#%s# #%s# #%s# #%s# #%s# #%s# #%s# #%s#\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); | 162 | // printf("#%s# #%s# #%s# #%s# #%s# #%s# #%s# #%s#\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); |
163 | if (gw != 0) | 163 | if (gw != 0) |
164 | printf(" %u.%u.%u.%u/%u via %u.%u.%u.%u, dev %s, metric %s\n", | 164 | printf(" %d.%d.%d.%d/%u via %d.%d.%d.%d, dev %s, metric %s\n", |
165 | PRINT_IP(destip), mask2bits(destmask), | 165 | PRINT_IP(destip), mask2bits(destmask), |
166 | PRINT_IP(gw), | 166 | PRINT_IP(gw), |
167 | ifname, | 167 | ifname, |
@@ -169,7 +169,7 @@ static void print_route(const char *fname) { | |||
169 | else { // this is an interface | 169 | else { // this is an interface |
170 | IfList *ifentry = list_find(destip, destmask); | 170 | IfList *ifentry = list_find(destip, destmask); |
171 | if (ifentry) { | 171 | if (ifentry) { |
172 | printf(" %u.%u.%u.%u/%u, dev %s, scope link src %d.%d.%d.%d\n", | 172 | printf(" %d.%d.%d.%d/%u, dev %s, scope link src %d.%d.%d.%d\n", |
173 | PRINT_IP(destip), mask2bits(destmask), | 173 | PRINT_IP(destip), mask2bits(destmask), |
174 | ifname, | 174 | ifname, |
175 | PRINT_IP(ifentry->ip)); | 175 | PRINT_IP(ifentry->ip)); |
diff --git a/src/firemon/usage.c b/src/firemon/usage.c index a4d642d66..5714643cc 100644 --- a/src/firemon/usage.c +++ b/src/firemon/usage.c | |||
@@ -75,7 +75,7 @@ static char *help_str = | |||
75 | "\tUser - The owner of the sandbox.\n" | 75 | "\tUser - The owner of the sandbox.\n" |
76 | "\n" | 76 | "\n" |
77 | "License GPL version 2 or later\n" | 77 | "License GPL version 2 or later\n" |
78 | "Homepage: http://firejail.wordpress.com\n" | 78 | "Homepage: https://firejail.wordpress.com\n" |
79 | "\n"; | 79 | "\n"; |
80 | 80 | ||
81 | void usage(void) { | 81 | void usage(void) { |
diff --git a/src/fnet/veth.c b/src/fnet/veth.c index 39a4f35d8..e4d3db21a 100644 --- a/src/fnet/veth.c +++ b/src/fnet/veth.c | |||
@@ -3,10 +3,10 @@ | |||
3 | * Original source code: | 3 | * Original source code: |
4 | * | 4 | * |
5 | * Information: | 5 | * Information: |
6 | * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 | 6 | * https://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 |
7 | * | 7 | * |
8 | * Download: | 8 | * Download: |
9 | * http://www.kernel.org/pub/linux/utils/net/iproute2/ | 9 | * https://www.kernel.org/pub/linux/utils/net/iproute2/ |
10 | * | 10 | * |
11 | * Repository: | 11 | * Repository: |
12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git | 12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git |
diff --git a/src/fnetfilter/main.c b/src/fnetfilter/main.c index ba58ba3c9..34ebf5926 100644 --- a/src/fnetfilter/main.c +++ b/src/fnetfilter/main.c | |||
@@ -79,13 +79,17 @@ static void process_template(char *src, const char *dest) { | |||
79 | *arg_start = '\0'; | 79 | *arg_start = '\0'; |
80 | arg_start++; | 80 | arg_start++; |
81 | if (*arg_start == '\0') { | 81 | if (*arg_start == '\0') { |
82 | fprintf(stderr, "Error fnetfilter: you need to provide at least on argument\n"); | 82 | fprintf(stderr, "Error fnetfilter: you need to provide at least one argument\n"); |
83 | exit(1); | 83 | exit(1); |
84 | } | 84 | } |
85 | 85 | ||
86 | // extract the arguments from command line | 86 | // extract the arguments from command line |
87 | char *token = strtok(arg_start, ","); | 87 | char *token = strtok(arg_start, ","); |
88 | while (token) { | 88 | while (token) { |
89 | if (argcnt == MAXARGS) { | ||
90 | fprintf(stderr, "Error fnetfilter: only up to %u arguments are supported\n", (unsigned) MAXARGS); | ||
91 | exit(1); | ||
92 | } | ||
89 | // look for abnormal things | 93 | // look for abnormal things |
90 | int len = strlen(token); | 94 | int len = strlen(token); |
91 | if (strcspn(token, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) { | 95 | if (strcspn(token, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) { |
@@ -125,7 +129,7 @@ for (i = 0; i < argcnt; i++) | |||
125 | else { | 129 | else { |
126 | // parsing | 130 | // parsing |
127 | int index = 0; | 131 | int index = 0; |
128 | int rv = sscanf(ptr, "$ARG%u", &index) ; | 132 | int rv = sscanf(ptr, "$ARG%d", &index) ; |
129 | if (rv != 1) { | 133 | if (rv != 1) { |
130 | fprintf(stderr, "Error fnetfilter: invalid template argument on line %d\n", line); | 134 | fprintf(stderr, "Error fnetfilter: invalid template argument on line %d\n", line); |
131 | exit(1); | 135 | exit(1); |
diff --git a/src/fsec-print/print.c b/src/fsec-print/print.c index faf59aa35..1042f0c3e 100644 --- a/src/fsec-print/print.c +++ b/src/fsec-print/print.c | |||
@@ -39,7 +39,7 @@ | |||
39 | * for more details. | 39 | * for more details. |
40 | * | 40 | * |
41 | * You should have received a copy of the GNU Lesser General Public License | 41 | * You should have received a copy of the GNU Lesser General Public License |
42 | * along with this library; if not, see <http://www.gnu.org/licenses>. | 42 | * along with this library; if not, see <https://www.gnu.org/licenses>. |
43 | */ | 43 | */ |
44 | 44 | ||
45 | #include "fsec_print.h" | 45 | #include "fsec_print.h" |
diff --git a/src/include/libnetlink.h b/src/include/libnetlink.h index 01fd2675d..0310ecad3 100644 --- a/src/include/libnetlink.h +++ b/src/include/libnetlink.h | |||
@@ -3,10 +3,10 @@ | |||
3 | * Original source code: | 3 | * Original source code: |
4 | * | 4 | * |
5 | * Information: | 5 | * Information: |
6 | * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 | 6 | * https://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 |
7 | * | 7 | * |
8 | * Download: | 8 | * Download: |
9 | * http://www.kernel.org/pub/linux/utils/net/iproute2/ | 9 | * https://www.kernel.org/pub/linux/utils/net/iproute2/ |
10 | * | 10 | * |
11 | * Repository: | 11 | * Repository: |
12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git | 12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git |
diff --git a/src/lib/libnetlink.c b/src/lib/libnetlink.c index d2975bd57..5f6ecd95c 100644 --- a/src/lib/libnetlink.c +++ b/src/lib/libnetlink.c | |||
@@ -3,10 +3,10 @@ | |||
3 | * Original source code: | 3 | * Original source code: |
4 | * | 4 | * |
5 | * Information: | 5 | * Information: |
6 | * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 | 6 | * https://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 |
7 | * | 7 | * |
8 | * Download: | 8 | * Download: |
9 | * http://www.kernel.org/pub/linux/utils/net/iproute2/ | 9 | * https://www.kernel.org/pub/linux/utils/net/iproute2/ |
10 | * | 10 | * |
11 | * Repository: | 11 | * Repository: |
12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git | 12 | * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git |
diff --git a/src/libtracelog/libtracelog.c b/src/libtracelog/libtracelog.c index 5ce41aca0..0f8d5a00d 100644 --- a/src/libtracelog/libtracelog.c +++ b/src/libtracelog/libtracelog.c | |||
@@ -64,9 +64,6 @@ static inline uint32_t hash(const char *str) { | |||
64 | } | 64 | } |
65 | 65 | ||
66 | static void storage_add(const char *str) { | 66 | static void storage_add(const char *str) { |
67 | #ifdef DEBUG | ||
68 | printf("add %s\n", str); | ||
69 | #endif | ||
70 | if (!str) { | 67 | if (!str) { |
71 | #ifdef DEBUG | 68 | #ifdef DEBUG |
72 | printf("null pointer passed to storage_add\n"); | 69 | printf("null pointer passed to storage_add\n"); |
@@ -74,6 +71,10 @@ static void storage_add(const char *str) { | |||
74 | return; | 71 | return; |
75 | } | 72 | } |
76 | 73 | ||
74 | #ifdef DEBUG | ||
75 | printf("add %s\n", str); | ||
76 | #endif | ||
77 | |||
77 | ListElem *ptr = malloc(sizeof(ListElem)); | 78 | ListElem *ptr = malloc(sizeof(ListElem)); |
78 | if (!ptr) { | 79 | if (!ptr) { |
79 | fprintf(stderr, "Error: cannot allocate memory\n"); | 80 | fprintf(stderr, "Error: cannot allocate memory\n"); |
@@ -96,15 +97,17 @@ static void storage_add(const char *str) { | |||
96 | static char* cwd = NULL; | 97 | static char* cwd = NULL; |
97 | 98 | ||
98 | static char *storage_find(const char *str) { | 99 | static char *storage_find(const char *str) { |
99 | #ifdef DEBUG | ||
100 | printf("storage find %s\n", str); | ||
101 | #endif | ||
102 | if (!str) { | 100 | if (!str) { |
103 | #ifdef DEBUG | 101 | #ifdef DEBUG |
104 | printf("null pointer passed to storage_find\n"); | 102 | printf("null pointer passed to storage_find\n"); |
105 | #endif | 103 | #endif |
106 | return NULL; | 104 | return NULL; |
107 | } | 105 | } |
106 | |||
107 | #ifdef DEBUG | ||
108 | printf("storage find %s\n", str); | ||
109 | #endif | ||
110 | |||
108 | const char *tofind = str; | 111 | const char *tofind = str; |
109 | int allocated = 0; | 112 | int allocated = 0; |
110 | 113 | ||