aboutsummaryrefslogtreecommitdiffstats
path: root/src/fcopy
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@yahoo.com>2016-11-16 11:10:32 -0500
committerLibravatar netblue30 <netblue30@yahoo.com>2016-11-16 11:10:32 -0500
commitedcd62d7523365165e23695d7daabc94f1e9f48d (patch)
tree25649c9b73acd260fbafa30ee66cd6f7ceae8f1c /src/fcopy
parentMerge pull request #912 from curiosity-seeker/master (diff)
downloadfirejail-edcd62d7523365165e23695d7daabc94f1e9f48d.tar.gz
firejail-edcd62d7523365165e23695d7daabc94f1e9f48d.tar.zst
firejail-edcd62d7523365165e23695d7daabc94f1e9f48d.zip
fcopy part 1
Diffstat (limited to 'src/fcopy')
-rw-r--r--src/fcopy/Makefile.in45
-rw-r--r--src/fcopy/main.c345
2 files changed, 390 insertions, 0 deletions
diff --git a/src/fcopy/Makefile.in b/src/fcopy/Makefile.in
new file mode 100644
index 000000000..278957a4f
--- /dev/null
+++ b/src/fcopy/Makefile.in
@@ -0,0 +1,45 @@
1all: fcopy
2
3prefix=@prefix@
4exec_prefix=@exec_prefix@
5libdir=@libdir@
6sysconfdir=@sysconfdir@
7
8VERSION=@PACKAGE_VERSION@
9NAME=@PACKAGE_NAME@
10HAVE_SECCOMP_H=@HAVE_SECCOMP_H@
11HAVE_SECCOMP=@HAVE_SECCOMP@
12HAVE_CHROOT=@HAVE_CHROOT@
13HAVE_BIND=@HAVE_BIND@
14HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@
15HAVE_NETWORK=@HAVE_NETWORK@
16HAVE_USERNS=@HAVE_USERNS@
17HAVE_X11=@HAVE_X11@
18HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@
19HAVE_WHITELIST=@HAVE_WHITELIST@
20HAVE_GLOBALCFG=@HAVE_GLOBALCFG@
21HAVE_APPARMOR=@HAVE_APPARMOR@
22HAVE_OVERLAYFS=@HAVE_OVERLAYFS@
23HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@
24EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
25HAVE_GCOV=@HAVE_GCOV@
26EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
27
28H_FILE_LIST = $(sort $(wildcard *.[h]))
29C_FILE_LIST = $(sort $(wildcard *.c))
30OBJS = $(C_FILE_LIST:.c=.o)
31BINOBJS = $(foreach file, $(OBJS), $file)
32CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security
33LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread
34
35%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
36 $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
37
38fcopy: $(OBJS)
39 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
40
41clean:; rm -f *.o fcopy *.gcov *.gcda *.gcno
42
43distclean: clean
44 rm -fr Makefile
45
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
new file mode 100644
index 000000000..4437b90e5
--- /dev/null
+++ b/src/fcopy/main.c
@@ -0,0 +1,345 @@
1/*
2 * Copyright (C) 2014-2016 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
25
26#define COPY_LIMIT (500 * 1024 *1024)
27static int size_limit_reached = 0;
28static unsigned file_cnt = 0;
29static unsigned size_cnt = 0;
30
31static char *outpath = NULL;
32static char *inpath = NULL;
33
34
35// modified version of the function from util.c
36static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) {
37 assert(srcname);
38 assert(destname);
39 mode &= 07777;
40
41 // open source
42 int src = open(srcname, O_RDONLY);
43 if (src < 0) {
44 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname);
45 return;
46 }
47
48 // open destination
49 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755);
50 if (dst < 0) {
51 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
52 close(src);
53 return;
54 }
55
56 // copy
57 ssize_t len;
58 static const int BUFLEN = 1024;
59 unsigned char buf[BUFLEN];
60 while ((len = read(src, buf, BUFLEN)) > 0) {
61 int done = 0;
62 while (done != len) {
63 int rv = write(dst, buf + done, len - done);
64 if (rv == -1)
65 goto errexit;
66 done += rv;
67 }
68 }
69 fflush(0);
70
71 if (fchown(dst, uid, gid) == -1)
72 goto errexit;
73 if (fchmod(dst, mode) == -1)
74 goto errexit;
75
76 close(src);
77 close(dst);
78
79 return;
80
81errexit:
82 close(src);
83 close(dst);
84 unlink(destname);
85 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname);
86}
87
88
89
90// modified version of the function in firejail/util.c
91static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) {
92 assert(fname);
93 mode &= 07777;
94
95 if (mkdir(fname, mode) == -1 ||
96 chmod(fname, mode) == -1) {
97 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname);
98 errExit("mkdir/chmod");
99 }
100 if (chown(fname, uid, gid))
101 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname);
102}
103
104void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) {
105 char *rp = realpath(target, NULL);
106 if (rp) {
107 if (symlink(rp, linkpath) == -1)
108 goto errout;
109 free(rp);
110 }
111 else
112 goto errout;
113
114 return;
115errout:
116 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target);
117}
118
119static int first = 1;
120static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
121 (void) st;
122 (void) sftw;
123 assert(infname);
124 assert(*infname != '\0');
125 assert(outpath);
126 assert(*outpath != '\0');
127 assert(inpath);
128
129 // check size limit
130 if (size_limit_reached)
131 return 0;
132
133 char *outfname;
134 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1)
135 errExit("asprintf");
136
137 // don't copy it if we already have the file
138 struct stat s;
139 if (stat(outfname, &s) == 0) {
140 if (first)
141 first = 0;
142 else
143 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname);
144 free(outfname);
145 return 0;
146 }
147
148 // extract mode and ownership
149 if (stat(infname, &s) != 0) {
150 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname);
151 free(outfname);
152 return 0;
153 }
154 uid_t uid = s.st_uid;
155 gid_t gid = s.st_gid;
156 mode_t mode = s.st_mode;
157
158 // recalculate size
159 if ((s.st_size + size_cnt) > COPY_LIMIT) {
160 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024);
161 size_limit_reached = 1;
162 free(outfname);
163 return 0;
164 }
165
166 file_cnt++;
167 size_cnt += s.st_size;
168
169 if(ftype == FTW_F) {
170 copy_file(infname, outfname, mode, uid, gid);
171 }
172 else if (ftype == FTW_D) {
173 mkdir_attr(outfname, mode, uid, gid);
174 }
175 else if (ftype == FTW_SL) {
176 copy_link(infname, outfname, mode, uid, gid);
177 }
178
179 return(0);
180}
181
182static char *check(const char *src) {
183 struct stat s;
184 char *rsrc = realpath(src, NULL);
185 if (!rsrc || stat(rsrc, &s) == -1) {
186 fprintf(stderr, "Error fcopy: cannot find %s directory\n", src);
187 exit(1);
188 }
189
190 // check uid
191 if (s.st_uid != getuid() || s.st_gid != getgid()) {
192 fprintf(stderr, "Error fcopy: uid/gid mismatch for %s\n", rsrc);
193 exit(1);
194 }
195
196 // dir, link, regular file
197 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) {
198 return rsrc; // normal exit from the function
199 }
200 fprintf(stderr, "Error fcopy: invalid directory %s\n", rsrc);
201 exit(1);
202}
203
204static void duplicate_dir(const char *src, const char *dest, struct stat *s) {
205 (void) s;
206 char *rsrc = check(src);
207 char *rdest = check(dest);
208 inpath = rsrc;
209 outpath = rdest;
210
211 // walk
212 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) {
213 fprintf(stderr, "Error: unable to copy file\n");
214 exit(1);
215 }
216
217 free(rsrc);
218 free(rdest);
219}
220
221static void duplicate_file(const char *src, const char *dest, struct stat *s) {
222 char *rsrc = check(src);
223 char *rdest = check(dest);
224 uid_t uid = s->st_uid;
225 gid_t gid = s->st_gid;
226 mode_t mode = s->st_mode;
227
228 // build destination file name
229 char *name;
230 char *ptr = strrchr(rsrc, '/');
231 ptr++;
232 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
233 errExit("asprintf");
234
235 // copy
236 copy_file(rsrc, name, mode, uid, gid);
237
238 free(name);
239 free(rsrc);
240 free(rdest);
241}
242
243static void duplicate_link(const char *src, const char *dest, struct stat *s) {
244 char *rsrc = check(src);
245 char *rdest = check(dest);
246 uid_t uid = s->st_uid;
247 gid_t gid = s->st_gid;
248 mode_t mode = s->st_mode;
249
250 // build destination file name
251 char *name;
252 char *ptr = strrchr(rsrc, '/');
253 ptr++;
254 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
255 errExit("asprintf");
256
257 // copy
258 copy_link(rsrc, name, mode, uid, gid);
259
260 free(name);
261 free(rsrc);
262 free(rdest);
263}
264
265static void usage(void) {
266 printf("Usage: fcopy src dest\n");
267 printf("Copy src file in dest directory. If src is a directory, copy all the files in\n");
268 printf("src recoursively\n");
269}
270
271int main(int argc, char **argv) {
272#if 0
273{
274//system("cat /proc/self/status");
275int i;
276for (i = 0; i < argc; i++)
277 printf("*%s* ", argv[i]);
278printf("\n");
279}
280#endif
281 if (argc != 3) {
282 fprintf(stderr, "Error fcopy: files missing\n");
283 usage();
284 exit(1);
285 }
286
287 int i;
288 int index = 1;
289 for (i = 1; i < (argc - 2); i++) {
290 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
291 usage();
292 return 0;
293 }
294 }
295
296 // check the two files; remove ending /
297 char *src = argv[index];
298 int len = strlen(src);
299 if (src[len - 1] == '/')
300 src[len - 1] = '\0';
301 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) {
302 fprintf(stderr, "Error fcopy: invalid file name %s\n", src);
303 exit(1);
304 }
305
306 char *dest = argv[index + 1];
307 len = strlen(dest);
308 if (dest[len - 1] == '/')
309 dest[len - 1] = '\0';
310 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) {
311 fprintf(stderr, "Error fcopy: invalid file name %s\n", dest);
312 exit(1);
313 }
314
315
316 // the destination should be a directory; remove ending /
317 struct stat s;
318 if (stat(dest, &s) == -1) {
319 fprintf(stderr, "Error fcopy: cannot find destination directory\n");
320 exit(1);
321 }
322 if (S_ISDIR(s.st_mode) == -1) {
323 fprintf(stderr, "Error fcopy: the destination should be a directory\n");
324 exit(1);
325 }
326
327 // copy files
328 if (lstat(src, &s) == -1) {
329 fprintf(stderr, "Error fcopy: cannot find source file\n");
330 exit(1);
331 }
332
333 if (S_ISDIR(s.st_mode))
334 duplicate_dir(src, dest, &s);
335 else if (S_ISREG(s.st_mode))
336 duplicate_file(src, dest, &s);
337 else if (S_ISLNK(s.st_mode))
338 duplicate_link(src, dest, &s);
339 else {
340 fprintf(stderr, "Error fcopy: source file unsupported\n");
341 exit(1);
342 }
343
344 return 0;
345}