diff options
author | 2009-03-08 07:14:17 -0400 | |
---|---|---|
committer | 2009-03-08 08:47:05 -0400 | |
commit | 7b0b914b4ea0e594867bad91fe1aaffa0c21d87b (patch) | |
tree | a0cbb36e49c17f91017c75c1c9b2e0cc80a1f53e | |
parent | libsandbox: push errno save/restore down in openat() (diff) | |
download | sandbox-7b0b914b4ea0e594867bad91fe1aaffa0c21d87b.tar.gz sandbox-7b0b914b4ea0e594867bad91fe1aaffa0c21d87b.tar.bz2 sandbox-7b0b914b4ea0e594867bad91fe1aaffa0c21d87b.zip |
libsandbox: handle symlinks properly
Make sure we handle edge cases that involve symlinks and functions that
operate on symlinks. This includes newer style *at functions that can go
between operating on symlinks and operating on the linked files, and on
symlinks to files that live in explicitly denied paths.
URL: http://bugs.gentoo.org/254914
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Reported-by: Mike Auty <ikelos@gentoo.org>
-rw-r--r-- | libsandbox/libsandbox.c | 98 | ||||
-rw-r--r-- | libsandbox/libsandbox.h | 8 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/fchmodat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/fchownat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/futimesat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/linkat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/mkdirat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/mkfifoat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/mknodat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/renameat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/symlinkat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/unlinkat.c | 2 | ||||
-rw-r--r-- | libsandbox/wrapper-funcs/utimensat.c | 2 | ||||
-rw-r--r-- | localdecls.h | 1 |
14 files changed, 83 insertions, 46 deletions
diff --git a/libsandbox/libsandbox.c b/libsandbox/libsandbox.c index 034d0e7..5b3ef73 100644 --- a/libsandbox/libsandbox.c +++ b/libsandbox/libsandbox.c @@ -503,18 +503,54 @@ static int check_prefixes(char **prefixes, int num_prefixes, const char *path) return 0; } -static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, const char *abs_path, const char *resolv_path) +/* Is this a func that works on symlinks, and is the file a symlink ? */ +static bool symlink_func(int sb_nr, int flags, const char *abs_path) +{ + struct stat st; + + /* These funcs always operate on symlinks */ + if (!(sb_nr == SB_NR_UNLINK || + sb_nr == SB_NR_UNLINKAT || + sb_nr == SB_NR_LCHOWN || + sb_nr == SB_NR_RENAME || + sb_nr == SB_NR_SYMLINK)) + { + /* These funcs sometimes operate on symlinks */ + if (!((sb_nr == SB_NR_FCHOWNAT || + sb_nr == SB_NR_FCHMODAT || + sb_nr == SB_NR_UTIMENSAT) && + (flags & AT_SYMLINK_NOFOLLOW))) + return false; + } + + if (-1 != lstat(abs_path, &st) && S_ISLNK(st.st_mode)) + return true; + else + return false; +} + +static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, + int flags, const char *abs_path, const char *resolv_path) { int old_errno = errno; int result = 0; int retval; + bool sym_func = symlink_func(sb_nr, flags, abs_path); retval = check_prefixes(sbcontext->deny_prefixes, - sbcontext->num_deny_prefixes, resolv_path); + sbcontext->num_deny_prefixes, abs_path); if (1 == retval) /* Fall in a read/write denied path, Deny Access */ goto out; + if (!sym_func) { + retval = check_prefixes(sbcontext->deny_prefixes, + sbcontext->num_deny_prefixes, resolv_path); + if (1 == retval) + /* Fall in a read/write denied path, Deny Access */ + goto out; + } + /* Hardcode denying write to the whole log dir. While this is a * parial match and so rejects paths that also start with this * string, that isn't going to happen in real life so live with @@ -601,19 +637,15 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, con goto out; } - /* XXX: Hack to enable us to remove symlinks pointing - * to protected stuff. First we make sure that the - * passed path is writable, and if so, check if its a - * symlink, and give access only if the resolved path - * of the symlink's parent also have write access. */ - struct stat st; - if ((sb_nr == SB_NR_UNLINK || - sb_nr == SB_NR_UNLINKAT || - sb_nr == SB_NR_LCHOWN || - sb_nr == SB_NR_RENAME || - sb_nr == SB_NR_SYMLINK) && - ((-1 != lstat(abs_path, &st)) && (S_ISLNK(st.st_mode)))) - { + /* XXX: Hack to enable us to remove symlinks pointing to + * protected stuff. First we make sure that the passed path + * is writable, and if so, check if it's a symlink, and give + * access only if the resolved path of the symlink's parent + * also have write access. We also want to let through funcs + * whose flags say they will operate on symlinks themselves + * rather than dereferencing them. + */ + if (sym_func) { /* Check if the symlink unresolved path have access */ retval = check_prefixes(sbcontext->write_prefixes, sbcontext->num_write_prefixes, abs_path); @@ -714,7 +746,8 @@ out: * 1: things worked out fine * 2: things worked out fine, but the errno should not be restored */ -static int check_syscall(sbcontext_t *sbcontext, int sb_nr, const char *func, const char *file) +static int check_syscall(sbcontext_t *sbcontext, int sb_nr, const char *func, + const char *file, int flags) { char *absolute_path = NULL; char *resolved_path = NULL; @@ -736,7 +769,7 @@ static int check_syscall(sbcontext_t *sbcontext, int sb_nr, const char *func, co if (debug) debug_log_path = getenv(ENV_SANDBOX_DEBUG_LOG); - result = check_access(sbcontext, sb_nr, func, absolute_path, resolved_path); + result = check_access(sbcontext, sb_nr, func, flags, absolute_path, resolved_path); if (verbose) { int sym_len = SB_MAX_STRING_LEN + 1 - strlen(func); @@ -817,12 +850,12 @@ bool is_sandbox_on(void) /* Need to protect the global sbcontext structure */ static pthread_mutex_t sb_syscall_lock = PTHREAD_MUTEX_INITIALIZER; -bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file) +bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, int flags) { int old_errno = errno; int result; // static sbcontext_t sbcontext; - char *at_file_buf = NULL; + char at_file_buf[SB_PATH_MAX]; if (file == NULL || file[0] == '\0') { /* The file/directory does not exist */ @@ -833,13 +866,19 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file) /* The *at style functions have the following semantics: * - dirfd = AT_FDCWD: same as non-at func: file is based on CWD * - file is absolute: dirfd is ignored - * - otherwise, file is relative to dirfd - * Since maintaining fd state based on open's, we'll just utilize - * the kernel doing it for us with /proc/<pid>/fd/ ... + * - otherwise: file is relative to dirfd + * Since maintaining fd state based on open's is real messy, we'll + * just rely on the kernel doing it for us with /proc/<pid>/fd/ ... */ if (dirfd != AT_FDCWD && file[0] != '/') { - at_file_buf = xmalloc(50 + strlen(file)); - sprintf(at_file_buf, "/proc/%i/fd/%i/%s", getpid(), dirfd, file); + size_t at_len = sizeof(at_file_buf) - 1 - 1 - strlen(file); + sprintf(at_file_buf, "/proc/%i/fd/%i", getpid(), dirfd); + ssize_t ret = readlink(at_file_buf, at_file_buf, at_len); + if (ret == -1) + return false; + at_file_buf[ret] = '/'; + at_file_buf[ret + 1] = '\0'; + strcat(at_file_buf, file); file = at_file_buf; } @@ -883,13 +922,10 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file) /* Might have been reset in check_access() */ sbcontext.show_access_violation = true; - result = check_syscall(&sbcontext, sb_nr, func, file); + result = check_syscall(&sbcontext, sb_nr, func, file, flags); pthread_mutex_unlock(&sb_syscall_lock); - if (at_file_buf) - free(at_file_buf); - if (0 == result) { if ((NULL != getenv(ENV_SANDBOX_PID)) && (is_env_on(ENV_SANDBOX_ABORT))) kill(atoi(getenv(ENV_SANDBOX_PID)), SIGUSR1); @@ -911,7 +947,7 @@ bool before_syscall_access(int dirfd, int sb_nr, const char *func, const char *f sb_nr = SB_NR_ACCESS_WR, ext_func = "access_wr"; else sb_nr = SB_NR_ACCESS_RD, ext_func = "access_rd"; - return before_syscall(dirfd, sb_nr, ext_func, file); + return before_syscall(dirfd, sb_nr, ext_func, file, flags); } bool before_syscall_open_int(int dirfd, int sb_nr, const char *func, const char *file, int flags) @@ -921,7 +957,7 @@ bool before_syscall_open_int(int dirfd, int sb_nr, const char *func, const char sb_nr = SB_NR_OPEN_WR, ext_func = "open_wr"; else sb_nr = SB_NR_OPEN_RD, ext_func = "open_rd"; - return before_syscall(dirfd, sb_nr, ext_func, file); + return before_syscall(dirfd, sb_nr, ext_func, file, flags); } bool before_syscall_open_char(int dirfd, int sb_nr, const char *func, const char *file, const char *mode) @@ -936,5 +972,5 @@ bool before_syscall_open_char(int dirfd, int sb_nr, const char *func, const char sb_nr = SB_NR_OPEN_RD, ext_func = "open_rd"; else sb_nr = SB_NR_OPEN_WR, ext_func = "open_wr"; - return before_syscall(dirfd, sb_nr, ext_func, file); + return before_syscall(dirfd, sb_nr, ext_func, file, 0); } diff --git a/libsandbox/libsandbox.h b/libsandbox/libsandbox.h index 8ed6d70..5e45b23 100644 --- a/libsandbox/libsandbox.h +++ b/libsandbox/libsandbox.h @@ -17,10 +17,10 @@ #define _FUNCTION_SANDBOX_SAFE(test) \ (!is_sandbox_on() || (test)) -#define FUNCTION_SANDBOX_SAFE_AT(_dirfd, _path) \ - _FUNCTION_SANDBOX_SAFE(before_syscall(_dirfd, WRAPPER_NR, STRING_NAME, _path)) +#define FUNCTION_SANDBOX_SAFE_AT(_dirfd, _path, _flags) \ + _FUNCTION_SANDBOX_SAFE(before_syscall(_dirfd, WRAPPER_NR, STRING_NAME, _path, _flags)) #define FUNCTION_SANDBOX_SAFE(_path) \ - FUNCTION_SANDBOX_SAFE_AT(AT_FDCWD, _path) + FUNCTION_SANDBOX_SAFE_AT(AT_FDCWD, _path, 0) #define FUNCTION_SANDBOX_SAFE_ACCESS_AT(_dirfd, _path, _flags) \ _FUNCTION_SANDBOX_SAFE(before_syscall_access(_dirfd, WRAPPER_NR, STRING_NAME, _path, _flags)) @@ -40,7 +40,7 @@ int canonicalize(const char *, char *); bool is_sandbox_on(void); -bool before_syscall(int, int, const char *, const char *); +bool before_syscall(int, int, const char *, const char *, int); bool before_syscall_access(int, int, const char *, const char *, int); bool before_syscall_open_int(int, int, const char *, const char *, int); bool before_syscall_open_char(int, int, const char *, const char *, const char *); diff --git a/libsandbox/wrapper-funcs/fchmodat.c b/libsandbox/wrapper-funcs/fchmodat.c index a548cbc..31b4b80 100644 --- a/libsandbox/wrapper-funcs/fchmodat.c +++ b/libsandbox/wrapper-funcs/fchmodat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *path, mode_t mode, int flags #define WRAPPER_ARGS dirfd, path, mode, flags -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, path) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, path, flags) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/fchownat.c b/libsandbox/wrapper-funcs/fchownat.c index a4f15f1..01983a4 100644 --- a/libsandbox/wrapper-funcs/fchownat.c +++ b/libsandbox/wrapper-funcs/fchownat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *path, uid_t owner, gid_t group, int flags #define WRAPPER_ARGS dirfd, path, owner, group, flags -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, path) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, path, flags) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/futimesat.c b/libsandbox/wrapper-funcs/futimesat.c index c66d442..d4724ee 100644 --- a/libsandbox/wrapper-funcs/futimesat.c +++ b/libsandbox/wrapper-funcs/futimesat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timeval times[] #define WRAPPER_ARGS dirfd, filename, times -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, filename) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, filename, 0) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/linkat.c b/libsandbox/wrapper-funcs/linkat.c index 819adb6..b177119 100644 --- a/libsandbox/wrapper-funcs/linkat.c +++ b/libsandbox/wrapper-funcs/linkat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags #define WRAPPER_ARGS olddirfd, oldpath, newdirfd, newpath, flags -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath, flags) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/mkdirat.c b/libsandbox/wrapper-funcs/mkdirat.c index 82ae34d..7d89c6a 100644 --- a/libsandbox/wrapper-funcs/mkdirat.c +++ b/libsandbox/wrapper-funcs/mkdirat.c @@ -8,7 +8,7 @@ #ifndef WRAPPER_ARGS_PROTO /* let mkdir() use us */ # define WRAPPER_ARGS_PROTO int dirfd, const char *pathname, mode_t mode # define WRAPPER_ARGS dirfd, pathname, mode -# define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname) +# define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname, 0) #endif static inline bool sb_mkdirat_pre_check(WRAPPER_ARGS_PROTO) diff --git a/libsandbox/wrapper-funcs/mkfifoat.c b/libsandbox/wrapper-funcs/mkfifoat.c index fe1b8e9..06479c5 100644 --- a/libsandbox/wrapper-funcs/mkfifoat.c +++ b/libsandbox/wrapper-funcs/mkfifoat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *pathname, mode_t mode #define WRAPPER_ARGS dirfd, pathname, mode -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname, 0) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/mknodat.c b/libsandbox/wrapper-funcs/mknodat.c index 50a235e..53f29cf 100644 --- a/libsandbox/wrapper-funcs/mknodat.c +++ b/libsandbox/wrapper-funcs/mknodat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *pathname, mode_t mode, dev_t dev #define WRAPPER_ARGS dirfd, pathname, mode, dev -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname, 0) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/renameat.c b/libsandbox/wrapper-funcs/renameat.c index 951fea1..84a9aba 100644 --- a/libsandbox/wrapper-funcs/renameat.c +++ b/libsandbox/wrapper-funcs/renameat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int olddirfd, const char *oldpath, int newdirfd, const char *newpath #define WRAPPER_ARGS olddirfd, oldpath, newdirfd, newpath -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(olddirfd, oldpath) && FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath) +#define WRAPPER_SAFE() (FUNCTION_SANDBOX_SAFE_AT(olddirfd, oldpath, 0) && FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath, 0)) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/symlinkat.c b/libsandbox/wrapper-funcs/symlinkat.c index 30c8db9..e54128b 100644 --- a/libsandbox/wrapper-funcs/symlinkat.c +++ b/libsandbox/wrapper-funcs/symlinkat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO const char *oldpath, int newdirfd, const char *newpath #define WRAPPER_ARGS oldpath, newdirfd, newpath -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(newdirfd, newpath, 0) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/unlinkat.c b/libsandbox/wrapper-funcs/unlinkat.c index 8c57f48..ddbbaf6 100644 --- a/libsandbox/wrapper-funcs/unlinkat.c +++ b/libsandbox/wrapper-funcs/unlinkat.c @@ -8,7 +8,7 @@ #ifndef WRAPPER_ARGS_PROTO /* let unlink() use us */ # define WRAPPER_ARGS_PROTO int dirfd, const char *pathname, int flags # define WRAPPER_ARGS dirfd, pathname, flags -# define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname) +# define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, pathname, flags) #endif static inline bool sb_unlinkat_pre_check(WRAPPER_ARGS_PROTO) diff --git a/libsandbox/wrapper-funcs/utimensat.c b/libsandbox/wrapper-funcs/utimensat.c index 54346f7..8a096ba 100644 --- a/libsandbox/wrapper-funcs/utimensat.c +++ b/libsandbox/wrapper-funcs/utimensat.c @@ -7,5 +7,5 @@ #define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timespec times[], int flags #define WRAPPER_ARGS dirfd, filename, times, flags -#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, filename) +#define WRAPPER_SAFE() FUNCTION_SANDBOX_SAFE_AT(dirfd, filename, flags) #include "__wrapper_simple.c" diff --git a/localdecls.h b/localdecls.h index a1a517c..2567ccd 100644 --- a/localdecls.h +++ b/localdecls.h @@ -68,6 +68,7 @@ typedef __sighandler_t sighandler_t; */ #ifndef AT_FDCWD # define AT_FDCWD -100 +# define AT_SYMLINK_NOFOLLOW 0 #endif #if !HAVE_DLVSYM |