Skip to content

Commit 057e042

Browse files
security: not allow to create subprocess in sandbox by default.
1 parent 3670ec2 commit 057e042

File tree

2 files changed

+243
-65
lines changed

2 files changed

+243
-65
lines changed

apps/common/utils/tool_code.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,21 @@ def _init_dir(self):
6060
os.system(f"chown -R {self.user}:root {tmp_dir_path}")
6161
if os.path.exists(self.sandbox_so_path):
6262
os.chmod(self.sandbox_so_path, 0o440)
63-
# 初始化host黑名单
64-
banned_hosts_file_path = f'{self.sandbox_path}/.SANDBOX_BANNED_HOSTS'
65-
if os.path.exists(banned_hosts_file_path):
66-
os.remove(banned_hosts_file_path)
63+
# 初始化sandbox配置文件
64+
sandbox_conf_file_path = f'{self.sandbox_path}/.sandbox.conf'
65+
if os.path.exists(sandbox_conf_file_path):
66+
os.remove(sandbox_conf_file_path)
67+
allow_subprocess = CONFIG.get("SANDBOX_PYTHON_ALLOW_SUBPROCESS", '0')
6768
banned_hosts = CONFIG.get("SANDBOX_PYTHON_BANNED_HOSTS", '').strip()
6869
if banned_hosts:
6970
hostname = socket.gethostname()
7071
local_ip = socket.gethostbyname(hostname)
7172
banned_hosts = f"{banned_hosts},{hostname},{local_ip}"
72-
with open(banned_hosts_file_path, "w") as f:
73-
f.write(banned_hosts)
74-
os.chmod(banned_hosts_file_path, 0o440)
73+
with open(sandbox_conf_file_path, "w") as f:
74+
f.write(f"SANDBOX_PYTHON_BANNED_HOSTS={banned_hosts}")
75+
f.write("\n")
76+
f.write(f"SANDBOX_PYTHON_ALLOW_SUBPROCESS={allow_subprocess}")
77+
os.chmod(sandbox_conf_file_path, 0o440)
7578

7679
def exec_code(self, code_str, keywords):
7780
self.validate_banned_keywords(code_str)

installer/sandbox.c

Lines changed: 233 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,82 +11,102 @@
1111
#include <errno.h>
1212
#include <limits.h>
1313
#include <libgen.h>
14+
#include <pwd.h>
15+
#include <stdarg.h>
16+
#include <spawn.h>
17+
#include <sys/syscall.h>
18+
#include <sys/types.h>
19+
#include <time.h>
20+
#include <execinfo.h>
21+
#include <dlfcn.h>
1422

