1818#include <sys/types.h>
1919#include <time.h>
2020#include <execinfo.h>
21- #include <dlfcn.h>
2221#include <linux/sched.h>
2322#include <pty.h>
23+ #include <stdint.h>
2424
2525#define CONFIG_FILE ".sandbox.conf"
2626#define KEY_BANNED_HOSTS "SANDBOX_PYTHON_BANNED_HOSTS"
@@ -30,25 +30,26 @@ static char *banned_hosts = NULL;
3030static int allow_subprocess = 0 ; // 默认禁止
3131
3232static void load_sandbox_config () {
33- Dl_info info ;
34- if (dladdr ((void * )load_sandbox_config , & info ) == 0 || !info .dli_fname ) {
35- banned_hosts = strdup ("" );
36- allow_subprocess = 0 ;
37- return ;
38- }
39- char so_path [PATH_MAX ];
40- strncpy (so_path , info .dli_fname , sizeof (so_path ));
41- so_path [sizeof (so_path ) - 1 ] = '\0' ;
42- char * dir = dirname (so_path );
43- char config_path [PATH_MAX ];
44- snprintf (config_path , sizeof (config_path ), "%s/%s" , dir , CONFIG_FILE );
45- FILE * fp = fopen (config_path , "r" );
46- if (!fp ) {
47- banned_hosts = strdup ("" );
48- allow_subprocess = 0 ;
49- return ;
50- }
33+ Dl_info info ;
34+ if (dladdr ((void * )load_sandbox_config , & info ) == 0 || !info .dli_fname ) {
35+ banned_hosts = strdup ("" );
36+ allow_subprocess = 0 ;
37+ return ;
38+ }
39+ char so_path [PATH_MAX ];
40+ strncpy (so_path , info .dli_fname , sizeof (so_path ));
41+ so_path [sizeof (so_path ) - 1 ] = '\0' ;
42+ char * dir = dirname (so_path );
43+ char config_path [PATH_MAX ];
44+ snprintf (config_path , sizeof (config_path ), "%s/%s" , dir , CONFIG_FILE );
45+ FILE * fp = fopen (config_path , "r" );
46+ if (!fp ) {
47+ banned_hosts = strdup ("" );
48+ allow_subprocess = 0 ;
49+ return ;
50+ }
5151 char line [512 ];
52+ if (banned_hosts ) { free (banned_hosts ); banned_hosts = NULL ; }
5253 banned_hosts = strdup ("" );
5354 allow_subprocess = 0 ;
5455 while (fgets (line , sizeof (line ), fp )) {
@@ -77,15 +78,15 @@ static int is_sandbox_user() {
7778 uid_t uid = getuid ();
7879 struct passwd * pw = getpwuid (uid );
7980 if (!pw || !pw -> pw_name ) {
80- return 1 ; // 无法识别用户 → 认为是sandbox
81+ return 1 ; // 无法识别用户 → 认为是 sandbox
8182 }
8283 if (strcmp (pw -> pw_name , "sandbox" ) == 0 ) {
8384 return 1 ;
8485 }
8586 return 0 ;
8687}
8788/**
88- * 精确匹配黑名单
89+ * 匹配黑名单(用于域名或具体字符串匹配)
8990 */
9091static int match_env_patterns (const char * target , const char * env_val ) {
9192 if (!target || !env_val || !* env_val ) return 0 ;
@@ -114,8 +115,43 @@ static int match_env_patterns(const char *target, const char *env_val) {
114115 free (patterns );
115116 return matched ;
116117}
117-
118- /** 拦截 connect() —— 精确匹配 IP */
118+ // ------------------ IP/CIDR 黑名单 ------------------
119+ static int match_banned_ip (const char * ip_str , const char * banned_list ) {
120+ if (!ip_str || !banned_list || !* banned_list ) return 0 ;
121+ char * list = strdup (banned_list );
122+ char * token = strtok (list , "," );
123+ int blocked = 0 ;
124+ while (token ) {
125+ while (* token == ' ' || * token == '\t' ) token ++ ;
126+ char * end = token + strlen (token ) - 1 ;
127+ while (end > token && (* end == ' ' || * end == '\t' )) * end -- = '\0' ;
128+ if (* token ) {
129+ char * slash = strchr (token , '/' );
130+ if (!slash ) {
131+ if (strcmp (ip_str , token ) == 0 ) {
132+ blocked = 1 ;
133+ break ;
134+ }
135+ } else {
136+ * slash = 0 ;
137+ int prefix = atoi (slash + 1 );
138+ struct in_addr ip , net , mask ;
139+ if (inet_pton (AF_INET , token , & net ) == 1 &&
140+ inet_pton (AF_INET , ip_str , & ip ) == 1 ) {
141+ mask .s_addr = prefix == 0 ? 0 : htonl (0xFFFFFFFF << (32 - prefix ));
142+ if ((ip .s_addr & mask .s_addr ) == (net .s_addr & mask .s_addr )) {
143+ blocked = 1 ;
144+ break ;
145+ }
146+ }
147+ }
148+ }
149+ token = strtok (NULL , "," );
150+ }
151+ free (list );
152+ return blocked ;
153+ }
154+ // ------------------ 网络拦截 ------------------
119155int connect (int sockfd , const struct sockaddr * addr , socklen_t addrlen ) {
120156 static int (* real_connect )(int , const struct sockaddr * , socklen_t ) = NULL ;
121157 if (!real_connect )
@@ -126,32 +162,34 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
126162 inet_ntop (AF_INET , & ((struct sockaddr_in * )addr )-> sin_addr , ip , sizeof (ip ));
127163 else if (addr -> sa_family == AF_INET6 )
128164 inet_ntop (AF_INET6 , & ((struct sockaddr_in6 * )addr )-> sin6_addr , ip , sizeof (ip ));
129- if (is_sandbox_user () && banned_hosts && * banned_hosts && match_env_patterns (ip , banned_hosts )) {
130- fprintf (stderr , "[sandbox] 🚫 Access to host %s is banned\n" , ip );
131- errno = EACCES ; // EACCES 的值是 13, 意思是 Permission denied
132- return -1 ;
165+
166+ if (is_sandbox_user () && banned_hosts && * banned_hosts ) {
167+ if (ip [0 ] && match_banned_ip (ip , banned_hosts )) {
168+ fprintf (stderr , "[sandbox] 🚫 Access to IP %s is banned\n" , ip );
169+ errno = EACCES ; // Permission denied
170+ return -1 ;
171+ }
133172 }
134173 return real_connect (sockfd , addr , addrlen );
135174}
136-
137- /** 拦截 getaddrinfo() —— 只拦截域名,不拦截纯 IP */
138175int getaddrinfo (const char * node , const char * service ,
139176 const struct addrinfo * hints , struct addrinfo * * res ) {
140177 static int (* real_getaddrinfo )(const char * , const char * ,
141178 const struct addrinfo * , struct addrinfo * * ) = NULL ;
142179 if (!real_getaddrinfo )
143180 real_getaddrinfo = dlsym (RTLD_NEXT , "getaddrinfo" );
144181 ensure_config_loaded ();
145- if (banned_hosts && * banned_hosts && node ) {
146- // 检测 node 是否是 IP
182+ if (banned_hosts && * banned_hosts && node && is_sandbox_user ()) {
147183 struct in_addr ipv4 ;
148184 struct in6_addr ipv6 ;
149- int is_ip = (inet_pton (AF_INET , node , & ipv4 ) == 1 ) ||
150- (inet_pton (AF_INET6 , node , & ipv6 ) == 1 );
151- // 只对“非IP的域名”进行屏蔽
152- if (is_sandbox_user () && !is_ip && match_env_patterns (node , banned_hosts )) {
153- fprintf (stderr , "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n" , node );
154- return EAI_FAIL ; // 模拟 DNS 层禁止
185+ int is_ip = inet_pton (AF_INET , node , & ipv4 ) == 1 ||
186+ inet_pton (AF_INET6 , node , & ipv6 ) == 1 ;
187+ if (!is_ip ) {
188+ // 仅对域名进行阻塞
189+ if (match_env_patterns (node , banned_hosts )) {
190+ fprintf (stderr , "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n" , node );
191+ return EAI_FAIL ;
192+ }
155193 }
156194 }
157195 return real_getaddrinfo (node , service , hints , res );
@@ -287,7 +325,6 @@ int posix_spawn(pid_t *pid, const char *path,
287325 if (!allow_create_subprocess ()) return deny ();
288326 return real_posix_spawn (pid , path , file_actions , attrp , argv , envp );
289327}
290-
291328int posix_spawnp (pid_t * pid , const char * file ,
292329 const posix_spawn_file_actions_t * file_actions ,
293330 const posix_spawnattr_t * attrp ,
@@ -304,7 +341,6 @@ int __posix_spawn(pid_t *pid, const char *path,
304341 if (!allow_create_subprocess ()) return deny ();
305342 return real___posix_spawn (pid , path , file_actions , attrp , argv , envp );
306343}
307-
308344int __posix_spawnp (pid_t * pid , const char * file ,
309345 const posix_spawn_file_actions_t * file_actions ,
310346 const posix_spawnattr_t * attrp ,
@@ -351,6 +387,7 @@ pid_t __libc_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, .
351387 va_end (ap );
352388 return real___libc_clone (fn , child_stack , flags , arg , (void * )a4 , (void * )a5 );
353389}
390+
354391pid_t forkpty (int * amaster , char * name , const struct termios * termp , const struct winsize * winp ) {
355392 RESOLVE_REAL (forkpty );
356393 if (!allow_create_subprocess ()) return deny ();
@@ -361,6 +398,7 @@ pid_t __forkpty(int *amaster, char *name, const struct termios *termp, const str
361398 if (!allow_create_subprocess ()) return deny ();
362399 return real___forkpty (amaster , name , termp , winp );
363400}
401+ /* syscall wrapper to intercept syscalls that directly create processes */
364402long (* real_syscall )(long , ...) = NULL ;
365403long syscall (long number , ...) {
366404 RESOLVE_REAL (syscall );
@@ -393,4 +431,4 @@ long syscall(long number, ...) {
393431 if (!allow_create_subprocess ()) return deny ();
394432 }
395433 return real_syscall (number , a1 , a2 , a3 , a4 , a5 , a6 );
396- }
434+ }
0 commit comments