mount_helper: open vault with O_NOFOLLOW (P1 #33a)
ober
b2e2fc7e6ee7113fb4722918fb4357bbce0930e4
--- a/src/mount_helper.c +++ b/src/mount_helper.c @@ -649,7 +649,7 @@ int jerboa_fuse_blockstore_open_bv(const unsigned char *path, int path_len) { char *path_c = copy_cstring(path, path_len, JERBOA_FUSE_MAX_CSTRING, 0); int fd; if (!path_c) return -1; - fd = open(path_c, O_RDWR | O_CLOEXEC); + fd = open(path_c, O_RDWR | O_NOFOLLOW | O_CLOEXEC); free(path_c); return fd; } --- a/tests/test_mount_helper.c +++ b/tests/test_mount_helper.c @@ -12,6 +12,7 @@ #include <unistd.h> int jerboa_fuse_blockstore_create_bv(const unsigned char *path, int path_len); +int jerboa_fuse_blockstore_open_bv(const unsigned char *path, int path_len); int jerboa_fuse_gcm_encrypt(const unsigned char *key, int key_len, const unsigned char *plaintext, int plaintext_len, unsigned char *out, int out_len); @@ -98,6 +99,48 @@ static void test_exclusive_nofollow_create(void) { (void)rmdir(tmp); } +static void test_nofollow_open(void) { + char tmp[] = "/tmp/jerboa-fuse-open-XXXXXX"; + char target[PATH_MAX]; + char linkpath[PATH_MAX]; + char buf[8] = {0}; + int fd; + + check(mkdtemp(tmp) != NULL, "mkdtemp succeeds"); + if (failures) return; + (void)snprintf(target, sizeof(target), "%s/target", tmp); + (void)snprintf(linkpath, sizeof(linkpath), "%s/vault", tmp); + + fd = open(target, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); + check(fd >= 0, "sentinel target create succeeds"); + if (fd >= 0) { + check(write_exact(fd, "sentinel", 8) == 0, + "sentinel target write succeeds"); + (void)close(fd); + } + check(symlink(target, linkpath) == 0, "vault symlink create succeeds"); + + errno = 0; + fd = jerboa_fuse_blockstore_open_bv((const unsigned char *)linkpath, + (int)strlen(linkpath)); + check(fd < 0, "blockstore open rejects a symlink path"); + check(errno == ELOOP, "blockstore open of a symlink fails with ELOOP"); + if (fd >= 0) (void)close(fd); + check(read_exact(target, buf, sizeof(buf)) == 0 && + memcmp(buf, "sentinel", sizeof(buf)) == 0, + "rejected symlink open leaves its target unchanged"); + + (void)unlink(linkpath); + errno = 0; + fd = jerboa_fuse_blockstore_open_bv((const unsigned char *)target, + (int)strlen(target)); + check(fd >= 0, "blockstore open succeeds on a regular file"); + if (fd >= 0) (void)close(fd); + + (void)unlink(target); + (void)rmdir(tmp); +} + static void test_bad_gcm_tag_zeros_plaintext(void) { unsigned char key[32] = {0}; unsigned char plaintext[32]; @@ -154,6 +197,7 @@ static void test_unmount_launcher_fails_safely(void) { int main(void) { test_exclusive_nofollow_create(); + test_nofollow_open(); test_bad_gcm_tag_zeros_plaintext(); test_unmount_launcher_fails_safely(); test_secmem_lock_failure_is_fatal();