diff --git a/main.c b/main.c index 98999c3..152d5ee 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,7 @@ +#include + #include "seccomp.h" int main(int argc, char **argv) { @@ -13,7 +15,13 @@ int main(int argc, char **argv) { const char *profile_path = argv[1]; FILE *file = sc_must_read_and_validate_header_from_file(profile_path, &hdr); sc_must_read_filter_from_file(file, hdr.len_filter, &prog_allow); - - + fclose(file); + sc_apply_seccomp_filter(&prog_allow); + + // Cleanup + free(prog_allow.filter); + + fprintf(stderr, "filter loaded okay\n"); + return 0; } diff --git a/seccomp.c b/seccomp.c index 27c5971..e0f7aca 100644 --- a/seccomp.c +++ b/seccomp.c @@ -41,6 +41,31 @@ FILE* sc_must_read_and_validate_header_from_file(const char *profile_path, struc if (num_read < sizeof(struct sc_seccomp_file_header)) { die("short read on seccomp header: %zu", num_read); } + if (hdr->header[0] != 'S' || hdr->header[1] != 'C') { + die("unexpected seccomp header: %x%x", hdr->header[0], hdr->header[1]); + } + if (hdr->version != SC_SECCOMP_VERSION) { + die("unsupported seccomp version: %u (expected %u)", hdr->version, SC_SECCOMP_VERSION); + } + if (hdr->unrestricted > 1) { + die("invalid unrestricted field: %u (must be 0 or 1)", hdr->unrestricted); + } + // Validate reserved2 is all zeros + for (int i = 0; i < sizeof(hdr->reserved2); i++) { + if (hdr->reserved2[i] != 0) { + die("reserved field must be zero at byte %d", i); + } + } + if (hdr->len_filter == 0) { + die("seccomp filter cannot be empty"); + } + if (hdr->len_filter % sizeof(struct sock_filter) != 0) { + die("seccomp filter length must be multiple of %zu, got %u", + sizeof(struct sock_filter), hdr->len_filter); + } + if (hdr->len_filter > MAX_BPF_SIZE) { + die("seccomp filter too large: %u", hdr->len_filter); + } return file; } @@ -53,9 +78,13 @@ void sc_must_read_filter_from_file(FILE *file, uint32_t len_bytes, struct sock_f } size_t num_read = fread(prog->filter, 1, len_bytes, file); if (ferror(file)) { + free(prog->filter); + prog->filter = NULL; die("cannot read filter"); } if (num_read != len_bytes) { + free(prog->filter); + prog->filter = NULL; die("short read for filter %zu != %i", num_read, len_bytes); } } diff --git a/seccomp.h b/seccomp.h index d60ce6b..50a7ef4 100644 --- a/seccomp.h +++ b/seccomp.h @@ -7,13 +7,15 @@ #include +#define SC_SECCOMP_VERSION 1 + struct sc_seccomp_file_header { char header[2]; uint8_t version; - uint8_t unrestricted; + uint8_t unrestricted; // Must be 0 or 1 uint32_t len_filter; - uint8_t reserved2[16]; + uint8_t reserved2[16]; // Reserved for future use, must be zero }; void sc_must_read_filter_from_file(FILE *file, uint32_t len_bytes, struct sock_fprog *prog); diff --git a/unit-tests/unit-tests.c b/unit-tests/unit-tests.c index 571ca01..ac0cbcb 100644 --- a/unit-tests/unit-tests.c +++ b/unit-tests/unit-tests.c @@ -17,6 +17,7 @@ static void test_must_read_and_validate_header_from_file__happy(void) .header[0] = 'S', .header[1] = 'C', .version = 1, + .len_filter = 8, // Valid filter length (1 sock_filter) }; char *profile = NULL; int fd = 0; @@ -27,10 +28,112 @@ static void test_must_read_and_validate_header_from_file__happy(void) g_assert_true(file != NULL); } +static void test_must_read_and_validate_header_from_file__missing_header(void) +{ + struct sc_seccomp_file_header hdr = {}; + + if (g_test_subprocess()) + { + char *profile = NULL; + int fd = 0; + make_seccomp_profile(&hdr, &fd, &profile); + FILE *file = + sc_must_read_and_validate_header_from_file(profile, &hdr); + g_assert_not_reached(); + g_assert_null(file); + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("unexpected seccomp header: 00\n"); +} + +static void test_must_read_and_validate_header_from_file__invalid_version(void) +{ + struct sc_seccomp_file_header hdr = { + .header[0] = 'S', + .header[1] = 'C', + .version = 99, // Invalid version + }; + + if (g_test_subprocess()) + { + char *profile = NULL; + int fd = 0; + make_seccomp_profile(&hdr, &fd, &profile); + FILE *file = + sc_must_read_and_validate_header_from_file(profile, &hdr); + g_assert_not_reached(); + g_assert_null(file); + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*unsupported seccomp version: 99*"); +} + +static void test_must_read_and_validate_header_from_file__filter_too_large(void) +{ + struct sc_seccomp_file_header hdr = { + .header[0] = 'S', + .header[1] = 'C', + .version = 1, + .len_filter = 64 * 1024, // Too large + }; + + if (g_test_subprocess()) + { + char *profile = NULL; + int fd = 0; + make_seccomp_profile(&hdr, &fd, &profile); + FILE *file = + sc_must_read_and_validate_header_from_file(profile, &hdr); + g_assert_not_reached(); + g_assert_null(file); + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*seccomp filter too large*"); +} + +static void test_must_read_and_validate_header_from_file__filter_empty(void) +{ + struct sc_seccomp_file_header hdr = { + .header[0] = 'S', + .header[1] = 'C', + .version = 1, + .len_filter = 0, // Empty + }; + + if (g_test_subprocess()) + { + char *profile = NULL; + int fd = 0; + make_seccomp_profile(&hdr, &fd, &profile); + FILE *file = + sc_must_read_and_validate_header_from_file(profile, &hdr); + g_assert_not_reached(); + g_assert_null(file); + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*seccomp filter cannot be empty*"); +} + static void __attribute__((constructor)) init(void) { g_test_add_func("/seccomp/must_read_and_validate_header_from_file/happy", test_must_read_and_validate_header_from_file__happy); + g_test_add_func("/seccomp/must_read_and_validate_header_from_file/missing_header", + test_must_read_and_validate_header_from_file__missing_header); + g_test_add_func("/seccomp/must_read_and_validate_header_from_file/invalid_version", + test_must_read_and_validate_header_from_file__invalid_version); + g_test_add_func("/seccomp/must_read_and_validate_header_from_file/filter_too_large", + test_must_read_and_validate_header_from_file__filter_too_large); + g_test_add_func("/seccomp/must_read_and_validate_header_from_file/filter_empty", + test_must_read_and_validate_header_from_file__filter_empty); } int main(int argc, char **argv)