new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / analysisd / format / json_extended.c
1 /* Copyright (C) 2015 Wazuh Inc\r
2  * All rights reserved.\r
3  *\r
4  */\r
5 \r
6 #include "json_extended.h"\r
7 #include <stddef.h>\r
8 \r
9 #define MAX_MATCHES 10\r
10 #define MAX_STRING 1024\r
11 #define MAX_STRING_LESS 30\r
12 \r
13 void W_ParseJSON(cJSON* root, const Eventinfo* lf)\r
14 {\r
15 \r
16     // Parse hostname & Parse AGENTIP\r
17     if(lf->hostname) {\r
18         W_JSON_ParseHostname(root, lf->hostname);\r
19         W_JSON_ParseAgentIP(root, lf);\r
20     }\r
21     // Parse timestamp\r
22     if(lf->year && lf->mon && lf->day && lf->hour) {\r
23         W_JSON_ParseTimestamp(root, lf);\r
24     }\r
25     // Parse Location\r
26     if(lf->location) {\r
27         W_JSON_ParseLocation(root, lf, 0);\r
28     }\r
29     // Parse groups && Parse PCIDSS && Parse CIS\r
30     if(lf->generated_rule->group) {\r
31         W_JSON_ParseGroups(root, lf, 1);\r
32     }\r
33     // Parse CIS and PCIDSS rules from rootcheck .txt benchmarks\r
34     if(lf->full_log && W_isRootcheck(root, 1)) {\r
35         W_JSON_ParseRootcheck(root, lf, 1);\r
36     }\r
37 }\r
38 \r
39 \r
40 // Detect if the alert is coming from rootcheck controls.\r
41 \r
42 int W_isRootcheck(cJSON* root, int nested)\r
43 {\r
44     cJSON* groups;\r
45     cJSON* group;\r
46     cJSON* rule;\r
47     char* group_json;\r
48 \r
49     int totalGroups, i;\r
50 \r
51     if(!nested)\r
52         rule = root;\r
53     else\r
54         rule = cJSON_GetObjectItem(root, "rule");\r
55 \r
56     groups = cJSON_GetObjectItem(rule, "groups");\r
57     totalGroups = cJSON_GetArraySize(groups);\r
58     for(i = 0; i < totalGroups; i++) {\r
59         group = cJSON_GetArrayItem(groups, i);\r
60         group_json = cJSON_Print(group);\r
61         if(strcmp(group_json, "\"rootcheck\"") == 0) {\r
62             free(group_json);\r
63             return 1;\r
64         }\r
65         free(group_json);\r
66     }\r
67     return 0;\r
68 }\r
69 \r
70 \r
71 // Getting security compliance field from rootcheck rules benchmarks .txt\r
72 void W_JSON_ParseRootcheck(cJSON* root, const Eventinfo* lf, int nested)\r
73 {\r
74     regex_t r;\r
75     cJSON* rule;\r
76     cJSON* compliance;\r
77     const char* regex_text;\r
78     const char* find_text;\r
79     char* token;\r
80     char* token2;\r
81     char* results[MAX_MATCHES];\r
82     int matches, i, j;\r
83     const char delim[2] = ":";\r
84     const char delim2[2] = ",";\r
85     char fullog[MAX_STRING];\r
86 \r
87     // Allocate memory\r
88     for(i = 0; i < MAX_MATCHES; i++)\r
89         results[i] = malloc((MAX_STRING_LESS) * sizeof(char));\r
90 \r
91     // Getting groups object JSON\r
92     if(!nested)\r
93         rule = root;\r
94     else\r
95         rule = cJSON_GetObjectItem(root, "rule");\r
96 \r
97     // Getting full log string\r
98     strncpy(fullog, lf->full_log, MAX_STRING - 1);\r
99     // Searching regex\r
100     regex_text = "\\{([A-Za-z0-9_]*: [A-Za-z0-9_., ]*)\\}";\r
101     find_text = fullog;\r
102     compile_regex(&r, regex_text);\r
103     matches = match_regex(&r, find_text, results);\r
104 \r
105     if(matches > 0) {\r
106         for(i = 0; i < matches; i++) {\r
107             token = strtok(results[i], delim);\r
108 \r
109             trim(token);\r
110             cJSON_AddItemToObject(rule, token, compliance = cJSON_CreateArray());\r
111             for(j = 0; token[j]; j++) {\r
112                 token[j] = tolower(token[j]);\r
113             }\r
114             if(token) {\r
115                 token = strtok(0, delim);\r
116                 trim(token);\r
117                 token2 = strtok(token, delim2);\r
118                 while(token2) {\r
119 \r
120                     trim(token2);\r
121                     cJSON_AddItemToArray(compliance, cJSON_CreateString(token2));\r
122                     token2 = strtok(0, delim2);\r
123                 }\r
124             }\r
125         }\r
126      }\r
127     regfree(&r);\r
128     for(i = 0; i < MAX_MATCHES; i++)\r
129          free(results[i]);\r
130\r
131 \r
132 \r
133  \r
134 // STRTOK every "-" delimiter to get differents groups to our json array. \r
135 void W_JSON_ParseGroups(cJSON* root, const Eventinfo* lf, int nested)\r
136 {\r
137     cJSON* groups;\r
138     cJSON* rule;\r
139     int firstPCI, firstCIS, foundCIS, foundPCI;\r
140     char delim[2];\r
141     char buffer[MAX_STRING];\r
142     char* token;\r
143 \r
144     firstPCI = firstCIS = 1;\r
145     foundPCI = foundCIS = 0;\r
146     delim[0] = ',';\r
147     delim[1] = 0;\r
148 \r
149     if(!nested)\r
150         rule = root;\r
151     else\r
152         rule = cJSON_GetObjectItem(root, "rule");\r
153 \r
154     cJSON_AddItemToObject(rule, "groups", groups = cJSON_CreateArray());\r
155     strncpy(buffer, lf->generated_rule->group, sizeof(buffer) - 1);\r
156 \r
157     token = strtok(buffer, delim);\r
158     while(token) {\r
159         foundPCI = foundCIS = 0;\r
160         foundPCI = add_groupPCI(rule, token, firstPCI);\r
161         if(!foundPCI)\r
162             foundCIS = add_groupCIS(rule, token, firstCIS);\r
163 \r
164         if(foundPCI && firstPCI)\r
165             firstPCI = 0;\r
166         if(foundCIS && firstCIS)\r
167             firstCIS = 0;\r
168 \r
169         if(!foundPCI && !foundCIS) {\r
170             cJSON_AddItemToArray(groups, cJSON_CreateString(token));\r
171         }\r
172         token = strtok(0, delim);\r
173     }\r
174 }\r
175 \r
176 \r
177  // Parse groups PCI\r
178 int add_groupPCI(cJSON* rule, char* group, int firstPCI)\r
179 {\r
180     //char* len = NULL;\r
181     cJSON* pci;\r
182     char aux[strlen(group)];\r
183     // If group begin with pci_dss_ we have a PCI group\r
184     if((startsWith("pci_dss_", group)) == 1) {\r
185         // Once we add pci_dss group and create array for PCI_DSS requirements\r
186         if(firstPCI == 1) {\r
187             pci = cJSON_CreateArray();\r
188             cJSON_AddItemToObject(rule, "PCI_DSS", pci);\r
189         } else {\r
190             pci = cJSON_GetObjectItem(rule, "PCI_DSS");\r
191         }\r
192         // Prepare string and add it to PCI dss array\r
193         strncpy(aux, group, strlen(group) - 1 );\r
194         str_cut(aux, 0, 8);\r
195         cJSON_AddItemToArray(pci, cJSON_CreateString(aux));\r
196         return 1;\r
197     }\r
198     return 0;\r
199 }\r
200 \r
201 \r
202 int add_groupCIS(cJSON* rule, char* group, int firstCIS)\r
203 {\r
204     //char* len = NULL;\r
205     cJSON* cis;\r
206     char aux[strlen(group)];\r
207     if((startsWith("cis_", group)) == 1) {\r
208         if(firstCIS == 1) {\r
209             cis = cJSON_CreateArray();\r
210             cJSON_AddItemToObject(rule, "CIS", cis);\r
211         } else {\r
212             cis = cJSON_GetObjectItem(rule, "CIS");\r
213         }\r
214         strncpy(aux, group, strlen(group) - 1);\r
215         str_cut(aux, 0, 4);\r
216         cJSON_AddItemToArray(cis, cJSON_CreateString(aux));\r
217         return 1;\r
218     }\r
219     return 0;\r
220 }\r
221 \r
222  \r
223 // If hostname being with "(" means that alerts came from an agent, so we will remove the brakets\r
224 // ** TODO ** Regex instead str_cut\r
225 void W_JSON_ParseHostname(cJSON* root, char* hostname)\r
226 {\r
227     if(hostname[0] == '(') {\r
228         char* search;\r
229         char string[MAX_STRING];\r
230         strncpy(string, hostname, MAX_STRING - 1);\r
231         int index;\r
232         search = strchr(string, ')');\r
233         if(search) {\r
234             index = (int)(search - string);\r
235             str_cut(string, index, -1);\r
236             str_cut(string, 0, 1);\r
237             cJSON_AddStringToObject(root, "hostname", string);\r
238         }\r
239     } else {\r
240         cJSON_AddStringToObject(root, "hostname", hostname);\r
241     }\r
242 }\r
243 // Parse timestamp\r
244 void W_JSON_ParseTimestamp(cJSON* root, const Eventinfo* lf)\r
245 {\r
246     char* dateTimestamp = malloc(21);\r
247     sprintf(dateTimestamp, "%d %s %02d %s", lf->year, lf->mon, lf->day, lf->hour);\r
248     cJSON_AddStringToObject(root, "timestamp", dateTimestamp);\r
249     free(dateTimestamp);\r
250 }\r
251 \r
252  \r
253 // The IP of an agent usually comes in "hostname" field, we will extract it.\r
254 // ** TODO ** Regex instead str_cut\r
255 void W_JSON_ParseAgentIP(cJSON* root, const Eventinfo* lf)\r
256 {\r
257     if(lf->hostname[0] == '(') {\r
258         char* search;\r
259         char string[MAX_STRING];\r
260         strncpy(string, lf->hostname, MAX_STRING - 1);\r
261         int index;\r
262         search = strchr(string, ')');\r
263         if(search) {\r
264             index = (int)(search - string);\r
265             str_cut(string, 0, index);\r
266             str_cut(string, 0, 2);\r
267             search = strchr(string, '-');\r
268             index = (int)(search - string);\r
269             str_cut(string, index, -1);\r
270             cJSON_AddStringToObject(root, "agentip", string);\r
271         }\r
272          \r
273     }\r
274 }\r
275  // 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
276 void W_JSON_ParseLocation(cJSON* root, const Eventinfo* lf, int archives)\r
277 {\r
278     if(lf->location[0] == '(') {\r
279         char* search;\r
280         char string[MAX_STRING];\r
281         strncpy(string, lf->location, MAX_STRING - 1);\r
282         int index;\r
283         search = strchr(string, '>');\r
284         if(search) {\r
285             index = (int)(search - string);\r
286             str_cut(string, 0, index);\r
287             str_cut(string, 0, 1);\r
288 \r
289             if(archives == 1)\r
290                 cJSON_AddStringToObject(root, "location_desc", string);\r
291             else\r
292                 cJSON_AddStringToObject(root, "location", string);\r
293         }\r
294     } else {\r
295         if(archives == 1)\r
296             cJSON_AddStringToObject(root, "location_desc", lf->location);\r
297         else\r
298             cJSON_AddStringToObject(root, "location", lf->location);\r
299     }\r
300 }\r
301 \r
302 \r
303 #define MAX_ERROR_MSG 0x1000\r
304 // Regex compilator\r
305 int compile_regex(regex_t* r, const char* regex_text)\r
306 {\r
307     int status = regcomp(r, regex_text, REG_EXTENDED | REG_NEWLINE);\r
308     if(status != 0) {\r
309         char error_message[MAX_ERROR_MSG];\r
310         regerror(status, r, error_message, MAX_ERROR_MSG);\r
311         debug1("Regex error compiling '%s': %s\n", regex_text, error_message);\r
312         return 1;\r
313     }\r
314     return 0;\r
315 }\r
316 \r
317 int match_regex(regex_t* r, const char* to_match, char* results[MAX_MATCHES])\r
318 {\r
319     const char* p = to_match;\r
320     const int n_matches = 10;\r
321     regmatch_t m[n_matches];\r
322     int totalResults = 0;\r
323     while(1) {\r
324         int i = 0;\r
325         int nomatch = regexec(r, p, n_matches, m, 0);\r
326         if(nomatch) {\r
327             //printf ("No more matches.\n");\r
328             return totalResults;\r
329         }\r
330         for(i = 0; i < n_matches; i++) {\r
331             int start;\r
332             int finish;\r
333             if(m[i].rm_so == -1) {\r
334 \r
335                 break;\r
336             }\r
337             start = m[i].rm_so + (p - to_match);\r
338             finish = m[i].rm_eo + (p - to_match);\r
339             if(i > 0) {\r
340                 sprintf(results[totalResults], "%.*s", (finish - start), to_match + start);\r
341                 totalResults = totalResults + 1;\r
342             }\r
343             \r
344         }\r
345         p += m[0].rm_eo;\r
346     }\r
347     return 0;\r
348 }\r
349 \r
350 int str_cut(char* str, int begin, int len)\r
351 {\r
352     int l = strlen(str);\r
353 \r
354     if(len < 0)\r
355         len = l - begin;\r
356     if(begin + len > l)\r
357         len = l - begin;\r
358     memmove(str + begin, str + begin + len, l - len + 1);\r
359 \r
360     return len;\r
361 }\r
362 \r
363 void trim(char* s)\r
364 {\r
365     char* p = s;\r
366     int l = strlen(p);\r
367 \r
368     while(isspace(p[l - 1]))\r
369         p[--l] = 0;\r
370     while(*p && isspace(*p))\r
371         ++p, --l;\r
372 \r
373     memmove(s, p, l + 1);\r
374 }\r
375 \r
376 int startsWith(const char *pre, const char *str)\r
377 {\r
378     size_t lenpre = strlen(pre),\r
379            lenstr = strlen(str);\r
380     return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0;\r
381 }\r
382 \r