USC安全系统

This commit is contained in:
2026-04-19 20:21:12 +08:00
parent c21c5b327e
commit 1a3510d8d9
4 changed files with 639 additions and 3 deletions

View File

@@ -181,6 +181,19 @@ cl_set_bool_cache(CLEONOS_CLKS_ENABLE_TTY_READY_LOG ON "Print TTY ready logs")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG ON "Print idle loop debug log")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_PROCFS ON "Enable virtual /proc procfs syscall layer")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG ON "Enable EXEC info logs on serial output")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC ON "Enable UserSafeController dangerous syscall confirmations")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR ON "USC intercept policy: FS_MKDIR")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE ON "USC intercept policy: FS_WRITE")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND ON "USC intercept policy: FS_APPEND")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE ON "USC intercept policy: FS_REMOVE")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH ON "USC intercept policy: EXEC_PATH")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV ON "USC intercept policy: EXEC_PATHV")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO ON "USC intercept policy: EXEC_PATHV_IO")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH ON "USC intercept policy: SPAWN_PATH")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV ON "USC intercept policy: SPAWN_PATHV")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL ON "USC intercept policy: PROC_KILL")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN ON "USC intercept policy: SHUTDOWN")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USC_SC_RESTART ON "USC intercept policy: RESTART")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY ON "Enable ALT+F1..F4 TTY switch hotkey")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS ON "Enable Ctrl+A/C/V keyboard shortcuts")
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY ON "Enable Ctrl+Alt+C force-stop hotkey")
@@ -214,6 +227,19 @@ cl_bool_to_int(CLEONOS_CLKS_ENABLE_TTY_READY_LOG CLKS_CFG_TTY_READY_LOG_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG CLKS_CFG_IDLE_DEBUG_LOG_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_PROCFS CLKS_CFG_PROCFS_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG CLKS_CFG_EXEC_SERIAL_LOG_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC CLKS_CFG_USC_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR CLKS_CFG_USC_SC_FS_MKDIR_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE CLKS_CFG_USC_SC_FS_WRITE_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND CLKS_CFG_USC_SC_FS_APPEND_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE CLKS_CFG_USC_SC_FS_REMOVE_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH CLKS_CFG_USC_SC_EXEC_PATH_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV CLKS_CFG_USC_SC_EXEC_PATHV_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO CLKS_CFG_USC_SC_EXEC_PATHV_IO_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH CLKS_CFG_USC_SC_SPAWN_PATH_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV CLKS_CFG_USC_SC_SPAWN_PATHV_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL CLKS_CFG_USC_SC_PROC_KILL_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN CLKS_CFG_USC_SC_SHUTDOWN_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USC_SC_RESTART CLKS_CFG_USC_SC_RESTART_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS CLKS_CFG_KBD_CTRL_SHORTCUTS_INT)
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT)
@@ -261,6 +287,19 @@ set(ARCH_CFLAGS
"-DCLKS_CFG_IDLE_DEBUG_LOG=${CLKS_CFG_IDLE_DEBUG_LOG_INT}"
"-DCLKS_CFG_PROCFS=${CLKS_CFG_PROCFS_INT}"
"-DCLKS_CFG_EXEC_SERIAL_LOG=${CLKS_CFG_EXEC_SERIAL_LOG_INT}"
"-DCLKS_CFG_USC=${CLKS_CFG_USC_INT}"
"-DCLKS_CFG_USC_SC_FS_MKDIR=${CLKS_CFG_USC_SC_FS_MKDIR_INT}"
"-DCLKS_CFG_USC_SC_FS_WRITE=${CLKS_CFG_USC_SC_FS_WRITE_INT}"
"-DCLKS_CFG_USC_SC_FS_APPEND=${CLKS_CFG_USC_SC_FS_APPEND_INT}"
"-DCLKS_CFG_USC_SC_FS_REMOVE=${CLKS_CFG_USC_SC_FS_REMOVE_INT}"
"-DCLKS_CFG_USC_SC_EXEC_PATH=${CLKS_CFG_USC_SC_EXEC_PATH_INT}"
"-DCLKS_CFG_USC_SC_EXEC_PATHV=${CLKS_CFG_USC_SC_EXEC_PATHV_INT}"
"-DCLKS_CFG_USC_SC_EXEC_PATHV_IO=${CLKS_CFG_USC_SC_EXEC_PATHV_IO_INT}"
"-DCLKS_CFG_USC_SC_SPAWN_PATH=${CLKS_CFG_USC_SC_SPAWN_PATH_INT}"
"-DCLKS_CFG_USC_SC_SPAWN_PATHV=${CLKS_CFG_USC_SC_SPAWN_PATHV_INT}"
"-DCLKS_CFG_USC_SC_PROC_KILL=${CLKS_CFG_USC_SC_PROC_KILL_INT}"
"-DCLKS_CFG_USC_SC_SHUTDOWN=${CLKS_CFG_USC_SC_SHUTDOWN_INT}"
"-DCLKS_CFG_USC_SC_RESTART=${CLKS_CFG_USC_SC_RESTART_INT}"
"-DCLKS_CFG_KBD_TTY_SWITCH_HOTKEY=${CLKS_CFG_KBD_TTY_SWITCH_HOTKEY_INT}"
"-DCLKS_CFG_KBD_CTRL_SHORTCUTS=${CLKS_CFG_KBD_CTRL_SHORTCUTS_INT}"
"-DCLKS_CFG_KBD_FORCE_STOP_HOTKEY=${CLKS_CFG_KBD_FORCE_STOP_HOTKEY_INT}"
@@ -344,6 +383,15 @@ list(REMOVE_DUPLICATES ASM_SOURCES)
list(SORT C_SOURCES)
list(SORT ASM_SOURCES)
set(CLKS_BOOT_LIMINE_SOURCE "clks/kernel/boot/limine/limine_requests.c")
if(EXISTS "${CMAKE_SOURCE_DIR}/${CLKS_BOOT_LIMINE_SOURCE}")
list(APPEND C_SOURCES "${CLKS_BOOT_LIMINE_SOURCE}")
list(REMOVE_DUPLICATES C_SOURCES)
list(SORT C_SOURCES)
else()
cl_log_error("missing required boot source: ${CLKS_BOOT_LIMINE_SOURCE}")
endif()
file(GLOB_RECURSE KERNEL_INC_SOURCES_ABS CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/clks/**/*.inc"
)

View File

@@ -34,11 +34,64 @@
#define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL
#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_EXEC_PATHV_IO
#define CLKS_SYSCALL_STATS_RING_SIZE 256U
#define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U
#ifndef CLKS_CFG_PROCFS
#define CLKS_CFG_PROCFS 1
#endif
#ifndef CLKS_CFG_USC
#define CLKS_CFG_USC 1
#endif
#ifndef CLKS_CFG_USC_SC_FS_MKDIR
#define CLKS_CFG_USC_SC_FS_MKDIR 1
#endif
#ifndef CLKS_CFG_USC_SC_FS_WRITE
#define CLKS_CFG_USC_SC_FS_WRITE 1
#endif
#ifndef CLKS_CFG_USC_SC_FS_APPEND
#define CLKS_CFG_USC_SC_FS_APPEND 1
#endif
#ifndef CLKS_CFG_USC_SC_FS_REMOVE
#define CLKS_CFG_USC_SC_FS_REMOVE 1
#endif
#ifndef CLKS_CFG_USC_SC_EXEC_PATH
#define CLKS_CFG_USC_SC_EXEC_PATH 1
#endif
#ifndef CLKS_CFG_USC_SC_EXEC_PATHV
#define CLKS_CFG_USC_SC_EXEC_PATHV 1
#endif
#ifndef CLKS_CFG_USC_SC_EXEC_PATHV_IO
#define CLKS_CFG_USC_SC_EXEC_PATHV_IO 1
#endif
#ifndef CLKS_CFG_USC_SC_SPAWN_PATH
#define CLKS_CFG_USC_SC_SPAWN_PATH 1
#endif
#ifndef CLKS_CFG_USC_SC_SPAWN_PATHV
#define CLKS_CFG_USC_SC_SPAWN_PATHV 1
#endif
#ifndef CLKS_CFG_USC_SC_PROC_KILL
#define CLKS_CFG_USC_SC_PROC_KILL 1
#endif
#ifndef CLKS_CFG_USC_SC_SHUTDOWN
#define CLKS_CFG_USC_SC_SHUTDOWN 1
#endif
#ifndef CLKS_CFG_USC_SC_RESTART
#define CLKS_CFG_USC_SC_RESTART 1
#endif
struct clks_syscall_frame {
u64 rax;
u64 rbx;
@@ -92,6 +145,10 @@ static u64 clks_syscall_stats_recent_id_count[CLKS_SYSCALL_STATS_MAX_ID + 1ULL];
static u16 clks_syscall_stats_recent_ring[CLKS_SYSCALL_STATS_RING_SIZE];
static u32 clks_syscall_stats_recent_head = 0U;
static u32 clks_syscall_stats_recent_size = 0U;
#if CLKS_CFG_USC != 0
static clks_bool clks_syscall_usc_allowed_used[CLKS_SYSCALL_USC_MAX_ALLOWED_APPS];
static char clks_syscall_usc_allowed_path[CLKS_SYSCALL_USC_MAX_ALLOWED_APPS][CLKS_EXEC_PROC_PATH_MAX];
#endif
#if defined(CLKS_ARCH_X86_64)
static inline void clks_syscall_outb(u16 port, u8 value) {
@@ -1641,6 +1698,264 @@ static void clks_syscall_serial_write_hex64(u64 value) {
}
}
#if CLKS_CFG_USC != 0
static void clks_syscall_usc_sleep_until_input(void) {
#if defined(CLKS_ARCH_X86_64)
u64 flags = 0ULL;
__asm__ volatile("pushfq; popq %0" : "=r"(flags) : : "memory");
if ((flags & (1ULL << 9)) != 0ULL) {
__asm__ volatile("hlt" : : : "memory");
} else {
__asm__ volatile("sti; hlt; cli" : : : "memory");
}
#elif defined(CLKS_ARCH_AARCH64)
clks_cpu_pause();
#endif
}
static const char *clks_syscall_usc_syscall_name(u64 id) {
switch (id) {
case CLKS_SYSCALL_FS_MKDIR:
return "FS_MKDIR";
case CLKS_SYSCALL_FS_WRITE:
return "FS_WRITE";
case CLKS_SYSCALL_FS_APPEND:
return "FS_APPEND";
case CLKS_SYSCALL_FS_REMOVE:
return "FS_REMOVE";
case CLKS_SYSCALL_EXEC_PATH:
return "EXEC_PATH";
case CLKS_SYSCALL_EXEC_PATHV:
return "EXEC_PATHV";
case CLKS_SYSCALL_EXEC_PATHV_IO:
return "EXEC_PATHV_IO";
case CLKS_SYSCALL_SPAWN_PATH:
return "SPAWN_PATH";
case CLKS_SYSCALL_SPAWN_PATHV:
return "SPAWN_PATHV";
case CLKS_SYSCALL_PROC_KILL:
return "PROC_KILL";
case CLKS_SYSCALL_SHUTDOWN:
return "SHUTDOWN";
case CLKS_SYSCALL_RESTART:
return "RESTART";
default:
return "UNKNOWN";
}
}
static clks_bool clks_syscall_usc_is_dangerous(u64 id) {
switch (id) {
case CLKS_SYSCALL_FS_MKDIR:
return (CLKS_CFG_USC_SC_FS_MKDIR != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_FS_WRITE:
return (CLKS_CFG_USC_SC_FS_WRITE != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_FS_APPEND:
return (CLKS_CFG_USC_SC_FS_APPEND != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_FS_REMOVE:
return (CLKS_CFG_USC_SC_FS_REMOVE != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_EXEC_PATH:
return (CLKS_CFG_USC_SC_EXEC_PATH != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_EXEC_PATHV:
return (CLKS_CFG_USC_SC_EXEC_PATHV != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_EXEC_PATHV_IO:
return (CLKS_CFG_USC_SC_EXEC_PATHV_IO != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_SPAWN_PATH:
return (CLKS_CFG_USC_SC_SPAWN_PATH != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_SPAWN_PATHV:
return (CLKS_CFG_USC_SC_SPAWN_PATHV != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_PROC_KILL:
return (CLKS_CFG_USC_SC_PROC_KILL != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_SHUTDOWN:
return (CLKS_CFG_USC_SC_SHUTDOWN != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_RESTART:
return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE;
default:
return CLKS_FALSE;
}
}
static void clks_syscall_usc_copy_path(char *dst, usize dst_size, const char *src) {
usize i = 0U;
if (dst == CLKS_NULL || dst_size == 0U) {
return;
}
if (src == CLKS_NULL) {
dst[0] = '\0';
return;
}
while (src[i] != '\0' && i + 1U < dst_size) {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
static clks_bool clks_syscall_usc_current_app_path(char *out_path, usize out_size) {
u64 pid;
struct clks_exec_proc_snapshot snap;
if (out_path == CLKS_NULL || out_size == 0U) {
return CLKS_FALSE;
}
out_path[0] = '\0';
pid = clks_exec_current_pid();
if (pid == 0ULL) {
return CLKS_FALSE;
}
if (clks_exec_proc_snapshot(pid, &snap) == CLKS_FALSE || snap.path[0] == '\0') {
return CLKS_FALSE;
}
clks_syscall_usc_copy_path(out_path, out_size, snap.path);
return CLKS_TRUE;
}
static i32 clks_syscall_usc_find_allowed_path(const char *path) {
u32 i;
if (path == CLKS_NULL || path[0] == '\0') {
return -1;
}
for (i = 0U; i < CLKS_SYSCALL_USC_MAX_ALLOWED_APPS; i++) {
if (clks_syscall_usc_allowed_used[i] == CLKS_TRUE &&
clks_strcmp(clks_syscall_usc_allowed_path[i], path) == 0) {
return (i32)i;
}
}
return -1;
}
static void clks_syscall_usc_remember_path(const char *path) {
u32 i;
if (path == CLKS_NULL || path[0] == '\0') {
return;
}
if (clks_syscall_usc_find_allowed_path(path) >= 0) {
return;
}
for (i = 0U; i < CLKS_SYSCALL_USC_MAX_ALLOWED_APPS; i++) {
if (clks_syscall_usc_allowed_used[i] == CLKS_FALSE) {
clks_syscall_usc_allowed_used[i] = CLKS_TRUE;
clks_syscall_usc_copy_path(clks_syscall_usc_allowed_path[i], sizeof(clks_syscall_usc_allowed_path[i]), path);
return;
}
}
}
static void clks_syscall_usc_emit_text_line(const char *label, const char *value) {
char message[320];
usize pos = 0U;
message[0] = '\0';
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, label);
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, ": ");
pos = clks_syscall_procfs_append_text(message, sizeof(message), pos, value);
(void)pos;
clks_log(CLKS_LOG_WARN, "USC", message);
}
static void clks_syscall_usc_emit_hex_line(const char *label, u64 value) {
clks_log_hex(CLKS_LOG_WARN, "USC", label, value);
}
static clks_bool clks_syscall_usc_prompt_allow(const char *app_path, u64 id, u64 arg0, u64 arg1, u64 arg2) {
const char *name = clks_syscall_usc_syscall_name(id);
u32 tty_index = clks_exec_current_tty();
#if !defined(CLKS_CFG_KEYBOARD) || (CLKS_CFG_KEYBOARD == 0)
(void)tty_index;
clks_syscall_usc_emit_text_line("BLOCK", "keyboard disabled, cannot prompt");
return CLKS_FALSE;
#else
clks_syscall_usc_emit_text_line("DANGEROUS_SYSCALL", "REQUEST DETECTED");
clks_syscall_usc_emit_text_line("APP", app_path);
clks_syscall_usc_emit_hex_line("SYSCALL_ID", id);
clks_syscall_usc_emit_text_line("SYSCALL_NAME", name);
clks_syscall_usc_emit_hex_line("ARG0", arg0);
clks_syscall_usc_emit_hex_line("ARG1", arg1);
clks_syscall_usc_emit_hex_line("ARG2", arg2);
clks_log(CLKS_LOG_WARN, "USC", "CONFIRM: Allow this app permanently? [y/N]");
clks_tty_write("[WARN][USC] Allow this app permanently? [y/N]: ");
clks_serial_write("[WARN][USC] Allow this app permanently? [y/N]: ");
while (1) {
char ch = '\0';
if (clks_keyboard_pop_char_for_tty(tty_index, &ch) == CLKS_TRUE) {
if (ch == 'y' || ch == 'Y') {
clks_tty_write("y\n");
clks_serial_write("y\n");
return CLKS_TRUE;
}
if (ch == 'n' || ch == 'N' || ch == '\n' || ch == '\r' || ch == 27) {
clks_tty_write("n\n");
clks_serial_write("n\n");
return CLKS_FALSE;
}
continue;
}
clks_syscall_usc_sleep_until_input();
}
#endif
}
#endif
static clks_bool clks_syscall_usc_check(u64 id, u64 arg0, u64 arg1, u64 arg2) {
#if CLKS_CFG_USC == 0
(void)id;
(void)arg0;
(void)arg1;
(void)arg2;
return CLKS_TRUE;
#else
char app_path[CLKS_EXEC_PROC_PATH_MAX];
if (clks_syscall_usc_is_dangerous(id) == CLKS_FALSE) {
return CLKS_TRUE;
}
if (clks_exec_is_running() == CLKS_FALSE || clks_exec_current_path_is_user() == CLKS_FALSE) {
return CLKS_TRUE;
}
if (clks_syscall_usc_current_app_path(app_path, sizeof(app_path)) == CLKS_FALSE) {
clks_syscall_usc_emit_text_line("BLOCK", "cannot resolve current app path");
return CLKS_FALSE;
}
if (clks_syscall_usc_find_allowed_path(app_path) >= 0) {
return CLKS_TRUE;
}
if (clks_syscall_usc_prompt_allow(app_path, id, arg0, arg1, arg2) == CLKS_TRUE) {
clks_syscall_usc_remember_path(app_path);
clks_syscall_usc_emit_text_line("ALLOW", app_path);
return CLKS_TRUE;
}
clks_syscall_usc_emit_text_line("DENY", app_path);
return CLKS_FALSE;
#endif
}
static void clks_syscall_stats_reset(void) {
clks_syscall_stats_total = 0ULL;
clks_memset(clks_syscall_stats_id_count, 0, sizeof(clks_syscall_stats_id_count));
@@ -1754,6 +2069,10 @@ void clks_syscall_init(void) {
clks_syscall_symbols_checked = CLKS_FALSE;
clks_syscall_symbols_data = CLKS_NULL;
clks_syscall_symbols_size = 0ULL;
#if CLKS_CFG_USC != 0
clks_memset(clks_syscall_usc_allowed_used, 0, sizeof(clks_syscall_usc_allowed_used));
clks_memset(clks_syscall_usc_allowed_path, 0, sizeof(clks_syscall_usc_allowed_path));
#endif
clks_syscall_stats_reset();
clks_log(CLKS_LOG_INFO, "SYSCALL", "INT80 FRAMEWORK ONLINE");
}
@@ -1773,6 +2092,10 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
clks_syscall_stats_record(id);
clks_syscall_trace_user_program(id);
if (clks_syscall_usc_check(id, frame->rbx, frame->rcx, frame->rdx) == CLKS_FALSE) {
return (u64)-1;
}
switch (id) {
case CLKS_SYSCALL_LOG_WRITE:
return clks_syscall_log_write(frame->rbx, frame->rcx);

View File

@@ -190,6 +190,123 @@
"type": "tristate",
"default": "y"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC",
"title": "UserSafeController (USC)",
"description": "Prompt before dangerous user syscalls and remember per-app approval for current boot.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_KEYBOARD",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_MKDIR",
"title": "Intercept FS_MKDIR",
"description": "USC prompt for syscall FS_MKDIR.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_WRITE",
"title": "Intercept FS_WRITE",
"description": "USC prompt for syscall FS_WRITE.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_APPEND",
"title": "Intercept FS_APPEND",
"description": "USC prompt for syscall FS_APPEND.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_FS_REMOVE",
"title": "Intercept FS_REMOVE",
"description": "USC prompt for syscall FS_REMOVE.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATH",
"title": "Intercept EXEC_PATH",
"description": "USC prompt for syscall EXEC_PATH.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV",
"title": "Intercept EXEC_PATHV",
"description": "USC prompt for syscall EXEC_PATHV.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_EXEC_PATHV_IO",
"title": "Intercept EXEC_PATHV_IO",
"description": "USC prompt for syscall EXEC_PATHV_IO.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATH",
"title": "Intercept SPAWN_PATH",
"description": "USC prompt for syscall SPAWN_PATH.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SPAWN_PATHV",
"title": "Intercept SPAWN_PATHV",
"description": "USC prompt for syscall SPAWN_PATHV.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_PROC_KILL",
"title": "Intercept PROC_KILL",
"description": "USC prompt for syscall PROC_KILL.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_SHUTDOWN",
"title": "Intercept SHUTDOWN",
"description": "USC prompt for syscall SHUTDOWN.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_USC_SC_RESTART",
"title": "Intercept RESTART",
"description": "USC prompt for syscall RESTART.",
"type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USC",
"group": "USC Syscall Policy"
},
{
"key": "CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY",
"title": "Keyboard TTY Switch Hotkey",

View File

@@ -56,6 +56,7 @@ class OptionItem:
depends_on: str
selects: Tuple[str, ...]
implies: Tuple[str, ...]
group: str
@dataclass
@@ -312,6 +313,7 @@ def load_clks_options() -> List[OptionItem]:
depends_on = str(entry.get("depends_on", entry.get("depends", ""))).strip()
selects = _normalize_key_list(entry.get("select", entry.get("selects", ())))
implies = _normalize_key_list(entry.get("imply", entry.get("implies", ())))
group = str(entry.get("group", entry.get("menu", "General"))).strip() or "General"
if not key:
continue
options.append(
@@ -324,6 +326,7 @@ def load_clks_options() -> List[OptionItem]:
depends_on=depends_on,
selects=selects,
implies=implies,
group=group,
)
)
@@ -378,6 +381,7 @@ def discover_user_apps() -> List[OptionItem]:
depends_on="",
selects=(),
implies=(),
group=section,
)
)
@@ -417,6 +421,27 @@ def _build_index(options: Iterable[OptionItem]) -> Dict[str, OptionItem]:
return {item.key: item for item in options}
def _grouped_options(options: List[OptionItem]) -> List[Tuple[str, List[OptionItem]]]:
groups: Dict[str, List[OptionItem]] = {}
ordered_names: List[str] = []
for item in options:
name = (item.group or "General").strip() or "General"
if name not in groups:
groups[name] = []
ordered_names.append(name)
groups[name].append(item)
out: List[Tuple[str, List[OptionItem]]] = []
for name in ordered_names:
out.append((name, groups[name]))
return out
def _group_enabled_count(group_options: List[OptionItem], ev: EvalResult) -> int:
return sum(1 for item in group_options if ev.effective.get(item.key, item.default) > TRI_N)
def _set_option_if_exists(values: Dict[str, int], option_index: Dict[str, OptionItem], key: str, level: int) -> None:
if key in values:
item = option_index.get(key)
@@ -829,6 +854,47 @@ def section_loop(title: str, section_options: List[OptionItem], all_options: Lis
print("unknown command")
def grouped_section_loop(
title: str,
section_options: List[OptionItem],
all_options: List[OptionItem],
values: Dict[str, int],
) -> None:
groups = _grouped_options(section_options)
if len(groups) <= 1:
section_loop(title, section_options, all_options, values)
return
while True:
ev = evaluate_config(all_options, values)
print()
print(f"== {title} / Groups ==")
print(f" 0. All ({_group_enabled_count(section_options, ev)}/{len(section_options)} enabled)")
for idx, (name, opts) in enumerate(groups, start=1):
print(f"{idx:3d}. {name} ({_group_enabled_count(opts, ev)}/{len(opts)} enabled)")
print("Commands: <number> open, b back")
raw = input(f"{title}/groups> ").strip().lower()
if not raw:
continue
if raw in {"b", "back", "q", "quit"}:
return
if not raw.isdigit():
print("invalid selection")
continue
idx = int(raw)
if idx == 0:
section_loop(title, section_options, all_options, values)
continue
if 1 <= idx <= len(groups):
group_name, group_items = groups[idx - 1]
section_loop(f"{title}/{group_name}", group_items, all_options, values)
continue
print("invalid selection")
def _safe_addnstr(stdscr, y: int, x: int, text: str, attr: int = 0) -> None:
h, w = stdscr.getmaxyx()
if y < 0 or y >= h or x >= w:
@@ -1213,6 +1279,75 @@ def _run_ncurses_section(
continue
def _run_ncurses_grouped_section(
stdscr,
theme: Dict[str, int],
title: str,
section_options: List[OptionItem],
all_options: List[OptionItem],
values: Dict[str, int],
) -> None:
groups = _grouped_options(section_options)
if len(groups) <= 1:
_run_ncurses_section(stdscr, theme, title, section_options, all_options, values)
return
selected = 0
while True:
ev = evaluate_config(all_options, values)
stdscr.erase()
h, w = stdscr.getmaxyx()
items: List[Tuple[str, List[OptionItem]]] = [("All", section_options)] + groups
if h < 12 or w < 56:
_safe_addnstr(stdscr, 0, 0, "Terminal too small for grouped view (need >= 56x12).", theme["status_warn"])
_safe_addnstr(stdscr, 2, 0, "Resize terminal then press any key, or ESC to go back.")
key = stdscr.getch()
if key in (27,):
return
continue
_safe_addnstr(stdscr, 0, 0, f" CLeonOS menuconfig / {title} / Groups ", theme["header"])
_safe_addnstr(stdscr, 1, 0, " Enter: open group ESC: back ", theme["subtitle"])
_draw_box(stdscr, 2, 0, h - 4, w, "CLKS Groups", theme["panel_border"], theme["panel_title"])
if selected < 0:
selected = 0
if selected >= len(items):
selected = len(items) - 1
for i, (name, opts) in enumerate(items):
row = 4 + i
if row >= h - 2:
break
on_count = _group_enabled_count(opts, ev)
line = f"{'>' if i == selected else ' '} {i:02d} {name} ({on_count}/{len(opts)} enabled)"
attr = theme["selected"] if i == selected else theme["value_label"]
_safe_addnstr(stdscr, row, 2, line, attr)
_safe_addnstr(stdscr, h - 1, 0, " Arrows/jk move Enter open ESC back ", theme["help"])
stdscr.refresh()
key = stdscr.getch()
if key in (27, ord("q"), ord("Q"), curses.KEY_LEFT):
return
if key in (curses.KEY_UP, ord("k"), ord("K")):
selected = (selected - 1) % len(items)
continue
if key in (curses.KEY_DOWN, ord("j"), ord("J")):
selected = (selected + 1) % len(items)
continue
if key in (curses.KEY_ENTER, 10, 13):
name, opts = items[selected]
if name == "All":
_run_ncurses_section(stdscr, theme, title, opts, all_options, values)
else:
_run_ncurses_section(stdscr, theme, f"{title}/{name}", opts, all_options, values)
continue
def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, int]) -> bool:
theme = _curses_theme()
all_options = clks_options + user_options
@@ -1288,7 +1423,7 @@ def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List
continue
if key in (curses.KEY_ENTER, 10, 13):
if selected == 0:
_run_ncurses_section(stdscr, theme, "CLKS", clks_options, all_options, values)
_run_ncurses_grouped_section(stdscr, theme, "CLKS", clks_options, all_options, values)
elif selected == 1:
_run_ncurses_section(stdscr, theme, "USER", user_options, all_options, values)
elif selected == 2:
@@ -1594,7 +1729,20 @@ def interactive_menu_gui(clks_options: List[OptionItem], user_options: List[Opti
_set_option_value(values, item, TRI_N)
self.refresh(keep_selection=False)
clks_groups = _grouped_options(clks_options)
if len(clks_groups) <= 1:
clks_panel = _SectionPanel("CLKS Features", clks_options)
else:
clks_panel = QtWidgets.QWidget()
clks_layout = QtWidgets.QVBoxLayout(clks_panel)
clks_layout.setContentsMargins(0, 0, 0, 0)
clks_layout.setSpacing(6)
clks_tabs = QtWidgets.QTabWidget()
clks_layout.addWidget(clks_tabs, 1)
clks_tabs.addTab(_SectionPanel("CLKS Features / All", clks_options), "All")
for group_name, group_items in clks_groups:
clks_tabs.addTab(_SectionPanel(f"CLKS Features / {group_name}", group_items), group_name)
user_panel = _SectionPanel("User Apps", user_options)
tabs.addTab(clks_panel, "CLKS")
tabs.addTab(user_panel, "USER")
@@ -1689,7 +1837,7 @@ def interactive_menu(clks_options: List[OptionItem], user_options: List[OptionIt
show_summary(clks_options, user_options, values)
choice = input("Select> ").strip().lower()
if choice == "1":
section_loop("CLKS", clks_options, all_options, values)
grouped_section_loop("CLKS", clks_options, all_options, values)
continue
if choice == "2":
section_loop("USER", user_options, all_options, values)