diff --git a/gcov.sh b/gcov.sh index 6f668d65f5d..900b7ca413b 100755 --- a/gcov.sh +++ b/gcov.sh @@ -1,22 +1,28 @@ #!/bin/bash +gcov_init() { + USER=`whoami` + firejail --help + 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() { - lcov --capture -d src/firejail -d src/firemon -d src/fseccomp -d src/fnet -d src/ftee -d src/lib -d src/firecfg --output-file gcov-file + lcov --capture -d src/firejail -d src/firemon -d src/fcopy -d src/fseccomp -d src/fnet -d src/ftee -d src/lib -d src/firecfg --output-file gcov-file rm -fr gcov-dir genhtml gcov-file --output-directory gcov-dir } -# init -USER=`whoami` -firejail --help -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 .` +gcov_init generate +echo "press any key to continue, or Ctrl-C to exit" +read text + # running tests make test-root diff --git a/src/fcopy/main.c b/src/fcopy/main.c index 4437b90e501..82d829bba2b 100644 --- a/src/fcopy/main.c +++ b/src/fcopy/main.c @@ -130,10 +130,16 @@ static int fs_copydir(const char *infname, const struct stat *st, int ftype, str if (size_limit_reached) return 0; + char *outfname; if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1) errExit("asprintf"); +//printf("outpaht %s\n", outpath); +//printf("inpath %s\n", inpath); +//printf("infname %s\n", infname); +//printf("outfname %s\n\n", outfname); + // don't copy it if we already have the file struct stat s; if (stat(outfname, &s) == 0) { @@ -265,7 +271,7 @@ static void duplicate_link(const char *src, const char *dest, struct stat *s) { 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"); + printf("src recoursively. If the destination directory does not exist, it will be created.\n"); } int main(int argc, char **argv) { @@ -276,25 +282,16 @@ int i; for (i = 0; i < argc; i++) printf("*%s* ", argv[i]); printf("\n"); -} -#endif +} +#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]; + char *src = argv[1]; int len = strlen(src); if (src[len - 1] == '/') src[len - 1] = '\0'; @@ -303,7 +300,7 @@ printf("\n"); exit(1); } - char *dest = argv[index + 1]; + char *dest = argv[2]; len = strlen(dest); if (dest[len - 1] == '/') dest[len - 1] = '\0'; @@ -313,14 +310,11 @@ printf("\n"); } - // the destination should be a directory; remove ending / + // the destination should be a directory; 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"); + if (stat(dest, &s) == -1 || + !S_ISDIR(s.st_mode)) { + fprintf(stderr, "Error fcopy: invalid destination directory\n"); exit(1); } diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index d7ba539e64a..80627fda869 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -490,8 +490,6 @@ void fs_private_template(void); void fs_check_private_dir(void); // check new private template home directory (--private-template= option) exit if it fails void fs_check_private_template(void); -// check directory list specified by user (--private-home option) - exit if it fails -void fs_check_home_list(void); void fs_private_home_list(void); @@ -557,7 +555,6 @@ void network_del_run_file(pid_t pid); void network_set_run_file(pid_t pid); // fs_etc.c -void fs_check_etc_list(void); void fs_private_etc_list(void); // no_sandbox.c @@ -681,6 +678,7 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar #define PATH_FNET (LIBDIR "/firejail/fnet") #define PATH_FIREMON (PREFIX "/bin/firemon") #define PATH_FSECCOMP (LIBDIR "/firejail/fseccomp") +#define PATH_FCOPY (LIBDIR "/firejail/fcopy") // bitmapped filters for sbox_run #define SBOX_ROOT (1 << 0) // run the sandbox as root #define SBOX_USER (1 << 1) // run the sandbox as a regular user diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 7e18840fd5e..6a70d482c5a 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c @@ -62,37 +62,6 @@ static int check_dir_or_file(const char *name) { exit(1); } -void fs_check_etc_list(void) { - EUID_ASSERT(); - if (strstr(cfg.etc_private_keep, "..")) { - fprintf(stderr, "Error: invalid private etc list\n"); - exit(1); - } - - char *dlist = strdup(cfg.etc_private_keep); - if (!dlist) - errExit("strdup"); - - // build a new list only with the files found - char *newlist = malloc(strlen(cfg.etc_private_keep) + 1); - if (!newlist) - errExit("malloc"); - *newlist = '\0'; - - char *ptr = strtok(dlist, ","); - if (check_dir_or_file(ptr)) - strcat(newlist, ptr); - while ((ptr = strtok(NULL, ",")) != NULL) { - if (check_dir_or_file(ptr)) { - strcat(newlist, ","); - strcat(newlist, ptr); - } - } - cfg.etc_private_keep = newlist; - - free(dlist); -} - static void duplicate(char *fname) { // copy the file if (arg_debug) diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 242482d2694..d8cd9ce4dd0 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c @@ -28,7 +28,7 @@ #include #include #include -#include +//#include static void skel(const char *homedir, uid_t u, gid_t g) { char *fname; @@ -349,106 +349,6 @@ void fs_check_private_dir(void) { //*********************************************************************************** // --private-home //*********************************************************************************** -#define PRIVATE_COPY_LIMIT (500 * 1024 *1024) -static int size_limit_reached = 0; -static unsigned file_cnt = 0; -static unsigned size_cnt = 0; -static char *check_dir_or_file(const char *name); - -int fs_copydir(const char *path, const struct stat *st, int ftype, struct FTW *sftw) { - (void) st; - (void) sftw; - if (size_limit_reached) - return 0; - - struct stat s; - char *dest; - if (asprintf(&dest, "%s%s", RUN_HOME_DIR, path + strlen(cfg.homedir)) == -1) - errExit("asprintf"); - - // don't copy it if we already have the file - if (stat(dest, &s) == 0) { - free(dest); - return 0; - } - - // extract mode and ownership - if (stat(path, &s) != 0) { - free(dest); - return 0; - } - - // check uid - if (s.st_uid != firejail_uid || s.st_gid != firejail_gid) { - free(dest); - return 0; - } - - if ((s.st_size + size_cnt) > PRIVATE_COPY_LIMIT) { - size_limit_reached = 1; - free(dest); - return 0; - } - - file_cnt++; - size_cnt += s.st_size; - - if(ftype == FTW_F) - copy_file(path, dest, firejail_uid, firejail_gid, s.st_mode); - else if (ftype == FTW_D) { - if (mkdir(dest, s.st_mode) == -1) - errExit("mkdir"); - if (set_perms(dest, firejail_uid, firejail_gid, s.st_mode)) - errExit("set_perms"); -#if 0 -struct stat s2; -if (stat(dest, &s2) == 0) { - printf("%s\t", dest); - printf((S_ISDIR(s.st_mode)) ? "d" : "-"); - printf((s.st_mode & S_IRUSR) ? "r" : "-"); - printf((s.st_mode & S_IWUSR) ? "w" : "-"); - printf((s.st_mode & S_IXUSR) ? "x" : "-"); - printf((s.st_mode & S_IRGRP) ? "r" : "-"); - printf((s.st_mode & S_IWGRP) ? "w" : "-"); - printf((s.st_mode & S_IXGRP) ? "x" : "-"); - printf((s.st_mode & S_IROTH) ? "r" : "-"); - printf((s.st_mode & S_IWOTH) ? "w" : "-"); - printf((s.st_mode & S_IXOTH) ? "x" : "-"); - printf("\n"); -} -#endif - - fs_logger2("clone", path); - } - - free(dest); - return(0); -} - -static void duplicate(char *name) { - char *fname = check_dir_or_file(name); - - if (arg_debug) - printf("Private home: duplicating %s\n", fname); - assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); - - struct stat s; - if (stat(fname, &s) == -1) { - free(fname); - return; - } - - if(nftw(fname, fs_copydir, 1, FTW_PHYS) != 0) { - fprintf(stderr, "Error: unable to copy template dir\n"); - exit(1); - } - fs_logger_print(); // save the current log - - free(fname); -} - - - static char *check_dir_or_file(const char *name) { assert(name); struct stat s; @@ -461,10 +361,7 @@ static char *check_dir_or_file(const char *name) { // expand home directory char *fname = expand_home(name, cfg.homedir); - if (!fname) { - fprintf(stderr, "Error: file %s not found.\n", name); - exit(1); - } + assert(fname); // If it doesn't start with '/', it must be relative to homedir if (fname[0] != '/') { @@ -475,31 +372,19 @@ static char *check_dir_or_file(const char *name) { fname = tmp; } - // check the file is in user home directory + // check the file is in user home directory, a full home directory is not allowed char *rname = realpath(fname, NULL); - if (!rname) { + if (!rname || + strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0 || + strcmp(rname, cfg.homedir) == 0) { fprintf(stderr, "Error: invalid file %s\n", name); exit(1); } - if (strncmp(rname, cfg.homedir, strlen(cfg.homedir)) != 0) { - fprintf(stderr, "Error: file %s is not in user home directory\n", name); - exit(1); - } - - // a full home directory is not allowed - if (strcmp(rname, cfg.homedir) == 0) { - fprintf(stderr, "Error: invalid directory %s\n", rname); - exit(1); - } // only top files and directories in user home are allowed char *ptr = rname + strlen(cfg.homedir); - if (*ptr == '\0') { - fprintf(stderr, "Error: invalid file %s\n", name); - exit(1); - } - ptr++; - ptr = strchr(ptr, '/'); + assert(*ptr != '\0'); + ptr = strchr(++ptr, '/'); if (ptr) { if (*ptr != '\0') { fprintf(stderr, "Error: only top files and directories in user home are allowed\n"); @@ -507,55 +392,42 @@ static char *check_dir_or_file(const char *name) { } } - if (stat(fname, &s) == -1) { - fprintf(stderr, "Error: file %s not found.\n", fname); - exit(1); - } - - // check uid - uid_t uid = getuid(); - gid_t gid = getgid(); - if (s.st_uid != uid || s.st_gid != gid) { - fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n"); - exit(1); - } - - // dir or regular file - if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { - free(fname); - return rname; // regular exit from the function - } - - fprintf(stderr, "Error: invalid file type, %s.\n", fname); - exit(1); + free(fname); + return rname; } +static void duplicate(char *name) { + char *fname = check_dir_or_file(name); + char *dest = RUN_HOME_DIR; -// check directory list specified by user (--private-home option) - exit if it fails -void fs_check_home_list(void) { - if (strstr(cfg.home_private_keep, "..")) { - fprintf(stderr, "Error: invalid private-home list\n"); - exit(1); - } - - char *dlist = strdup(cfg.home_private_keep); - if (!dlist) - errExit("strdup"); - - char *ptr = strtok(dlist, ","); - char *tmp = check_dir_or_file(ptr); - free(tmp); + if (arg_debug) + printf("Private home: duplicating %s\n", fname); + assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); - while ((ptr = strtok(NULL, ",")) != NULL) { - tmp = check_dir_or_file(ptr); - free(tmp); + struct stat s; + if (stat(fname, &s) == -1) { + free(fname); + return; + } + else if (S_ISDIR(s.st_mode)) { + // create the directory in RUN_HOME_DIR + char *name; + char *ptr = strrchr(fname, '/'); + ptr++; + if (asprintf(&name, "%s/%s", RUN_HOME_DIR, ptr) == -1) + errExit("asprintf"); + mkdir_attr(name, 0755, getuid(), getgid()); + sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, name); + free(name); } + else + sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, RUN_HOME_DIR); + fs_logger2("clone", fname); + fs_logger_print(); // save the current log - free(dlist); + free(fname); } - - // private mode (--private-home=list): // mount homedir on top of /home/user, // tmpfs on top of /root in nonroot mode, @@ -571,8 +443,8 @@ void fs_private_home_list(void) { int xflag = store_xauthority(); int aflag = store_asoundrc(); - uid_t u = firejail_uid; - gid_t g = firejail_gid; + uid_t uid = getuid(); + gid_t gid = getgid(); struct stat s; if (stat(homedir, &s) == -1) { fprintf(stderr, "Error: cannot find user home directory\n"); @@ -580,59 +452,24 @@ void fs_private_home_list(void) { } // create /run/firejail/mnt/home directory - int rv = mkdir(RUN_HOME_DIR, 0755); - if (rv == -1) - errExit("mkdir"); - if (set_perms(RUN_HOME_DIR, u, g, 0755)) - errExit("set_perms"); - ASSERT_PERMS(RUN_HOME_DIR, u, g, 0755); - + mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); fs_logger_print(); // save the current log + if (arg_debug) + printf("Copying files in the new home:\n"); + // copy the list of files in the new home directory - // using a new child process without root privileges - pid_t child = fork(); - if (child < 0) - errExit("fork"); - if (child == 0) { - if (arg_debug) - printf("Copying files in the new home:\n"); - - // drop privileges - if (setgroups(0, NULL) < 0) - errExit("setgroups"); - if (setgid(getgid()) < 0) - errExit("setgid/getgid"); - if (setuid(getuid()) < 0) - errExit("setuid/getuid"); - - // copy the list of files in the new home directory - char *dlist = strdup(cfg.home_private_keep); - if (!dlist) - errExit("strdup"); - - char *ptr = strtok(dlist, ","); + char *dlist = strdup(cfg.home_private_keep); + if (!dlist) + errExit("strdup"); + + char *ptr = strtok(dlist, ","); + duplicate(ptr); + while ((ptr = strtok(NULL, ",")) != NULL) duplicate(ptr); - while ((ptr = strtok(NULL, ",")) != NULL) - duplicate(ptr); - - if (!arg_quiet) { - if (size_limit_reached) - fprintf(stderr, "Warning: private-home copy limit of %u MB reached, not all the files were copied\n", - PRIVATE_COPY_LIMIT / (1024 *1024)); - else - printf("Private home: %u files, total size %u bytes\n", file_cnt, size_cnt); - } - fs_logger_print(); // save the current log - free(dlist); -#ifdef HAVE_GCOV - __gcov_flush(); -#endif - _exit(0); - } - // wait for the child to finish - waitpid(child, NULL, 0); + fs_logger_print(); // save the current log + free(dlist); if (arg_debug) printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); @@ -640,7 +477,7 @@ void fs_private_home_list(void) { if (mount(RUN_HOME_DIR, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); - if (u != 0) { + if (uid != 0) { // mask /root if (arg_debug) printf("Mounting a new /root directory\n"); @@ -655,7 +492,7 @@ void fs_private_home_list(void) { errExit("mounting home directory"); } - skel(homedir, u, g); + skel(homedir, uid, gid); if (xflag) copy_xauthority(); if (aflag) diff --git a/src/firejail/main.c b/src/firejail/main.c index ec0c3128513..5bfa04cc9e2 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -1601,7 +1601,6 @@ int main(int argc, char **argv) { // extract private home dirname cfg.home_private_keep = argv[i] + 15; - fs_check_home_list(); arg_private = 1; } else { @@ -1625,7 +1624,6 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: invalid private-etc option\n"); exit(1); } - fs_check_etc_list(); arg_private_etc = 1; } else if (strncmp(argv[i], "--private-bin=", 14) == 0) { diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 0fd45d1ef7f..693b1dc30fd 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -173,7 +173,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #ifdef HAVE_PRIVATE_HOME if (checkcfg(CFG_PRIVATE_HOME)) { cfg.home_private_keep = ptr + 13; - fs_check_home_list(); arg_private = 1; } else @@ -737,7 +736,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { exit(1); } cfg.etc_private_keep = ptr + 12; - fs_check_etc_list(); arg_private_etc = 1; return 0; diff --git a/src/firejail/util.c b/src/firejail/util.c index 027f1cd4784..c56380ca1ae 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -562,7 +562,10 @@ char *expand_home(const char *path, const char* homedir) { return new_name; } - return strdup(path); + char *rv = strdup(path); + if (!rv) + errExit("strdup"); + return rv; } @@ -625,7 +628,7 @@ uid_t pid_get_uid(pid_t pid) { void invalid_filename(const char *fname) { - EUID_ASSERT(); +// EUID_ASSERT(); assert(fname); const char *ptr = fname; diff --git a/test/fcopy/cmdline.exp b/test/fcopy/cmdline.exp index 95e22132194..24bb193515d 100755 --- a/test/fcopy/cmdline.exp +++ b/test/fcopy/cmdline.exp @@ -43,14 +43,4 @@ expect { } 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" diff --git a/test/fcopy/filecopy.exp b/test/fcopy/filecopy.exp index 9927e18fe48..d1f0a44245a 100755 --- a/test/fcopy/filecopy.exp +++ b/test/fcopy/filecopy.exp @@ -13,7 +13,7 @@ match_max 100000 send -- "rm -fr dest/*\r" after 100 -send -- "/usr/lib/firejail/fcopy src/dircopy.exp dest\r" +send -- "/usr/lib/firejail/fcopy dircopy.exp dest\r" after 100 send -- "find dest\r" @@ -31,7 +31,7 @@ after 100 send -- "ls -al dest\r" expect { timeout {puts "TESTING ERROR 2\n";exit} - "lrwxrwxrwx" + "rwxr-xr-x" } after 100 @@ -45,7 +45,7 @@ expect { send -- "file dest/dircopy.exp\r" expect { timeout {puts "TESTING ERROR 5\n";exit} - "symbolic link" + "ASCII text" } send -- "rm -fr dest/*\r" diff --git a/test/fcopy/linkcopy.exp b/test/fcopy/linkcopy.exp index b87f24a599f..9927e18fe48 100755 --- a/test/fcopy/linkcopy.exp +++ b/test/fcopy/linkcopy.exp @@ -13,7 +13,7 @@ match_max 100000 send -- "rm -fr dest/*\r" after 100 -send -- "/usr/lib/firejail/fcopy src dest\r" +send -- "/usr/lib/firejail/fcopy src/dircopy.exp dest\r" after 100 send -- "find dest\r" @@ -23,60 +23,28 @@ expect { } 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--" + timeout {puts "TESTING ERROR 2\n";exit} + "lrwxrwxrwx" } after 100 -send -- "diff -q src/a/b/file4 dest/a/b/file4; echo done\r" +send -- "diff -q dircopy.exp dest/dircopy.exp; echo done\r" expect { - timeout {puts "TESTING ERROR 11\n";exit} - "differ" {puts "TESTING ERROR 12\n";exit} + 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 13\n";exit} + timeout {puts "TESTING ERROR 5\n";exit} "symbolic link" }