#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 main(int argc, char *argv[], char **env) { // 更新病毒库 if (DEBISN_SYSTEM == check_system() || CENTOS_SYSTEM == check_system()) { char **head_argvs; int head_argc = 0; char *argvs[ARGS_NUM] = { NULL }; argvs[0] = argv[0]; argvs[1] = "--user=root"; argvs[2] = "--quiet"; argvs[3] = "--no-warnings"; head_argvs = &(argvs[0]); head_argc = 2; // freshclam配置文件 if (access("/etc/clamav/freshclam.conf", F_OK) == -1) { system("mkdir -p /etc/clamav/"); system("cp freshclam.conf /etc/clamav/"); } _freshclam(head_argc, head_argvs); } signal(SIGCHLD, sig_child); // 创建捕捉子进程退出信号 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; 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; // 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