aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2009-03-08 07:14:17 -0400
committerMike Frysinger <vapier@gentoo.org>2009-03-08 08:47:05 -0400
commit7b0b914b4ea0e594867bad91fe1aaffa0c21d87b (patch)
treea0cbb36e49c17f91017c75c1c9b2e0cc80a1f53e
parentlibsandbox: push errno save/restore down in openat() (diff)
downloadsandbox-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.c98
-rw-r--r--libsandbox/libsandbox.h8
-rw-r--r--libsandbox/wrapper-funcs/fchmodat.c2
-rw-r--r--libsandbox/wrapper-funcs/fchownat.c2
-rw-r--r--libsandbox/wrapper-funcs/futimesat.c2
-rw-r--r--libsandbox/wrapper-funcs/linkat.c2
-rw-r--r--libsandbox/wrapper-funcs/mkdirat.c2
-rw-r--r--libsandbox/wrapper-funcs/mkfifoat.c2
-rw-r--r--libsandbox/wrapper-funcs/mknodat.c2
-rw-r--r--libsandbox/wrapper-funcs/renameat.c2
-rw-r--r--libsandbox/wrapper-funcs/symlinkat.c2
-rw-r--r--libsandbox/wrapper-funcs/unlinkat.c2
-rw-r--r--libsandbox/wrapper-funcs/utimensat.c2
-rw-r--r--localdecls.h1
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