15-
static const char *BANNED_FILE_NAME = ".SANDBOX_BANNED_HOSTS";
16-
17-
/**
18-
* 从 .so 文件所在目录读取 .SANDBOX_BANNED_HOSTS 文件内容
19-
* 返回 malloc 出的字符串(需 free),读取失败则返回空字符串
20-
*/
21-
static char *load_banned_hosts() {
22-
Dl_info info;
23-
if (dladdr((void *)load_banned_hosts, &info) == 0 || !info.dli_fname) {
24-
fprintf(stderr, "[sandbox] ⚠️ Unable to locate shared object path — allowing all hosts\n");
25-
return strdup("");
26-
}
27-
28-
char so_path[PATH_MAX];
29-
strncpy(so_path, info.dli_fname, sizeof(so_path));
30-
so_path[sizeof(so_path) - 1] = '\0';
31-
32-
char *dir = dirname(so_path);
33-
char file_path[PATH_MAX];
34-
snprintf(file_path, sizeof(file_path), "%s/%s", dir, BANNED_FILE_NAME);
23+
#define CONFIG_FILE ".sandbox.conf"
3524

36-
FILE *fp = fopen(file_path, "r");
37-
if (!fp) {
38-
fprintf(stderr, "[sandbox] ⚠️ Cannot open %s — allowing all hosts\n", file_path);
39-
return strdup("");
40-
}
25+
static char *banned_hosts = NULL;
26+
static int allow_subprocess = 0; // 默认禁止
4127

42-
char *buf = malloc(4096);
43-
if (!buf) {
44-
fclose(fp);
45-
fprintf(stderr, "[sandbox] ⚠️ Memory allocation failed — allowing all hosts\n");
46-
return strdup("");
28+
static void load_sandbox_config() {
29+
Dl_info info;
30+
if (dladdr((void *)load_sandbox_config, &info) == 0 || !info.dli_fname) {
31+
banned_hosts = strdup("");
32+
allow_subprocess = 0;
33+
return;
34+
}
35+
char so_path[PATH_MAX];
36+
strncpy(so_path, info.dli_fname, sizeof(so_path));
37+
so_path[sizeof(so_path) - 1] = '\0';
38+
char *dir = dirname(so_path);
39+
char config_path[PATH_MAX];
40+
snprintf(config_path, sizeof(config_path), "%s/%s", dir, CONFIG_FILE);
41+
FILE *fp = fopen(config_path, "r");
42+
if (!fp) {
43+
banned_hosts = strdup("");
44+
allow_subprocess = 0;
45+
return;
46+
}
47+
char line[512];
48+
banned_hosts = strdup("");
49+
allow_subprocess = 0;
50+
while (fgets(line, sizeof(line), fp)) {
51+
char *key = strtok(line, "=");
52+
char *value = strtok(NULL, "\n");
53+
if (!key || !value) continue;
54+
while (*key == ' ' || *key == '\t') key++;
55+
char *kend = key + strlen(key) - 1;
56+
while (kend > key && (*kend == ' ' || *kend == '\t')) *kend-- = '\0';
57+
while (*value == ' ' || *value == '\t') value++;
58+
char *vend = value + strlen(value) - 1;
59+
while (vend > value && (*vend == ' ' || *vend == '\t')) *vend-- = '\0';
60+
if (strcmp(key, "SANDBOX_PYTHON_BANNED_HOSTS") == 0) {
61+
free(banned_hosts);
62+
banned_hosts = strdup(value);
63+
} else if (strcmp(key, "SANDBOX_PYTHON_ALLOW_SUBPROCESS") == 0) {
64+
allow_subprocess = atoi(value);
65+
}
4766
}
48-
49-
size_t len = fread(buf, 1, 4095, fp);
50-
buf[len] = '\0';
5167
fclose(fp);
52-
return buf;
5368
}
54-
69+
static void ensure_config_loaded() {
70+
if (!banned_hosts) load_sandbox_config();
71+
}
72+
static int is_sandbox_user() {
73+
uid_t uid = getuid();
74+
struct passwd *pw = getpwuid(uid);
75+
if (!pw || !pw->pw_name) {
76+
return 1; // 无法识别用户 → 认为是sandbox
77+
}
78+
if (strcmp(pw->pw_name, "sandbox") == 0) {
79+
return 1;
80+
}
81+
return 0;
82+
}
5583
/**
5684
* 精确匹配黑名单
5785
*/
5886
static int match_env_patterns(const char *target, const char *env_val) {
5987
if (!target || !env_val || !*env_val) return 0;
60-
6188
char *patterns = strdup(env_val);
6289
char *token = strtok(patterns, ",");
6390
int matched = 0;
64-
6591
while (token) {
66-
// 去掉前后空格
6792
while (*token == ' ' || *token == '\t') token++;
6893
char *end = token + strlen(token) - 1;
6994
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
70-
7195
if (*token) {
7296
regex_t regex;
7397
char fullpattern[512];
7498
snprintf(fullpattern, sizeof(fullpattern), "^%s$", token);
75-
7699
if (regcomp(&regex, fullpattern, REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0) {
77100
if (regexec(&regex, target, 0, NULL, 0) == 0) {
78101
matched = 1;
79102
regfree(&regex);
80103
break;
81104
}
82105
regfree(&regex);
83-
} else {
84-
fprintf(stderr, "[sandbox] ⚠️ Invalid regex '%s' — allowing host by default\n", token);
85106
}
86107
}
87108
token = strtok(NULL, ",");
88109
}
89-
90110
free(patterns);
91111
return matched;
92112
}
@@ -96,22 +116,17 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
96116
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
97117
if (!real_connect)
98118
real_connect = dlsym(RTLD_NEXT, "connect");
99-
100-
static char *banned_env = NULL;
101-
if (!banned_env) banned_env = load_banned_hosts();
102-
119+
ensure_config_loaded();
103120
char ip[INET6_ADDRSTRLEN] = {0};
104121
if (addr->sa_family == AF_INET)
105122
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
106123
else if (addr->sa_family == AF_INET6)
107124
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
108-
109-
if (banned_env && *banned_env && match_env_patterns(ip, banned_env)) {
125+
if (is_sandbox_user() && banned_hosts && *banned_hosts && match_env_patterns(ip, banned_hosts)) {
110126
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned\n", ip);
111127
errno = EACCES; // EACCES 的值是 13, 意思是 Permission denied
112128
return -1;
113129
}
114-
115130
return real_connect(sockfd, addr, addrlen);
116131
}
117132

@@ -122,23 +137,183 @@ int getaddrinfo(const char *node, const char *service,
122137
const struct addrinfo *, struct addrinfo **) = NULL;
123138
if (!real_getaddrinfo)
124139
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
125-
126-
static char *banned_env = NULL;
127-
if (!banned_env) banned_env = load_banned_hosts();
128-
129-
if (banned_env && *banned_env && node) {
140+
ensure_config_loaded();
141+
if (banned_hosts && *banned_hosts && node) {
130142
// 检测 node 是否是 IP
131143
struct in_addr ipv4;
132144
struct in6_addr ipv6;
133145
int is_ip = (inet_pton(AF_INET, node, &ipv4) == 1) ||
134146
(inet_pton(AF_INET6, node, &ipv6) == 1);
135-
136147
// 只对“非IP的域名”进行屏蔽
137-
if (!is_ip && match_env_patterns(node, banned_env)) {
148+
if (is_sandbox_user() && !is_ip && match_env_patterns(node, banned_hosts )) {
138149
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n", node);
139150
return EAI_FAIL; // 模拟 DNS 层禁止
140151
}
141152
}
142-
143153
return real_getaddrinfo(node, service, hints, res);
144154
}
155+
/* ------------------ 禁止创建子进程------------------ */
156+
static int allow_create_subprocess() {
157+
ensure_config_loaded();
158+
return allow_subprocess || !is_sandbox_user();
159+
}
160+
static int return_deny() {
161+
fprintf(stderr, "[sandbox] Permission denied to create subprocess in sandbox.\n");
162+
_exit(1);
163+
return -1;
164+
}
165+
int execve(const char *filename, char *const argv[], char *const envp[]) {
166+
if (!allow_create_subprocess()) {
167+
return return_deny();
168+
}
169+
static int (*real_execve)(const char *, char *const[], char *const[]) = NULL;
170+
if (!real_execve) real_execve = dlsym(RTLD_NEXT, "execve");
171+
return real_execve(filename, argv, envp);
172+
}
173+
174+
int execveat(int dirfd, const char *pathname,
175+
char *const argv[], char *const envp[], int flags) {
176+
if (!allow_create_subprocess()) {
177+
return return_deny();
178+
}
179+
static int (*real_execveat)(int, const char *, char *const[], char *const[], int) = NULL;
180+
if (!real_execveat) real_execveat = dlsym(RTLD_NEXT, "execveat");
181+
return real_execveat(dirfd, pathname, argv, envp, flags);
182+
}
183+
184+
pid_t fork(void) {
185+
if (!allow_create_subprocess()) {
186+
return return_deny();
187+
}
188+
static pid_t (*real_fork)(void) = NULL;
189+
if (!real_fork) real_fork = dlsym(RTLD_NEXT, "fork");
190+
return real_fork();
191+
}
192+
193+
pid_t vfork(void) {
194+
if (!allow_create_subprocess()) {
195+
return return_deny();
196+
}
197+
static pid_t (*real_vfork)(void) = NULL;
198+
if (!real_vfork) real_vfork = dlsym(RTLD_NEXT, "vfork");
199+
return real_vfork();
200+
}
201+
202+
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) {
203+
if (!allow_create_subprocess()) {
204+
return return_deny();
205+
}
206+
static int (*real_clone)(int (*)(void *), void *, int, void *, ...) = NULL;
207+
if (!real_clone) real_clone = dlsym(RTLD_NEXT, "clone");
208+
va_list ap;
209+
va_start(ap, arg);
210+
int ret = real_clone(fn, child_stack, flags, arg, ap);
211+
va_end(ap);
212+
return ret;
213+
}
214+
215+
int posix_spawn(pid_t *pid, const char *path,
216+
const posix_spawn_file_actions_t *file_actions,
217+
const posix_spawnattr_t *attrp,
218+
char *const argv[], char *const envp[]) {
219+
if (!allow_create_subprocess()) {
220+
return return_deny();
221+
}
222+
static int (*real_posix_spawn)(pid_t *, const char *, const posix_spawn_file_actions_t *,
223+
const posix_spawnattr_t *, char *const[], char *const[]) = NULL;
224+
if (!real_posix_spawn) real_posix_spawn = dlsym(RTLD_NEXT, "posix_spawn");
225+
return real_posix_spawn(pid, path, file_actions, attrp, argv, envp);
226+
}
227+
228+
int posix_spawnp(pid_t *pid, const char *file,
229+
const posix_spawn_file_actions_t *file_actions,
230+
const posix_spawnattr_t *attrp,
231+
char *const argv[], char *const envp[]) {
232+
if (!allow_create_subprocess()) {
233+
return return_deny();
234+
}
235+
static int (*real_posix_spawnp)(pid_t *, const char *, const posix_spawn_file_actions_t *,
236+
const posix_spawnattr_t *, char *const[], char *const[]) = NULL;
237+
if (!real_posix_spawnp) real_posix_spawnp = dlsym(RTLD_NEXT, "posix_spawnp");
238+
return real_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
239+
}
240+
int __posix_spawn(pid_t *pid, const char *path,
241+
const posix_spawn_file_actions_t *file_actions,
242+
const posix_spawnattr_t *attrp,
243+
char *const argv[], char *const envp[]) {
244+
if (!allow_create_subprocess()) {
245+
return return_deny();
246+
}
247+
248+
static int (*real___posix_spawn)(pid_t *, const char *,
249+
const posix_spawn_file_actions_t *,
250+
const posix_spawnattr_t *,
251+
char *const[], char *const[]) = NULL;
252+
253+
if (!real___posix_spawn)
254+
real___posix_spawn = dlsym(RTLD_NEXT, "__posix_spawn");
255+
256+
return real___posix_spawn(pid, path, file_actions, attrp, argv, envp);
257+
}
258+
259+
int __posix_spawnp(pid_t *pid, const char *file,
260+
const posix_spawn_file_actions_t *file_actions,
261+
const posix_spawnattr_t *attrp,
262+
char *const argv[], char *const envp[]) {
263+
if (!allow_create_subprocess()) {
264+
return return_deny();
265+
}
266+
267+
static int (*real___posix_spawnp)(pid_t *, const char *,
268+
const posix_spawn_file_actions_t *,
269+
const posix_spawnattr_t *,
270+
char *const[], char *const[]) = NULL;
271+
272+
if (!real___posix_spawnp)
273+
real___posix_spawnp = dlsym(RTLD_NEXT, "__posix_spawnp");
274+
275+
return real___posix_spawnp(pid, file, file_actions, attrp, argv, envp);
276+
}
277+
278+
int system(const char *command) {
279+
if (!allow_create_subprocess()) {
280+
return return_deny();
281+
}
282+
static int (*real_system)(const char *) = NULL;
283+
if (!real_system) real_system = dlsym(RTLD_NEXT, "system");
284+
return real_system(command);
285+
}
286+
int __libc_system(const char *command) {
287+
if (!allow_create_subprocess()) {
288+
return return_deny();
289+
}
290+
static int (*real___libc_system)(const char *) = NULL;
291+
if (!real___libc_system)
292+
real___libc_system = dlsym(RTLD_NEXT, "__libc_system");
293+
294+
return real___libc_system(command);
295+
}
296+
long (*real_syscall)(long, ...) = NULL;
297+
298+
long syscall(long number, ...) {
299+
if (!real_syscall) real_syscall = dlsym(RTLD_NEXT, "syscall");
300+
301+
va_list ap;
302+
va_start(ap, number);
303+
long a1 = va_arg(ap, long);
304+
long a2 = va_arg(ap, long);
305+
long a3 = va_arg(ap, long);
306+
long a4 = va_arg(ap, long);
307+
long a5 = va_arg(ap, long);
308+
long a6 = va_arg(ap, long);
309+
va_end(ap);
310+
311+
if (number == SYS_execve || number == SYS_execveat ||
312+
number == SYS_fork || number == SYS_vfork || number == SYS_clone) {
313+
if (!allow_create_subprocess()) {
314+
return return_deny();
315+
}
316+
}
317+
318+
return real_syscall(number, a1, a2, a3, a4, a5, a6);
319+
}

0 commit comments

Comments
 (0)