From 02a66f7e4086097a98dfdac0b47c9909908360a0 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Fri, 19 Feb 2016 14:57:58 -0500 Subject: euid switching --- src/firejail/Makefile.in | 2 +- src/firejail/caps.c | 6 ++ src/firejail/cgroup.c | 2 + src/firejail/env.c | 1 + src/firejail/errno.c | 6 +- src/firejail/fs.c | 1 + src/firejail/fs_bin.c | 1 + src/firejail/fs_home.c | 1 + src/firejail/fs_logger.c | 5 ++ src/firejail/fs_mkdir.c | 30 ++------ src/firejail/join.c | 5 ++ src/firejail/list.c | 12 +-- src/firejail/main.c | 101 +++++++++++-------------- src/firejail/netfilter.c | 1 + src/firejail/network_main.c | 5 +- src/firejail/no_sandbox.c | 13 +--- src/firejail/output.c | 4 +- src/firejail/profile.c | 6 ++ src/firejail/protocol.c | 8 ++ src/firejail/restricted_shell.c | 1 + src/firejail/run_symlink.c | 2 + src/firejail/seccomp.c | 4 + src/firejail/shutdown.c | 4 + src/firejail/syscall.c | 2 + src/firejail/user.c | 1 + src/firejail/util.c | 3 + src/include/euid_common.h | 9 ++- test/features/3.11.exp | 164 ++++++++++++++++++++++++++++++++++++++++ test/features/3.11.profile | 2 + test/features/features.txt | 3 + test/features/test.sh | 3 + 31 files changed, 305 insertions(+), 103 deletions(-) create mode 100755 test/features/3.11.exp create mode 100644 test/features/3.11.profile diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in index 83a2b0592..cf57d96d5 100644 --- a/src/firejail/Makefile.in +++ b/src/firejail/Makefile.in @@ -23,7 +23,7 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) -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) +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o diff --git a/src/firejail/caps.c b/src/firejail/caps.c index 1c4ac8d37..838934aee 100644 --- a/src/firejail/caps.c +++ b/src/firejail/caps.c @@ -193,6 +193,7 @@ static int caps_find_name(const char *name) { // return 1 if error, 0 if OK int caps_check_list(const char *clist, void (*callback)(int)) { + // don't allow empty lists if (clist == NULL || *clist == '\0') { fprintf(stderr, "Error: empty capabilities lists are not allowed\n"); @@ -240,6 +241,7 @@ int caps_check_list(const char *clist, void (*callback)(int)) { } void caps_print(void) { + EUID_ASSERT(); int i; int elems = sizeof(capslist) / sizeof(capslist[0]); @@ -364,6 +366,8 @@ void caps_keep_list(const char *clist) { #define MAXBUF 4098 static uint64_t extract_caps(int pid) { + EUID_ASSERT(); + char *file; if (asprintf(&file, "/proc/%d/status", pid) == -1) { errExit("asprintf"); @@ -410,6 +414,8 @@ void caps_print_filter_name(const char *name) { } void caps_print_filter(pid_t pid) { + EUID_ASSERT(); + // if the pid is that of a firejail process, use the pid of the first child process char *comm = pid_proc_comm(pid); if (comm) { diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c index 040a1f934..ebd87f0d2 100644 --- a/src/firejail/cgroup.c +++ b/src/firejail/cgroup.c @@ -71,6 +71,8 @@ errout: void set_cgroup(const char *path) { + EUID_ASSERT(); + invalid_filename(path); // path starts with /sys/fs/cgroup diff --git a/src/firejail/env.c b/src/firejail/env.c index cccab966d..7fbf7aac5 100644 --- a/src/firejail/env.c +++ b/src/firejail/env.c @@ -125,6 +125,7 @@ void env_defaults(void) { // parse and store the environment setting void env_store(const char *str) { + EUID_ASSERT(); assert(str); // some basic checking diff --git a/src/firejail/errno.c b/src/firejail/errno.c index 6c8bd3876..c493dfa09 100644 --- a/src/firejail/errno.c +++ b/src/firejail/errno.c @@ -172,7 +172,7 @@ static ErrnoEntry errnolist[] = { }; int errno_highest_nr(void) { - int i, max = 0; + int i, max = 0; int elems = sizeof(errnolist) / sizeof(errnolist[0]); for (i = 0; i < elems; i++) { if (errnolist[i].nr > max) @@ -183,6 +183,8 @@ int errno_highest_nr(void) { } int errno_find_name(const char *name) { + EUID_ASSERT(); + int i; int elems = sizeof(errnolist) / sizeof(errnolist[0]); for (i = 0; i < elems; i++) { @@ -205,6 +207,8 @@ char *errno_find_nr(int nr) { } void errno_print(void) { + EUID_ASSERT(); + int i; int elems = sizeof(errnolist) / sizeof(errnolist[0]); for (i = 0; i < elems; i++) { diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 616b87562..3b1a87310 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -874,6 +874,7 @@ void fs_overlayfs(void) { #ifdef HAVE_CHROOT // return 1 if error int fs_check_chroot_dir(const char *rootdir) { + EUID_ASSERT(); assert(rootdir); struct stat s; char *name; diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c index af67ac290..447ef7f8f 100644 --- a/src/firejail/fs_bin.c +++ b/src/firejail/fs_bin.c @@ -64,6 +64,7 @@ static char *check_dir_or_file(const char *name) { } void fs_check_bin_list(void) { + EUID_ASSERT(); if (strstr(cfg.bin_private_keep, "..")) { fprintf(stderr, "Error: invalid private bin list\n"); exit(1); diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 2bfabbe89..2b6142c6c 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c @@ -338,6 +338,7 @@ void fs_private(void) { // check new private home directory (--private= option) - exit if it fails void fs_check_private_dir(void) { + EUID_ASSERT(); invalid_filename(cfg.home_private); // Expand the home directory diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c index 0829ad7ac..f803982d7 100644 --- a/src/firejail/fs_logger.c +++ b/src/firejail/fs_logger.c @@ -122,6 +122,8 @@ void fs_logger_change_owner(void) { } void fs_logger_print_log_name(const char *name) { + EUID_ASSERT(); + if (!name || strlen(name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); @@ -136,6 +138,8 @@ void fs_logger_print_log_name(const char *name) { } void fs_logger_print_log(pid_t pid) { + EUID_ASSERT(); + // if the pid is that of a firejail process, use the pid of the first child process char *comm = pid_proc_comm(pid); if (comm) { @@ -163,6 +167,7 @@ void fs_logger_print_log(pid_t pid) { } // print RUN_FSLOGGER_FILE + EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_FSLOGGER_FILE) == -1) errExit("asprintf"); diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c index 69bf2fae7..398c534bf 100644 --- a/src/firejail/fs_mkdir.c +++ b/src/firejail/fs_mkdir.c @@ -25,6 +25,8 @@ #include void fs_mkdir(const char *name) { + EUID_ASSERT(); + // check directory name invalid_filename(name); char *expanded = expand_home(name, cfg.homedir); @@ -39,31 +41,9 @@ void fs_mkdir(const char *name) { goto doexit; } - // fork a process, drop privileges, and create the directory - // no error recovery will be attempted - pid_t child = fork(); - if (child < 0) - errExit("fork"); - if (child == 0) { - if (arg_debug) - printf("Create %s directory\n", expanded); - - // drop privileges - if (setgroups(0, NULL) < 0) - errExit("setgroups"); - if (setgid(getgid()) < 0) - errExit("setgid/getgid"); - if (setuid(getuid()) < 0) - errExit("setuid/getuid"); - - // create directory - if (mkdir(expanded, 0700) == -1) - fprintf(stderr, "Warning: cannot create %s directory\n", expanded); - exit(0); - } - - // wait for the child to finish - waitpid(child, NULL, 0); + // create directory + if (mkdir(expanded, 0700) == -1) + fprintf(stderr, "Warning: cannot create %s directory\n", expanded); doexit: free(expanded); diff --git a/src/firejail/join.c b/src/firejail/join.c index e471e4a21..4cd315d90 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c @@ -30,6 +30,7 @@ static int apply_seccomp = 0; #define BUFLEN 4096 static void extract_command(int argc, char **argv, int index) { + EUID_ASSERT(); if (index >= argc) return; @@ -179,6 +180,7 @@ static void extract_user_namespace(pid_t pid) { } void join_name(const char *name, const char *homedir, int argc, char **argv, int index) { + EUID_ASSERT(); if (!name || strlen(name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); @@ -193,6 +195,8 @@ void join_name(const char *name, const char *homedir, int argc, char **argv, int } void join(pid_t pid, const char *homedir, int argc, char **argv, int index) { + EUID_ASSERT(); + extract_command(argc, argv, index); // if the pid is that of a firejail process, use the pid of the first child process @@ -222,6 +226,7 @@ void join(pid_t pid, const char *homedir, int argc, char **argv, int index) { } } + EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); diff --git a/src/firejail/list.c b/src/firejail/list.c index d5ef1ac2c..676df6a14 100644 --- a/src/firejail/list.c +++ b/src/firejail/list.c @@ -20,8 +20,7 @@ #include "firejail.h" void top(void) { - if (getuid() != geteuid()) - drop_privs(1); + EUID_ASSERT(); char *arg[4]; arg[0] = "bash"; @@ -32,8 +31,7 @@ void top(void) { } void netstats(void) { - if (getuid() != geteuid()) - drop_privs(1); + EUID_ASSERT(); char *arg[4]; arg[0] = "bash"; @@ -44,8 +42,7 @@ void netstats(void) { } void list(void) { - if (getuid() != geteuid()) - drop_privs(1); + EUID_ASSERT(); char *arg[4]; arg[0] = "bash"; @@ -56,8 +53,7 @@ void list(void) { } void tree(void) { - if (getuid() != geteuid()) - drop_privs(1); + EUID_ASSERT(); char *arg[4]; arg[0] = "bash"; diff --git a/src/firejail/main.c b/src/firejail/main.c index 3c714f385..fe4027a55 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -128,40 +128,6 @@ static void my_handler(int s){ myexit(1); } -static void extract_user_data(void) { - // check suid - EUID_ROOT(); - if (geteuid()) { - fprintf(stderr, "Error: the sandbox is not setuid root\n"); - exit(1); - } - EUID_USER(); - - struct passwd *pw = getpwuid(getuid()); - if (!pw) - errExit("getpwuid"); - cfg.username = strdup(pw->pw_name); - if (!cfg.username) - errExit("strdup"); - - // build home directory name - cfg.homedir = NULL; - if (pw->pw_dir != NULL) { - cfg.homedir = strdup(pw->pw_dir); - if (!cfg.homedir) - errExit("strdup"); - } - else { - fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); - exit(1); - } - - cfg.cwd = getcwd(NULL, 0); -} - - - - static inline Bridge *last_bridge_configured(void) { if (cfg.bridge3.configured) return &cfg.bridge3; @@ -175,8 +141,6 @@ static inline Bridge *last_bridge_configured(void) { return NULL; } - - // return 1 if error, 0 if a valid pid was found static int read_pid(char *str, pid_t *pid) { char *endptr; @@ -193,15 +157,43 @@ static int read_pid(char *str, pid_t *pid) { return 0; } -static void init_cfg(void) { +// init configuration +static void init_cfg(int argc, char **argv) { + EUID_ASSERT(); memset(&cfg, 0, sizeof(cfg)); - + + cfg.original_argv = argv; + cfg.original_argc = argc; cfg.bridge0.devsandbox = "eth0"; cfg.bridge1.devsandbox = "eth1"; cfg.bridge2.devsandbox = "eth2"; cfg.bridge3.devsandbox = "eth3"; - extract_user_data(); + // extract user data + struct passwd *pw = getpwuid(getuid()); + if (!pw) + errExit("getpwuid"); + cfg.username = strdup(pw->pw_name); + if (!cfg.username) + errExit("strdup"); + + // build home directory name + cfg.homedir = NULL; + if (pw->pw_dir != NULL) { + cfg.homedir = strdup(pw->pw_dir); + if (!cfg.homedir) + errExit("strdup"); + } + else { + fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); + exit(1); + } + cfg.cwd = getcwd(NULL, 0); + + // initialize random number generator + sandbox_pid = getpid(); + time_t t = time(NULL); + srand(t ^ sandbox_pid); } static void check_network(Bridge *br) { @@ -219,6 +211,7 @@ static void check_network(Bridge *br) { #ifdef HAVE_USERNS void check_user_namespace(void) { + EUID_ASSERT(); if (getuid() == 0) { fprintf(stderr, "Error: --noroot option cannot be used when starting the sandbox as root.\n"); exit(1); @@ -241,6 +234,8 @@ void check_user_namespace(void) { // exit commands static void run_cmd_and_exit(int i, int argc, char **argv) { + EUID_ASSERT(); + //************************************* // basic arguments //************************************* @@ -346,7 +341,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { // print seccomp filter for a sandbox specified by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 16, &pid) == 0) seccomp_print_filter(pid); else @@ -360,7 +354,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { // print seccomp filter for a sandbox specified by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 17, &pid) == 0) protocol_print_filter(pid); else @@ -371,7 +364,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--caps.print=", 13) == 0) { // join sandbox by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 13, &pid) == 0) caps_print_filter(pid); else @@ -381,7 +373,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--fs.print=", 11) == 0) { // join sandbox by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 11, &pid) == 0) fs_logger_print_log(pid); else @@ -391,7 +382,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--dns.print=", 12) == 0) { // join sandbox by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 12, &pid) == 0) net_dns_print(pid); else @@ -425,7 +415,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { // join sandbox by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 7, &pid) == 0) join(pid, cfg.homedir, argc, argv, i + 1); else @@ -471,7 +460,6 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { // shutdown sandbox by pid or by name pid_t pid; - EUID_ROOT(); if (read_pid(argv[i] + 11, &pid) == 0) shut(pid); else @@ -526,7 +514,8 @@ int main(int argc, char **argv) { #ifdef HAVE_SECCOMP int highest_errno = errno_highest_nr(); #endif - + + // drop permissions by default and rise them when required EUID_INIT(); EUID_USER(); @@ -535,7 +524,6 @@ int main(int argc, char **argv) { if (*argv[0] != '-') run_symlink(argc, argv); - // check if we already have a sandbox running int rv = check_kernel_procs(); if (rv == 0) { @@ -556,19 +544,22 @@ int main(int argc, char **argv) { } } + // check root/suid + EUID_ROOT(); + if (geteuid()) { + fprintf(stderr, "Error: the sandbox is not setuid root\n"); + exit(1); + } + EUID_USER(); + // initialize globals - init_cfg(); - cfg.original_argv = argv; - cfg.original_argc = argc; + init_cfg(argc, argv); - // initialize random number generator - sandbox_pid = getpid(); - time_t t = time(NULL); - srand(t ^ sandbox_pid); // check firejail directories EUID_ROOT(); fs_build_firejail_dir(); + // todo: deprecate shm functions shm_create_firejail_dir(); bandwidth_shm_del_file(sandbox_pid); EUID_USER(); diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c index 2ed09434a..4a5499699 100644 --- a/src/firejail/netfilter.c +++ b/src/firejail/netfilter.c @@ -44,6 +44,7 @@ static char *client_filter = "COMMIT\n"; void check_netfilter_file(const char *fname) { + EUID_ASSERT(); invalid_filename(fname); if (is_dir(fname) || is_link(fname) || strstr(fname, "..")) { diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index bd104343a..9ddd56dcd 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c @@ -165,6 +165,7 @@ void check_default_gw(uint32_t defaultgw) { } void net_check_cfg(void) { + EUID_ASSERT(); int net_configured = 0; if (cfg.bridge0.configured) net_configured++; @@ -223,6 +224,7 @@ void net_check_cfg(void) { void net_dns_print_name(const char *name) { + EUID_ASSERT(); if (!name || strlen(name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); @@ -238,8 +240,8 @@ void net_dns_print_name(const char *name) { #define MAXBUF 4096 void net_dns_print(pid_t pid) { + EUID_ASSERT(); // drop privileges - will not be able to read /etc/resolv.conf for --noroot option -// drop_privs(1); // if the pid is that of a firejail process, use the pid of the first child process char *comm = pid_proc_comm(pid); @@ -258,6 +260,7 @@ void net_dns_print(pid_t pid) { } char *fname; + EUID_ROOT(); if (asprintf(&fname, "/proc/%d/root/etc/resolv.conf", pid) == -1) errExit("asprintf"); diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c index 15d61362a..9f9ace527 100644 --- a/src/firejail/no_sandbox.c +++ b/src/firejail/no_sandbox.c @@ -26,6 +26,8 @@ // check process space for kernel processes // return 1 if found, 0 if not found int check_kernel_procs(void) { + EUID_ASSERT(); + char *kern_proc[] = { "kthreadd", "ksoftirqd", @@ -97,14 +99,7 @@ int check_kernel_procs(void) { } void run_no_sandbox(int argc, char **argv) { - // drop privileges - int rv = setgroups(0, NULL); // this could fail - (void) rv; - if (setgid(getgid()) < 0) - errExit("setgid/getgid"); - if (setuid(getuid()) < 0) - errExit("setuid/getuid"); - + EUID_ASSERT(); // build command char *command = NULL; @@ -141,7 +136,7 @@ void run_no_sandbox(int argc, char **argv) { // start the program in /bin/sh fprintf(stderr, "Warning: an existing sandbox was detected. " "%s will run without any additional sandboxing features in a /bin/sh shell\n", command); - rv = system(command); + int rv = system(command); (void) rv; if (allocated) free(command); diff --git a/src/firejail/output.c b/src/firejail/output.c index d553e283f..a554b76aa 100644 --- a/src/firejail/output.c +++ b/src/firejail/output.c @@ -23,6 +23,8 @@ #include void check_output(int argc, char **argv) { + EUID_ASSERT(); + int i; char *outfile = NULL; // drop_privs(0); @@ -55,8 +57,6 @@ void check_output(int argc, char **argv) { } } - // drop privileges and try to open the file for writing - drop_privs(0); /* coverity[toctou] */ FILE *fp = fopen(outfile, "a"); if (!fp) { diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 0c28eefd8..518fdf194 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -66,6 +66,8 @@ int profile_find(const char *name, const char *dir) { // return 1 if the command is to be added to the linked list of profile commands // return 0 if the command was already executed inside the function int profile_check_line(char *ptr, int lineno, const char *fname) { + EUID_ASSERT(); + // check ignore list int i; for (i = 0; i < MAX_PROFILE_IGNORE; i++) { @@ -458,6 +460,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { // add a profile entry in cfg.profile list; use str to populate the list void profile_add(char *str) { + EUID_ASSERT(); + ProfileEntry *prf = malloc(sizeof(ProfileEntry)); if (!prf) errExit("malloc"); @@ -479,6 +483,8 @@ void profile_add(char *str) { // read a profile file static int include_level = 0; void profile_read(const char *fname) { + EUID_ASSERT(); + // exit program if maximum include level was reached if (include_level > MAX_INCLUDE_LEVEL) { fprintf(stderr, "Error: maximum profile include level was reached\n"); diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 407f8c62d..24fbfc024 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -101,6 +101,8 @@ static struct sock_filter *find_protocol_domain(const char *p) { // --debug-protocols void protocol_list(void) { + EUID_ASSERT(); + #ifndef SYS_socket fprintf(stderr, "Warning: --protocol not supported on this platform\n"); return; @@ -117,6 +119,7 @@ void protocol_list(void) { // check protocol list and store it in cfg structure void protocol_store(const char *prlist) { + EUID_ASSERT(); assert(prlist); if (cfg.protocol) { @@ -308,6 +311,8 @@ void protocol_filter_load(const char *fname) { // --protocol.print void protocol_print_filter_name(const char *name) { + EUID_ASSERT(); + (void) name; #ifdef SYS_socket if (!name || strlen(name) == 0) { @@ -329,6 +334,8 @@ void protocol_print_filter_name(const char *name) { // --protocol.print void protocol_print_filter(pid_t pid) { + EUID_ASSERT(); + (void) pid; #ifdef SYS_socket // if the pid is that of a firejail process, use the pid of the first child process @@ -358,6 +365,7 @@ void protocol_print_filter(pid_t pid) { } // find the seccomp filter + EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_PROTOCOL_CFG) == -1) errExit("asprintf"); diff --git a/src/firejail/restricted_shell.c b/src/firejail/restricted_shell.c index 95e543031..da4e9d332 100644 --- a/src/firejail/restricted_shell.c +++ b/src/firejail/restricted_shell.c @@ -24,6 +24,7 @@ char *restricted_user = NULL; int restricted_shell(const char *user) { + EUID_ASSERT(); assert(user); // open profile file: diff --git a/src/firejail/run_symlink.c b/src/firejail/run_symlink.c index bc1bb3011..d57816e12 100644 --- a/src/firejail/run_symlink.c +++ b/src/firejail/run_symlink.c @@ -23,6 +23,8 @@ #include void run_symlink(int argc, char **argv) { + EUID_ASSERT(); + char *program = strrchr(argv[0], '/'); if (program) program += 1; diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 57f483b1c..f9a9df211 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -800,6 +800,7 @@ void seccomp_set(void) { } void seccomp_print_filter_name(const char *name) { + EUID_ASSERT(); if (!name || strlen(name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); @@ -814,6 +815,8 @@ void seccomp_print_filter_name(const char *name) { } void seccomp_print_filter(pid_t pid) { + EUID_ASSERT(); + // if the pid is that of a firejail process, use the pid of the first child process char *comm = pid_proc_comm(pid); if (comm) { @@ -842,6 +845,7 @@ void seccomp_print_filter(pid_t pid) { // find the seccomp filter + EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_CFG) == -1) errExit("asprintf"); diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c index edaac7eb9..c88683aaa 100644 --- a/src/firejail/shutdown.c +++ b/src/firejail/shutdown.c @@ -24,6 +24,7 @@ #include void shut_name(const char *name) { + EUID_ASSERT(); if (!name || strlen(name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); @@ -39,6 +40,8 @@ void shut_name(const char *name) { } void shut(pid_t pid) { + EUID_ASSERT(); + pid_t parent = pid; // if the pid is that of a firejail process, use the pid of a child process inside the sandbox char *comm = pid_proc_comm(pid); @@ -73,6 +76,7 @@ void shut(pid_t pid) { } } + EUID_ROOT(); printf("Sending SIGTERM to %u\n", pid); kill(pid, SIGTERM); sleep(2); diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c index 50d59391e..985cc8bb8 100644 --- a/src/firejail/syscall.c +++ b/src/firejail/syscall.c @@ -103,6 +103,8 @@ int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg) } void syscall_print(void) { + EUID_ASSERT(); + int i; int elems = sizeof(syslist) / sizeof(syslist[0]); for (i = 0; i < elems; i++) { diff --git a/src/firejail/user.c b/src/firejail/user.c index e5f7848e8..a2f34392c 100644 --- a/src/firejail/user.c +++ b/src/firejail/user.c @@ -26,6 +26,7 @@ void check_user(int argc, char **argv) { + EUID_ASSERT(); int i; char *user = NULL; diff --git a/src/firejail/util.c b/src/firejail/util.c index d969f6439..a74693c7a 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -321,6 +321,8 @@ char *split_comma(char *str) { int not_unsigned(const char *str) { + EUID_ASSERT(); + int rv = 0; const char *ptr = str; while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') { @@ -399,6 +401,7 @@ int find_child(pid_t parent, pid_t *child) { void extract_command_name(int index, char **argv) { + EUID_ASSERT(); assert(argv); assert(argv[index]); diff --git a/src/include/euid_common.h b/src/include/euid_common.h index 1cba548ab..f07cf2868 100644 --- a/src/include/euid_common.h +++ b/src/include/euid_common.h @@ -23,9 +23,16 @@ #include #include #include +#include + +#define EUID_ASSERT() { \ + if (getuid() != 0) \ + assert(geteuid() != 0); \ +} extern uid_t firejail_uid; -extern uid_t firejail_uid_switch; + + static inline void EUID_ROOT(void) { if (seteuid(0) == -1) diff --git a/test/features/3.11.exp b/test/features/3.11.exp new file mode 100755 index 000000000..aeaf28bf8 --- /dev/null +++ b/test/features/3.11.exp @@ -0,0 +1,164 @@ +#!/usr/bin/expect -f +# +# mkdir +# + +set timeout 10 +spawn $env(SHELL) +match_max 100000 +set overlay [lindex $argv 0] +set chroot [lindex $argv 1] + +# +# N +# +send -- "rm -fr ~/firejail-xy76_u9\r" +sleep 1 + +send -- "firejail --profile=3.11.profile\r" +expect { + timeout {puts "TESTING ERROR 0\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "ls -l ~ | grep xy76_u9\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "drwx------" +} +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "netblue netblue" +} +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "firejail-xy76_u9" +} +after 100 + +send -- "ls -l ~/firejail-xy76_u9\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "drwx------" +} +expect { + timeout {puts "TESTING ERROR 5\n";exit} + "netblue netblue" +} +expect { + timeout {puts "TESTING ERROR 6\n";exit} + "testdir" +} +after 100 + +send -- "exit\r" +sleep 1 +send -- "rm -fr ~/firejail-xy76_u9\r" +sleep 1 + +# +# O +# +if { $overlay == "overlay" } { + send -- "rm -fr ~/firejail-xy76_u9\r" + sleep 1 + + send -- "firejail --profile=3.11.profile\r" + expect { + timeout {puts "TESTING ERROR 10\n";exit} + "Child process initialized" + } + sleep 1 + + send -- "ls -l ~ | grep xy76_u9\r" + expect { + timeout {puts "TESTING ERROR 11\n";exit} + "drwx------" + } + expect { + timeout {puts "TESTING ERROR 12\n";exit} + "netblue netblue" + } + expect { + timeout {puts "TESTING ERROR 13\n";exit} + "firejail-xy76_u9" + } + after 100 + + send -- "ls -l ~/firejail-xy76_u9\r" + expect { + timeout {puts "TESTING ERROR 14\n";exit} + "drwx------" + } + expect { + timeout {puts "TESTING ERROR 15\n";exit} + "netblue netblue" + } + expect { + timeout {puts "TESTING ERROR 16\n";exit} + "testdir" + } + after 100 + + send -- "exit\r" + sleep 1 + send -- "rm -fr ~/firejail-xy76_u9\r" + sleep 1 + + +} + +# +# C +# +if { $chroot == "chroot" } { + send -- "rm -fr ~/firejail-xy76_u9\r" + sleep 1 + + send -- "firejail --profile=3.11.profile\r" + expect { + timeout {puts "TESTING ERROR 20\n";exit} + "Child process initialized" + } + sleep 1 + + send -- "ls -l ~ | grep xy76_u9\r" + expect { + timeout {puts "TESTING ERROR 21\n";exit} + "drwx------" + } + expect { + timeout {puts "TESTING ERROR 22\n";exit} + "netblue netblue" + } + expect { + timeout {puts "TESTING ERROR 23\n";exit} + "firejail-xy76_u9" + } + after 100 + + send -- "ls -l ~/firejail-xy76_u9\r" + expect { + timeout {puts "TESTING ERROR 24\n";exit} + "drwx------" + } + expect { + timeout {puts "TESTING ERROR 25\n";exit} + "netblue netblue" + } + expect { + timeout {puts "TESTING ERROR 26\n";exit} + "testdir" + } + after 100 + + send -- "rm -fr ~/firejail-xy76_u9\r" + sleep 1 + + send -- "exit\r" + +} + + +puts "\nall done\n" diff --git a/test/features/3.11.profile b/test/features/3.11.profile new file mode 100644 index 000000000..144733f8f --- /dev/null +++ b/test/features/3.11.profile @@ -0,0 +1,2 @@ +mkdir ~/firejail-xy76_u9 +mkdir ~/firejail-xy76_u9/testdir diff --git a/test/features/features.txt b/test/features/features.txt index 4d8821a92..283e85d93 100644 --- a/test/features/features.txt +++ b/test/features/features.txt @@ -25,6 +25,7 @@ C - chroot filesystem 1.9 mount namespace 1.10 disable /selinux + 2. Networking features 2.1 Hostname (use --hostname=newhostname, do a ping and cat /etc/hostname) @@ -37,6 +38,7 @@ C - chroot filesystem 2.5 interface 2.6 Default gw (--noprofile --net=eth0 --defaultgw=192.168.1.10, run netstat -rn) + 3. Filesystem features (use --noprofile) 3.1 private @@ -56,6 +58,7 @@ C - chroot filesystem - N not working on Debian wheezy (32-bit and 64-bit) - todo 3.10 whitelist tmp - O not working on Arch Linux - todo +3.11 mkdir diff --git a/test/features/test.sh b/test/features/test.sh index 495996551..11306b52d 100755 --- a/test/features/test.sh +++ b/test/features/test.sh @@ -113,3 +113,6 @@ echo "TESTING: 3.9 whitelist dev" echo "TESTING: 3.10 whitelist tmp" ./3.10.exp $OVERLAY $CHROOT +echo "TESTING: 3.11 mkdir" +./3.11.exp $OVERLAY $CHROOT + -- cgit v1.2.3-70-g09d2