From 581c002bf75548914a600cabaa7c9b4c9b08d4a9 Mon Sep 17 00:00:00 2001 From: short <> Date: Thu, 21 Aug 2003 16:06:25 +0000 Subject: [PATCH] Leave '/var/lib/sandbox/sandbox-server-$$' directory locked. Delete all unlocked directories in '/var/lib/sandbox/' Security: chrooted_unlink_recursive(): Protect removal against enemy symlinks. --- src/client/sandbox-server/main.c | 113 +++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/src/client/sandbox-server/main.c b/src/client/sandbox-server/main.c index cab66c7..14e294f 100644 --- a/src/client/sandbox-server/main.c +++ b/src/client/sandbox-server/main.c @@ -38,6 +38,8 @@ #include "../../libcaptive/sandbox/split.h" /* for captive_sandbox_fd_closeup(); FIXME */ #include #include +#include +#include /* CONFIG: */ @@ -144,30 +146,33 @@ int linkbuflen; depth--; } -static void unlink_recursive_suidsafe(const gchar *pathname) +static void chrooted_unlink_recursive(const gchar *pathname) { DIR *dir; struct dirent *dirent; static gint depth=0; +struct stat statbuf; if (++depth>=1000) - fatal("Loop count >=%d during unlink_recursive_suidsafe(\"%s\")",depth,pathname); + fatal("Loop count >=%d during chrooted_unlink_recursive(\"%s\")",depth,pathname); - if (!(dir=opendir(pathname))) { - if (errno!=ENOTDIR) - fatal("Cannot opendir(\"%s\") to delete leftover sandbox files: %m",pathname); - /* errno==ENOTDIR, a regular file */ + /* Security: Do not allow anyone to escape the sandbox directory by symlinks. */ + if (lstat(pathname,&statbuf)) + fatal("Cannot lstat(\"%s\") to delete leftover sandbox files: %m",pathname); + if (!S_ISDIR(statbuf.st_mode)) { if (unlink(pathname)) - fatal("Cannot pathname(\"%s\") to delete leftover sandbox files: %m",pathname); + fatal("Cannot unlink(\"%s\") to delete leftover sandbox files: %m",pathname); goto done; } + if (!(dir=opendir(pathname))) + fatal("Cannot opendir(\"%s\") to delete leftover sandbox files: %m",pathname); while (errno=0,(dirent=readdir(dir))) { gchar *dirent_path; if (!strcmp(dirent->d_name,".") || !strcmp(dirent->d_name,"..")) continue; dirent_path=g_strdup_printf("%s/%s",pathname,dirent->d_name); - unlink_recursive_suidsafe(dirent_path); + chrooted_unlink_recursive(dirent_path); g_free(dirent_path); } if (errno) @@ -181,14 +186,84 @@ done: depth--; } -static void chrooted_createdir(const gchar *dir,uid_t uid,gid_t gid) +static void chrooted_cleanuplockeddirs(const gchar *pathname) +{ +DIR *dir; +struct dirent *dirent; + + if (!(dir=opendir(pathname))) { + if (errno!=ENOTDIR) + fatal("Cannot opendir(\"%s\") to delete leftover sandbox files: %m",pathname); + /* errno==ENOTDIR, a regular file */ + if (unlink(pathname)) + fatal("Cannot unlink(\"%s\") to delete leftover sandbox files: %m",pathname); + return; + } + while (errno=0,(dirent=readdir(dir))) { +gchar *dirent_path; +int direntfd; + + if (!strcmp(dirent->d_name,".") || !strcmp(dirent->d_name,"..")) + continue; + dirent_path=g_strdup_printf("%s/%s",pathname,dirent->d_name); + if (-1==(direntfd=open(dirent_path,O_RDONLY))) { + if (errno==ENOENT) /* It could disappear in the meantime. */ + goto next_dirent_free_dirent_path; + fatal("Cannot open(\"%s\") as the child directory during delete of leftover sandbox files: %m",dirent_path); + } + if (flock(direntfd,LOCK_EX|LOCK_NB)) { + if (errno==EWOULDBLOCK) /* Valid directory in use. */ + goto next_dirent_close_direntfd; + fatal("Cannot flock(\"%s\",LOCK_EX|LOCK_NB) child directory during delete of leftover sandbox files: %m",dirent_path); + } + chrooted_unlink_recursive(dirent_path); +next_dirent_close_direntfd: + if (close(direntfd)) + fatal("Cannot close(\"%s\") child directory during delete of leftover sandbox files: %m",dirent_path); +next_dirent_free_dirent_path: + g_free(dirent_path); + } + if (errno) + fatal("Cannot readdir(\"%s\") during delete of leftover sandbox files: %m",pathname); + if (closedir(dir)) + fatal("Cannot closedir(\"%s\") during delete of leftover sandbox files: %m",pathname); +} + +static void chrooted_createdir(const gchar *dir,uid_t uid,gid_t gid,gboolean lock) { - if (mkdir(dir,0711)) { - if (errno!=EEXIST) - fatal("Failed to create chroot directory \"%s\": %m",dir); - unlink_recursive_suidsafe(dir); - if (mkdir(dir,0711)) - fatal("Failed to create chroot directory \"%s\" after attempted unlink: %m",dir); +gint retries; + + for (retries=0;retries<10;retries++) { +struct stat statbuf; +int dirfd; + + if (mkdir(dir,0711)) { + if (errno!=EEXIST) + fatal("Failed to create chroot directory \"%s\": %m",dir); + chrooted_unlink_recursive(dir); + if (mkdir(dir,0711)) + fatal("Failed to create chroot directory \"%s\" after attempted unlink: %m",dir); + } + if (!lock) + break; + dirfd=open(dir,O_RDONLY); + if (dirfd==-1) { + if (errno!=ENOENT) + fatal("Failed to open created chroot directory \"%s\" to lock it: %m",dir); + continue; + } + /* Do not use 'LOCK_NB' here as the garbage collector should release it soon. */ + if (flock(dirfd,LOCK_EX)) + fatal("Failed to lock created chroot directory \"%s\": %m",dir); + if (lstat(dir,&statbuf)) { + if (errno!=ENOENT) + fatal("Failed to lstat(2) created chroot directory \"%s\": %m",dir); + if (close(dirfd)) + fatal("Failed to close created and locked chroot directory \"%s\": %m",dir); + continue; + } + /* Leave 'dirfd' open to leave it LOCK_EX-ed. */ + break; } if (chown(dir,uid,gid)) fatal("Failed to chown(\"%s\",%d,%d): %m",dir,uid,gid); @@ -280,10 +355,14 @@ gint gi; } g_rand_free(grand); *s=0; + if (geteuid()==0) /* Not 'fragile' as we can be native 'root'. */ + chrooted_cleanuplockeddirs(optarg_chroot); chroot_pid_dir=captive_printf_alloca("%s/sandbox-server-%d",optarg_chroot,(int)getpid()); - chrooted_createdir(chroot_pid_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid)); + chrooted_createdir(chroot_pid_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid), + TRUE); /* lock */ chroot_pid_hashkey_dir=captive_printf_alloca("%s/%s",chroot_pid_dir,chroot_hashkey); - chrooted_createdir(chroot_pid_hashkey_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid)); + chrooted_createdir(chroot_pid_hashkey_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid), + FALSE); /* lock */ if (chroot(chroot_pid_hashkey_dir)) fatal("Failed to chroot(\"%s\"): %m",chroot_pid_hashkey_dir); if (chdir("/")) -- 1.8.3.1