|
|
|
@ -9,10 +9,15 @@
|
|
|
|
|
#include<stdio.h>
|
|
|
|
|
#include"milepost.h"
|
|
|
|
|
|
|
|
|
|
int test_write(mem_arena* arena, str8 path, str8 test_string)
|
|
|
|
|
int test_write()
|
|
|
|
|
{
|
|
|
|
|
log_info("writing\n");
|
|
|
|
|
|
|
|
|
|
mem_arena* arena = mem_scratch();
|
|
|
|
|
|
|
|
|
|
str8 path = STR8("./data/write_test.txt");
|
|
|
|
|
str8 test_string = STR8("Hello from write_test.txt");
|
|
|
|
|
|
|
|
|
|
file_handle f = file_open(path, FILE_ACCESS_WRITE, FILE_OPEN_CREATE|FILE_OPEN_TRUNCATE);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
@ -48,10 +53,31 @@ int test_write(mem_arena* arena, str8 path, str8 test_string)
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_read(mem_arena* arena, str8 path, str8 test_string)
|
|
|
|
|
int check_string(file_handle f, str8 test_string)
|
|
|
|
|
{
|
|
|
|
|
char buffer[256];
|
|
|
|
|
i64 n = file_read(f, 256, buffer);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
|
log_error("Error while reading test string\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(str8_cmp(test_string, str8_from_buffer(n, buffer)))
|
|
|
|
|
{
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_read()
|
|
|
|
|
{
|
|
|
|
|
log_info("reading\n");
|
|
|
|
|
|
|
|
|
|
str8 path = STR8("./data/regular.txt");
|
|
|
|
|
str8 test_string = STR8("Hello from regular.txt");
|
|
|
|
|
|
|
|
|
|
file_handle f = file_open(path, FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
@ -59,28 +85,24 @@ int test_read(mem_arena* arena, str8 path, str8 test_string)
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char buffer[256];
|
|
|
|
|
i64 n = file_read(f, 256, buffer);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
if(check_string(f, test_string))
|
|
|
|
|
{
|
|
|
|
|
log_error("Error while reading %.*s\n", (int)path.len, path.ptr);
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
if(str8_cmp(test_string, str8_from_buffer(n, buffer)))
|
|
|
|
|
{
|
|
|
|
|
log_error("Didn't recover test string\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_stat_size(str8 path, u64 size)
|
|
|
|
|
int test_stat_size()
|
|
|
|
|
{
|
|
|
|
|
log_info("stat size\n");
|
|
|
|
|
|
|
|
|
|
str8 path = STR8("./data/regular.txt");
|
|
|
|
|
str8 test_string = STR8("Hello from regular.txt");
|
|
|
|
|
u64 size = test_string.len;
|
|
|
|
|
|
|
|
|
|
file_handle f = file_open(path, 0, 0);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
@ -106,16 +128,11 @@ int test_stat_size(str8 path, u64 size)
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_stat_type(mem_arena* arena, str8 dataDir)
|
|
|
|
|
int test_stat_type()
|
|
|
|
|
{
|
|
|
|
|
str8 regular = path_append(arena, dataDir, STR8("regular.txt"));
|
|
|
|
|
str8 dir = path_append(arena, dataDir, STR8("directory"));
|
|
|
|
|
|
|
|
|
|
#if PLATFORM_WINDOWS
|
|
|
|
|
str8 link = path_append(arena, dataDir, STR8("win32_symlink"));
|
|
|
|
|
#else
|
|
|
|
|
str8 link = path_append(arena, dataDir, STR8("posix_symlink"));
|
|
|
|
|
#endif
|
|
|
|
|
str8 regular = STR8("./data/regular.txt");
|
|
|
|
|
str8 dir = STR8("./data/directory");
|
|
|
|
|
str8 link = STR8("./data/symlink");
|
|
|
|
|
|
|
|
|
|
log_info("stat type, regular\n");
|
|
|
|
|
|
|
|
|
@ -186,6 +203,120 @@ int test_stat_type(mem_arena* arena, str8 dataDir)
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_symlinks()
|
|
|
|
|
{
|
|
|
|
|
// open symlink target
|
|
|
|
|
log_info("open symlink target\n");
|
|
|
|
|
file_handle f = file_open_at(file_handle_nil(), STR8("./data/symlink"), FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
|
log_error("failed to open ./data/symlink\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if(check_string(f, STR8("Hello from regular.txt")))
|
|
|
|
|
{
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
// open symlink file
|
|
|
|
|
log_info("open symlink file\n");
|
|
|
|
|
f = file_open_at(file_handle_nil(), STR8("./data/symlink"), FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
|
log_error("failed to open ./data/symlink\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_status status = file_get_status(f);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
|
log_error("Error while retrieving file status\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if(status.type != MP_FILE_SYMLINK)
|
|
|
|
|
{
|
|
|
|
|
log_error("file type doesn't match\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
|
int n = file_read(f, 512, buffer);
|
|
|
|
|
|
|
|
|
|
if(n || (file_last_error(f) == IO_OK))
|
|
|
|
|
{
|
|
|
|
|
log_error("file read should fail on symlinks\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_args()
|
|
|
|
|
{
|
|
|
|
|
//NOTE: nil handle
|
|
|
|
|
log_info("check open_at with nil handle\n");
|
|
|
|
|
file_handle f = file_open_at(file_handle_nil(), STR8("./data/regular.txt"), FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f))
|
|
|
|
|
{
|
|
|
|
|
log_error("file_open_at() with nil handle failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if(check_string(f, STR8("Hello from regular.txt")))
|
|
|
|
|
{
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: invalid handle
|
|
|
|
|
log_info("check open_at with nil handle\n");
|
|
|
|
|
file_handle wrongHandle = {.h = 123456789 };
|
|
|
|
|
|
|
|
|
|
f = file_open_at(wrongHandle, STR8("./data/regular.txt"), FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
log_error("file_open_at() with non-nil invalid handle should return IO_ERR_HANDLE\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: nil/wrong handle and FILE_OPEN_RESTRICT
|
|
|
|
|
log_info("check open_at with nil handle and FILE_OPEN_RESTRICT\n");
|
|
|
|
|
|
|
|
|
|
f = file_open_at(file_handle_nil(), STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
log_error("file_open_at() with nil handle and FILE_OPEN_RESTRICT should return IO_ERR_HANDLE\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
f = file_open_at(wrongHandle, STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
log_error("file_open_at() with invalid handle and FILE_OPEN_RESTRICT should return IO_ERR_HANDLE\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: empty path
|
|
|
|
|
log_info("check empty path\n");
|
|
|
|
|
|
|
|
|
|
f = file_open_at(file_handle_nil(), STR8(""), FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_ARG)
|
|
|
|
|
{
|
|
|
|
|
log_error("empty path should return IO_ERR_ARG\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_jail()
|
|
|
|
|
{
|
|
|
|
|
log_info("test jail\n");
|
|
|
|
@ -197,8 +328,22 @@ int test_jail()
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check escapes
|
|
|
|
|
file_handle f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
//NOTE: Check escapes
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
log_info("check potential escapes\n");
|
|
|
|
|
|
|
|
|
|
//NOTE: escape with absolute path
|
|
|
|
|
file_handle f = file_open_at(jail, STR8("/tmp"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_NO_ENTRY)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with absolute path /tmp\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: escape with ..
|
|
|
|
|
f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_WALKOUT)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with relative path ..\n");
|
|
|
|
@ -206,47 +351,134 @@ int test_jail()
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
//NOTE: escape with dir/../..
|
|
|
|
|
f = file_open_at(jail, STR8("dir/../.."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_WALKOUT)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with relative path dir1/../..\n");
|
|
|
|
|
log_error("Escaped jail with relative path dir/../..\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
f = file_open_at(jail, STR8("/escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
//NOTE: escape with symlink to parent
|
|
|
|
|
f = file_open_at(jail, STR8("/dir_escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_WALKOUT)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with symlink\n");
|
|
|
|
|
log_error("Escaped jail with symlink to parent\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
// Check legitimates open
|
|
|
|
|
//NOTE: escape to file with symlink to parent
|
|
|
|
|
f = file_open_at(jail, STR8("/dir_escape/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_WALKOUT)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail to regular.txt with symlink to parent\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: escape with symlink to file
|
|
|
|
|
f = file_open_at(jail, STR8("/file_escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_WALKOUT)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with symlink to file regular.txt\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: escape with bad root handle
|
|
|
|
|
file_handle wrong_handle = {0};
|
|
|
|
|
f = file_open_at(wrong_handle, STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) == IO_OK)
|
|
|
|
|
{
|
|
|
|
|
log_error("Escaped jail with nil root handle\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if(file_last_error(f) != IO_ERR_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
log_error("FILE_OPEN_RESTRICT with invalid root handle should return IO_ERR_HANDLE\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
//NOTE: empty path
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
log_info("check empty path\n");
|
|
|
|
|
|
|
|
|
|
f = file_open_at(jail, STR8(""), FILE_ACCESS_READ, 0);
|
|
|
|
|
if(file_last_error(f) != IO_ERR_ARG)
|
|
|
|
|
{
|
|
|
|
|
log_error("empty path should return IO_ERR_ARG\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
//NOTE: Check legitimates open
|
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
log_info("check legitimates open\n");
|
|
|
|
|
|
|
|
|
|
//NOTE: regular file jail/test.txt
|
|
|
|
|
f = file_open_at(jail, STR8("/test.txt"), FILE_ACCESS_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_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_OK)
|
|
|
|
|
if(check_string(f, STR8("Hello from jail/test.txt")))
|
|
|
|
|
{
|
|
|
|
|
log_error("Can't open jail/dir1/../test.txt\n");
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: valid file traversal to jail/test.txt
|
|
|
|
|
f = file_open_at(jail, STR8("/dir/../test.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_OK)
|
|
|
|
|
{
|
|
|
|
|
log_error("Can't open jail/dir/../test.txt\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if(check_string(f, STR8("Hello from jail/test.txt")))
|
|
|
|
|
{
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
//NOTE: re-open root directory
|
|
|
|
|
f = file_open_at(jail, STR8("."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT);
|
|
|
|
|
if(file_last_error(f) != IO_OK)
|
|
|
|
|
{
|
|
|
|
|
log_error("Can't open jail/.\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
//NOTE: access regular file test.txt inside reopened root
|
|
|
|
|
file_handle f2 = file_open_at(f, STR8("test.txt"), FILE_ACCESS_READ, 0);
|
|
|
|
|
|
|
|
|
|
if(check_string(f2, STR8("Hello from jail/test.txt")))
|
|
|
|
|
{
|
|
|
|
|
log_error("Check string failed\n");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
file_close(f2);
|
|
|
|
|
}
|
|
|
|
|
file_close(f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int test_rights(mem_arena* arena, str8 dirPath)
|
|
|
|
|
int test_rights()
|
|
|
|
|
{
|
|
|
|
|
log_info("test rights\n");
|
|
|
|
|
|
|
|
|
|
str8 dirPath = STR8("./data");
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
|
// base dir with no access
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
@ -395,19 +627,16 @@ int main(int argc, char** argv)
|
|
|
|
|
|
|
|
|
|
mem_arena* arena = mem_scratch();
|
|
|
|
|
|
|
|
|
|
str8 dataDir = STR8("./data");
|
|
|
|
|
str8 path = STR8("./test.txt");
|
|
|
|
|
|
|
|
|
|
str8 test_string = STR8("Hello, world!");
|
|
|
|
|
|
|
|
|
|
if(test_write(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_type(arena, dataDir)) { return(-1); }
|
|
|
|
|
if(test_rights(arena, dataDir)) { return(-1); }
|
|
|
|
|
if(test_write()) { return(-1); }
|
|
|
|
|
if(test_read()) { return(-1); }
|
|
|
|
|
if(test_stat_size()) { return(-1); }
|
|
|
|
|
if(test_stat_type()) { return(-1); }
|
|
|
|
|
if(test_args()) { return(-1); }
|
|
|
|
|
if(test_symlinks()) { return(-1); }
|
|
|
|
|
if(test_rights()) { return(-1); }
|
|
|
|
|
if(test_jail()) { return(-1); }
|
|
|
|
|
|
|
|
|
|
remove("./test.txt");
|
|
|
|
|
remove("./data/write_test.txt");
|
|
|
|
|
|
|
|
|
|
log_info("OK\n");
|
|
|
|
|
|
|
|
|
|