summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-01-04 20:00:28 +0100
committerSven Eden <yamakuzure@gmx.net>2018-05-30 07:50:08 +0200
commit941f374947c8db8311bbde1bba478749d455be43 (patch)
treee3da7023270163af0924169d33d1c42ca8c19f21 /src
parente2c4475028606343176752bd0974df2c5ed6a520 (diff)
fs-util: add new chase_symlinks() flag CHASE_OPEN
The new flag returns the O_PATH fd of the final component, which may be converted into a proper fd by open()ing it again through the /proc/self/fd/xyz path. Together with O_SAFE this provides us with a somewhat safe way to open() files in directories potentially owned by unprivileged code, where we want to refuse operation if any symlink tricks are played pointing to privileged files.
Diffstat (limited to 'src')
-rw-r--r--src/basic/fs-util.c17
-rw-r--r--src/basic/fs-util.h1
-rw-r--r--src/test/test-fs-util.c28
3 files changed, 45 insertions, 1 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 154163535..32b1fb605 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -647,6 +647,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
assert(path);
+ /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+ if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
@@ -896,6 +900,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
done = NULL;
}
+ if (flags & CHASE_OPEN) {
+ int q;
+
+ /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+ * opening /proc/self/fd/xyz. */
+
+ assert(fd >= 0);
+ q = fd;
+ fd = -1;
+
+ return q;
+ }
+
return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 13008fdda..fad0a2eed 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -99,6 +99,7 @@ enum {
CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
};
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 1c453e11a..22aa64f4c 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -22,12 +22,15 @@
#include "alloc-util.h"
#include "fd-util.h"
+//#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+//#include "id128-util.h"
#include "macro.h"
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+//#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
//#include "user-util.h"
@@ -37,7 +40,7 @@ static void test_chase_symlinks(void) {
_cleanup_free_ char *result = NULL;
char temp[] = "/tmp/test-chase.XXXXXX";
const char *top, *p, *pslash, *q, *qslash;
- int r;
+ int r, pfd;
assert_se(mkdtemp(temp));
@@ -262,6 +265,29 @@ static void test_chase_symlinks(void) {
assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
}
+ p = strjoina(temp, "/machine-id-test");
+ assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+
+ pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
+ if (pfd != -ENOENT) {
+ char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(pfd) + 1];
+ _cleanup_close_ int fd = -1;
+ sd_id128_t a, b;
+
+ assert_se(pfd >= 0);
+
+ xsprintf(procfs, "/proc/self/fd/%i", pfd);
+
+ fd = open(procfs, O_RDONLY|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ safe_close(pfd);
+
+ assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+ assert_se(sd_id128_get_machine(&b) >= 0);
+ assert_se(sd_id128_equal(a, b));
+ }
+
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}