summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib')
-rw-r--r--util/nvmutil/lib/file.c290
1 files changed, 143 insertions, 147 deletions
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index 3fc1416c..20c49164 100644
--- a/util/nvmutil/lib/file.c
+++ b/util/nvmutil/lib/file.c
@@ -164,14 +164,15 @@ fsync_dir(const char *path)
dirbuf[1] = '\0';
}
- dirfd = fs_resolve(dirbuf, O_RDONLY | O_CLOEXEC | O_NOCTTY
+ dirfd = fs_open(dirbuf,
+ O_RDONLY | O_CLOEXEC | O_NOCTTY
#ifdef O_DIRECTORY
| O_DIRECTORY
#endif
#ifdef O_NOFOLLOW
| O_NOFOLLOW
#endif
- );
+);
if (dirfd < 0)
goto err_fsync_dir;
@@ -404,7 +405,8 @@ new_tmpfile(int *fd, int local,
/* ALWAYS set this right after
* split path, to avoid leaking fd:
*/
- dirfd = fs_resolve(dir, O_RDONLY | O_DIRECTORY);
+
+ dirfd = fs_open(dir, O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
goto err_new_tmpfile;
@@ -426,7 +428,7 @@ new_tmpfile(int *fd, int local,
memcpy(dest + dirlen + 1, suffix,
sizeof(suffix) - 1);
- dirfd = fs_resolve(tmpdir,
+ dirfd = fs_open(tmpdir,
O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
goto err_new_tmpfile;
@@ -614,11 +616,11 @@ same_dir(const char *a, const char *b)
if (rval_scmp == 0)
goto success_same_dir;
- fd_a = fs_resolve(a, O_RDONLY | O_DIRECTORY);
+ fd_a = fs_open(a, O_RDONLY | O_DIRECTORY);
if (fd_a < 0)
goto err_same_dir;
- fd_b = fs_resolve(b, O_RDONLY | O_DIRECTORY);
+ fd_b = fs_open(b, O_RDONLY | O_DIRECTORY);
if (fd_b < 0)
goto err_same_dir;
@@ -694,7 +696,7 @@ world_writeable_and_sticky(
/* mitigate symlink attacks*
*/
- dirfd = fs_resolve(s, O_RDONLY | O_DIRECTORY);
+ dirfd = fs_open(s, O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
goto sticky_hell;
@@ -1854,128 +1856,98 @@ fsync_on_eintr(int fd)
return r;
}
-/* pathless resolution. we
- * walk directories ourselves;
- * will also be used for a portable
- * fallback against openat2 if unavailable
- *
- * only accepts global paths, from / because
- * we don't want relative tmpdirs!
- */
int
-fs_resolve(const char *path, int flags)
+fs_rename_at(int olddirfd, const char *old,
+ int newdirfd, const char *new)
{
-#if defined(PATH_LEN) && \
-(PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
- size_t len;
- int dirfd = -1;
- int nextfd = -1;
-
- const char *p;
- char name[256];
+ if (new == NULL || old == NULL) {
- int saved_errno = errno;
- int close_errno;
- int r;
-
- int is_last;
-
- if (path == NULL || *path != '/') {
- errno = EINVAL;
+ errno = EFAULT;
return -1;
}
- /* block "/" if flags pertain to creation/write.
- don't mess with the user's root!
- */
- if (path[0] == '/' && path[1] == '\0') {
- if (flags & (O_CREAT | O_TRUNC | O_RDWR | O_WRONLY)) {
- errno = EISDIR;
- return -1;
- }
- }
-
- if (slen(path, maxlen, &len) < 0)
- return -1;
+ if (olddirfd < 0 || newdirfd < 0) {
- dirfd = fs_root_fd();
- if (dirfd < 0)
+ errno = EBADF;
return -1;
+ }
- p = path;
-
- for (;;) {
-
- r = fs_next_component(&p, name, sizeof(name));
- if (r < 0)
- goto err;
- if (r == 0)
- break;
-
- is_last = (*p == '\0');
-
- nextfd = fs_open_component(dirfd,
- name, flags, is_last);
- if (nextfd < 0)
- goto err;
+ return renameat(olddirfd, old, newdirfd, new);
+}
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
+int
+fs_rm_rf_at(int dirfd, const char *path)
+{
+ int fd = -1;
+ struct stat st;
- dirfd = nextfd;
- nextfd = -1;
+ if (path == NULL) {
+ errno = EFAULT;
+ return -1;
}
- errno = saved_errno;
- return dirfd;
+ fd = fs_resolve_at(dirfd, path, O_RDONLY | O_DIRECTORY);
+ if (fd >= 0) {
+ /* directory */
+ /* iterate entries */
+ /* recurse */
+ /* unlinkat(dirfd, path, AT_REMOVEDIR) */
-err:
- saved_errno = errno;
-
- if (dirfd >= 0)
- (void) close_on_eintr(dirfd);
- if (nextfd >= 0)
- (void) close_on_eintr(nextfd);
+ return 0;
+ }
- errno = saved_errno;
- return -1;
+ /* fallback: file */
+ return unlinkat(dirfd, path, 0);
}
int
-fs_root_fd(void)
+fs_mkdir_p(const char *path, mode_t mode)
{
- /* TODO: consider more flags here (and/or make configurable */
- return open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ struct filesystem *fs;
+
+ if (path == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fs = rootfs();
+ if (fs == NULL)
+ return -1;
+
+ return fs_mkdir_p_at(fs->rootfd, path + 1, mode);
}
/* implementation of: mkdir -p
*/
int
-fs_mkdir_p_at(int dirfd, const char *path, mode_t mode,
- struct stat *st_dir_initial)
+fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
{
- const char *p;
+ const char *p = path;
char name[256];
int nextfd = -1;
+ struct stat st_parent_initial;
+ struct stat st_parent_now;
int saved_errno = errno;
- int close_errno;
int r;
- int is_last;
- struct stat st_dir_now;
- if (dirfd < 0 || path == NULL ||
- st_dir_initial == NULL || *path == '\0') {
+ if (path == NULL) {
+
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (dirfd < 0 || *path == '\0') {
errno = EINVAL;
return -1;
}
- p = path;
+ if (fstat(dirfd, &st_parent_initial) < 0)
+ return -1;
for (;;) {
@@ -1985,88 +1957,112 @@ fs_mkdir_p_at(int dirfd, const char *path, mode_t mode,
if (r == 0)
break;
- is_last = (*p == '\0');
+ /* check parent integrity */
+ if (fstat(dirfd, &st_parent_now) < 0)
+ goto err;
+
+ if (st_parent_now.st_dev != st_parent_initial.st_dev ||
+ st_parent_now.st_ino != st_parent_initial.st_ino) {
+ errno = ESTALE;
+ goto err;
+ }
- /* TODO: consider more flags
- * or make configurable
- */
nextfd = openat2p(dirfd, name,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC, mode);
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (nextfd < 0) {
-
if (errno != ENOENT)
goto err;
if (mkdirat(dirfd, name, mode) < 0)
goto err;
- /*
- * use the file descriptor instead.
- * (danach prüfen wir die Sicherheit des Verzeichnisses)
- */
- if (fstat(dirfd, &st_dir_now) < 0)
- goto err;
- /*
- * mitigate symlink attacks before open
- */
- if (st_dir_now.st_dev != st_dir_initial->st_dev ||
- st_dir_now.st_ino != st_dir_initial->st_ino) {
- errno = ESTALE;
- goto err;
- }
-
-
- /* TODO: consider more flags
- * or make configurable?
- */
nextfd = openat2p(dirfd, name,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC,
- mode);
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (nextfd < 0)
goto err;
}
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
-
+ (void)close_on_eintr(dirfd);
dirfd = nextfd;
nextfd = -1;
+
+ st_parent_initial = st_parent_now;
}
errno = saved_errno;
- return 0;
+ return (0);
err:
- saved_errno = errno;
-
if (dirfd >= 0)
- (void) close_on_eintr(dirfd);
+ (void)close_on_eintr(dirfd);
if (nextfd >= 0)
- (void) close_on_eintr(nextfd);
+ (void)close_on_eintr(nextfd);
errno = saved_errno;
- return -1;
+ return (-1);
}
-/* use in conjunction with fs_resolve. example:
- * int rootfd = fs_resolve("/safe/root", O_RDONLY | O_DIRECTORY);
- * now, you can resolve everything relatively, for instance:
- * fd = fs_resolve_at(rootfd, "file.txt", O_RDONLY);
- * if a user then tries e.g. ../etc/password ??????
- * BLOCKED
- * basically userspace sandboxing, similar to e.g.
- * openbsd unveil
- * freebsd capsicum
- * openat2 RESOLVE_BENEATH
- * ...but in ****userspace****
- * no need for chroot (you should still use one in critical code)
- * cannot escape the relative root that you set.
- * no dependence on CWD
- * probably safe in multi-threaded code (with some care)
+/* secure open, based on
+ * relative path to root
*
- * ps: you should still use unveil. unveil is awesome.
+ * always a fixed fd for /
+ * see: rootfs()
+ */
+int
+fs_open(const char *path, int flags)
+{
+ struct filesystem *fs;
+ const char *rel;
+
+ if (path == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fs = rootfs();
+ if (!fs)
+ return -1;
+
+ rel = path + 1;
+
+ return fs_resolve_at(fs->rootfd, rel, flags);
+}
+
+/* singleton function
+ * that returns a fixed
+ * descriptor of /
+ *
+ * used throughout, for
+ * repeated integrity checks
+ */
+struct filesystem *
+rootfs(void)
+{
+ static struct filesystem global_fs;
+ static int fs_initialised = 0;
+
+ if (!fs_initialised) {
+
+ global_fs.rootfd =
+ open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+
+ if (global_fs.rootfd < 0)
+ return NULL;
+
+ fs_initialised = 1;
+ }
+
+ return &global_fs;
+}
+
+/* filesystem sandboxing.
+ * (in userspace)
*/
int
fs_resolve_at(int dirfd, const char *path, int flags)