#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 curl_slist *headers = NULL; struct MemoryStruct chunk; chunk.memory = malloc(1); /* 将根据上述再分配的需要增长 */ chunk.size = 0; /* 此时没有数据 */ curl_global_init(CURL_GLOBAL_ALL); /* 初始化curl会话 */ curl_handle = curl_easy_init(); char *p = NULL; char *buff; p = strstr(URL, "-H"); if (p) { buff = (char *)alloca(p - URL + 1); if (buff == NULL) perror("out of memory."); memset(buff, 0, p - URL + 1); memcpy(buff, URL, (int)(p - URL - 1)); // 赋值header值 headers = curl_slist_append(headers, p + 3); // 设置header curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle, CURLOPT_URL, buff); } else { /* 指定要获取的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; } // 解析Json char *process_json(char *buff, char *api) { char *area = NULL; int area_len = 0; char *p = NULL; if (buff == NULL) { return NULL; } cJSON *cjson_init = cJSON_Parse(buff); if (cjson_init == NULL) { perror("cJSON_Parse"); return NULL; } if ((p = strstr(api, "baidu")) != NULL) { // baidu Api int i; cJSON *data = cJSON_GetObjectItem(cjson_init, "data"); if (data != NULL) { for (i = 0; i < cJSON_GetArraySize(data); i++) { cJSON *svalue = cJSON_GetArrayItem(data, i); cJSON *location = cJSON_GetObjectItem(svalue, "location"); area_len = strlen(location->valuestring); area = (char *)alloca(area_len + 1); if (area == NULL) perror("out of memory."); memset(area, 0, area_len + 1); snprintf(area, area_len+1, "%s", location->valuestring); } } else { return NULL; } } else { cJSON_Delete(cjson_init); return NULL; } cJSON_Delete(cjson_init); return strdup(area); } // 检测系统 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, char *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) { fclose(fp); return 1; } #define JSIN "{ \ \"msgtype\": \"text\", \ \"text\": { \ \"content\": \"Alert @%s 服务器地址:%s,封禁非法入侵主机:(%s%s)\" \ }, \ \"at\": { \ \"atMobiles\": [\"%s\"], \ \"isAtAll\": false \ } \ }" snprintf(jsonObj, BUFFER, JSIN, conf->PHONE, temp, ip, 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, char *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'; snprintf(text, BUFFER, "echo \"主机:%s, 禁止(%s%s)访问\" | mail -s \"System ban IP\" %s", temp, ip, 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, char *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'; snprintf(text, BUFFER, "主机:%s, 禁止(%s%s)访问!", temp, ip, illegal_ip); snprintf(string, BUFFER + (sizeof(QQMAIL)) + 1, QQMAIL, conf->RECV_MAIL, text); return system(string); } // 第三方邮箱告警, 感染病毒邮件提醒 int QQ_mail_warning_Virus_files(char *local_ip, int Virus_number, conf * conf) { char *command; char *text; char temp[32]; command = (char *)alloca(BUFFER + (sizeof(QQMAIL)) + 1); text = (char *)alloca(BUFFER); memset(command, 0, BUFFER + (sizeof(QQMAIL)) + 1); memset(text, 0, BUFFER); memset(temp, 0, 32); strcpy(temp, local_ip); temp[strlen(local_ip) - 1] = '\0'; snprintf(text, BUFFER, "Host:%s, Infected files: %d, Please handle!", temp, Virus_number); snprintf(command, BUFFER+BUFFER + (sizeof(QQMAIL)) + 1, QQMAIL_Virus, conf->RECV_MAIL, text); return system(command); } // 第三方邮箱告警, 磁盘使用率 int QQ_mail_warning_Disk_Use(char *local_ip, int disk_use, conf * conf) { char *command; char *text; char temp[32]; command = (char *)alloca(BUFFER + (sizeof(QQMAIL)) + 1); text = (char *)alloca(BUFFER); memset(command, 0, BUFFER + (sizeof(QQMAIL)) + 1); memset(text, 0, BUFFER); memset(temp, 0, 32); strcpy(temp, local_ip); temp[strlen(local_ip) - 1] = '\0'; snprintf(text, BUFFER, "Host:%s, Disk usage reaches threshold!, Please handle!", temp); snprintf(command, BUFFER, QQMAIL_DISK_USE, conf->RECV_MAIL, text); return system(command); } // 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; } //printf("%s %s\n", str, region_list[i]); // 在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; } // 磁盘使用率 int disk_waring(int threshold) { FILE *fp = NULL; char buffer[BUFFER]; char command[BUFFER]; int is = 0; #define DF "for u in `df -mh | grep -E -e \".:.\" -e \"^/dev\" | awk '{print $5}' | sed 's|%%||g'`; do if test \"$u\" -ge %d; then echo \"$u\"; fi done" memset(buffer, 0, BUFFER); memset(command, 0, BUFFER); snprintf(command, BUFFER, DF, threshold); //printf("%s\n", command); fp = popen(command, "r"); while(fgets(buffer, BUFFER, fp) != NULL) { printf("%s", buffer); is = 1; break; } pclose(fp); return is; } // 封禁非法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 REGION_LIST_COPY[conf->REGION_LIST_LEN+1]; char IPV4_WHITE_LIST_COPY[conf->IPV4_WHITE_LIST_LEN+1]; char p_two[2], *command, *splice_command, *temp, buffer[BUFFER], awk[BUFFER]; FILE *fp, *fc; time_t timep; struct tm *tp; long int ip_length = 1; fp = NULL; fc = NULL; timep = time(NULL); tp = localtime(&timep); memset(buffer, 0, BUFFER); memset(awk, 0, BUFFER); memset(p_two, 0, 2); 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; } splice_command = (char *)malloc(ip_length); if (splice_command == NULL) { free(splice_command); return -1; } memset(splice_command, 0, ip_length); while (fgets(buffer, BUFFER, fp) != NULL) { char *new_splice_command; temp = strstr(buffer, "rhost"); sscanf(temp, "rhost=%64s", temp); if (atoi(strncpy(p_two, temp, 1)) > 0) { ip_length += strlen(temp)+1; new_splice_command = (char *)realloc(splice_command, ip_length + 32); if (new_splice_command == NULL) { free(splice_command); return -1; } splice_command = new_splice_command; strcat(splice_command, temp); strcat(splice_command, "\n"); } } printf("%s", splice_command); // 打印所有非法IP //printf("%ld\n", ip_length); command = (char *)malloc(ip_length + BUFFER); if (command == NULL) { free(command); return -1; } memset(command, 0, ip_length + BUFFER); snprintf(awk, BUFFER, AWK, conf->REFUSE_NUMBER); // 拼接命令 memcpy(command, "echo \"", 7); strcat(command, splice_command); strcat(command, "\""); strcat(command, awk); if ((fc = popen(command, "r")) == NULL) // 执行命令 { perror("popen command"); return -1; } if (splice_command != NULL) { free(splice_command); } if (command != NULL) { free(command); } while (fgets(buffer, BUFFER, fc) != NULL) // 执行命令后, 为空时就不会 { buffer[strlen(buffer) - 1] = '\0'; // 去除回车 memset(REGION_LIST_COPY, 0, conf->REGION_LIST_LEN+1); memset(IPV4_WHITE_LIST_COPY, 0, conf->IPV4_WHITE_LIST_LEN+1); memcpy(REGION_LIST_COPY, conf->REGION_LIST, conf->REGION_LIST_LEN); // 复制配置字符串,split_string()会改变原数据 memcpy(IPV4_WHITE_LIST_COPY, conf->IPV4_WHITE_LIST, conf->IPV4_WHITE_LIST_LEN); // split_string(IPV4_WHITE_LIST_COPY, " ", whitelist_ip); split_string(REGION_LIST_COPY, " ", 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_json = NULL; char *area = NULL; char URL[conf->REGION_URL_LEN + 32]; char *xdb_path = "ip2region.xdb"; // 地域白名单 if (conf->REGION == 1) { memset(URL, 0, conf->REGION_URL_LEN + 32); snprintf(URL, conf->REGION_URL_LEN+32, conf->REGION_URL, buffer); if (conf->IP2REGION == 1) { // ip2region 地址定位库 printf("Use ip2region !!!\n"); if (-1 == access(xdb_path, F_OK)) // 判断 ip2region 地址定位库是否存在 { xdb_path = "ip2region/ip2region.xdb"; if (-1 == access(xdb_path, F_OK)) { printf("ip2region.xdb DOESN'T EXISIT!\n"); goto AREA; } } area = ip2region(xdb_path, buffer); if (area == NULL) { printf("ip2region解析地域错误\n"); goto BLOCKED; } } else { AREA: location_json = GET_PUBLIC_IP(URL); if (location_json == NULL) { printf("获取地域错误\n"); goto BLOCKED; } area = process_json(location_json, conf->REGION_URL); if (area == NULL) { printf("解析地域错误\n"); goto BLOCKED; } } if (isregion(area, region_list) == 1) { printf("Ip Address: %s, 地域白名单: %s\n", buffer, area); continue; } } printf("攻击者IP地址:%s, %s\n", buffer, area); if (conf->IS_DING_WEBHOOK == 1) // 钉钉告警 { dingding_warning(area, public_ip, buffer, conf); sleep(3); } if (conf->IS_MAIL == 1) // 邮件告警 { mail_warning(area, public_ip, buffer, conf); sleep(3); } if (conf->IS_QQMAIL == 1) // 邮件告警 { QQ_mail_warning(area, public_ip, buffer, 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 (location_json != NULL) free(location_json); if (area != NULL) free(area); } } 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 *processname_ptr; if (readlink("/proc/self/exe", processdir, len) <= 0) return -1; if ((processname_ptr = strrchr(processdir, '/')) == NULL) return -1; processname_ptr++; strcpy(processname, processname_ptr); *processname_ptr = '\0'; return (int)(processname_ptr - 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; } static int get_clamav_log(char *file) { FILE *fp = NULL; char buffer[BUFFER], *temp=NULL, *command=NULL; command = (char *)alloca(BUFFER); memset(buffer, 0, BUFFER); memset(command, 0, BUFFER); memcpy(command, "tail -n 12 ", 11); strcat(command, file); fp = popen(command, "r"); if (fp == NULL) { perror("popen"); return -1; } while (fgets(buffer, BUFFER, fp) != NULL) { //printf("%s", buffer); temp = strstr(buffer, "Infected"); if (temp) sscanf(temp, "Infected files: %32s", temp); if (temp != NULL) { //printf("%s\n", temp); break; } } pclose(fp); if (temp != NULL) { printf("%d\n", atoi(temp)); return atoi(temp); } else { return -1; } 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; iCLAMAV_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; iPUBLIC_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; iCLAMAV_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; //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("CRON %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); // 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; int virus_files = -1; // 扫描病毒前,更新病毒库 update_freshclam(argc, argv); r = _clamscan(head_argc, head_argvs); virus_files = get_clamav_log("clamscan.log"); if (virus_files > 0) { if (conf->IS_QQMAIL == 1) { QQ_mail_warning_Virus_files(public_ip, virus_files, conf); sleep(3); } } // 磁盘告警 if (1 == conf->IS_DISK) { if (disk_waring(conf->DISK_USE) == 1) { printf("Disk usage reaches threshold!, Please handle!\n"); if (conf->IS_QQMAIL == 1) { QQ_mail_warning_Disk_Use(public_ip, 0, conf); sleep(3); } } else { printf("Disk usage does not reach threshold!\n"); } } _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