aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libsandbox/libsandbox.c86
-rw-r--r--libsandbox/libsandbox.h1
-rw-r--r--libsandbox/wrapper-funcs/mkdirat_pre_check.c15
-rw-r--r--libsandbox/wrapper-funcs/openat_pre_check.c45
-rw-r--r--libsandbox/wrapper-funcs/unlinkat_pre_check.c34
5 files changed, 123 insertions, 58 deletions
diff --git a/libsandbox/libsandbox.c b/libsandbox/libsandbox.c
index e5619dc..2fe9ae9 100644
--- a/libsandbox/libsandbox.c
+++ b/libsandbox/libsandbox.c
@@ -136,6 +136,54 @@ static const char *sb_get_cmdline(pid_t pid)
return path;
}
+/* resolve_dirfd_path - get the path relative to a dirfd
+ *
+ * return value:
+ * -1 - error!
+ * 0 - path is in @resolved_path
+ * 1 - path is in @path (no resolution necessary)
+ * 2 - errno issues -- ignore this path
+ */
+int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path)
+{
+ /* 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 is real messy, we'll
+ * just rely on the kernel doing it for us with /proc/<pid>/fd/ ...
+ */
+ if (dirfd == AT_FDCWD || !path || path[0] == '/')
+ return 1;
+
+ save_errno();
+
+ size_t at_len = sizeof(resolved_path) - 1 - 1 - (path ? strlen(path) : 0);
+ sprintf(resolved_path, "/proc/%i/fd/%i", trace_pid ? : getpid(), dirfd);
+ ssize_t ret = readlink(resolved_path, resolved_path, at_len);
+ if (ret == -1) {
+ /* see comments at end of check_syscall() */
+ if (errno_is_too_long()) {
+ restore_errno();
+ return 2;
+ }
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("AT_FD LOOKUP", " fail: %s: %s\n",
+ resolved_path, strerror(errno));
+ /* If the fd isn't found, some guys (glibc) expect errno */
+ if (errno == ENOENT)
+ errno = EBADF;
+ return -1;
+ }
+ resolved_path[ret] = '/';
+ resolved_path[ret + 1] = '\0';
+ if (path)
+ strcat(resolved_path, path);
+
+ restore_errno();
+ return 0;
+}
+
int canonicalize(const char *path, char *resolved_path)
{
int old_errno = errno;
@@ -1011,40 +1059,14 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in
}
}
- save_errno();
-
- /* 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 is real messy, we'll
- * just rely on the kernel doing it for us with /proc/<pid>/fd/ ...
- */
- if (dirfd != AT_FDCWD && (!file || file[0] != '/')) {
- size_t at_len = sizeof(at_file_buf) - 1 - 1 - (file ? strlen(file) : 0);
- sprintf(at_file_buf, "/proc/%i/fd/%i", trace_pid ? : getpid(), dirfd);
- ssize_t ret = readlink(at_file_buf, at_file_buf, at_len);
- if (ret == -1) {
- /* see comments at end of check_syscall() */
- if (errno_is_too_long()) {
- restore_errno();
- return true;
- }
- if (is_env_on(ENV_SANDBOX_DEBUG))
- SB_EINFO("AT_FD LOOKUP", " fail: %s: %s\n",
- at_file_buf, strerror(errno));
- /* If the fd isn't found, some guys (glibc) expect errno */
- if (errno == ENOENT)
- errno = EBADF;
- return false;
- }
- at_file_buf[ret] = '/';
- at_file_buf[ret + 1] = '\0';
- if (file)
- strcat(at_file_buf, file);
- file = at_file_buf;
+ switch (resolve_dirfd_path(dirfd, file, at_file_buf)) {
+ case -1: return false;
+ case 0: file = at_file_buf; break;
+ case 2: return true;
}
+ save_errno();
+
/* Need to protect the global sbcontext structure */
sb_lock();
diff --git a/libsandbox/libsandbox.h b/libsandbox/libsandbox.h
index dc847cb..3ef7c71 100644
--- a/libsandbox/libsandbox.h
+++ b/libsandbox/libsandbox.h
@@ -71,6 +71,7 @@ __attribute__((noreturn)) void sb_abort(void);
char *erealpath(const char *, char *);
char *egetcwd(char *, size_t);
int canonicalize(const char *, char *);
+int resolve_dirfd_path(int, const char *, char *);
/* most linux systems use ENAMETOOLONG, but some (ia64) use ERANGE, as do some BSDs */
#define errno_is_too_long() (errno == ENAMETOOLONG || errno == ERANGE)
diff --git a/libsandbox/wrapper-funcs/mkdirat_pre_check.c b/libsandbox/wrapper-funcs/mkdirat_pre_check.c
index c999e46..d037546 100644
--- a/libsandbox/wrapper-funcs/mkdirat_pre_check.c
+++ b/libsandbox/wrapper-funcs/mkdirat_pre_check.c
@@ -8,10 +8,23 @@
bool sb_mkdirat_pre_check(const char *func, const char *pathname, int dirfd)
{
char canonic[SB_PATH_MAX];
+ char dirfd_path[SB_PATH_MAX];
save_errno();
- /* XXX: need to check pathname with dirfd */
+ /* Expand the dirfd path first */
+ switch (resolve_dirfd_path(dirfd, pathname, dirfd_path)) {
+ case -1:
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s) @ resolve_dirfd_path: %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ case 0:
+ pathname = dirfd_path;
+ break;
+ }
+
+ /* Then break down any relative/symlink paths */
if (-1 == canonicalize(pathname, canonic))
/* see comments in check_syscall() */
if (ENAMETOOLONG != errno) {
diff --git a/libsandbox/wrapper-funcs/openat_pre_check.c b/libsandbox/wrapper-funcs/openat_pre_check.c
index 7f5e823..4a63413 100644
--- a/libsandbox/wrapper-funcs/openat_pre_check.c
+++ b/libsandbox/wrapper-funcs/openat_pre_check.c
@@ -7,22 +7,37 @@
bool sb_openat_pre_check(const char *func, const char *pathname, int dirfd, int flags)
{
- if (!(flags & O_CREAT)) {
- /* If we're not trying to create, fail normally if
- * file does not stat
- */
- if (dirfd == AT_FDCWD || pathname[0] == '/') {
- struct stat st;
- save_errno();
- if (-1 == stat(pathname, &st)) {
- if (is_env_on(ENV_SANDBOX_DEBUG))
- SB_EINFO("EARLY FAIL", " %s(%s): %s\n",
- func, pathname, strerror(errno));
- return false;
- }
- restore_errno();
- }
+ /* If we're not trying to create, fail normally if
+ * file does not stat
+ */
+ if (flags & O_CREAT)
+ return true;
+
+ save_errno();
+
+ /* Expand the dirfd path first */
+ char dirfd_path[SB_PATH_MAX];
+ switch (resolve_dirfd_path(dirfd, pathname, dirfd_path)) {
+ case -1:
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s) @ resolve_dirfd_path: %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ case 0:
+ pathname = dirfd_path;
+ break;
}
+ /* Doesn't exist -> skip permission checks */
+ struct stat st;
+ if (-1 == stat(pathname, &st)) {
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s): %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ }
+
+ restore_errno();
+
return true;
}
diff --git a/libsandbox/wrapper-funcs/unlinkat_pre_check.c b/libsandbox/wrapper-funcs/unlinkat_pre_check.c
index 961c31f..4e4a38d 100644
--- a/libsandbox/wrapper-funcs/unlinkat_pre_check.c
+++ b/libsandbox/wrapper-funcs/unlinkat_pre_check.c
@@ -8,14 +8,31 @@
bool sb_unlinkat_pre_check(const char *func, const char *pathname, int dirfd)
{
char canonic[SB_PATH_MAX];
+ char dirfd_path[SB_PATH_MAX];
save_errno();
- /* XXX: need to check pathname with dirfd */
+ /* Expand the dirfd path first */
+ switch (resolve_dirfd_path(dirfd, pathname, dirfd_path)) {
+ case -1:
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s) @ resolve_dirfd_path: %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ case 0:
+ pathname = dirfd_path;
+ break;
+ }
+
+ /* Then break down any relative/symlink paths */
if (-1 == canonicalize(pathname, canonic))
/* see comments in check_syscall() */
- if (ENAMETOOLONG != errno)
- goto error;
+ if (ENAMETOOLONG != errno) {
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s) @ canonicalize: %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ }
/* XXX: Hack to make sure sandboxed process cannot remove
* a device node, bug #79836. */
@@ -23,16 +40,13 @@ bool sb_unlinkat_pre_check(const char *func, const char *pathname, int dirfd)
0 == strcmp(canonic, "/dev/zero"))
{
errno = EACCES;
- goto error;
+ if (is_env_on(ENV_SANDBOX_DEBUG))
+ SB_EINFO("EARLY FAIL", " %s(%s): %s\n",
+ func, pathname, strerror(errno));
+ return false;
}
restore_errno();
return true;
-
- error:
- if (is_env_on(ENV_SANDBOX_DEBUG))
- SB_EINFO("EARLY FAIL", " %s(%s): %s\n",
- func, pathname, strerror(errno));
- return false;
}