- Added tests for sandboxing file io with file_open_at() and FILE_OPEN_RESTRICT

- Fixed bug in io_open_restrict() that closed the dir file descriptor, making it impossible to use it in subsequent calls.
This commit is contained in:
Martin Fouilleul 2023-06-12 18:04:59 +02:00
parent b2d2d2a587
commit b147aed85c
7 changed files with 77 additions and 3 deletions

View File

@ -74,7 +74,7 @@ typedef struct io_req
} io_req; } io_req;
typedef i32 io_error; typedef i32 io_error;
enum { enum _io_error {
IO_OK = 0, IO_OK = 0,
IO_ERR_UNKNOWN, IO_ERR_UNKNOWN,
IO_ERR_OP, // unsupported operation 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(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 file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags);
MP_API void file_close(file_handle file); MP_API void file_close(file_handle file);

View File

@ -25,6 +25,18 @@ file_handle file_open(str8 path, file_open_flags flags)
return(cmp.handle); 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) void file_close(file_handle file)
{ {
io_req req = {.op = IO_OP_CLOSE, io_req req = {.op = IO_OP_CLOSE,

View File

@ -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_push(scratch.arena, &sep, STR8("/"));
str8_list pathElements = str8_split(scratch.arena, path, sep); str8_list pathElements = str8_split(scratch.arena, path, sep);
result.fd = dirFd; result.fd = dup(dirFd);
if(result.fd < 0) if(result.fd < 0)
{ {
result.error = io_convert_errno(errno); result.error = io_convert_errno(errno);

1
test/files/data/jail/escape Symbolic link
View File

@ -0,0 +1 @@
..

View File

@ -0,0 +1 @@
Hello, world!

View File

@ -1 +0,0 @@
regular.txt

View File

@ -0,0 +1 @@
regular

View File

@ -186,6 +186,62 @@ int test_stat_type(mem_arena* arena, str8 dataDir)
return(0); 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) int main(int argc, char** argv)
{ {
mem_arena* arena = mem_scratch(); 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_read(arena, path, test_string)) { return(-1); }
if(test_stat_size(path, test_string.len)) { return(-1); } if(test_stat_size(path, test_string.len)) { return(-1); }
if(test_stat_type(arena, dataDir)) { return(-1); } if(test_stat_type(arena, dataDir)) { return(-1); }
if(test_jail()) { return(-1); }
remove("./test.txt");
log_info("OK\n"); log_info("OK\n");