summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/firejail.h18
-rw-r--r--src/firejail/fs_whitelist.c184
-rw-r--r--src/firejail/macros.c381
-rw-r--r--src/firejail/util.c334
-rw-r--r--test/fs/user-dirs.dirs15
5 files changed, 398 insertions, 534 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 471f2e55c..f31d6a2bc 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -491,16 +491,14 @@ int arp_check(const char *dev, uint32_t destaddr);
491// assign an IP address using arp scanning 491// assign an IP address using arp scanning
492uint32_t arp_assign(const char *dev, Bridge *br); 492uint32_t arp_assign(const char *dev, Bridge *br);
493 493
494// macros.c
495char *expand_home(const char *path, const char *homedir);
496char *resolve_macro(const char *name);
497void invalid_filename(const char *fname, int globbing);
498int is_macro(const char *name);
499
500
494// util.c 501// util.c
495extern char *dentry[];
496extern char *mentry[];
497extern char *ventry[];
498extern char *pentry[];
499extern char *deentry[];
500extern char *doentry[];
501
502char *resolve_xdg(int flags, const char *var, size_t length, const char *prnt);
503char *resolve_hardcoded(int flags, char *entries[], const char *prnt);
504void errLogExit(char* fmt, ...); 502void errLogExit(char* fmt, ...);
505void fwarning(char* fmt, ...); 503void fwarning(char* fmt, ...);
506void fmessage(char* fmt, ...); 504void fmessage(char* fmt, ...);
@@ -525,10 +523,8 @@ void check_private_dir(void);
525void update_map(char *mapping, char *map_file); 523void update_map(char *mapping, char *map_file);
526void wait_for_other(int fd); 524void wait_for_other(int fd);
527void notify_other(int fd); 525void notify_other(int fd);
528char *expand_home(const char *path, const char* homedir);
529const char *gnu_basename(const char *path); 526const char *gnu_basename(const char *path);
530uid_t pid_get_uid(pid_t pid); 527uid_t pid_get_uid(pid_t pid);
531void invalid_filename(const char *fname, int globbing);
532uid_t get_group_id(const char *group); 528uid_t get_group_id(const char *group);
533int remove_overlay_directory(void); 529int remove_overlay_directory(void);
534void flush_stdin(void); 530void flush_stdin(void);
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 6a6e91bc9..a2803ccbc 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -370,185 +370,21 @@ void fs_whitelist(void) {
370 } 370 }
371 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; 371 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
372 372
373 // resolve ${DOWNLOADS} 373 // resolve macros
374 if (strcmp(dataptr, "${DOWNLOADS}") == 0) { 374 if (is_macro(dataptr)) {
375 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_DOWNLOAD_DIR=\"$HOME/", 24, "Downloads"); 375 char *tmp = resolve_macro(dataptr);
376 char *tmpw1 = NULL; 376 if (tmp != NULL)
377 if (tmp1 != NULL) 377 tmp = parse_nowhitelist(nowhitelist_flag, tmp);
378 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1); 378
379 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, dentry, "Downloads"); 379 if (tmp) {
380 char *tmpw2 = NULL; 380 entry->data = tmp;
381 if (tmp2 != NULL)
382 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
383 if (tmp1 && tmpw1) {
384 entry->data = tmpw1;
385 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
386 }
387 else if (tmp2 && tmpw2) {
388 entry->data = tmpw2;
389 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
390 }
391 else {
392 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
393 fprintf(stderr, "***\n");
394 fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n");
395 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
396 fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n");
397 fprintf(stderr, "***\n");
398 }
399 entry->data = EMPTY_STRING;
400 continue;
401 }
402 }
403
404 // resolve ${MUSIC}
405 if (strcmp(dataptr, "${MUSIC}") == 0) {
406 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_MUSIC_DIR=\"$HOME/", 21, "Music");
407 char *tmpw1 = NULL;
408 if (tmp1 != NULL)
409 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1);
410 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, mentry, "Music");
411 char *tmpw2 = NULL;
412 if (tmp2 != NULL)
413 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
414 if (tmp1 && tmpw1) {
415 entry->data = tmpw1;
416 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
417 }
418 else if (tmp2 && tmpw2) {
419 entry->data = tmpw2;
420 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
421 }
422 else {
423 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
424 fprintf(stderr, "***\n");
425 fprintf(stderr, "*** Warning: cannot whitelist Music directory\n");
426 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
427 fprintf(stderr, "*** \tPlease create a proper Music directory for your application.\n");
428 fprintf(stderr, "***\n");
429 }
430 entry->data = EMPTY_STRING;
431 continue;
432 }
433 }
434
435 // resolve ${VIDEOS}
436 if (strcmp(dataptr, "${VIDEOS}") == 0) {
437 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_VIDEOS_DIR=\"$HOME/", 22, "Videos");
438 char *tmpw1 = NULL;
439 if (tmp1 != NULL)
440 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1);
441 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, ventry, "Videos");
442 char *tmpw2 = NULL;
443 if (tmp2 != NULL)
444 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
445 if (tmp1 && tmpw1) {
446 entry->data = tmpw1;
447 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
448 }
449 else if (tmp2 && tmpw2) {
450 entry->data = tmpw2;
451 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
452 }
453 else {
454 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
455 fprintf(stderr, "***\n");
456 fprintf(stderr, "*** Warning: cannot whitelist Videos directory\n");
457 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
458 fprintf(stderr, "*** \tPlease create a proper Videos directory for your application.\n");
459 fprintf(stderr, "***\n");
460 }
461 entry->data = EMPTY_STRING;
462 continue;
463 }
464 }
465
466 // resolve ${PICTURES}
467 if (strcmp(dataptr, "${PICTURES}") == 0) {
468 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_PICTURES_DIR=\"$HOME/", 24, "Pictures");
469 char *tmpw1 = NULL;
470 if (tmp1 != NULL)
471 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1);
472 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, pentry, "Pictures");
473 char *tmpw2 = NULL;
474 if (tmp2 != NULL)
475 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
476 if (tmp1 && tmpw1) {
477 entry->data = tmpw1;
478 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; 381 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
479 } 382 }
480 else if (tmp2 && tmpw2) {
481 entry->data = tmpw2;
482 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
483 }
484 else {
485 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
486 fprintf(stderr, "***\n");
487 fprintf(stderr, "*** Warning: cannot whitelist Pictures directory\n");
488 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
489 fprintf(stderr, "*** \tPlease create a proper Pictures directory for your application.\n");
490 fprintf(stderr, "***\n");
491 }
492 entry->data = EMPTY_STRING;
493 continue;
494 }
495 }
496
497 // resolve ${DESKTOP}
498 if (strcmp(dataptr, "${DESKTOP}") == 0) {
499 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_DESKTOP_DIR=\"$HOME/", 24, "Desktop");
500 char *tmpw1 = NULL;
501 if (tmp1 != NULL)
502 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1);
503 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, deentry, "Desktop");
504 char *tmpw2 = NULL;
505 if (tmp2 != NULL)
506 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
507 if (tmp1 && tmpw1) {
508 entry->data = tmpw1;
509 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
510 }
511 else if (tmp2 && tmpw2) {
512 entry->data = tmpw2;
513 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
514 }
515 else {
516 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
517 fprintf(stderr, "***\n");
518 fprintf(stderr, "*** Warning: cannot whitelist Desktop directory\n");
519 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
520 fprintf(stderr, "*** \tPlease create a proper Desktop directory for your application.\n");
521 fprintf(stderr, "***\n");
522 }
523 entry->data = EMPTY_STRING;
524 continue;
525 }
526 }
527
528 // resolve ${DOCUMENTS}
529 if (strcmp(dataptr, "${DOCUMENTS}") == 0) {
530 char *tmp1 = resolve_xdg(arg_debug || arg_debug_whitelists, "XDG_DOCUMENTS_DIR=\"$HOME/", 25, "Documents");
531 char *tmpw1 = NULL;
532 if (tmp1 != NULL)
533 tmpw1 = parse_nowhitelist(nowhitelist_flag, tmp1);
534 char *tmp2 = resolve_hardcoded(arg_debug || arg_debug_whitelists, doentry, "Documents");
535 char *tmpw2 = NULL;
536 if (tmp2 != NULL)
537 tmpw2 = parse_nowhitelist(nowhitelist_flag, tmp2);
538 if (tmp1 && tmpw1) {
539 entry->data = tmpw1;
540 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
541 }
542 else if (tmp2 && tmpw2) {
543 entry->data = tmpw2;
544 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
545 }
546 else { 383 else {
547 if (!nowhitelist_flag && !arg_quiet && !arg_private) { 384 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
548 fprintf(stderr, "***\n"); 385 fprintf(stderr, "***\n");
549 fprintf(stderr, "*** Warning: cannot whitelist Documents directory\n"); 386 fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", dataptr);
550 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); 387 fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n");
551 fprintf(stderr, "*** \tPlease create a proper Documents directory for your application.\n");
552 fprintf(stderr, "***\n"); 388 fprintf(stderr, "***\n");
553 } 389 }
554 entry->data = EMPTY_STRING; 390 entry->data = EMPTY_STRING;
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
new file mode 100644
index 000000000..f111802d7
--- /dev/null
+++ b/src/firejail/macros.c
@@ -0,0 +1,381 @@
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
24typedef 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
31Macro 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
72int 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
83int 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
93static char *resolve_xdg(const char *var) {
94 char *fname;
95 struct stat s;
96 size_t length = strlen(var);
97
98 if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1)
99 errExit("asprintf");
100 FILE *fp = fopen(fname, "r");
101 if (!fp) {
102 free(fname);
103 return NULL;
104 }
105 free(fname);
106
107 char buf[MAXBUF];
108 while (fgets(buf, MAXBUF, fp)) {
109 char *ptr = buf;
110
111 // skip blanks
112 while (*ptr == ' ' || *ptr == '\t')
113 ptr++;
114 if (*ptr == '\0' || *ptr == '\n' || *ptr == '#')
115 continue;
116
117 if (strncmp(ptr, var, length) == 0) {
118 char *ptr1 = ptr + length;
119 char *ptr2 = strchr(ptr1, '"');
120 if (ptr2) {
121 fclose(fp);
122 *ptr2 = '\0';
123 if (strlen(ptr1) != 0) {
124 if (asprintf(&fname, "%s/%s", cfg.homedir, ptr1) == -1)
125 errExit("asprintf");
126
127 if (stat(fname, &s) == -1) {
128 free(fname);
129 return NULL;
130 }
131 free(fname);
132
133 char *rv = strdup(ptr1);
134 if (!rv)
135 errExit(ptr1);
136 return rv;
137 }
138 else
139 return NULL;
140 }
141 }
142 }
143
144 fclose(fp);
145 return NULL;
146}
147
148static char *resolve_hardcoded(char *entries[]) {
149 char *fname;
150 struct stat s;
151
152 int i = 0;
153 while (entries[i] != NULL) {
154 if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1)
155 errExit("asprintf");
156
157 if (stat(fname, &s) == 0) {
158 free(fname);
159 return entries[i];
160 }
161 free(fname);
162 i++;
163 }
164
165 return NULL;
166}
167
168char *resolve_macro(const char *name) {
169 char *rv = NULL;
170 int id = macro_id(name);
171 if (id == -1)
172 return NULL;
173
174 rv = resolve_xdg(macro[id].xdg);
175 if (rv == NULL)
176 rv = resolve_hardcoded(macro[id].translation);
177 if (rv)
178 printf("Directory %s resolved as %s\n", name, rv);
179
180 return rv;
181}
182
183// This function takes a pathname supplied by the user and expands '~' and
184// '${HOME}' at the start, to refer to a path relative to the user's home
185// directory (supplied).
186// The return value is allocated using malloc and must be freed by the caller.
187// The function returns NULL if there are any errors.
188char *expand_home(const char *path, const char *homedir) {
189 assert(path);
190 assert(homedir);
191
192 int called_as_root = 0;
193
194 if(geteuid() == 0)
195 called_as_root = 1;
196
197 if(called_as_root) {
198 EUID_USER();
199 }
200
201 EUID_ASSERT();
202
203 // Replace home macro
204 char *new_name = NULL;
205 if (strncmp(path, "${HOME}", 7) == 0) {
206 if (asprintf(&new_name, "%s%s", homedir, path + 7) == -1)
207 errExit("asprintf");
208 if(called_as_root)
209 EUID_ROOT();
210 return new_name;
211 }
212 else if (*path == '~') {
213 if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1)
214 errExit("asprintf");
215 if(called_as_root)
216 EUID_ROOT();
217 return new_name;
218 }
219 else if (strncmp(path, "${CFG}", 6) == 0) {
220 if (asprintf(&new_name, "%s%s", SYSCONFDIR, path + 6) == -1)
221 errExit("asprintf");
222 if(called_as_root)
223 EUID_ROOT();
224 return new_name;
225 }
226#if 0
227 else if (strncmp(path, "${DOWNLOADS}", 12) == 0) {
228 char *tmp = resolve_xdg("XDG_DOWNLOAD_DIR=\"$HOME/", 24, "Downloads");
229 char *tmp2 = resolve_hardcoded(dentry, "Downloads");
230 if(tmp) {
231 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
232 errExit("asprintf");
233 if(called_as_root)
234 EUID_ROOT();
235 return new_name;
236 }
237 else if(tmp2) {
238 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
239 errExit("asprintf");
240 if(called_as_root)
241 EUID_ROOT();
242 return new_name;
243 }
244 }
245
246 else if (strncmp(path, "${MUSIC}", 8) == 0) {
247 char *tmp = resolve_xdg("XDG_MUSIC_DIR=\"$HOME/", 21, "Music");
248 char *tmp2 = resolve_hardcoded(mentry, "Music");
249 if(tmp) {
250 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 8) == -1)
251 errExit("asprintf");
252 if(called_as_root)
253 EUID_ROOT();
254 return new_name;
255 }
256 else if(tmp2) {
257 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 8) == -1)
258 errExit("asprintf");
259 if(called_as_root)
260 EUID_ROOT();
261 return new_name;
262 }
263 }
264
265 else if (strncmp(path, "${VIDEOS}", 9) == 0) {
266 char *tmp = resolve_xdg("XDG_VIDEOS_DIR=\"$HOME/", 22, "Videos");
267 char *tmp2 = resolve_hardcoded(ventry, "Videos");
268 if(tmp) {
269 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 9) == -1)
270 errExit("asprintf");
271 if(called_as_root)
272 EUID_ROOT();
273 return new_name;
274 }
275 else if(tmp2) {
276 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 9) == -1)
277 errExit("asprintf");
278 if(called_as_root)
279 EUID_ROOT();
280 return new_name;
281 }
282 }
283
284 else if (strncmp(path, "${PICTURES}", 11) == 0) {
285 char *tmp = resolve_xdg("XDG_PICTURES_DIR=\"$HOME/", 24, "Pictures");
286 char *tmp2 = resolve_hardcoded(pentry, "Pictures");
287 if(tmp) {
288 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 11) == -1)
289 errExit("asprintf");
290 if(called_as_root)
291 EUID_ROOT();
292 return new_name;
293 }
294 else if(tmp2) {
295 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 11) == -1)
296 errExit("asprintf");
297 if(called_as_root)
298 EUID_ROOT();
299 return new_name;
300 }
301 }
302
303 else if (strncmp(path, "${DESKTOP}", 10) == 0) {
304 char *tmp = resolve_xdg("XDG_DESKTOP_DIR=\"$HOME/", 24, "Desktop");
305 char *tmp2 = resolve_hardcoded(deentry, "Desktop");
306 if(tmp) {
307 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 10) == -1)
308 errExit("asprintf");
309 if(called_as_root)
310 EUID_ROOT();
311 return new_name;
312 }
313 else if(tmp2) {
314 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 10) == -1)
315 errExit("asprintf");
316 if(called_as_root)
317 EUID_ROOT();
318 return new_name;
319 }
320 }
321
322 else if (strncmp(path, "${DOCUMENTS}", 12) == 0) {
323 char *tmp = resolve_xdg("XDG_DOCUMENTS_DIR=\"$HOME/", 25, "Documents");
324 char *tmp2 = resolve_hardcoded(doentry, "Documents");
325 if(tmp) {
326 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
327 errExit("asprintf");
328 if(called_as_root)
329 EUID_ROOT();
330 return new_name;
331 }
332 else if(tmp2) {
333 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
334 errExit("asprintf");
335 if(called_as_root)
336 EUID_ROOT();
337 return new_name;
338 }
339 }
340#endif
341 char *rv = strdup(path);
342 if (!rv)
343 errExit("strdup");
344
345 if(called_as_root)
346 EUID_ROOT();
347
348 return rv;
349}
350
351void invalid_filename(const char *fname, int globbing) {
352// EUID_ASSERT();
353 assert(fname);
354 const char *ptr = fname;
355
356 if (strncmp(ptr, "${HOME}", 7) == 0)
357 ptr = fname + 7;
358 else if (strncmp(ptr, "${PATH}", 7) == 0)
359 ptr = fname + 7;
360 else {
361 int id = macro_id(fname);
362 if (id != -1)
363 return;
364 }
365
366 int len = strlen(ptr);
367
368 if (globbing) {
369 // file globbing ('*?[]') is allowed
370 if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) {
371 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
372 exit(1);
373 }
374 }
375 else {
376 if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
377 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
378 exit(1);
379 }
380 }
381}
diff --git a/src/firejail/util.c b/src/firejail/util.c
index d79e955a7..67776b36c 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -34,136 +34,7 @@
34#define MAX_GROUPS 1024 34#define MAX_GROUPS 1024
35#define MAXBUF 4098 35#define MAXBUF 4098
36 36
37char *dentry[] = {
38 "Downloads",
39 "Загрузки",
40 "Téléchargement",
41 NULL
42};
43
44char *mentry[] = {
45 "Music",
46 "Музыка",
47 "Musique",
48 NULL
49};
50
51char *ventry[] = {
52 "Videos",
53 "Видео",
54 "Vidéos",
55 NULL
56};
57
58char *pentry[] = {
59 "Pictures",
60 "Изображения",
61 "Photos",
62 NULL
63};
64
65char *deentry[] = {
66 "Desktop",
67 "Рабочий стол",
68 "Bureau",
69 NULL
70};
71
72char *doentry[] = {
73 "Documents",
74 "Документы",
75 "Documents",
76 NULL
77};
78
79char *resolve_xdg(int flags, const char *var, size_t length, const char *prnt) {
80 char *fname;
81 struct stat s;
82
83 if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1)
84 errExit("asprintf");
85 FILE *fp = fopen(fname, "r");
86 if (!fp) {
87 free(fname);
88 return NULL;
89 }
90 free(fname);
91
92 char buf[MAXBUF];
93 while (fgets(buf, MAXBUF, fp)) {
94 char *ptr = buf;
95
96 // skip blanks
97 while (*ptr == ' ' || *ptr == '\t')
98 ptr++;
99 if (*ptr == '\0' || *ptr == '\n' || *ptr == '#')
100 continue;
101
102 if (strncmp(ptr, var, length) == 0) {
103 char *ptr1 = ptr + length;
104 char *ptr2 = strchr(ptr1, '"');
105 if (ptr2) {
106 fclose(fp);
107 *ptr2 = '\0';
108 if (flags)
109 printf("extracted %s from ~/.config/user-dirs.dirs\n", ptr1);
110 if (strlen(ptr1) != 0) {
111 if (flags)
112 printf("%s ",prnt);
113 printf("directory resolved as \"%s\"\n", ptr1);
114
115 if (asprintf(&fname, "%s/%s", cfg.homedir, ptr1) == -1)
116 errExit("asprintf");
117
118 if (stat(fname, &s) == -1) {
119 free(fname);
120 goto errout;
121 }
122 free(fname);
123 return ptr1;
124 }
125 else
126 goto errout;
127 }
128 }
129 }
130
131 fclose(fp);
132 return NULL;
133
134 errout:
135 if (!arg_private && arg_debug) {
136 fprintf(stderr, "***\n");
137 fprintf(stderr, "*** Error: %s directory was not found in user home.\n",prnt);
138 fprintf(stderr, "*** \tAny files saved by the program, will be lost when the sandbox is closed.\n");
139 fprintf(stderr, "***\n");
140 }
141 return NULL;
142}
143 37
144char *resolve_hardcoded(int flags, char *entries[], const char *prnt) {
145 char *fname;
146 struct stat s;
147
148 int i = 0;
149 while (entries[i] != NULL) {
150 if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1)
151 errExit("asprintf");
152
153 if (stat(fname, &s) == 0) {
154 if (flags) {
155 printf("%s ", prnt);
156 printf("directory resolved as \"%s\"\n", fname);
157 }
158 free(fname);
159 return entries[i];
160 }
161 free(fname);
162 i++;
163 }
164
165 return NULL;
166}
167 38
168// 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
169void errLogExit(char* fmt, ...) { 40void errLogExit(char* fmt, ...) {
@@ -855,173 +726,6 @@ void notify_other(int fd) {
855} 726}
856 727
857 728
858// This function takes a pathname supplied by the user and expands '~' and
859// '${HOME}' at the start, to refer to a path relative to the user's home
860// directory (supplied).
861// The return value is allocated using malloc and must be freed by the caller.
862// The function returns NULL if there are any errors.
863char *expand_home(const char *path, const char* homedir) {
864 assert(path);
865 assert(homedir);
866
867 int called_as_root = 0;
868
869 if(geteuid() == 0)
870 called_as_root = 1;
871
872 if(called_as_root) {
873 EUID_USER();
874 }
875
876 EUID_ASSERT();
877
878 // Replace home macro
879 char *new_name = NULL;
880 if (strncmp(path, "${HOME}", 7) == 0) {
881 if (asprintf(&new_name, "%s%s", homedir, path + 7) == -1)
882 errExit("asprintf");
883 if(called_as_root)
884 EUID_ROOT();
885 return new_name;
886 }
887 else if (*path == '~') {
888 if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1)
889 errExit("asprintf");
890 if(called_as_root)
891 EUID_ROOT();
892 return new_name;
893 }
894 else if (strncmp(path, "${CFG}", 6) == 0) {
895 if (asprintf(&new_name, "%s%s", SYSCONFDIR, path + 6) == -1)
896 errExit("asprintf");
897 if(called_as_root)
898 EUID_ROOT();
899 return new_name;
900 }
901
902 else if (strncmp(path, "${DOWNLOADS}", 12) == 0) {
903 char *tmp = resolve_xdg(arg_debug, "XDG_DOWNLOAD_DIR=\"$HOME/", 24, "Downloads");
904 char *tmp2 = resolve_hardcoded(arg_debug, dentry, "Downloads");
905 if(tmp) {
906 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
907 errExit("asprintf");
908 if(called_as_root)
909 EUID_ROOT();
910 return new_name;
911 }
912 else if(tmp2) {
913 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
914 errExit("asprintf");
915 if(called_as_root)
916 EUID_ROOT();
917 return new_name;
918 }
919 }
920
921 else if (strncmp(path, "${MUSIC}", 8) == 0) {
922 char *tmp = resolve_xdg(arg_debug, "XDG_MUSIC_DIR=\"$HOME/", 21, "Music");
923 char *tmp2 = resolve_hardcoded(arg_debug, mentry, "Music");
924 if(tmp) {
925 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 8) == -1)
926 errExit("asprintf");
927 if(called_as_root)
928 EUID_ROOT();
929 return new_name;
930 }
931 else if(tmp2) {
932 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 8) == -1)
933 errExit("asprintf");
934 if(called_as_root)
935 EUID_ROOT();
936 return new_name;
937 }
938 }
939
940 else if (strncmp(path, "${VIDEOS}", 9) == 0) {
941 char *tmp = resolve_xdg(arg_debug, "XDG_VIDEOS_DIR=\"$HOME/", 22, "Videos");
942 char *tmp2 = resolve_hardcoded(arg_debug, ventry, "Videos");
943 if(tmp) {
944 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 9) == -1)
945 errExit("asprintf");
946 if(called_as_root)
947 EUID_ROOT();
948 return new_name;
949 }
950 else if(tmp2) {
951 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 9) == -1)
952 errExit("asprintf");
953 if(called_as_root)
954 EUID_ROOT();
955 return new_name;
956 }
957 }
958
959 else if (strncmp(path, "${PICTURES}", 11) == 0) {
960 char *tmp = resolve_xdg(arg_debug, "XDG_PICTURES_DIR=\"$HOME/", 24, "Pictures");
961 char *tmp2 = resolve_hardcoded(arg_debug, pentry, "Pictures");
962 if(tmp) {
963 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 11) == -1)
964 errExit("asprintf");
965 if(called_as_root)
966 EUID_ROOT();
967 return new_name;
968 }
969 else if(tmp2) {
970 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 11) == -1)
971 errExit("asprintf");
972 if(called_as_root)
973 EUID_ROOT();
974 return new_name;
975 }
976 }
977
978 else if (strncmp(path, "${DESKTOP}", 10) == 0) {
979 char *tmp = resolve_xdg(arg_debug, "XDG_DESKTOP_DIR=\"$HOME/", 24, "Desktop");
980 char *tmp2 = resolve_hardcoded(arg_debug, deentry, "Desktop");
981 if(tmp) {
982 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 10) == -1)
983 errExit("asprintf");
984 if(called_as_root)
985 EUID_ROOT();
986 return new_name;
987 }
988 else if(tmp2) {
989 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 10) == -1)
990 errExit("asprintf");
991 if(called_as_root)
992 EUID_ROOT();
993 return new_name;
994 }
995 }
996
997 else if (strncmp(path, "${DOCUMENTS}", 12) == 0) {
998 char *tmp = resolve_xdg(arg_debug, "XDG_DOCUMENTS_DIR=\"$HOME/", 25, "Documents");
999 char *tmp2 = resolve_hardcoded(arg_debug, doentry, "Documents");
1000 if(tmp) {
1001 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
1002 errExit("asprintf");
1003 if(called_as_root)
1004 EUID_ROOT();
1005 return new_name;
1006 }
1007 else if(tmp2) {
1008 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
1009 errExit("asprintf");
1010 if(called_as_root)
1011 EUID_ROOT();
1012 return new_name;
1013 }
1014 }
1015
1016 char *rv = strdup(path);
1017 if (!rv)
1018 errExit("strdup");
1019
1020 if(called_as_root)
1021 EUID_ROOT();
1022
1023 return rv;
1024}
1025 729
1026 730
1027// Equivalent to the GNU version of basename, which is incompatible with 731// Equivalent to the GNU version of basename, which is incompatible with
@@ -1082,44 +786,6 @@ uid_t pid_get_uid(pid_t pid) {
1082} 786}
1083 787
1084 788
1085void invalid_filename(const char *fname, int globbing) {
1086// EUID_ASSERT();
1087 assert(fname);
1088 const char *ptr = fname;
1089
1090 if (strncmp(ptr, "${HOME}", 7) == 0)
1091 ptr = fname + 7;
1092 else if (strncmp(ptr, "${PATH}", 7) == 0)
1093 ptr = fname + 7;
1094 else if (strcmp(fname, "${DOWNLOADS}") == 0)
1095 return;
1096 else if (strcmp(fname, "${MUSIC}") == 0)
1097 return;
1098 else if (strcmp(fname, "${VIDEOS}") == 0)
1099 return;
1100 else if (strcmp(fname, "${PICTURES}") == 0)
1101 return;
1102 else if (strcmp(fname, "${DESKTOP}") == 0)
1103 return;
1104 else if (strcmp(fname, "${DOCUMENTS}") == 0)
1105 return;
1106
1107 int len = strlen(ptr);
1108
1109 if (globbing) {
1110 // file globbing ('*?[]') is allowed
1111 if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) {
1112 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
1113 exit(1);
1114 }
1115 }
1116 else {
1117 if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
1118 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
1119 exit(1);
1120 }
1121 }
1122}
1123 789
1124 790
1125uid_t get_group_id(const char *group) { 791uid_t get_group_id(const char *group) {
diff --git a/test/fs/user-dirs.dirs b/test/fs/user-dirs.dirs
deleted file mode 100644
index ea3a3a4c2..000000000
--- a/test/fs/user-dirs.dirs
+++ /dev/null
@@ -1,15 +0,0 @@
1# This file is written by xdg-user-dirs-update
2# If you want to change or add directories, just edit the line you're
3# interested in. All local changes will be retained on the next run
4# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
5# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
6# absolute path. No other format is supported.
7#
8XDG_DESKTOP_DIR="$HOME/Desktop"
9XDG_DOWNLOAD_DIR="$HOME/Downloads"
10XDG_TEMPLATES_DIR="$HOME/Templates"
11XDG_PUBLICSHARE_DIR="$HOME/Public"
12XDG_DOCUMENTS_DIR="$HOME/Documents"
13XDG_MUSIC_DIR="$HOME/Music"
14XDG_PICTURES_DIR="$HOME/Pictures"
15XDG_VIDEOS_DIR="$HOME/Videos"