Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs_home.c: run more code with euid of the user #4374

Merged
merged 2 commits into from
Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/firejail/firejail.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ void logargs(int argc, char **argv) ;
void logerr(const char *msg);
void set_nice(int inc);
int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
void copy_file_as_user(const char *srcname, const char *destname, mode_t mode);
void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
void touch_file_as_user(const char *fname, mode_t mode);
int is_dir(const char *fname);
Expand Down
99 changes: 62 additions & 37 deletions src/firejail/fs_home.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@
#define O_PATH 010000000
#endif

static void skel(const char *homedir, uid_t u, gid_t g) {
char *fname;
static void skel(const char *homedir) {
EUID_ASSERT();

// zsh
if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) {
// copy skel files
char *fname;
if (asprintf(&fname, "%s/.zshrc", homedir) == -1)
errExit("asprintf");
// don't copy it if we already have the file
Expand All @@ -50,7 +51,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
exit(1);
}
if (access("/etc/skel/.zshrc", R_OK) == 0) {
copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user
copy_file_as_user("/etc/skel/.zshrc", fname, 0644); // regular user
fs_logger("clone /etc/skel/.zshrc");
fs_logger2("clone", fname);
}
Expand All @@ -64,6 +65,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
// csh
else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) {
// copy skel files
char *fname;
if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
errExit("asprintf");
// don't copy it if we already have the file
Expand All @@ -74,7 +76,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
exit(1);
}
if (access("/etc/skel/.cshrc", R_OK) == 0) {
copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user
copy_file_as_user("/etc/skel/.cshrc", fname, 0644); // regular user
fs_logger("clone /etc/skel/.cshrc");
fs_logger2("clone", fname);
}
Expand All @@ -88,6 +90,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
// bash etc.
else {
// copy skel files
char *fname;
if (asprintf(&fname, "%s/.bashrc", homedir) == -1)
errExit("asprintf");
// don't copy it if we already have the file
Expand All @@ -98,7 +101,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
exit(1);
}
if (access("/etc/skel/.bashrc", R_OK) == 0) {
copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user
copy_file_as_user("/etc/skel/.bashrc", fname, 0644); // regular user
fs_logger("clone /etc/skel/.bashrc");
fs_logger2("clone", fname);
}
Expand All @@ -108,6 +111,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
}

