diff options
Diffstat (limited to 'src/firejail/fs_etc.c')
-rw-r--r-- | src/firejail/fs_etc.c | 146 |
1 files changed, 42 insertions, 104 deletions
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 7e18840fd..80329d5ba 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -21,20 +21,13 @@ | |||
21 | #include <sys/mount.h> | 21 | #include <sys/mount.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <sys/types.h> | 23 | #include <sys/types.h> |
24 | #include <sys/wait.h> | ||
25 | #include <unistd.h> | 24 | #include <unistd.h> |
26 | 25 | ||
27 | // return 0 if file not found, 1 if found | 26 | // return 0 if file not found, 1 if found |
28 | static int check_dir_or_file(const char *name) { | 27 | static int check_dir_or_file(const char *fname) { |
29 | assert(name); | 28 | assert(fname); |
30 | invalid_filename(name); | ||
31 | 29 | ||
32 | struct stat s; | 30 | struct stat s; |
33 | char *fname; | ||
34 | if (asprintf(&fname, "/etc/%s", name) == -1) | ||
35 | errExit("asprintf"); | ||
36 | if (arg_debug) | ||
37 | printf("Checking %s\n", fname); | ||
38 | if (stat(fname, &s) == -1) { | 31 | if (stat(fname, &s) == -1) { |
39 | if (arg_debug) | 32 | if (arg_debug) |
40 | printf("Warning: file %s not found.\n", fname); | 33 | printf("Warning: file %s not found.\n", fname); |
@@ -46,78 +39,46 @@ static int check_dir_or_file(const char *name) { | |||
46 | goto errexit; | 39 | goto errexit; |
47 | 40 | ||
48 | // dir or regular file | 41 | // dir or regular file |
49 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { | 42 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || !is_link(fname)) |
50 | free(fname); | 43 | return 1; // normal exit |
51 | return 1; | ||
52 | } | ||
53 | |||
54 | if (!is_link(fname)) { | ||
55 | free(fname); | ||
56 | return 1; | ||
57 | } | ||
58 | |||
59 | 44 | ||
60 | errexit: | 45 | errexit: |
61 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); | 46 | fprintf(stderr, "Error: invalid file type, %s.\n", fname); |
62 | exit(1); | 47 | exit(1); |
63 | } | 48 | } |
64 | 49 | ||
65 | void fs_check_etc_list(void) { | 50 | static void duplicate(char *fname) { |
66 | EUID_ASSERT(); | 51 | if (*fname == '~' || *fname == '/' || strstr(fname, "..")) { |
67 | if (strstr(cfg.etc_private_keep, "..")) { | 52 | fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); |
68 | fprintf(stderr, "Error: invalid private etc list\n"); | ||
69 | exit(1); | 53 | exit(1); |
70 | } | 54 | } |
71 | 55 | invalid_filename(fname); | |
72 | char *dlist = strdup(cfg.etc_private_keep); | 56 | |
73 | if (!dlist) | 57 | char *src; |
74 | errExit("strdup"); | 58 | if (asprintf(&src, "/etc/%s", fname) == -1) |
75 | 59 | errExit("asprintf"); | |
76 | // build a new list only with the files found | 60 | if (check_dir_or_file(src) == 0) { |
77 | char *newlist = malloc(strlen(cfg.etc_private_keep) + 1); | 61 | if (!arg_quiet) |
78 | if (!newlist) | 62 | fprintf(stderr, "Warning: skipping %s for private bin\n", fname); |
79 | errExit("malloc"); | 63 | free(src); |
80 | *newlist = '\0'; | 64 | return; |
81 | |||
82 | char *ptr = strtok(dlist, ","); | ||
83 | if (check_dir_or_file(ptr)) | ||
84 | strcat(newlist, ptr); | ||
85 | while ((ptr = strtok(NULL, ",")) != NULL) { | ||
86 | if (check_dir_or_file(ptr)) { | ||
87 | strcat(newlist, ","); | ||
88 | strcat(newlist, ptr); | ||
89 | } | ||
90 | } | 65 | } |
91 | cfg.etc_private_keep = newlist; | ||
92 | |||
93 | free(dlist); | ||
94 | } | ||
95 | 66 | ||
96 | static void duplicate(char *fname) { | 67 | struct stat s; |
97 | // copy the file | 68 | if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) { |
98 | if (arg_debug) | 69 | // create the directory in RUN_ETC_DIR |
99 | printf("running: %s -a --parents /etc/%s %s\n", RUN_CP_COMMAND, fname, RUN_MNT_DIR); | 70 | char *dirname; |
100 | 71 | if (asprintf(&dirname, "%s/%s", RUN_ETC_DIR, fname) == -1) | |
101 | pid_t child = fork(); | ||
102 | if (child < 0) | ||
103 | errExit("fork"); | ||
104 | if (child == 0) { | ||
105 | char *f; | ||
106 | if (asprintf(&f, "/etc/%s", fname) == -1) | ||
107 | errExit("asprintf"); | 72 | errExit("asprintf"); |
108 | clearenv(); | 73 | create_empty_dir_as_root(dirname, s.st_mode); |
109 | execlp(RUN_CP_COMMAND, RUN_CP_COMMAND, "-a", "--parents", f, RUN_MNT_DIR, NULL); | 74 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, dirname); |
110 | perror("execlp"); | 75 | free(dirname); |
111 | _exit(1); | ||
112 | } | 76 | } |
113 | // wait for the child to finish | 77 | else |
114 | waitpid(child, NULL, 0); | 78 | sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, RUN_ETC_DIR); |
115 | 79 | ||
116 | char *name; | 80 | fs_logger2("clone", src); |
117 | if (asprintf(&name, "/etc/%s", fname) == -1) | 81 | free(src); |
118 | errExit("asprintf"); | ||
119 | fs_logger2("clone", name); | ||
120 | free(name); | ||
121 | } | 82 | } |
122 | 83 | ||
123 | 84 | ||
@@ -125,12 +86,6 @@ void fs_private_etc_list(void) { | |||
125 | char *private_list = cfg.etc_private_keep; | 86 | char *private_list = cfg.etc_private_keep; |
126 | assert(private_list); | 87 | assert(private_list); |
127 | 88 | ||
128 | struct stat s; | ||
129 | if (stat("/etc", &s) == -1) { | ||
130 | fprintf(stderr, "Error: cannot find user /etc directory\n"); | ||
131 | exit(1); | ||
132 | } | ||
133 | |||
134 | // create /run/firejail/mnt/etc directory | 89 | // create /run/firejail/mnt/etc directory |
135 | mkdir_attr(RUN_ETC_DIR, 0755, 0, 0); | 90 | mkdir_attr(RUN_ETC_DIR, 0755, 0, 0); |
136 | fs_logger("tmpfs /etc"); | 91 | fs_logger("tmpfs /etc"); |
@@ -141,39 +96,22 @@ void fs_private_etc_list(void) { | |||
141 | // copy the list of files in the new etc directory | 96 | // copy the list of files in the new etc directory |
142 | // using a new child process with root privileges | 97 | // using a new child process with root privileges |
143 | if (*private_list != '\0') { | 98 | if (*private_list != '\0') { |
144 | pid_t child = fork(); | 99 | if (arg_debug) |
145 | if (child < 0) | 100 | printf("Copying files in the new etc directory:\n"); |
146 | errExit("fork"); | 101 | |
147 | if (child == 0) { | 102 | // copy the list of files in the new home directory |
148 | if (arg_debug) | 103 | char *dlist = strdup(private_list); |
149 | printf("Copying files in the new etc directory:\n"); | 104 | if (!dlist) |
105 | errExit("strdup"); | ||
150 | 106 | ||
151 | // elevate privileges - files in the new /etc directory belong to root | 107 | |
152 | if (setreuid(0, 0) < 0) | 108 | char *ptr = strtok(dlist, ","); |
153 | errExit("setreuid"); | 109 | duplicate(ptr); |
154 | if (setregid(0, 0) < 0) | ||
155 | errExit("setregid"); | ||
156 | |||
157 | // copy the list of files in the new home directory | ||
158 | char *dlist = strdup(private_list); | ||
159 | if (!dlist) | ||
160 | errExit("strdup"); | ||
161 | |||
162 | 110 | ||
163 | char *ptr = strtok(dlist, ","); | 111 | while ((ptr = strtok(NULL, ",")) != NULL) |
164 | duplicate(ptr); | 112 | duplicate(ptr); |
165 | 113 | free(dlist); | |
166 | while ((ptr = strtok(NULL, ",")) != NULL) | 114 | fs_logger_print(); |
167 | duplicate(ptr); | ||
168 | free(dlist); | ||
169 | fs_logger_print(); | ||
170 | #ifdef HAVE_GCOV | ||
171 | __gcov_flush(); | ||
172 | #endif | ||
173 | _exit(0); | ||
174 | } | ||
175 | // wait for the child to finish | ||
176 | waitpid(child, NULL, 0); | ||
177 | } | 115 | } |
178 | 116 | ||
179 | if (arg_debug) | 117 | if (arg_debug) |