diff options
Diffstat (limited to 'src/fcopy')
-rw-r--r-- | src/fcopy/Makefile.in | 14 | ||||
-rw-r--r-- | src/fcopy/main.c | 405 |
2 files changed, 0 insertions, 419 deletions
diff --git a/src/fcopy/Makefile.in b/src/fcopy/Makefile.in deleted file mode 100644 index c9e7d87ab..000000000 --- a/src/fcopy/Makefile.in +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | all: fcopy | ||
2 | |||
3 | include ../common.mk | ||
4 | |||
5 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h | ||
6 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
7 | |||
8 | fcopy: $(OBJS) | ||
9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
10 | |||
11 | clean:; rm -f *.o fcopy *.gcov *.gcda *.gcno | ||
12 | |||
13 | distclean: clean | ||
14 | rm -fr Makefile | ||
diff --git a/src/fcopy/main.c b/src/fcopy/main.c deleted file mode 100644 index e93cd1cb8..000000000 --- a/src/fcopy/main.c +++ /dev/null | |||
@@ -1,405 +0,0 @@ | |||
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 | |||
21 | #include "../include/common.h" | ||
22 | #include <fcntl.h> | ||
23 | #include <ftw.h> | ||
24 | #include <errno.h> | ||
25 | #include <pwd.h> | ||
26 | |||
27 | int arg_quiet = 0; | ||
28 | static int arg_follow_link = 0; | ||
29 | |||
30 | #define COPY_LIMIT (500 * 1024 *1024) | ||
31 | static int size_limit_reached = 0; | ||
32 | static unsigned file_cnt = 0; | ||
33 | static unsigned size_cnt = 0; | ||
34 | |||
35 | static char *outpath = NULL; | ||
36 | static char *inpath = NULL; | ||
37 | |||
38 | // modified version of the function from util.c | ||
39 | static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) { | ||
40 | assert(srcname); | ||
41 | assert(destname); | ||
42 | mode &= 07777; | ||
43 | |||
44 | // don't copy the file if it is already there | ||
45 | struct stat s; | ||
46 | if (stat(destname, &s) == 0) | ||
47 | return; | ||
48 | |||
49 | // open source | ||
50 | int src = open(srcname, O_RDONLY); | ||
51 | if (src < 0) { | ||
52 | if (!arg_quiet) | ||
53 | fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname); | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | // open destination | ||
58 | int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755); | ||
59 | if (dst < 0) { | ||
60 | if (!arg_quiet) | ||
61 | fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname); | ||
62 | close(src); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | // copy | ||
67 | ssize_t len; | ||
68 | static const int BUFLEN = 1024; | ||
69 | unsigned char buf[BUFLEN]; | ||
70 | while ((len = read(src, buf, BUFLEN)) > 0) { | ||
71 | int done = 0; | ||
72 | while (done != len) { | ||
73 | int rv = write(dst, buf + done, len - done); | ||
74 | if (rv == -1) | ||
75 | goto errexit; | ||
76 | done += rv; | ||
77 | } | ||
78 | } | ||
79 | fflush(0); | ||
80 | |||
81 | if (fchown(dst, uid, gid) == -1) | ||
82 | goto errexit; | ||
83 | if (fchmod(dst, mode) == -1) | ||
84 | goto errexit; | ||
85 | |||
86 | close(src); | ||
87 | close(dst); | ||
88 | |||
89 | return; | ||
90 | |||
91 | errexit: | ||
92 | close(src); | ||
93 | close(dst); | ||
94 | unlink(destname); | ||
95 | if (!arg_quiet) | ||
96 | fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname); | ||
97 | } | ||
98 | |||
99 | |||
100 | // modified version of the function in firejail/util.c | ||
101 | static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) { | ||
102 | assert(fname); | ||
103 | mode &= 07777; | ||
104 | |||
105 | if (mkdir(fname, mode) == -1 || | ||
106 | chmod(fname, mode) == -1) { | ||
107 | fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname); | ||
108 | errExit("mkdir/chmod"); | ||
109 | } | ||
110 | if (chown(fname, uid, gid)) { | ||
111 | if (!arg_quiet) | ||
112 | fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | |||
117 | void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) { | ||
118 | (void) mode; | ||
119 | (void) uid; | ||
120 | (void) gid; | ||
121 | |||
122 | // if the link is already there, don't create it | ||
123 | struct stat s; | ||
124 | if (stat(linkpath, &s) == 0) | ||
125 | return; | ||
126 | |||
127 | char *rp = realpath(target, NULL); | ||
128 | if (rp) { | ||
129 | if (symlink(rp, linkpath) == -1) { | ||
130 | free(rp); | ||
131 | goto errout; | ||
132 | } | ||
133 | free(rp); | ||
134 | } | ||
135 | else | ||
136 | goto errout; | ||
137 | |||
138 | return; | ||
139 | errout: | ||
140 | if (!arg_quiet) | ||
141 | fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target); | ||
142 | } | ||
143 | |||
144 | |||
145 | |||
146 | static int first = 1; | ||
147 | static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) { | ||
148 | (void) st; | ||
149 | (void) sftw; | ||
150 | assert(infname); | ||
151 | assert(*infname != '\0'); | ||
152 | assert(outpath); | ||
153 | assert(*outpath != '\0'); | ||
154 | assert(inpath); | ||
155 | |||
156 | // check size limit | ||
157 | if (size_limit_reached) | ||
158 | return 0; | ||
159 | |||
160 | char *outfname; | ||
161 | if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1) | ||
162 | errExit("asprintf"); | ||
163 | |||
164 | // don't copy it if we already have the file | ||
165 | struct stat s; | ||
166 | if (stat(outfname, &s) == 0) { | ||
167 | if (first) | ||
168 | first = 0; | ||
169 | else if (!arg_quiet) | ||
170 | fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname); | ||
171 | free(outfname); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | // extract mode and ownership | ||
176 | if (stat(infname, &s) != 0) { | ||
177 | if (!arg_quiet) | ||
178 | fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname); | ||
179 | free(outfname); | ||
180 | return 0; | ||
181 | } | ||
182 | uid_t uid = s.st_uid; | ||
183 | gid_t gid = s.st_gid; | ||
184 | mode_t mode = s.st_mode; | ||
185 | |||
186 | // recalculate size | ||
187 | if ((s.st_size + size_cnt) > COPY_LIMIT) { | ||
188 | fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024); | ||
189 | size_limit_reached = 1; | ||
190 | free(outfname); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | file_cnt++; | ||
195 | size_cnt += s.st_size; | ||
196 | |||
197 | if(ftype == FTW_F) { | ||
198 | copy_file(infname, outfname, mode, uid, gid); | ||
199 | } | ||
200 | else if (ftype == FTW_D) { | ||
201 | mkdir_attr(outfname, mode, uid, gid); | ||
202 | } | ||
203 | else if (ftype == FTW_SL) { | ||
204 | copy_link(infname, outfname, mode, uid, gid); | ||
205 | } | ||
206 | |||
207 | return(0); | ||
208 | } | ||
209 | |||
210 | |||
211 | static char *check(const char *src) { | ||
212 | struct stat s; | ||
213 | char *rsrc = realpath(src, NULL); | ||
214 | if (!rsrc || stat(rsrc, &s) == -1) | ||
215 | goto errexit; | ||
216 | |||
217 | // on systems with systemd-resolved installed /etc/resolve.conf is a symlink to | ||
218 | // /run/systemd/resolve/resolv.conf; this file is owned by systemd-resolve user | ||
219 | // checking gid will fail for files with a larger group such as /usr/bin/mutt_dotlock | ||
220 | uid_t user = getuid(); | ||
221 | if (user == 0 && strncmp(rsrc, "/run/systemd/resolve/", 21) == 0) { | ||
222 | // check user systemd-resolve | ||
223 | struct passwd *p = getpwnam("systemd-resolve"); | ||
224 | if (!p) | ||
225 | goto errexit; | ||
226 | if (s.st_uid != user && s.st_uid != p->pw_uid) | ||
227 | goto errexit; | ||
228 | } | ||
229 | else { | ||
230 | if (s.st_uid != user) | ||
231 | goto errexit; | ||
232 | } | ||
233 | |||
234 | // dir, link, regular file | ||
235 | if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) | ||
236 | return rsrc; // normal exit from the function | ||
237 | |||
238 | errexit: | ||
239 | fprintf(stderr, "Error fcopy: invalid file %s\n", src); | ||
240 | exit(1); | ||
241 | } | ||
242 | |||
243 | |||
244 | static void duplicate_dir(const char *src, const char *dest, struct stat *s) { | ||
245 | (void) s; | ||
246 | char *rsrc = check(src); | ||
247 | char *rdest = check(dest); | ||
248 | inpath = rsrc; | ||
249 | outpath = rdest; | ||
250 | |||
251 | // walk | ||
252 | if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) { | ||
253 | fprintf(stderr, "Error: unable to copy file\n"); | ||
254 | exit(1); | ||
255 | } | ||
256 | |||
257 | free(rsrc); | ||
258 | free(rdest); | ||
259 | } | ||
260 | |||
261 | |||
262 | static void duplicate_file(const char *src, const char *dest, struct stat *s) { | ||
263 | char *rsrc = check(src); | ||
264 | char *rdest = check(dest); | ||
265 | uid_t uid = s->st_uid; | ||
266 | gid_t gid = s->st_gid; | ||
267 | mode_t mode = s->st_mode; | ||
268 | |||
269 | // build destination file name | ||
270 | char *name; | ||
271 | char *ptr = (arg_follow_link)? strrchr(src, '/'): strrchr(rsrc, '/'); | ||
272 | ptr++; | ||
273 | if (asprintf(&name, "%s/%s", rdest, ptr) == -1) | ||
274 | errExit("asprintf"); | ||
275 | |||
276 | // copy | ||
277 | copy_file(rsrc, name, mode, uid, gid); | ||
278 | |||
279 | free(name); | ||
280 | free(rsrc); | ||
281 | free(rdest); | ||
282 | } | ||
283 | |||
284 | |||
285 | static void duplicate_link(const char *src, const char *dest, struct stat *s) { | ||
286 | char *rsrc = check(src); // we drop the result and use the original name | ||
287 | char *rdest = check(dest); | ||
288 | uid_t uid = s->st_uid; | ||
289 | gid_t gid = s->st_gid; | ||
290 | mode_t mode = s->st_mode; | ||
291 | |||
292 | // build destination file name | ||
293 | char *name; | ||
294 | // char *ptr = strrchr(rsrc, '/'); | ||
295 | char *ptr = strrchr(src, '/'); | ||
296 | ptr++; | ||
297 | if (asprintf(&name, "%s/%s", rdest, ptr) == -1) | ||
298 | errExit("asprintf"); | ||
299 | |||
300 | // copy | ||
301 | copy_link(rsrc, name, mode, uid, gid); | ||
302 | |||
303 | free(name); | ||
304 | free(rsrc); | ||
305 | free(rdest); | ||
306 | } | ||
307 | |||
308 | |||
309 | static void usage(void) { | ||
310 | fputs("Usage: fcopy [--follow-link] src dest\n" | ||
311 | "\n" | ||
312 | "Copy SRC to DEST/SRC. SRC may be a file, directory, or symbolic link.\n" | ||
313 | "If SRC is a directory it is copied recursively. If it is a symlink,\n" | ||
314 | "the link itself is duplicated, unless --follow-link is given,\n" | ||
315 | "in which case the destination of the link is copied.\n" | ||
316 | "DEST must already exist and must be a directory.\n", stderr); | ||
317 | } | ||
318 | |||
319 | |||
320 | int main(int argc, char **argv) { | ||
321 | #if 0 | ||
322 | { | ||
323 | //system("cat /proc/self/status"); | ||
324 | int i; | ||
325 | for (i = 0; i < argc; i++) | ||
326 | printf("*%s* ", argv[i]); | ||
327 | printf("\n"); | ||
328 | } | ||
329 | #endif | ||
330 | char *quiet = getenv("FIREJAIL_QUIET"); | ||
331 | if (quiet && strcmp(quiet, "yes") == 0) | ||
332 | arg_quiet = 1; | ||
333 | |||
334 | char *src; | ||
335 | char *dest; | ||
336 | |||
337 | if (argc == 3) { | ||
338 | src = argv[1]; | ||
339 | dest = argv[2]; | ||
340 | arg_follow_link = 0; | ||
341 | } | ||
342 | else if (argc == 4 && !strcmp(argv[1], "--follow-link")) { | ||
343 | src = argv[2]; | ||
344 | dest = argv[3]; | ||
345 | arg_follow_link = 1; | ||
346 | } | ||
347 | else { | ||
348 | fprintf(stderr, "Error: arguments missing\n"); | ||
349 | usage(); | ||
350 | exit(1); | ||
351 | } | ||
352 | |||
353 | // trim trailing chars | ||
354 | if (src[strlen(src) - 1] == '/') | ||
355 | src[strlen(src) - 1] = '\0'; | ||
356 | if (dest[strlen(dest) - 1] == '/') | ||
357 | dest[strlen(dest) - 1] = '\0'; | ||
358 | |||
359 | // check the two files; remove ending / | ||
360 | int len = strlen(src); | ||
361 | if (src[len - 1] == '/') | ||
362 | src[len - 1] = '\0'; | ||
363 | if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) { | ||
364 | fprintf(stderr, "Error fcopy: invalid source file name %s\n", src); | ||
365 | exit(1); | ||
366 | } | ||
367 | |||
368 | len = strlen(dest); | ||
369 | if (dest[len - 1] == '/') | ||
370 | dest[len - 1] = '\0'; | ||
371 | if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) { | ||
372 | fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest); | ||
373 | exit(1); | ||
374 | } | ||
375 | |||
376 | // the destination should be a directory; | ||
377 | struct stat s; | ||
378 | if (stat(dest, &s) == -1) { | ||
379 | fprintf(stderr, "Error fcopy: dest dir %s: %s\n", dest, strerror(errno)); | ||
380 | exit(1); | ||
381 | } | ||
382 | if (!S_ISDIR(s.st_mode)) { | ||
383 | fprintf(stderr, "Error fcopy: dest %s is not a directory\n", dest); | ||
384 | exit(1); | ||
385 | } | ||
386 | |||
387 | // copy files | ||
388 | if ((arg_follow_link ? stat : lstat)(src, &s) == -1) { | ||
389 | fprintf(stderr, "Error fcopy: src %s: %s\n", src, strerror(errno)); | ||
390 | exit(1); | ||
391 | } | ||
392 | |||
393 | if (S_ISDIR(s.st_mode)) | ||
394 | duplicate_dir(src, dest, &s); | ||
395 | else if (S_ISREG(s.st_mode)) | ||
396 | duplicate_file(src, dest, &s); | ||
397 | else if (S_ISLNK(s.st_mode)) | ||
398 | duplicate_link(src, dest, &s); | ||
399 | else { | ||
400 | fprintf(stderr, "Error fcopy: src %s is an unsupported type of file\n", src); | ||
401 | exit(1); | ||
402 | } | ||
403 | |||
404 | return 0; | ||
405 | } | ||