diff --git a/src/platform/platform_io.h b/src/platform/platform_io.h index 0cdcd8d..9531149 100644 --- a/src/platform/platform_io.h +++ b/src/platform/platform_io.h @@ -74,7 +74,7 @@ typedef struct io_req } io_req; typedef i32 io_error; -enum { +enum _io_error { IO_OK = 0, IO_ERR_UNKNOWN, IO_ERR_OP, // unsupported operation @@ -127,6 +127,7 @@ MP_API io_cmp io_wait_single_req(io_req* req); //---------------------------------------------------------------- MP_API file_handle file_open(str8 path, file_open_flags flags); +MP_API file_handle file_open_at(file_handle dir, str8 path, file_open_flags flags); MP_API file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags); MP_API void file_close(file_handle file); diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index 303a9e8..9db1a0c 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -25,6 +25,18 @@ file_handle file_open(str8 path, file_open_flags flags) return(cmp.handle); } +file_handle file_open_at(file_handle dir, str8 path, file_open_flags flags) +{ + io_req req = {.op = IO_OP_OPEN_AT, + .handle = dir, + .size = path.len, + .buffer = path.ptr, + .openFlags = flags}; + + io_cmp cmp = io_wait_single_req(&req); + return(cmp.handle); +} + void file_close(file_handle file) { io_req req = {.op = IO_OP_CLOSE, diff --git a/src/platform/posix_io.c b/src/platform/posix_io.c index a11d62b..53915ef 100644 --- a/src/platform/posix_io.c +++ b/src/platform/posix_io.c @@ -168,7 +168,7 @@ io_open_restrict_result io_open_restrict(int dirFd, str8 path, int flags, mode_t str8_list_push(scratch.arena, &sep, STR8("/")); str8_list pathElements = str8_split(scratch.arena, path, sep); - result.fd = dirFd; + result.fd = dup(dirFd); if(result.fd < 0) { result.error = io_convert_errno(errno); diff --git a/test/files/data/jail/escape b/test/files/data/jail/escape new file mode 120000 index 0000000..a96aa0e --- /dev/null +++ b/test/files/data/jail/escape @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/test/files/data/jail/test.txt b/test/files/data/jail/test.txt new file mode 100644 index 0000000..5dd01c1 --- /dev/null +++ b/test/files/data/jail/test.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file diff --git a/test/files/data/posix_symlink b/test/files/data/posix_symlink deleted file mode 100644 index 397e107..0000000 --- a/test/files/data/posix_symlink +++ /dev/null @@ -1 +0,0 @@ -regular.txt \ No newline at end of file diff --git a/test/files/data/posix_symlink b/test/files/data/posix_symlink new file mode 120000 index 0000000..1d89e5a --- /dev/null +++ b/test/files/data/posix_symlink @@ -0,0 +1 @@ +regular \ No newline at end of file diff --git a/test/files/main.c b/test/files/main.c index 44c367f..3d940f8 100644 --- a/test/files/main.c +++ b/test/files/main.c @@ -186,6 +186,62 @@ int test_stat_type(mem_arena* arena, str8 dataDir) return(0); } +int test_jail() +{ + log_info("test jail\n"); + + file_handle jail = file_open(STR8("./data/jail"), FILE_OPEN_READ); + if(file_last_error(jail)) + { + log_error("Can't open jail directory\n"); + return(-1); + } + + // Check legitimates open + file_handle f = file_open_at(jail, STR8("/test.txt"), FILE_OPEN_READ | FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_OK) + { + log_error("Can't open jail/test.txt\n"); + return(-1); + } + file_close(f); + + f = file_open_at(jail, STR8("/dir1/../test.txt"), FILE_OPEN_READ | FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_OK) + { + log_error("Can't open jail/dir1/../test.txt\n"); + return(-1); + } + file_close(f); + + // Check escapes + f = file_open_at(jail, STR8(".."), FILE_OPEN_READ | FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_WALKOUT) + { + log_error("Escaped jail with relative path ..\n"); + return(-1); + } + file_close(f); + + f = file_open_at(jail, STR8(".."), FILE_OPEN_READ | FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_WALKOUT) + { + log_error("Escaped jail with relative path dir1/../..\n"); + return(-1); + } + file_close(f); + + f = file_open_at(jail, STR8("/escape"), FILE_OPEN_READ | FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_WALKOUT) + { + log_error("Escaped jail with symlink\n"); + return(-1); + } + file_close(f); + + return(0); +} + int main(int argc, char** argv) { mem_arena* arena = mem_scratch(); @@ -199,6 +255,9 @@ int main(int argc, char** argv) if(test_read(arena, path, test_string)) { return(-1); } if(test_stat_size(path, test_string.len)) { return(-1); } if(test_stat_type(arena, dataDir)) { return(-1); } + if(test_jail()) { return(-1); } + + remove("./test.txt"); log_info("OK\n");