denyhosts/rhost.c

864 lines
22 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.

#include "conf.h"
#include "rhost.h"
#include "libiptc.h"
#include "libclamav.h"
#include "clamscan.h"
#include "ccronexpr.h"
// 存储公网IP
char *public_ip;
struct MemoryStruct
{
char *memory;
size_t size;
};
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
// 注意这里根据每次被调用获得的数据重新动态分配缓存区的大小
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if (ptr == NULL)
{
/* 内存不足! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
// 获取公网IP
static char *GET_PUBLIC_IP(char *URL)
{
CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1); /* 将根据上述再分配的需要增长 */
chunk.size = 0; /* 此时没有数据 */
curl_global_init(CURL_GLOBAL_ALL);
/* 初始化curl会话 */
curl_handle = curl_easy_init();
/* 指定要获取的URL */
curl_easy_setopt(curl_handle, CURLOPT_URL, URL);
/* 将所有数据发送到此函数 */
//对于同一次阻塞的curl_easy_perform而言在写完获取的数据之前会多次调用 WriteMemoryCallback
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* 将"chunk"结构传递给回调函数 */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
//对于同一次阻塞的curl_easy_perform而言在写完获取的数据之前会多次调用 WriteMemoryCallback
res = curl_easy_perform(curl_handle);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
else
{
//printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
//printf("%s", chunk.memory);
;
}
curl_easy_cleanup(curl_handle);
curl_global_cleanup();
return chunk.memory;
}
// 检测系统
int check_system()
{
if (0 == access("/etc/debian_version", F_OK))
{
return DEBISN_SYSTEM;
}
else if (0 == access("/etc/centos-release", F_OK))
{
return CENTOS_SYSTEM;
}
return UNKNOWN_SYSTEM;
}
// 钉钉告警
int dingding_warning(char *illegal_ip, char *public_ip, conf * conf)
{
FILE *fp;
char temp[64];
char jsonObj[BUFFER];
memset(jsonObj, 0, BUFFER);
memset(temp, 0, 64);
strcpy(temp, public_ip);
temp[strlen(public_ip) - 1] = '\0';
if ((fp = fopen("libcurl.log", "wt+")) == NULL)
{
return 1;
}
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl == NULL)
{
return 1;
}
#define JSIN "{ \
\"msgtype\": \"text\", \
\"text\": { \
\"content\": \"Alert @%s 服务器地址:%s封禁非法入侵主机:%s\" \
}, \
\"at\": { \
\"atMobiles\": [\"%s\"], \
\"isAtAll\": false \
} \
}"
sprintf(jsonObj, JSIN, conf->PHONE, temp, illegal_ip, conf->PHONE);
printf("%s\n", jsonObj);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charset: utf-8");
curl_easy_setopt(curl, CURLOPT_URL, conf->DING_WEBHOOK);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/0.1");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
fclose(fp);
return res;
}
// 邮件告警
int mail_warning(char *illegal_ip, char *public_ip, conf * conf)
{
FILE *fp = NULL;
char buff[BUFFER];
char text[BUFFER];
char temp[64];
memset(buff, 0, BUFFER);
memset(text, 0, BUFFER);
memset(temp, 0, 64);
strcpy(temp, public_ip);
temp[strlen(public_ip) - 1] = '\0';
sprintf(text, "echo \"主机:%s, 禁止%s访问\" | mail -s \"System ban IP\" %s", temp, illegal_ip, conf->RECV_MAIL);
if (NULL == (fp = popen(text, "r")))
{
perror("popen text");
}
while (fgets(buff, BUFFER, fp) != NULL)
{
buff[strlen(buff) - 1] = '\0';
}
if (NULL != fp)
pclose(fp);
return 0;
}
// 第三方邮箱告警
int QQ_mail_warning(char *illegal_ip, char *public_ip, conf * conf)
{
char string[BUFFER + (sizeof(QQMAIL)) + 1];
char text[BUFFER];
char temp[32];
memset(string, 0, BUFFER + (sizeof(QQMAIL)) + 1);
memset(text, 0, BUFFER);
memset(temp, 0, 32);
strcpy(temp, public_ip);
temp[strlen(public_ip) - 1] = '\0';
sprintf(text, "主机:%s, 禁止%s访问", temp, illegal_ip);
sprintf(string, QQMAIL, conf->RECV_MAIL, text);
return system(string);
}
// IP段白名单对比
int whitelist(char *client_ip, char (*whitelist_ip)[WHITELIST_IP_NUM])
{
int i;
for (i = 1; i < WHITELIST_IP_NUM - 1; i++)
{
if (strcmp(whitelist_ip[i], "\0") == 0) // 如果字符串为空就跳出循环
{
break;
}
if ((strncmp(client_ip, whitelist_ip[i], strlen(whitelist_ip[i]))) == 0) // 对比client_ip长度,
{
return 1;
}
}
return 0;
}
// 地域段白名单对比
int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
{
int i;
char *p;
for (i = 1; i < WHITELIST_IP_NUM - 1; i++)
{
if (strcmp(region_list[i], "\0") == 0) // 如果字符串为空就跳出循环
{
break;
}
// 在str中查找region_list[i]
p = strstr(str, region_list[i]);
if (p != NULL)
{
return 1;
}
}
return 0;
}
// 去除空格
char *remove_space(const char *str)
{
unsigned int i = 0, j = 0;
unsigned int uLen = strlen(str);
char *strRet;
if (0 == uLen)
{
return '\0';
}
strRet = (char *)malloc(uLen + 2);
memset(strRet, 0, uLen + 2);
for (i = 0; i < uLen; i++)
{
if (str[i] != ' ')
{
strRet[j++] = str[i];
}
}
strRet[j] = '\0';
return strRet;
}
// 封禁非法IP
int rule(conf * conf)
{
char whitelist_ip[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
char region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
char p[2], splice_command[LONG_BUFFER], command[LONG_BUFFER], *temp, buffer[BUFFER], awk[BUFFER];
FILE *fp, *fc;
time_t timep;
struct tm *tp;
fp = NULL;
fc = NULL;
timep = time(NULL);
tp = localtime(&timep);
memset(splice_command, 0, LONG_BUFFER);
memset(command, 0, LONG_BUFFER);
memset(buffer, 0, BUFFER);
memset(awk, 0, BUFFER);
if (DEBISN_SYSTEM == check_system()) // Debian 系统规则
{
if (tp->tm_mday >= 10)
{
if ((fp = popen(GE_10, "r")) == NULL)
{
perror("GE_10");
return 1;
}
}
else
{
if ((fp = popen(LE_10, "r")) == NULL)
{
perror("LE_10");
return 1;
}
}
}
else if (CENTOS_SYSTEM == check_system()) // Centos 7系统规则
{
if (tp->tm_mday >= 10)
{
if ((fp = popen(CENTOS_GE_10, "r")) == NULL)
{
perror("CENTOS_GE_10");
return 1;
}
}
else
{
if ((fp = popen(CENTOS_LE_10, "r")) == NULL)
{
perror("CENTOS_LE_10");
return 1;
}
}
}
else
{
return UNKNOWN_SYSTEM;
}
while (fgets(buffer, BUFFER, fp) != NULL)
{
temp = strstr(buffer, "rhost");
sscanf(temp, "rhost=%s", temp);
if (atoi(strncpy(p, temp, 1)) > 0)
{
strcat(splice_command, temp);
strcat(splice_command, "\n");
}
}
printf("%s", splice_command); // 打印所有非法IP
sprintf(awk, AWK, conf->REFUSE_NUMBER); // 拼接命令
strcpy(command, "echo \"");
strcat(command, splice_command);
strcat(command, "\"");
strcat(command, awk);
if ((fc = popen(command, "r")) == NULL) // 执行命令
{
perror("popen command");
return 1;
}
while (fgets(buffer, BUFFER, fc) != NULL) // 执行命令后, 为空时就不会
{
buffer[strlen(buffer) - 1] = '\0'; // 去除回车
split_string(conf->IPV4_WHITE_LIST, " ", whitelist_ip);
split_string(conf->REGION_LIST, " ", region_list);
if (conf->IPV4_RESTRICTION == 1) // 是否启用白名单
{
if (whitelist(buffer, whitelist_ip) == 1)
{
printf("白名单IPV4:%s\n", buffer);
continue;
}
}
if (0 != show_all_rule(buffer)) // libiptc库判断否存在规则
{
char *location = NULL;
char *location_json = NULL;
char iplocation[BUFFER];
char URL[BUFFER + 70];
char temp[BUFFER];
char *p;
char *p1;
memset(URL, 0, BUFFER + 70);
//sprintf(URL, "http://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8", buffer);
sprintf(URL, conf->REGION_URL, buffer);
//printf("%s\n", URL);
location_json = GET_PUBLIC_IP(URL);
if (NULL == location_json)
{
printf("获取IP位置错误!\n");
goto BLOCKED;
}
else
{
p = strstr(location_json, "\"location\"");
p1 = strstr(p, "\",");
memset(temp, 0, BUFFER);
memcpy(temp, p + 12, p1 - p - 12);
location = remove_space(temp);
printf("%s\n", location );
}
memset(iplocation, 0, BUFFER);
strcpy(iplocation, buffer);
strcat(iplocation, "(");
strcat(iplocation, location);
strcat(iplocation, ")");
printf("%s\n", iplocation );
if (location != NULL)
free(location);
if (location_json != NULL)
free(location_json);
// 地域白名单
if (conf->REGION == 1)
{
if (isregion(iplocation, region_list) == 1)
{
printf("地域白名单: %s\n", iplocation);
continue;
}
}
if (conf->IS_DING_WEBHOOK == 1) // 钉钉告警
{
dingding_warning(iplocation, public_ip, conf);
sleep(3);
}
if (conf->IS_MAIL == 1) // 邮件告警
{
mail_warning(iplocation, public_ip, conf);
sleep(3);
}
if (conf->IS_QQMAIL == 1) // 邮件告警
{
QQ_mail_warning(iplocation, public_ip, conf);
sleep(3);
}
BLOCKED:
// 是否封禁攻击IP
if (conf->IS_BLOCKED == 1)
{
// libiptc 库插入规则 iptables -t filter -A INPUT -p tcp -m tcp -s xxxx -j DROP
unsigned int srcIp;
inet_pton(AF_INET, buffer, &srcIp);
iptc_add_rule("filter", "INPUT", IPPROTO_TCP, NULL, NULL, srcIp, 0, NULL, NULL, "DROP", NULL, 1);
}
}
}
if (fp != NULL)
pclose(fp);
if (fc != NULL)
pclose(fc);
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
// 处理僵尸进程
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) ;
return;
}
static int get_executable_path(char *processdir, char *processname, int len)
{
char *filename;
if (readlink("/proc/self/exe", processdir, len) <= 0)
return -1;
filename = strrchr(processdir, '/');
if (filename == NULL)
return -1;
++filename;
strcpy(processname, filename);
*filename = '\0';
return (int)(filename - processdir);
}
// 处理参数
int process_argv(int argc, char *argv[], char **argvs)
{
argvs[0] = argv[0];
int i;
int j;
for (i = 0; i <= argc - 1; i++)
{
if (i == 1)
{
for (j = i; j <= argc - 2; j++)
{
argvs[j] = argv[j + 1];
}
}
}
return 0;
}
int _crontab(struct tm **calnext, char *string)
{
const char *err = NULL;
time_t cur;
time_t datenext;
time(&cur);
cron_expr parsed;
cron_parse_expr(string, &parsed, &err);
datenext = cron_next(&parsed, cur);
*calnext = localtime(&datenext);
assert(*calnext);
return 0;
}
int update_freshclam(int argc, char *argv[])
{
if (DEBISN_SYSTEM == check_system() || CENTOS_SYSTEM == check_system()) {
char **fre_argv;
int fre_argc = 0;
char *fre_argvss[ARGS_NUM] = { NULL };
fre_argvss[0] = argv[0];
fre_argvss[1] = "--user=root";
fre_argvss[2] = "--quiet";
fre_argvss[3] = "--no-warnings";
fre_argv = &(fre_argvss[0]);
fre_argc = 2;
// freshclam配置文件
if (access("/etc/clamav/freshclam.conf", F_OK) == -1) {
system("mkdir -p /etc/clamav/");
system("cp freshclam.conf /etc/clamav/");
}
// 打印clamav参数
for(int i=0; i<fre_argc; i++)
{
printf("%s %d\n", fre_argv[i], i);
}
pid_t pid;
pid = fork();
if (pid < 0) {
printf("fork error.\n");
return -1;
}
else if (pid == 0) // child process
{
int r = 0;
r = _freshclam(fre_argc, fre_argv);
_exit(r);
}
else
{
int status = 0;
wait(&status); // wait the end of child process
if (WIFEXITED(status))
{
;
printf("child process return %d\n", WEXITSTATUS(status));
}
sleep(3);
return 0;
}
}
return -1;
}
int main(int argc, char *argv[], char **env)
{
signal(SIGCHLD, sig_child); // 创建捕捉子进程退出信号
// 更新病毒库
update_freshclam(argc, argv);
int pid;
int i;
char move[BUFFER];
// 读取配置
char path[BUFFER] = { 0 };
char executable_filename[BUFFER] = { 0 };
(void)get_executable_path(path, executable_filename, sizeof(path));
strcat(executable_filename, ".conf");
strcat(path, executable_filename);
if (1 == access(path, F_OK))
{
printf("配置文件不存在!\n");
}
conf *conf = (struct CONF *)malloc(sizeof(struct CONF));
read_conf(path, conf);
//ptintf_conf(conf);
// 创建移除目录
if (conf->CLAMAV_ARG)
{
char temp[BUFFER];
char *p, *p1;
memset(temp, 0, BUFFER);
memset(move, 0, BUFFER);
p = strstr(conf->CLAMAV_ARG, "--move=");
if (p != NULL)
{
p1 = strstr(p, " ");
if ((p1-p) > 7)
{
memcpy(temp, p, p1-p);
p = strstr(temp, "=");
strcpy(move, "mkdir -p ");
strcat(move, p+1);
//printf("%s %ld \n", move, strlen(move));
system(move);
}
}
}
// 处理clamav参数
char **head_argvs;
int head_argc = 0;
char *argvs[ARGS_NUM] = { NULL };
char args[ARGS_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
//printf("%d\n", argc);
if (argc > 3) // 手动输入参数(如果手动输入参数个数大于3个, 则使用用户输入的参数)
{
process_argv(argc, argv, &(argvs[0]));
head_argvs = &(argvs[0]); // head_argvs指向argvs[0]
head_argc = argc - 1; // 改变argc数
}
else // 读取配置文件参数
{
argvs[0] = argv[0];
split_string(conf->CLAMAV_ARG, " ", args);
for (i=1; i<ARGS_NUM; i++)
{
if (args[i][0] == '\0')
{
continue;
}
else
{
argvs[i] = strdup(args[i]);
head_argc++;
}
}
head_argvs = &(argvs[0]);
head_argc += 1;
}
/*
// 打印clamav参数
for(int i=0; i<head_argc; i++)
{
printf("%s %d\n", head_argvs[i], i);
}
_clamscan(head_argc, head_argvs);
*/
now_next_time *t = (now_next_time *)malloc(sizeof(struct now_next_time));
memset(t, 0, sizeof(struct now_next_time));
// 获取公网IP
public_ip = GET_PUBLIC_IP(conf->PUBLIC_IP);
//printf("%s", public_ip);
if (0 == strcmp(conf->DAEMON, "on"))
{
goto goto_daemon;
}
if (argv[1] != NULL && 0 == strcmp(argv[1], "-d"))
{
goto_daemon:
/*
if (daemon(1, 1)) // 守护进程
{
perror("daemon");
return -1;
}
*/
// 守护进程
if ((pid = fork()) < 0) {
return 0;
} else if (0 != pid) {
for(i=1; i<head_argc; i++)
{
if (head_argvs[i])
free(head_argvs[i]);
}
free(t);
free_conf(conf);
free(conf);
free(public_ip);
exit(0);
}
if (setsid() < 0) {
return 0;
}
signal(SIGHUP, SIG_IGN);
if ((pid = fork()) < 0) {
return 0;
} else if (0 != pid) {
for(i=1; i<head_argc; i++)
{
if (head_argvs[i])
free(head_argvs[i]);
}
free(t);
free_conf(conf);
free(conf);
free(public_ip);
exit(0);
}
while (1)
{
// Cron
struct tm *calnext; //取得Cron规则时间
calnext = (struct tm *)malloc(sizeof(struct tm));
memset(calnext, 0, sizeof(struct tm));
_crontab(&calnext, conf->CLAMAV_TIME);
t->next_year = 1900 + calnext->tm_year;
t->next_mon = 1 + calnext->tm_mon;
t->next_day = calnext->tm_mday;
t->next_hour = calnext->tm_hour;
t->next_min = calnext->tm_min;
t->next_sec = calnext->tm_sec;
// 取得现在时间
time_t timep;
struct tm *p;
timep = time(NULL);
p = localtime(&timep);
t->now_year = 1900 + p->tm_year;
t->now_mon = 1 + p->tm_mon;
t->now_day = p->tm_mday;
t->now_hour = p->tm_hour;
t->now_min = p->tm_min;
t->now_sec = p->tm_sec;
// Clamav call
if (1 == conf->CLAMAV)
{
if ( t->now_year == t->next_year && t->now_mon == t->next_mon && t->now_day == t->next_day && t->now_hour == t->next_hour && t->now_min == t->next_min )
{
//printf("%d%d%d %d:%d:%d\n", t->now_year, t->now_mon, t->now_day, t->now_hour, t->now_min, t->now_sec);
//printf("%d%d%d %d:%d:%d\n", t->next_year, t->next_mon, t->next_day, t->next_hour, t->next_min, t->next_sec);
pid_t pid;
pid = fork();
if (pid < 0) {
printf("fork error.\n");
return -1;
}
else if (pid == 0) // child process
{
int r = 0;
r = _clamscan(head_argc, head_argvs);
_exit(r);
}
else
{
int status = 0;
wait(&status); // wait the end of child process
if (WIFEXITED(status))
{
;
//printf("child process return %d\n", WEXITSTATUS(status));
}
sleep(60); // 跳过这一分钟
}
}
}
rule(conf);
sleep(conf->TIME);
}
}
else
{
rule(conf);
}
free(t);
free_conf(conf);
free(conf);
free(public_ip);
for(i=1; i<head_argc; i++)
{
//printf("%s %d\n", head_argvs[i], i);
if (head_argvs[i])
free(head_argvs[i]);
}
return 0;
}