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 */
Accumulate_Init()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 */
Accumulate(Eventinfo * lf)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
Accumulate_CleanUp()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 */
InitACMStore()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 */
FreeACMStore(OS_ACM_Store * obj)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
acm_str_replace(char ** dst,const char * src)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
340