Commit edcd62d7 authored by netblue30's avatar netblue30

fcopy part 1

parent 2c8cb7db
all: apps man
MYLIBS = src/lib
APPS = src/firejail src/firemon src/firecfg src/libtrace src/libtracelog src/ftee src/faudit src/libconnect src/fnet src/fseccomp
APPS = src/firejail src/firemon src/firecfg src/libtrace src/libtracelog src/ftee src/faudit src/libconnect src/fnet src/fseccomp src/fcopy
MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5
prefix=@prefix@
......@@ -78,6 +78,7 @@ realinstall:
install -c -m 0755 src/faudit/faudit $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0755 src/fnet/fnet $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0755 src/fseccomp/fseccomp $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0755 src/fcopy/fcopy $(DESTDIR)/$(libdir)/firejail/.
# documents
install -m 0755 -d $(DESTDIR)/$(DOCDIR)
install -c -m 0644 COPYING $(DESTDIR)/$(DOCDIR)/.
......@@ -128,6 +129,7 @@ install-strip: all
strip src/faudit/faudit
strip src/fnet/fnet
strip src/fseccomp/fseccomp
strip src/fcopy/fcopy
$(MAKE) realinstall
uninstall:
......@@ -145,7 +147,7 @@ uninstall:
rm -f $(DESTDIR)/$(datarootdir)/bash-completion/completions/firecfg
DISTFILES = "src etc platform configure configure.ac Makefile.in install.sh mkman.sh mketc.sh mkdeb.sh mkuid.sh COPYING README RELNOTES"
DISTFILES_TEST = "test/rlimit test/apps test/apps-x11 test/apps-x11-xorg test/root test/environment test/profiles test/utils test/compile test/filters test/network test/arguments test/fs test/sysutils"
DISTFILES_TEST = "test/rlimit test/apps test/apps-x11 test/apps-x11-xorg test/root test/fcopy test/environment test/profiles test/utils test/compile test/filters test/network test/arguments test/fs test/sysutils"
dist:
mv config.status config.status.old
......@@ -226,7 +228,10 @@ test-fs:
test-rlimit:
cd test/rlimit; ./rlimit.sh | grep TESTING
test: test-profiles test-fs test-utils test-environment test-apps test-apps-x11 test-apps-x11-xorg test-filters test-arguments test-rlimit
test-fcopy:
cd test/fcopy; ./fcopy.sh | grep TESTING
test: test-profiles test-fcopy test-fs test-utils test-environment test-apps test-apps-x11 test-apps-x11-xorg test-filters test-arguments test-rlimit
echo "TEST COMPLETE"
##########################################
......@@ -254,6 +259,6 @@ test-overlay:
# For testing hidepid system, the command to set it up is "mount -o remount,rw,hidepid=2 /proc"
test-all: test-root test-network test-appimage test-overlay test
test-all: test-root test-network test-appimage test-overlay test-fcopy test
echo "TEST COMPLETE"
\ No newline at end of file
......@@ -3777,7 +3777,7 @@ if test "$prefix" = /usr; then
sysconfdir="/etc"
fi
ac_config_files="$ac_config_files Makefile src/lib/Makefile src/fnet/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/ftee/Makefile src/faudit/Makefile src/libconnect/Makefile src/fseccomp/Makefile"
ac_config_files="$ac_config_files Makefile src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/ftee/Makefile src/faudit/Makefile src/libconnect/Makefile src/fseccomp/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
......@@ -4488,6 +4488,7 @@ do
case $ac_config_target in
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"src/lib/Makefile") CONFIG_FILES="$CONFIG_FILES src/lib/Makefile" ;;
"src/fcopy/Makefile") CONFIG_FILES="$CONFIG_FILES src/fcopy/Makefile" ;;
"src/fnet/Makefile") CONFIG_FILES="$CONFIG_FILES src/fnet/Makefile" ;;
"src/firejail/Makefile") CONFIG_FILES="$CONFIG_FILES src/firejail/Makefile" ;;
"src/firemon/Makefile") CONFIG_FILES="$CONFIG_FILES src/firemon/Makefile" ;;
......
......@@ -158,8 +158,9 @@ if test "$prefix" = /usr; then
sysconfdir="/etc"
fi
AC_OUTPUT(Makefile src/lib/Makefile src/fnet/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile \
src/firecfg/Makefile src/ftee/Makefile src/faudit/Makefile src/libconnect/Makefile src/fseccomp/Makefile)
AC_OUTPUT(Makefile src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile \
src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile \
src/ftee/Makefile src/faudit/Makefile src/libconnect/Makefile src/fseccomp/Makefile)
echo
echo "Configuration options:"
......
......@@ -13,6 +13,7 @@ firemon --help
/usr/lib/firejail/fnet --help
/usr/lib/firejail/fseccomp --help
/usr/lib/firejail/ftee --help
/usr/lib/firejail/fcopy --help
firecfg --help
sudo chown $USER:$USER `find .`
generate
......@@ -34,6 +35,10 @@ make test-overlay
generate
sleep 2
make test-fcopy
generate
sleep 2
make test-profiles
generate
sleep 2
......
all: fcopy
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
sysconfdir=@sysconfdir@
VERSION=@PACKAGE_VERSION@
NAME=@PACKAGE_NAME@
HAVE_SECCOMP_H=@HAVE_SECCOMP_H@
HAVE_SECCOMP=@HAVE_SECCOMP@
HAVE_CHROOT=@HAVE_CHROOT@
HAVE_BIND=@HAVE_BIND@
HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@
HAVE_NETWORK=@HAVE_NETWORK@
HAVE_USERNS=@HAVE_USERNS@
HAVE_X11=@HAVE_X11@
HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@
HAVE_WHITELIST=@HAVE_WHITELIST@
HAVE_GLOBALCFG=@HAVE_GLOBALCFG@
HAVE_APPARMOR=@HAVE_APPARMOR@
HAVE_OVERLAYFS=@HAVE_OVERLAYFS@
HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@
EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
HAVE_GCOV=@HAVE_GCOV@
EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
H_FILE_LIST = $(sort $(wildcard *.[h]))
C_FILE_LIST = $(sort $(wildcard *.c))
OBJS = $(C_FILE_LIST:.c=.o)
BINOBJS = $(foreach file, $(OBJS), $file)
CFLAGS += -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
LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread
%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
fcopy: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
clean:; rm -f *.o fcopy *.gcov *.gcda *.gcno
distclean: clean
rm -fr Makefile
/*
* Copyright (C) 2014-2016 Firejail Authors
*
* This file is part of firejail project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../include/common.h"
#include <fcntl.h>
#include <ftw.h>
#define COPY_LIMIT (500 * 1024 *1024)
static int size_limit_reached = 0;
static unsigned file_cnt = 0;
static unsigned size_cnt = 0;
static char *outpath = NULL;
static char *inpath = NULL;
// modified version of the function from util.c
static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) {
assert(srcname);
assert(destname);
mode &= 07777;
// open source
int src = open(srcname, O_RDONLY);
if (src < 0) {
fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname);
return;
}
// open destination
int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755);
if (dst < 0) {
fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
close(src);
return;
}
// copy
ssize_t len;
static const int BUFLEN = 1024;
unsigned char buf[BUFLEN];
while ((len = read(src, buf, BUFLEN)) > 0) {
int done = 0;
while (done != len) {
int rv = write(dst, buf + done, len - done);
if (rv == -1)
goto errexit;
done += rv;
}
}
fflush(0);
if (fchown(dst, uid, gid) == -1)
goto errexit;
if (fchmod(dst, mode) == -1)
goto errexit;
close(src);
close(dst);
return;
errexit:
close(src);
close(dst);
unlink(destname);
fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname);
}
// modified version of the function in firejail/util.c
static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) {
assert(fname);
mode &= 07777;
if (mkdir(fname, mode) == -1 ||
chmod(fname, mode) == -1) {
fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname);
errExit("mkdir/chmod");
}
if (chown(fname, uid, gid))
fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname);
}
void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) {
char *rp = realpath(target, NULL);
if (rp) {
if (symlink(rp, linkpath) == -1)
goto errout;
free(rp);
}
else
goto errout;
return;
errout:
fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target);
}
static int first = 1;
static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
(void) st;
(void) sftw;
assert(infname);
assert(*infname != '\0');
assert(outpath);
assert(*outpath != '\0');
assert(inpath);
// check size limit
if (size_limit_reached)
return 0;
char *outfname;
if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1)
errExit("asprintf");
// don't copy it if we already have the file
struct stat s;
if (stat(outfname, &s) == 0) {
if (first)
first = 0;
else
fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname);
free(outfname);
return 0;
}
// extract mode and ownership
if (stat(infname, &s) != 0) {
fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname);
free(outfname);
return 0;
}
uid_t uid = s.st_uid;
gid_t gid = s.st_gid;
mode_t mode = s.st_mode;
// recalculate size
if ((s.st_size + size_cnt) > COPY_LIMIT) {
fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024);
size_limit_reached = 1;
free(outfname);
return 0;
}
file_cnt++;
size_cnt += s.st_size;
if(ftype == FTW_F) {
copy_file(infname, outfname, mode, uid, gid);
}
else if (ftype == FTW_D) {
mkdir_attr(outfname, mode, uid, gid);
}
else if (ftype == FTW_SL) {
copy_link(infname, outfname, mode, uid, gid);
}
return(0);
}
static char *check(const char *src) {
struct stat s;
char *rsrc = realpath(src, NULL);
if (!rsrc || stat(rsrc, &s) == -1) {
fprintf(stderr, "Error fcopy: cannot find %s directory\n", src);
exit(1);
}
// check uid
if (s.st_uid != getuid() || s.st_gid != getgid()) {
fprintf(stderr, "Error fcopy: uid/gid mismatch for %s\n", rsrc);
exit(1);
}
// dir, link, regular file
if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) {
return rsrc; // normal exit from the function
}
fprintf(stderr, "Error fcopy: invalid directory %s\n", rsrc);
exit(1);
}
static void duplicate_dir(const char *src, const char *dest, struct stat *s) {
(void) s;
char *rsrc = check(src);
char *rdest = check(dest);
inpath = rsrc;
outpath = rdest;
// walk
if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) {
fprintf(stderr, "Error: unable to copy file\n");
exit(1);
}
free(rsrc);
free(rdest);
}
static void duplicate_file(const char *src, const char *dest, struct stat *s) {
char *rsrc = check(src);
char *rdest = check(dest);
uid_t uid = s->st_uid;
gid_t gid = s->st_gid;
mode_t mode = s->st_mode;
// build destination file name
char *name;
char *ptr = strrchr(rsrc, '/');
ptr++;
if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
errExit("asprintf");
// copy
copy_file(rsrc, name, mode, uid, gid);
free(name);
free(rsrc);
free(rdest);
}
static void duplicate_link(const char *src, const char *dest, struct stat *s) {
char *rsrc = check(src);
char *rdest = check(dest);
uid_t uid = s->st_uid;
gid_t gid = s->st_gid;
mode_t mode = s->st_mode;
// build destination file name
char *name;
char *ptr = strrchr(rsrc, '/');
ptr++;
if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
errExit("asprintf");
// copy
copy_link(rsrc, name, mode, uid, gid);
free(name);
free(rsrc);
free(rdest);
}
static void usage(void) {
printf("Usage: fcopy src dest\n");
printf("Copy src file in dest directory. If src is a directory, copy all the files in\n");
printf("src recoursively\n");
}
int main(int argc, char **argv) {
#if 0
{
//system("cat /proc/self/status");
int i;
for (i = 0; i < argc; i++)
printf("*%s* ", argv[i]);
printf("\n");
}
#endif
if (argc != 3) {
fprintf(stderr, "Error fcopy: files missing\n");
usage();
exit(1);
}
int i;
int index = 1;
for (i = 1; i < (argc - 2); i++) {
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
usage();
return 0;
}
}
// check the two files; remove ending /
char *src = argv[index];
int len = strlen(src);
if (src[len - 1] == '/')
src[len - 1] = '\0';
if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) {
fprintf(stderr, "Error fcopy: invalid file name %s\n", src);
exit(1);
}
char *dest = argv[index + 1];
len = strlen(dest);
if (dest[len - 1] == '/')
dest[len - 1] = '\0';
if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) {
fprintf(stderr, "Error fcopy: invalid file name %s\n", dest);
exit(1);
}
// the destination should be a directory; remove ending /
struct stat s;
if (stat(dest, &s) == -1) {
fprintf(stderr, "Error fcopy: cannot find destination directory\n");
exit(1);
}
if (S_ISDIR(s.st_mode) == -1) {
fprintf(stderr, "Error fcopy: the destination should be a directory\n");
exit(1);
}
// copy files
if (lstat(src, &s) == -1) {
fprintf(stderr, "Error fcopy: cannot find source file\n");
exit(1);
}
if (S_ISDIR(s.st_mode))
duplicate_dir(src, dest, &s);
else if (S_ISREG(s.st_mode))
duplicate_file(src, dest, &s);
else if (S_ISLNK(s.st_mode))
duplicate_link(src, dest, &s);
else {
fprintf(stderr, "Error fcopy: source file unsupported\n");
exit(1);
}
return 0;
}
......@@ -206,6 +206,7 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
done += rv;
}
}
fflush(0);
if (fchown(dst, uid, gid) == -1)
errExit("fchown");
......
......@@ -42,10 +42,10 @@ for (i = 0; i < argc; i++)
printf("\n");
}
#endif
if (argc < 2)
if (argc < 2) {
usage();
return 1;
}
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
usage();
......
......@@ -47,8 +47,10 @@ for (i = 0; i < argc; i++)
printf("\n");
}
#endif
if (argc < 2)
if (argc < 2) {
usage();
return 1;
}
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
usage();
......
#!/usr/bin/expect -f
# This file is part of Firejail project
# Copyright (C) 2014-2016 Firejail Authors
# License GPL v2
set timeout 10
spawn $env(SHELL)
match_max 100000
send -- "/usr/lib/firejail/fcopy\r"
expect {
timeout {puts "TESTING ERROR 0\n";exit}
"files missing"
}
expect {
timeout {puts "TESTING ERROR 1\n";exit}
"Usage:"
}
after 100
send -- "/usr/lib/firejail/fcopy foo\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"files missing"
}
expect {
timeout {puts "TESTING ERROR 3\n";exit}
"Usage:"
}
after 100
send -- "/usr/lib/firejail/fcopy f%oo1 foo2\r"
expect {
timeout {puts "TESTING ERROR 4\n";exit}
"invalid file name"
}
after 100
send -- "/usr/lib/firejail/fcopy foo1 f,oo2\r"
expect {
timeout {puts "TESTING ERROR 5\n";exit}
"invalid file name"
}
after 100
send -- "/usr/lib/firejail/fcopy foo1 foo2\r"
expect {
timeout {puts "TESTING ERROR 6\n";exit}
"cannot find destination directory"
}
after 100
puts "\nall done\n"
#!/usr/bin/expect -f
# This file is part of Firejail project
# Copyright (C) 2014-2016 Firejail Authors
# License GPL v2
#
# copy directory src to dest
#
set timeout 10
spawn $env(SHELL)
match_max 100000
send -- "rm -fr dest/*\r"
after 100
send -- "/usr/lib/firejail/fcopy src dest\r"
after 100
send -- "find dest\r"
expect {
timeout {puts "TESTING ERROR 0\n";exit}
"dest/"
}
expect {
timeout {puts "TESTING ERROR 1\n";exit}
"dest/a"
}
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"dest/a/b"
}
expect {
timeout {puts "TESTING ERROR 3\n";exit}
"dest/a/b/file4"
}
expect {
timeout {puts "TESTING ERROR 4\n";exit}
"dest/a/file3"
}
expect {
timeout {puts "TESTING ERROR 5\n";exit}
"dest/dircopy.exp"
}
expect {
timeout {puts "TESTING ERROR 6\n";exit}
"dest/file2"
}
expect {
timeout {puts "TESTING ERROR 7\n";exit}
"dest/file1"
}
after 100
send -- "ls -al dest\r"
expect {
timeout {puts "TESTING ERROR 8\n";exit}
"drwx--x--x"
}
expect {
timeout {puts "TESTING ERROR 9\n";exit}
"rwxrwxrwx"
}
expect {
timeout {puts "TESTING ERROR 10\n";exit}
"rw-r--r--"
}
after 100
send -- "diff -q src/a/b/file4 dest/a/b/file4; echo done\r"
expect {
timeout {puts "TESTING ERROR 11\n";exit}
"differ" {puts "TESTING ERROR 12\n";exit}
"done"
}
send -- "file dest/dircopy.exp\r"
expect {
timeout {puts "TESTING ERROR 13\n";exit}
"symbolic link"
}
send -- "rm -fr dest/*\r"
after 100
puts "\nall done\n"
#!/bin/bash
# This file is part of Firejail project
# Copyright (C) 2014-2016 Firejail Authors
# License GPL v2
export MALLOC_CHECK_=3
export MALLOC_PERTURB_=$(($RANDOM % 255 + 1))
rm -fr dest/*
echo "TESTING: fcopy cmdline (test/fcopy/cmdline.exp)"
./cmdline.exp
echo "TESTING: fcopy directory (test/fcopy/dircopy.exp)"
./dircopy.exp
echo "TESTING: fcopy file (test/fcopy/filecopy.exp)"
./filecopy.exp
echo "TESTING: fcopy link (test/fcopy/linkcopy.exp)"
./linkcopy.exp
rm -fr dest/*
#!/usr/bin/expect -f
# This file is part of Firejail project
# Copyright (C) 2014-2016 Firejail Authors
# License GPL v2
#
# copy directory src to dest
#
set timeout 10
spawn $env(SHELL)
match_max 100000
send -- "rm -fr dest/*\r"
after 100
send -- "/usr/lib/firejail/fcopy src/dircopy.exp dest\r"
after 100
send -- "find dest\r"
expect {
timeout {puts "TESTING ERROR 0\n";exit}
"dest/"
}
expect {
timeout {puts "TESTING ERROR 1\n";exit}
"dest/dircopy.exp"
}
after 100
send -- "ls -al dest\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"lrwxrwxrwx"
}
after 100
send -- "diff -q dircopy.exp dest/dircopy.exp; echo done\r"
expect {
timeout {puts "TESTING ERROR 3\n";exit}
"differ" {puts "TESTING ERROR 4\n";exit}
"done"
}
send -- "file dest/dircopy.exp\r"
expect {
timeout {puts "TESTING ERROR 5\n";exit}
"symbolic link"
}
send -- "rm -fr dest/*\r"
after 100
puts "\nall done\n"
#!/usr/bin/expect -f
# This file is part of Firejail project
# Copyright (C) 2014-2016 Firejail Authors
# License GPL v2
#