MaxKB/installer/sandbox.c

135 lines
4.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <limits.h>
#include <libgen.h>
static const char *BANNED_FILE_NAME = ".SANDBOX_BANNED_HOSTS";
/**
* 从 .so 文件所在目录读取 .SANDBOX_BANNED_HOSTS 文件内容
* 返回 malloc 出的字符串(需 free读取失败则返回空字符串
*/
static char *load_banned_hosts() {
Dl_info info;
if (dladdr((void *)load_banned_hosts, &info) == 0 || !info.dli_fname) {
fprintf(stderr, "[sandbox] ⚠️ Unable to locate shared object path — allowing all hosts\n");
return strdup("");
}
char so_path[PATH_MAX];
strncpy(so_path, info.dli_fname, sizeof(so_path));
so_path[sizeof(so_path) - 1] = '\0';
char *dir = dirname(so_path);
char file_path[PATH_MAX];
snprintf(file_path, sizeof(file_path), "%s/%s", dir, BANNED_FILE_NAME);
FILE *fp = fopen(file_path, "r");
if (!fp) {
fprintf(stderr, "[sandbox] ⚠️ Cannot open %s — allowing all hosts\n", file_path);
return strdup("");
}
char *buf = malloc(4096);
if (!buf) {
fclose(fp);
fprintf(stderr, "[sandbox] ⚠️ Memory allocation failed — allowing all hosts\n");
return strdup("");
}
size_t len = fread(buf, 1, 4095, fp);
buf[len] = '\0';
fclose(fp);
return buf;
}
/**
* 精确匹配黑名单
*/
static int match_env_patterns(const char *target, const char *env_val) {
if (!target || !env_val || !*env_val) return 0;
char *patterns = strdup(env_val);
char *token = strtok(patterns, ",");
int matched = 0;
while (token) {
// 去掉前后空格
while (*token == ' ' || *token == '\t') token++;
char *end = token + strlen(token) - 1;
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
if (*token) {
regex_t regex;
char fullpattern[512];
snprintf(fullpattern, sizeof(fullpattern), "^%s$", token);
if (regcomp(&regex, fullpattern, REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0) {
if (regexec(&regex, target, 0, NULL, 0) == 0) {
matched = 1;
regfree(&regex);
break;
}
regfree(&regex);
} else {
fprintf(stderr, "[sandbox] ⚠️ Invalid regex '%s' — allowing host by default\n", token);
}
}
token = strtok(NULL, ",");
}
free(patterns);
return matched;
}
/** 拦截 connect() —— 精确匹配 IP */
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
if (!real_connect)
real_connect = dlsym(RTLD_NEXT, "connect");
static char *banned_env = NULL;
if (!banned_env) banned_env = load_banned_hosts();
char ip[INET6_ADDRSTRLEN] = {0};
if (addr->sa_family == AF_INET)
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
else if (addr->sa_family == AF_INET6)
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
if (banned_env && *banned_env && match_env_patterns(ip, banned_env)) {
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned\n", ip);
errno = EACCES;
return -1;
}
return real_connect(sockfd, addr, addrlen);
}
/** 拦截 getaddrinfo() —— 精确匹配域名 */
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res) {
static int (*real_getaddrinfo)(const char *, const char *,
const struct addrinfo *, struct addrinfo **) = NULL;
if (!real_getaddrinfo)
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
static char *banned_env = NULL;
if (!banned_env) banned_env = load_banned_hosts();
if (banned_env && *banned_env && node && match_env_patterns(node, banned_env)) {
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned\n", node);
return EAI_FAIL;
}
return real_getaddrinfo(node, service, hints, res);
}