static int store_xauthority(void) {
EUID_ASSERT();
if (arg_x11_block)
return 0;

Expand All @@ -118,14 +122,15 @@ static int store_xauthority(void) {
errExit("asprintf");

struct stat s;
if (lstat_as_user(src, &s) == 0) {
if (lstat(src, &s) == 0) {
if (S_ISLNK(s.st_mode)) {
fwarning("invalid .Xauthority file\n");
free(src);
return 0;
}

// create an empty file as root, and change ownership to user
EUID_ROOT();
FILE *fp = fopen(dest, "we");
if (fp) {
fprintf(fp, "\n");
Expand All @@ -134,10 +139,11 @@ static int store_xauthority(void) {
}
else
errExit("fopen");
EUID_USER();

copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user
fs_logger2("clone", dest);
copy_file_as_user(src, dest, 0600); // regular user
selinux_relabel_path(dest, src);
fs_logger2("clone", dest);
free(src);
return 1; // file copied
}
Expand All @@ -147,6 +153,7 @@ static int store_xauthority(void) {
}

static int store_asoundrc(void) {
EUID_ASSERT();
if (arg_nosound)
return 0;

Expand All @@ -157,11 +164,11 @@ static int store_asoundrc(void) {
errExit("asprintf");

struct stat s;
if (lstat_as_user(src, &s) == 0) {
if (lstat(src, &s) == 0) {
if (S_ISLNK(s.st_mode)) {
// make sure the real path of the file is inside the home directory
/* coverity[toctou] */
char *rp = realpath_as_user(src);
char *rp = realpath(src, NULL);
if (!rp) {
fprintf(stderr, "Error: Cannot access %s\n", src);
exit(1);
Expand All @@ -174,6 +181,7 @@ static int store_asoundrc(void) {
}

// create an empty file as root, and change ownership to user
EUID_ROOT();
FILE *fp = fopen(dest, "we");
if (fp) {
fprintf(fp, "\n");
Expand All @@ -182,10 +190,11 @@ static int store_asoundrc(void) {
}
else
errExit("fopen");
EUID_USER();

copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user
selinux_relabel_path(dest, src);
copy_file_as_user(src, dest, 0644); // regular user
fs_logger2("clone", dest);
selinux_relabel_path(dest, src);
free(src);
return 1; // file copied
}
Expand All @@ -195,6 +204,7 @@ static int store_asoundrc(void) {
}

static void copy_xauthority(void) {
EUID_ASSERT();
// copy XAUTHORITY_FILE in the new home directory
char *src = RUN_XAUTHORITY_FILE ;
char *dest;
Expand All @@ -207,16 +217,18 @@ static void copy_xauthority(void) {
exit(1);
}

copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
selinux_relabel_path(dest, src);
copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
fs_logger2("clone", dest);
selinux_relabel_path(dest, dest);
free(dest);

// delete the temporary file
unlink(src);
EUID_ROOT();
unlink(src); // delete the temporary file
EUID_USER();
}

static void copy_asoundrc(void) {
EUID_ASSERT();
// copy ASOUNDRC_FILE in the new home directory
char *src = RUN_ASOUNDRC_FILE ;
char *dest;
Expand All @@ -229,13 +241,14 @@ static void copy_asoundrc(void) {
exit(1);
}

copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
selinux_relabel_path(dest, src);
copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
fs_logger2("clone", dest);
selinux_relabel_path(dest, dest);
free(dest);

// delete the temporary file
unlink(src);
EUID_ROOT();
unlink(src); // delete the temporary file
EUID_USER();
}

// private mode (--private=homedir):
Expand All @@ -248,18 +261,18 @@ void fs_private_homedir(void) {
char *private_homedir = cfg.home_private;
assert(homedir);
assert(private_homedir);
EUID_ASSERT();

uid_t u = getuid();
// gid_t g = getgid();

int xflag = store_xauthority();
int aflag = store_asoundrc();

uid_t u = getuid();
gid_t g = getgid();

// mount bind private_homedir on top of homedir
if (arg_debug)
printf("Mount-bind %s on top of %s\n", private_homedir, homedir);
// get file descriptors for homedir and private_homedir, fails if there is any symlink
EUID_USER();
int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_|O_CLOEXEC);
if (src == -1)
errExit("opening private directory");
Expand Down Expand Up @@ -287,6 +300,7 @@ void fs_private_homedir(void) {
EUID_ROOT();
if (bind_mount_by_fd(src, dst))
errExit("mount bind");
EUID_USER();

// check /proc/self/mountinfo to confirm the mount is ok
MountData *mptr = get_last_mount();
Expand All @@ -305,6 +319,7 @@ void fs_private_homedir(void) {
// if (chmod(homedir, s.st_mode) == -1)
// errExit("mount-bind chmod");

EUID_ROOT();
if (u != 0) {
// mask /root
if (arg_debug)
Expand All @@ -323,8 +338,9 @@ void fs_private_homedir(void) {
selinux_relabel_path("/home", "/home");
fs_logger("tmpfs /home");
}
EUID_USER();

skel(homedir, u, g);
skel(homedir);
if (xflag)
copy_xauthority();
if (aflag)
Expand All @@ -339,12 +355,15 @@ void fs_private_homedir(void) {
void fs_private(void) {
char *homedir = cfg.homedir;
assert(homedir);
EUID_ASSERT();

uid_t u = getuid();
gid_t g = getgid();

int xflag = store_xauthority();
int aflag = store_asoundrc();

EUID_ROOT();
// mask /root
if (arg_debug)
printf("Mounting a new /root directory\n");
Expand Down Expand Up @@ -387,8 +406,9 @@ void fs_private(void) {

selinux_relabel_path(homedir, homedir);
}
EUID_USER();

skel(homedir, u, g);
skel(homedir);
if (xflag)
copy_xauthority();
if (aflag)
Expand Down Expand Up @@ -530,26 +550,29 @@ static void duplicate(char *name) {
// set skel files,
// restore .Xauthority
void fs_private_home_list(void) {
timetrace_start();

char *homedir = cfg.homedir;
char *private_list = cfg.home_private_keep;
assert(homedir);
assert(private_list);
EUID_ASSERT();

int xflag = store_xauthority();
int aflag = store_asoundrc();
timetrace_start();

uid_t uid = getuid();
gid_t gid = getgid();

int xflag = store_xauthority();
int aflag = store_asoundrc();

// create /run/firejail/mnt/home directory
EUID_ROOT();
mkdir_attr(RUN_HOME_DIR, 0755, uid, gid);
selinux_relabel_path(RUN_HOME_DIR, homedir);

fs_logger_print(); // save the current log
EUID_USER();

// copy the list of files in the new home directory
EUID_USER();
if (arg_debug)
printf("Copying files in the new home:\n");
char *dlist = strdup(cfg.home_private_keep);
Expand Down Expand Up @@ -588,18 +611,15 @@ void fs_private_home_list(void) {
if (bind_mount_path_to_fd(RUN_HOME_DIR, fd))
errExit("mount bind");
close(fd);
EUID_USER();

// check /proc/self/mountinfo to confirm the mount is ok
MountData *mptr = get_last_mount();
if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
errLogExit("invalid private-home mount");
fs_logger2("tmpfs", homedir);

// mask RUN_HOME_DIR, it is writable and not noexec
if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
errExit("mounting tmpfs");
fs_logger2("tmpfs", RUN_HOME_DIR);

EUID_ROOT();
if (uid != 0) {
// mask /root
if (arg_debug)
Expand All @@ -619,7 +639,12 @@ void fs_private_home_list(void) {
fs_logger("tmpfs /home");
}

skel(homedir, uid, gid);
// mask RUN_HOME_DIR, it is writable and not noexec
if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
errExit("mounting tmpfs");
EUID_USER();

skel(homedir);
if (xflag)
copy_xauthority();
if (aflag)
Expand Down
5 changes: 4 additions & 1 deletion src/firejail/fs_whitelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,12 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
}

// user home directory
if (tmpfs_home)
if (tmpfs_home) {
// checks owner if outside /home
EUID_USER();
fs_private();
EUID_ROOT();
}

// /run/user/$UID directory
if (tmpfs_runuser) {
Expand Down
2 changes: 2 additions & 0 deletions src/firejail/sandbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ int sandbox(void* sandbox_arg) {
// private mode
//****************************
if (arg_private) {
EUID_USER();
if (cfg.home_private) { // --private=
if (cfg.chrootdir)
fwarning("private=directory feature is disabled in chroot\n");
Expand All @@ -858,6 +859,7 @@ int sandbox(void* sandbox_arg) {
}
else // --private
fs_private();
EUID_ROOT();
}

if (arg_private_dev)
Expand Down
Loading