From 228c7b653f6aec861c50cb8d29746324b94f6846 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Sun, 2 Oct 2022 14:09:25 +0800 Subject: [PATCH] feat: tiny sandbox --- .clang-format | 14 +++++ .gitignore | 4 ++ .idea/.gitignore | 8 +++ .idea/.name | 1 + .idea/codeStyles/Project.xml | 7 +++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ .idea/misc.xml | 10 ++++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 +++ .idea/woj-sandbox.iml | 2 + CMakeLists.txt | 22 ++++++++ build_libseccomp.sh | 11 ++++ err.h | 12 +++++ library.c | 9 ++++ rules/lang.h | 6 +++ rules/lang_c.c | 22 ++++++++ rules/rules.c | 35 +++++++++++++ rules/rules.h | 18 +++++++ sandbox.c | 78 ++++++++++++++++++++++++++++ sandbox.h | 16 ++++++ test.c | 9 ++++ utils/list.h | 21 ++++++++ utils/log.h | 24 +++++++++ version_script.txt | 3 ++ 24 files changed, 351 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/woj-sandbox.iml create mode 100644 CMakeLists.txt create mode 100755 build_libseccomp.sh create mode 100644 err.h create mode 100644 library.c create mode 100644 rules/lang.h create mode 100644 rules/lang_c.c create mode 100644 rules/rules.c create mode 100644 rules/rules.h create mode 100644 sandbox.c create mode 100644 sandbox.h create mode 100644 test.c create mode 100644 utils/list.h create mode 100644 utils/log.h create mode 100644 version_script.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2f65933 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +--- +BasedOnStyle: LLVM +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: AcrossEmptyLinesAndComments +AlignConsecutiveDeclarations: Consecutive +AlignEscapedNewlines: Left +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +ColumnLimit: 80 +IndentWidth: 4 +UseTab: Never +SeparateDefinitionBlocks: Leave diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b09fd5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/cmake-build-* +/build +/libseccomp +*.out diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..6c61990 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +woj_sandbox \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..f603881 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..42113af --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6cc324c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/woj-sandbox.iml b/.idea/woj-sandbox.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/woj-sandbox.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e6db8e3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.23) +project(woj_sandbox C) + +set(CMAKE_C_STANDARD 23) + +file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/*.c ${PROJECT_SOURCE_DIR}/rules/*.c ${PROJECT_SOURCE_DIR}/utils/*.c) +list(FILTER SRC_FILES EXCLUDE REGEX ".*test\\.c$") + +set(VERSION_SCRIPT ${PROJECT_SOURCE_DIR}/version_script.txt) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libseccomp/include) + +add_library(woj_sandbox SHARED ${SRC_FILES}) +add_executable(woj_sandbox_test ${PROJECT_SOURCE_DIR}/test.c ${SRC_FILES}) + +target_link_libraries(woj_sandbox ${CMAKE_SOURCE_DIR}/libseccomp/src/.libs/libseccomp.a) +target_link_libraries(woj_sandbox_test ${CMAKE_SOURCE_DIR}/libseccomp/src/.libs/libseccomp.a) + +set_target_properties(woj_sandbox PROPERTIES C_VISIBILITY_PRESET hidden) +set_property(TARGET woj_sandbox APPEND_STRING + PROPERTY LINK_FLAGS " -Wl,--version-script=${VERSION_SCRIPT}") +set_target_properties(woj_sandbox PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT}) diff --git a/build_libseccomp.sh b/build_libseccomp.sh new file mode 100755 index 0000000..eed4048 --- /dev/null +++ b/build_libseccomp.sh @@ -0,0 +1,11 @@ +VERSION=v2.5.4 +if [ -d ./libseccomp ]; then exit 0; fi + +set -x + +git clone https://github.com/seccomp/libseccomp.git &>/dev/null +cd libseccomp || exit 1 +git checkout $VERSION &>/dev/null +./autogen.sh &>/dev/null +./configure --enable-shared=no &>/dev/null +make -j &>/dev/null diff --git a/err.h b/err.h new file mode 100644 index 0000000..6e552c8 --- /dev/null +++ b/err.h @@ -0,0 +1,12 @@ +#ifndef WOJ_SANDBOX_ERR_H +#define WOJ_SANDBOX_ERR_H + +// Error Code +#define ERR_SECCOMP_ENV 10 +#define ERR_SECCOMP_INIT 11 +#define ERR_SECCOMP_RESOLVE 12 +#define ERR_SECCOMP_LOAD 13 +#define ERR_UNSETENV 14 +#define ERR_NO_RULE_FOUND 15 + +#endif // WOJ_SANDBOX_ERR_H diff --git a/library.c b/library.c new file mode 100644 index 0000000..4ac3238 --- /dev/null +++ b/library.c @@ -0,0 +1,9 @@ +#include "rules/lang.h" +#include "sandbox.h" +#include "utils/log.h" + +static __attribute__((constructor)) void inject(void) { + LOG_INFO("Setting up..."); + register_lang_c(); + setup_seccomp(); +} diff --git a/rules/lang.h b/rules/lang.h new file mode 100644 index 0000000..c6f6bd7 --- /dev/null +++ b/rules/lang.h @@ -0,0 +1,6 @@ +#ifndef WOJ_SANDBOX_LANG_H +#define WOJ_SANDBOX_LANG_H + +void register_lang_c(void); + +#endif // WOJ_SANDBOX_LANG_H diff --git a/rules/lang_c.c b/rules/lang_c.c new file mode 100644 index 0000000..a2a3942 --- /dev/null +++ b/rules/lang_c.c @@ -0,0 +1,22 @@ +#include "../sandbox.h" +#include "lang.h" +#include "rules.h" + +#include + +void setup_lang_c(scmp_filter_ctx ctx) { + int white[] = {SCMP_SYS(read), SCMP_SYS(write), SCMP_SYS(getpid), + SCMP_SYS(futex), SCMP_SYS(exit_group), SCMP_SYS(newfstatat)}; + int white_len = sizeof(white) / sizeof(white[0]); + + for (int i = 0; i < white_len; i++) { + add_syscall_nr(white[i], ctx, SCMP_ACT_ALLOW); + } +} + +struct rule lang_c_rule = { + .name = "lang_c", + .setup = setup_lang_c, +}; + +void register_lang_c(void) { register_rule(&lang_c_rule); } diff --git a/rules/rules.c b/rules/rules.c new file mode 100644 index 0000000..bde92c9 --- /dev/null +++ b/rules/rules.c @@ -0,0 +1,35 @@ +#include "rules.h" +#include "../err.h" +#include "../utils/log.h" + +#include +#include + +LIST_HEAD(seccomp_rules); + +void register_rule(struct rule *rule) { list_add(&rule->list, &seccomp_rules); } + +void setup_rule(char *name, scmp_filter_ctx ctx) { + struct list_head *current; + struct rule *rule; + list_for_each(current, &seccomp_rules) { + rule = list_entry(current, struct rule, list); + if (strcmp(rule->name, name) == 0) { + rule->setup(ctx); + return; + } + } + LOG_ERR("No rule found for %s", name); + dump_rules(); + exit(ERR_NO_RULE_FOUND); +} + +void dump_rules(void) { + struct list_head *current; + struct rule *rule; + LOG_INFO("Available Rules:"); + list_for_each(current, &seccomp_rules) { + rule = list_entry(current, struct rule, list); + LOG_INFO("> %s", rule->name); + } +} diff --git a/rules/rules.h b/rules/rules.h new file mode 100644 index 0000000..219b6c4 --- /dev/null +++ b/rules/rules.h @@ -0,0 +1,18 @@ +#ifndef WOJ_SANDBOX_RULES_H +#define WOJ_SANDBOX_RULES_H + +#include "../utils/list.h" + +#include "seccomp.h" + +struct rule { + char *name; + void (*setup)(scmp_filter_ctx); + struct list_head list; +}; + +void register_rule(struct rule *rule); +void setup_rule(char *name, scmp_filter_ctx ctx); +void dump_rules(void); + +#endif // WOJ_SANDBOX_RULES_H diff --git a/sandbox.c b/sandbox.c new file mode 100644 index 0000000..59acb2f --- /dev/null +++ b/sandbox.c @@ -0,0 +1,78 @@ +#include "sandbox.h" +#include "err.h" +#include "rules/rules.h" +#include "utils/log.h" + +#include +#include +#include + +void add_syscall_nr(int syscall_nr, scmp_filter_ctx ctx, uint32_t action) { + if (seccomp_rule_add_exact(ctx, action, syscall_nr, 0)) { + LOG_ERR("Failed to add syscall %d", syscall_nr); + seccomp_release(ctx); + exit(ERR_SECCOMP_RESOLVE); + } +} + +void add_syscall_name(const char *syscall_name, scmp_filter_ctx ctx, + uint32_t action) { + int syscall_nr = seccomp_syscall_resolve_name(syscall_name); + if (syscall_nr == __NR_SCMP_ERROR) { + LOG_ERR("Failed to resolve syscall %s", syscall_name); + seccomp_release(ctx); + exit(ERR_SECCOMP_RESOLVE); + } + + add_syscall_nr(syscall_nr, ctx, action); +} + +void setup_seccomp(void) { + LOG_INFO("Setting seccomp rules..."); + + char *template = getenv(SANDBOX_TEMPLATE); + char *action = getenv(SANDBOX_ACTION); + + bool kill = true; + if (action && strncmp(action, "log", sizeof("log")) == 0) { + kill = false; + } + + if (kill && !template) { + LOG_ERR("Environment variable %s required", SANDBOX_TEMPLATE); + dump_rules(); + exit(ERR_SECCOMP_ENV); + } + + scmp_filter_ctx ctx = + seccomp_init(kill ? SCMP_ACT_KILL_PROCESS : SCMP_ACT_LOG); + if (!ctx) { + LOG_ERR("Failed to init seccomp context"); + exit(ERR_SECCOMP_INIT); + } + + setup_rule(template, ctx); + +#define UNSETENV(name) \ + do { \ + if (unsetenv(name)) { \ + LOG_ERR("Failed to unset environment variable %s", name); \ + seccomp_release(ctx); \ + exit(ERR_UNSETENV); \ + } \ + } while (0) + + UNSETENV(SANDBOX_TEMPLATE); + UNSETENV(SANDBOX_ACTION); + +#undef UNSETENV + + if (seccomp_load(ctx)) { + LOG_ERR("Failed to load seccomp context"); + seccomp_release(ctx); + exit(ERR_SECCOMP_LOAD); + } + seccomp_release(ctx); + + LOG_INFO("Preload Done"); +} diff --git a/sandbox.h b/sandbox.h new file mode 100644 index 0000000..5d5e3da --- /dev/null +++ b/sandbox.h @@ -0,0 +1,16 @@ +#ifndef WOJ_SANDBOX_SANDBOX_H +#define WOJ_SANDBOX_SANDBOX_H + +#include +#include + +// Configuration Environment Variables +#define SANDBOX_TEMPLATE "SANDBOX_TEMPLATE" +#define SANDBOX_ACTION "SANDBOX_ACTION" + +void setup_seccomp(void); +void add_syscall_name(const char *syscall_name, scmp_filter_ctx ctx, + uint32_t action); +void add_syscall_nr(int syscall_nr, scmp_filter_ctx ctx, uint32_t action); + +#endif // WOJ_SANDBOX_SANDBOX_H diff --git a/test.c b/test.c new file mode 100644 index 0000000..aa839d6 --- /dev/null +++ b/test.c @@ -0,0 +1,9 @@ +#include "utils/log.h" + +int main() { + char buf[128]; + scanf("%s", buf); + printf("Hello %s\n", buf); + + return 0; +} diff --git a/utils/list.h b/utils/list.h new file mode 100644 index 0000000..44c5390 --- /dev/null +++ b/utils/list.h @@ -0,0 +1,21 @@ +#ifndef WOJ_SANDBOX_LIST_H +#define WOJ_SANDBOX_LIST_H + +struct list_head { + struct list_head *next; +}; + +#define LIST_HEAD(name) struct list_head name = {&(name)}; + +static inline void list_add(struct list_head *new, struct list_head *head) { + new->next = head->next; + head->next = new; +} + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) + +#endif // WOJ_SANDBOX_LIST_H diff --git a/utils/log.h b/utils/log.h new file mode 100644 index 0000000..9776c22 --- /dev/null +++ b/utils/log.h @@ -0,0 +1,24 @@ +#ifndef WOJ_SANDBOX_LOG_H +#define WOJ_SANDBOX_LOG_H + +#include +#include + +// Log +#define COLOR_RED "\x1B[1;31m" +#define COLOR_YELLOW "\x1B[1;33m" +#define COLOR_GREEN "\x1B[1;32m" +#define COLOR_STDOUT_RESET "\x1B[0m" + +#define _LOG(color, level, fmt, ...) \ + do { \ + fprintf(stderr, \ + color "[" level "](%d)(%s:%d): " fmt COLOR_STDOUT_RESET "\n", \ + getpid(), __FILE__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +#define LOG_INFO(fmt, ...) _LOG(COLOR_GREEN, "info", fmt, ##__VA_ARGS__) +#define LOG_WARN(fmt, ...) _LOG(COLOR_YELLOW, "warn", fmt, ##__VA_ARGS__) +#define LOG_ERR(fmt, ...) _LOG(COLOR_RED, "err", fmt, ##__VA_ARGS__) + +#endif // WOJ_SANDBOX_LOG_H diff --git a/version_script.txt b/version_script.txt new file mode 100644 index 0000000..3513c92 --- /dev/null +++ b/version_script.txt @@ -0,0 +1,3 @@ +{ + local: seccomp_*; +};