aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'libsandbox/pre_check_mkdirat.c')
-rw-r--r--libsandbox/pre_check_mkdirat.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/libsandbox/pre_check_mkdirat.c b/libsandbox/pre_check_mkdirat.c
new file mode 100644
index 0000000..49c382a
--- /dev/null
+++ b/libsandbox/pre_check_mkdirat.c
@@ -0,0 +1,65 @@
+/*
+ * mkdir*() pre-check.
+ *
+ * Copyright 1999-2012 Gentoo Foundation
+ * Licensed under the GPL-2
+ */
+
+#include "headers.h"
+#include "sbutil.h"
+#include "libsandbox.h"
+#include "wrappers.h"
+
+bool sb_mkdirat_pre_check(const char *func, const char *pathname, int dirfd)
+{
+ char canonic[SB_PATH_MAX];
+
+ save_errno();
+
+ /* Check incoming args against common *at issues */
+ char dirfd_path[SB_PATH_MAX];
+ if (!sb_common_at_pre_check(func, &pathname, dirfd, dirfd_path, sizeof(dirfd_path)))
+ return false;
+
+ /* Then break down any relative/symlink paths */
+ if (-1 == canonicalize(pathname, canonic))
+ /* see comments in check_syscall() */
+ if (ENAMETOOLONG != errno) {
+ sb_debug_dyn("EARLY FAIL: %s(%s) @ canonicalize: %s\n",
+ func, pathname, strerror(errno));
+ return false;
+ }
+
+ /* XXX: Hack to prevent errors if the directory exist, and are
+ * not writable - we rather return EEXIST than fail. This can
+ * occur if doing something like `mkdir -p /`. We certainly do
+ * not want to pass this attempt up to the higher levels as those
+ * will trigger a sandbox violation.
+ */
+ struct stat64 st;
+ if (0 == lstat64(pathname, &st)) {
+ int new_errno;
+ sb_debug_dyn("EARLY FAIL: %s(%s[%s]) @ lstat: %s\n",
+ func, pathname, canonic, strerror(errno));
+
+ new_errno = EEXIST;
+
+ /* Hmm, is this a broken symlink we're trying to extend ? */
+ if (S_ISLNK(st.st_mode) && stat64(pathname, &st) != 0) {
+ /* XXX: This awful hack should probably be turned into a
+ * common func that does a better job. For now, we have
+ * enough crap to catch gnulib tests #297026.
+ */
+ char *parent = strrchr(pathname, '/');
+ if (parent && (strcmp(parent, "/.") == 0 || strcmp(parent, "/..") == 0))
+ new_errno = ENOENT;
+ }
+
+ errno = new_errno;
+ return false;
+ }
+
+ restore_errno();
+
+ return true;
+}