new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / analysisd / accumulator.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation.
8  */
9
10 /* Accumulator Functions which accumulate objects based on an ID */
11
12 #include <sys/time.h>
13
14 #include "shared.h"
15 #include "accumulator.h"
16 #include "eventinfo.h"
17
18 /* Local variables */
19 static OSHash *acm_store = NULL;
20
21 /* Counters for Purging */
22 static int    acm_lookups = 0;
23 static time_t acm_purge_ts = 0;
24
25 /* Accumulator Constants */
26 #define OS_ACM_EXPIRE_ELM      120
27 #define OS_ACM_PURGE_INTERVAL  300
28 #define OS_ACM_PURGE_COUNT     200
29
30 /* Accumulator Max Values */
31 #define OS_ACM_MAXKEY 256
32 #define OS_ACM_MAXELM 81
33
34 typedef struct _OS_ACM_Store {
35     time_t timestamp;
36     char *dstuser;
37     char *srcuser;
38     char *dstip;
39     char *srcip;
40     char *dstport;
41     char *srcport;
42     char *data;
43 } OS_ACM_Store;
44
45 /* Internal Functions */
46 static int acm_str_replace(char **dst, const char *src);
47 static OS_ACM_Store *InitACMStore(void);
48 static void FreeACMStore(OS_ACM_Store *obj);
49
50 /* Start the Accumulator module */
51 int Accumulate_Init()
52 {
53     struct timeval tp;
54
55     /* Create store data */
56     acm_store = OSHash_Create();
57     if (!acm_store) {
58         merror(LIST_ERROR, ARGV0);
59         return (0);
60     }
61     if (!OSHash_setSize(acm_store, 2048)) {
62         merror(LIST_ERROR, ARGV0);
63         return (0);
64     }
65
66     /* Default Expiry */
67     gettimeofday(&tp, NULL);
68     acm_purge_ts = tp.tv_sec;
69
70     debug1("%s: DEBUG: Accumulator Init completed.", ARGV0);
71     return (1);
72 }
73
74 /* Accumulate data from events sharing the same ID */
75 Eventinfo *Accumulate(Eventinfo *lf)
76 {
77     int result;
78     int do_update = 0;
79
80     char _key[OS_ACM_MAXKEY];
81     OS_ACM_Store *stored_data = 0;
82
83     time_t  current_ts;
84     struct timeval tp;
85
86     if ( lf == NULL ) {
87         debug1("accumulator: DEBUG: Received NULL EventInfo");
88         return lf;
89     }
90     if ( lf->id == NULL ) {
91         debug2("accumulator: DEBUG: No id available");
92         return lf;
93     }
94     if ( lf->decoder_info == NULL ) {
95         debug1("accumulator: DEBUG: No decoder_info available");
96         return lf;
97     }
98     if ( lf->decoder_info->name == NULL ) {
99         debug1("accumulator: DEBUG: No decoder name available");
100         return lf;
101     }
102
103     /* Purge the cache as needed */
104     Accumulate_CleanUp();
105
106     gettimeofday(&tp, NULL);
107     current_ts = tp.tv_sec;
108
109     /* Accumulator Key */
110     result = snprintf(_key, OS_FLSIZE, "%s %s %s",
111                       lf->hostname,
112                       lf->decoder_info->name,
113                       lf->id
114                      );
115     if ( result < 0 || (unsigned) result >= sizeof(_key) ) {
116         debug1("accumulator: DEBUG: error setting accumulator key, id:%s,name:%s", lf->id, lf->decoder_info->name);
117         return lf;
118     }
119
120     /* Check if acm is already present */
121     if ((stored_data = (OS_ACM_Store *)OSHash_Get(acm_store, _key)) != NULL) {
122         debug2("accumulator: DEBUG: Lookup for '%s' found a stored value!", _key);
123
124         if ( stored_data->timestamp > 0 && stored_data->timestamp < current_ts - OS_ACM_EXPIRE_ELM ) {
125             if ( OSHash_Delete(acm_store, _key) != NULL ) {
126                 debug1("accumulator: DEBUG: Deleted expired hash entry for '%s'", _key);
127                 /* Clear this memory */
128                 FreeACMStore(stored_data);
129                 /* Reallocate what we need */
130                 stored_data = InitACMStore();
131             }
132         } else {
133             /* Update the event */
134             do_update = 1;
135             if (acm_str_replace(&lf->dstuser, stored_data->dstuser) == 0) {
136                 debug2("accumulator: DEBUG: (%s) updated lf->dstuser to %s", _key, lf->dstuser);
137             }
138
139             if (acm_str_replace(&lf->srcuser, stored_data->srcuser) == 0) {
140                 debug2("accumulator: DEBUG: (%s) updated lf->srcuser to %s", _key, lf->srcuser);
141             }
142
143             if (acm_str_replace(&lf->dstip, stored_data->dstip) == 0) {
144                 debug2("accumulator: DEBUG: (%s) updated lf->dstip to %s", _key, lf->dstip);
145             }
146
147             if (acm_str_replace(&lf->srcip, stored_data->srcip) == 0) {
148                 debug2("accumulator: DEBUG: (%s) updated lf->srcip to %s", _key, lf->srcip);
149             }
150
151             if (acm_str_replace(&lf->dstport, stored_data->dstport) == 0) {
152                 debug2("accumulator: DEBUG: (%s) updated lf->dstport to %s", _key, lf->dstport);
153             }
154
155             if (acm_str_replace(&lf->srcport, stored_data->srcport) == 0) {
156                 debug2("accumulator: DEBUG: (%s) updated lf->srcport to %s", _key, lf->srcport);
157             }
158
159             if (acm_str_replace(&lf->data, stored_data->data) == 0) {
160                 debug2("accumulator: DEBUG: (%s) updated lf->data to %s", _key, lf->data);
161             }
162         }
163     } else {
164         stored_data = InitACMStore();
165     }
166
167     /* Store the object in the cache */
168     stored_data->timestamp = current_ts;
169     if (acm_str_replace(&stored_data->dstuser, lf->dstuser) == 0) {
170         debug2("accumulator: DEBUG: (%s) updated stored_data->dstuser to %s", _key, stored_data->dstuser);
171     }
172
173     if (acm_str_replace(&stored_data->srcuser, lf->srcuser) == 0) {
174         debug2("accumulator: DEBUG: (%s) updated stored_data->srcuser to %s", _key, stored_data->srcuser);
175     }
176
177     if (acm_str_replace(&stored_data->dstip, lf->dstip) == 0) {
178         debug2("accumulator: DEBUG: (%s) updated stored_data->dstip to %s", _key, stored_data->dstip);
179     }
180
181     if (acm_str_replace(&stored_data->srcip, lf->srcip) == 0) {
182         debug2("accumulator: DEBUG: (%s) updated stored_data->srcip to %s", _key, stored_data->srcip);
183     }
184
185     if (acm_str_replace(&stored_data->dstport, lf->dstport) == 0) {
186         debug2("accumulator: DEBUG: (%s) updated stored_data->dstport to %s", _key, stored_data->dstport);
187     }
188
189     if (acm_str_replace(&stored_data->srcport, lf->srcport) == 0) {
190         debug2("accumulator: DEBUG: (%s) updated stored_data->srcport to %s", _key, stored_data->srcport);
191     }
192
193     if (acm_str_replace(&stored_data->data, lf->data) == 0) {
194         debug2("accumulator: DEBUG: (%s) updated stored_data->data to %s", _key, stored_data->data);
195     }
196
197     /* Update or Add to the hash */
198     if ( do_update == 1 ) {
199         /* Update the hash entry */
200         if ( (result = OSHash_Update(acm_store, _key, stored_data)) != 1) {
201             verbose("accumulator: ERROR: Update of stored data for %s failed (%d).", _key, result);
202         } else {
203             debug1("accumulator: DEBUG: Updated stored data for %s", _key);
204         }
205     } else {
206         if ((result = OSHash_Add(acm_store, _key, stored_data)) != 2 ) {
207             verbose("accumulator: ERROR: Addition of stored data for %s failed (%d).", _key, result);
208         } else {
209             debug1("accumulator: DEBUG: Added stored data for %s", _key);
210         }
211     }
212
213     return lf;
214 }
215
216 void Accumulate_CleanUp()
217 {
218     struct timeval tp;
219     time_t current_ts = 0;
220     int expired = 0;
221
222     OSHashNode *curr;
223     OS_ACM_Store *stored_data;
224     char *key;
225     unsigned int ti;
226
227     /* Keep track of how many times we're called */
228     acm_lookups++;
229
230     gettimeofday(&tp, NULL);
231     current_ts = tp.tv_sec;
232
233     /* Do we really need to purge? */
234     if ( acm_lookups < OS_ACM_PURGE_COUNT && acm_purge_ts < current_ts + OS_ACM_PURGE_INTERVAL ) {
235         return;
236     }
237     debug1("accumulator: DEBUG: Accumulator_CleanUp() running .. ");
238
239     /* Yes, we do */
240     acm_lookups = 0;
241     acm_purge_ts = current_ts;
242
243     /* Loop through the hash */
244     for ( ti = 0; ti < acm_store->rows; ti++ ) {
245         curr = acm_store->table[ti];
246         while ( curr != NULL ) {
247             /* Get the Key and Data */
248             key  = (char *) curr->key;
249             stored_data = (OS_ACM_Store *) curr->data;
250             /* Increment to the next element */
251             curr = curr->next;
252
253             debug2("accumulator: DEBUG: CleanUp() evaluating cached key: %s ", key);
254             /* Check for a valid element */
255             if ( stored_data != NULL ) {
256                 /* Check for expiration */
257                 debug2("accumulator: DEBUG: CleanUp() elm:%ld, curr:%ld", (long int)stored_data->timestamp, (long int)current_ts);
258                 if ( stored_data->timestamp < current_ts - OS_ACM_EXPIRE_ELM ) {
259                     debug2("accumulator: DEBUG: CleanUp() Expiring '%s'", key);
260                     if ( OSHash_Delete(acm_store, key) != NULL ) {
261                         FreeACMStore(stored_data);
262                         expired++;
263                     } else {
264                         debug1("accumulator: DEBUG: CleanUp() failed to find key '%s'", key);
265                     }
266                 }
267             }
268         }
269     }
270     debug1("accumulator: DEBUG: Expired %d elements", expired);
271 }
272
273 /* Initialize a storage object */
274 OS_ACM_Store *InitACMStore()
275 {
276     OS_ACM_Store *obj;
277     os_calloc(1, sizeof(OS_ACM_Store), obj);
278
279     obj->timestamp = 0;
280     obj->srcuser = NULL;
281     obj->dstuser = NULL;
282     obj->srcip = NULL;
283     obj->dstip = NULL;
284     obj->srcport = NULL;
285     obj->dstport = NULL;
286     obj->data = NULL;
287
288     return obj;
289 }
290
291 /* Free an accumulation store struct */
292 void FreeACMStore(OS_ACM_Store *obj)
293 {
294     if ( obj != NULL ) {
295         debug2("accumulator: DEBUG: Freeing an accumulator struct.");
296         free(obj->dstuser);
297         free(obj->srcuser);
298         free(obj->dstip);
299         free(obj->srcip);
300         free(obj->dstport);
301         free(obj->srcport);
302         free(obj->data);
303         free(obj);
304     }
305 }
306
307 int acm_str_replace(char **dst, const char *src)
308 {
309     int result = 0;
310
311     /* Don't overwrite with a null str */
312     if ( src == NULL ) {
313         return -1;
314     }
315
316     /* Don't overwrite something we already know */
317     if (*dst != NULL && **dst != '\0') {
318         return -1;
319     }
320
321     /* Make sure we have data to write */
322     size_t slen = strlen(src);
323     if ( slen <= 0  || slen > OS_ACM_MAXELM - 1 ) {
324         return -1;
325     }
326
327     /* Free dst, and malloc the memory we need! */
328     if ( *dst != NULL ) {
329         free(*dst);
330     }
331     os_malloc(slen + 1, *dst);
332
333     result = strcpy(*dst, src) == NULL ? -1 : 0;
334     if (result < 0) {
335         debug1("accumulator: DEBUG: error in acm_str_replace()");
336     }
337     return result;
338 }
339