--- /dev/null
+/* Copyright (C) 2015 Wazuh Inc\r
+ * All rights reserved.\r
+ *\r
+ */\r
+\r
+#include "json_extended.h"\r
+#include <stddef.h>\r
+\r
+#define MAX_MATCHES 10\r
+#define MAX_STRING 1024\r
+#define MAX_STRING_LESS 30\r
+\r
+void W_ParseJSON(cJSON* root, const Eventinfo* lf)\r
+{\r
+\r
+ // Parse hostname & Parse AGENTIP\r
+ if(lf->hostname) {\r
+ W_JSON_ParseHostname(root, lf->hostname);\r
+ W_JSON_ParseAgentIP(root, lf);\r
+ }\r
+ // Parse timestamp\r
+ if(lf->year && lf->mon && lf->day && lf->hour) {\r
+ W_JSON_ParseTimestamp(root, lf);\r
+ }\r
+ // Parse Location\r
+ if(lf->location) {\r
+ W_JSON_ParseLocation(root, lf, 0);\r
+ }\r
+ // Parse groups && Parse PCIDSS && Parse CIS\r
+ if(lf->generated_rule->group) {\r
+ W_JSON_ParseGroups(root, lf, 1);\r
+ }\r
+ // Parse CIS and PCIDSS rules from rootcheck .txt benchmarks\r
+ if(lf->full_log && W_isRootcheck(root, 1)) {\r
+ W_JSON_ParseRootcheck(root, lf, 1);\r
+ }\r
+}\r
+\r
+\r
+// Detect if the alert is coming from rootcheck controls.\r
+\r
+int W_isRootcheck(cJSON* root, int nested)\r
+{\r
+ cJSON* groups;\r
+ cJSON* group;\r
+ cJSON* rule;\r
+ char* group_json;\r
+\r
+ int totalGroups, i;\r
+\r
+ if(!nested)\r
+ rule = root;\r
+ else\r
+ rule = cJSON_GetObjectItem(root, "rule");\r
+\r
+ groups = cJSON_GetObjectItem(rule, "groups");\r
+ totalGroups = cJSON_GetArraySize(groups);\r
+ for(i = 0; i < totalGroups; i++) {\r
+ group = cJSON_GetArrayItem(groups, i);\r
+ group_json = cJSON_Print(group);\r
+ if(strcmp(group_json, "\"rootcheck\"") == 0) {\r
+ free(group_json);\r
+ return 1;\r
+ }\r
+ free(group_json);\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+// Getting security compliance field from rootcheck rules benchmarks .txt\r
+void W_JSON_ParseRootcheck(cJSON* root, const Eventinfo* lf, int nested)\r
+{\r
+ regex_t r;\r
+ cJSON* rule;\r
+ cJSON* compliance;\r
+ const char* regex_text;\r
+ const char* find_text;\r
+ char* token;\r
+ char* token2;\r
+ char* results[MAX_MATCHES];\r
+ int matches, i, j;\r
+ const char delim[2] = ":";\r
+ const char delim2[2] = ",";\r
+ char fullog[MAX_STRING];\r
+\r
+ // Allocate memory\r
+ for(i = 0; i < MAX_MATCHES; i++)\r
+ results[i] = malloc((MAX_STRING_LESS) * sizeof(char));\r
+\r
+ // Getting groups object JSON\r
+ if(!nested)\r
+ rule = root;\r
+ else\r
+ rule = cJSON_GetObjectItem(root, "rule");\r
+\r
+ // Getting full log string\r
+ strncpy(fullog, lf->full_log, MAX_STRING - 1);\r
+ // Searching regex\r
+ regex_text = "\\{([A-Za-z0-9_]*: [A-Za-z0-9_., ]*)\\}";\r
+ find_text = fullog;\r
+ compile_regex(&r, regex_text);\r
+ matches = match_regex(&r, find_text, results);\r
+\r
+ if(matches > 0) {\r
+ for(i = 0; i < matches; i++) {\r
+ token = strtok(results[i], delim);\r
+\r
+ trim(token);\r
+ cJSON_AddItemToObject(rule, token, compliance = cJSON_CreateArray());\r
+ for(j = 0; token[j]; j++) {\r
+ token[j] = tolower(token[j]);\r
+ }\r
+ if(token) {\r
+ token = strtok(0, delim);\r
+ trim(token);\r
+ token2 = strtok(token, delim2);\r
+ while(token2) {\r
+\r
+ trim(token2);\r
+ cJSON_AddItemToArray(compliance, cJSON_CreateString(token2));\r
+ token2 = strtok(0, delim2);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ regfree(&r);\r
+ for(i = 0; i < MAX_MATCHES; i++)\r
+ free(results[i]);\r
+} \r
+\r
+\r
+ \r
+// STRTOK every "-" delimiter to get differents groups to our json array. \r
+void W_JSON_ParseGroups(cJSON* root, const Eventinfo* lf, int nested)\r
+{\r
+ cJSON* groups;\r
+ cJSON* rule;\r
+ int firstPCI, firstCIS, foundCIS, foundPCI;\r
+ char delim[2];\r
+ char buffer[MAX_STRING];\r
+ char* token;\r
+\r
+ firstPCI = firstCIS = 1;\r
+ foundPCI = foundCIS = 0;\r
+ delim[0] = ',';\r
+ delim[1] = 0;\r
+\r
+ if(!nested)\r
+ rule = root;\r
+ else\r
+ rule = cJSON_GetObjectItem(root, "rule");\r
+\r
+ cJSON_AddItemToObject(rule, "groups", groups = cJSON_CreateArray());\r
+ strncpy(buffer, lf->generated_rule->group, sizeof(buffer) - 1);\r
+\r
+ token = strtok(buffer, delim);\r
+ while(token) {\r
+ foundPCI = foundCIS = 0;\r
+ foundPCI = add_groupPCI(rule, token, firstPCI);\r
+ if(!foundPCI)\r
+ foundCIS = add_groupCIS(rule, token, firstCIS);\r
+\r
+ if(foundPCI && firstPCI)\r
+ firstPCI = 0;\r
+ if(foundCIS && firstCIS)\r
+ firstCIS = 0;\r
+\r
+ if(!foundPCI && !foundCIS) {\r
+ cJSON_AddItemToArray(groups, cJSON_CreateString(token));\r
+ }\r
+ token = strtok(0, delim);\r
+ }\r
+}\r
+\r
+\r
+ // Parse groups PCI\r
+int add_groupPCI(cJSON* rule, char* group, int firstPCI)\r
+{\r
+ //char* len = NULL;\r
+ cJSON* pci;\r
+ char aux[strlen(group)];\r
+ // If group begin with pci_dss_ we have a PCI group\r
+ if((startsWith("pci_dss_", group)) == 1) {\r
+ // Once we add pci_dss group and create array for PCI_DSS requirements\r
+ if(firstPCI == 1) {\r
+ pci = cJSON_CreateArray();\r
+ cJSON_AddItemToObject(rule, "PCI_DSS", pci);\r
+ } else {\r
+ pci = cJSON_GetObjectItem(rule, "PCI_DSS");\r
+ }\r
+ // Prepare string and add it to PCI dss array\r
+ strncpy(aux, group, strlen(group) - 1 );\r
+ str_cut(aux, 0, 8);\r
+ cJSON_AddItemToArray(pci, cJSON_CreateString(aux));\r
+ return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+int add_groupCIS(cJSON* rule, char* group, int firstCIS)\r
+{\r
+ //char* len = NULL;\r
+ cJSON* cis;\r
+ char aux[strlen(group)];\r
+ if((startsWith("cis_", group)) == 1) {\r
+ if(firstCIS == 1) {\r
+ cis = cJSON_CreateArray();\r
+ cJSON_AddItemToObject(rule, "CIS", cis);\r
+ } else {\r
+ cis = cJSON_GetObjectItem(rule, "CIS");\r
+ }\r
+ strncpy(aux, group, strlen(group) - 1);\r
+ str_cut(aux, 0, 4);\r
+ cJSON_AddItemToArray(cis, cJSON_CreateString(aux));\r
+ return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+ \r
+// If hostname being with "(" means that alerts came from an agent, so we will remove the brakets\r
+// ** TODO ** Regex instead str_cut\r
+void W_JSON_ParseHostname(cJSON* root, char* hostname)\r
+{\r
+ if(hostname[0] == '(') {\r
+ char* search;\r
+ char string[MAX_STRING];\r
+ strncpy(string, hostname, MAX_STRING - 1);\r
+ int index;\r
+ search = strchr(string, ')');\r
+ if(search) {\r
+ index = (int)(search - string);\r
+ str_cut(string, index, -1);\r
+ str_cut(string, 0, 1);\r
+ cJSON_AddStringToObject(root, "hostname", string);\r
+ }\r
+ } else {\r
+ cJSON_AddStringToObject(root, "hostname", hostname);\r
+ }\r
+}\r
+// Parse timestamp\r
+void W_JSON_ParseTimestamp(cJSON* root, const Eventinfo* lf)\r
+{\r
+ char* dateTimestamp = malloc(21);\r
+ sprintf(dateTimestamp, "%d %s %02d %s", lf->year, lf->mon, lf->day, lf->hour);\r
+ cJSON_AddStringToObject(root, "timestamp", dateTimestamp);\r
+ free(dateTimestamp);\r
+}\r
+\r
+ \r
+// The IP of an agent usually comes in "hostname" field, we will extract it.\r
+// ** TODO ** Regex instead str_cut\r
+void W_JSON_ParseAgentIP(cJSON* root, const Eventinfo* lf)\r
+{\r
+ if(lf->hostname[0] == '(') {\r
+ char* search;\r
+ char string[MAX_STRING];\r
+ strncpy(string, lf->hostname, MAX_STRING - 1);\r
+ int index;\r
+ search = strchr(string, ')');\r
+ if(search) {\r
+ index = (int)(search - string);\r
+ str_cut(string, 0, index);\r
+ str_cut(string, 0, 2);\r
+ search = strchr(string, '-');\r
+ index = (int)(search - string);\r
+ str_cut(string, index, -1);\r
+ cJSON_AddStringToObject(root, "agentip", string);\r
+ }\r
+ \r
+ }\r
+}\r
+ // The file location usually comes with more information about the alert (like hostname or ip) we will extract just the "/var/folder/file.log".\r
+void W_JSON_ParseLocation(cJSON* root, const Eventinfo* lf, int archives)\r
+{\r
+ if(lf->location[0] == '(') {\r
+ char* search;\r
+ char string[MAX_STRING];\r
+ strncpy(string, lf->location, MAX_STRING - 1);\r
+ int index;\r
+ search = strchr(string, '>');\r
+ if(search) {\r
+ index = (int)(search - string);\r
+ str_cut(string, 0, index);\r
+ str_cut(string, 0, 1);\r
+\r
+ if(archives == 1)\r
+ cJSON_AddStringToObject(root, "location_desc", string);\r
+ else\r
+ cJSON_AddStringToObject(root, "location", string);\r
+ }\r
+ } else {\r
+ if(archives == 1)\r
+ cJSON_AddStringToObject(root, "location_desc", lf->location);\r
+ else\r
+ cJSON_AddStringToObject(root, "location", lf->location);\r
+ }\r
+}\r
+\r
+\r
+#define MAX_ERROR_MSG 0x1000\r
+// Regex compilator\r
+int compile_regex(regex_t* r, const char* regex_text)\r
+{\r
+ int status = regcomp(r, regex_text, REG_EXTENDED | REG_NEWLINE);\r
+ if(status != 0) {\r
+ char error_message[MAX_ERROR_MSG];\r
+ regerror(status, r, error_message, MAX_ERROR_MSG);\r
+ debug1("Regex error compiling '%s': %s\n", regex_text, error_message);\r
+ return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+int match_regex(regex_t* r, const char* to_match, char* results[MAX_MATCHES])\r
+{\r
+ const char* p = to_match;\r
+ const int n_matches = 10;\r
+ regmatch_t m[n_matches];\r
+ int totalResults = 0;\r
+ while(1) {\r
+ int i = 0;\r
+ int nomatch = regexec(r, p, n_matches, m, 0);\r
+ if(nomatch) {\r
+ //printf ("No more matches.\n");\r
+ return totalResults;\r
+ }\r
+ for(i = 0; i < n_matches; i++) {\r
+ int start;\r
+ int finish;\r
+ if(m[i].rm_so == -1) {\r
+\r
+ break;\r
+ }\r
+ start = m[i].rm_so + (p - to_match);\r
+ finish = m[i].rm_eo + (p - to_match);\r
+ if(i > 0) {\r
+ sprintf(results[totalResults], "%.*s", (finish - start), to_match + start);\r
+ totalResults = totalResults + 1;\r
+ }\r
+ \r
+ }\r
+ p += m[0].rm_eo;\r
+ }\r
+ return 0;\r
+}\r
+\r
+int str_cut(char* str, int begin, int len)\r
+{\r
+ int l = strlen(str);\r
+\r
+ if(len < 0)\r
+ len = l - begin;\r
+ if(begin + len > l)\r
+ len = l - begin;\r
+ memmove(str + begin, str + begin + len, l - len + 1);\r
+\r
+ return len;\r
+}\r
+\r
+void trim(char* s)\r
+{\r
+ char* p = s;\r
+ int l = strlen(p);\r
+\r
+ while(isspace(p[l - 1]))\r
+ p[--l] = 0;\r
+ while(*p && isspace(*p))\r
+ ++p, --l;\r
+\r
+ memmove(s, p, l + 1);\r
+}\r
+\r
+int startsWith(const char *pre, const char *str)\r
+{\r
+ size_t lenpre = strlen(pre),\r
+ lenstr = strlen(str);\r
+ return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0;\r
+}\r
+\r