new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / analysisd / stats.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All right 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 #include "shared.h"
11 #include "analysisd.h"
12 #include "stats.h"
13 #include "rules.h"
14 #include "error_messages/error_messages.h"
15 #include "headers/file_op.h"
16 #include "alerts/alerts.h"
17 #include "headers/debug_op.h"
18
19 /* Global definition */
20 char __stats_comment[192];
21
22 static const char *(weekdays[]) = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
23                       "Friday", "Saturday"
24                      };
25
26 static const char *(l_month[]) = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
27                      "Sep", "Oct", "Nov", "Dec"
28                     };
29
30 /* Global variables */
31
32 /* Hour 25 is internally used */
33 static int _RWHour[7][25];
34 static int _CWHour[7][25];
35
36 static int _RHour[25];
37 static int _CHour[25];
38
39 static int _cignorehour = 0;
40 static int _fired = 0;
41 static int _daily_errors = 0;
42 static int maxdiff = 0;
43 static int mindiff = 0;
44 static int percent_diff = 20;
45
46 /* Last msgs, to avoid floods */
47 static char *_lastmsg;
48 static char *_prevlast;
49 static char *_pprevlast;
50
51
52 static void print_totals(void)
53 {
54     int i, totals = 0;
55     char logfile[OS_FLSIZE + 1];
56     FILE *flog;
57
58     /* Create the path for the logs */
59     snprintf(logfile, OS_FLSIZE, "%s/%d/", STATSAVED, prev_year);
60     if (IsDir(logfile) == -1)
61         if (mkdir(logfile, 0770) == -1) {
62             merror(MKDIR_ERROR, ARGV0, logfile, errno, strerror(errno));
63             return;
64         }
65
66     snprintf(logfile, OS_FLSIZE, "%s/%d/%s", STATSAVED, prev_year, prev_month);
67
68     if (IsDir(logfile) == -1)
69         if (mkdir(logfile, 0770) == -1) {
70             merror(MKDIR_ERROR, ARGV0, logfile, errno, strerror(errno));
71             return;
72         }
73
74     /* Create the logfile name */
75     snprintf(logfile, OS_FLSIZE, "%s/%d/%s/ossec-%s-%02d.log",
76              STATSAVED,
77              prev_year,
78              prev_month,
79              "totals",
80              today);
81
82     flog = fopen(logfile, "a");
83     if (!flog) {
84         merror(FOPEN_ERROR, ARGV0, logfile, errno, strerror(errno));
85         return;
86     }
87
88     /* Print the hourly stats */
89     for (i = 0; i <= 23; i++) {
90         fprintf(flog, "Hour totals - %d:%d\n", i, _CHour[i]);
91         totals += _CHour[i];
92     }
93     fprintf(flog, "Total events for day:%d\n", totals);
94
95     fclose(flog);
96 }
97
98 /* Return the parameter (event_number + 20 % of it)
99  * If event_number < mindiff, return mindiff
100  * If event_number > maxdiff, return maxdiff
101  */
102 static int gethour(int event_number)
103 {
104     int event_diff;
105
106     event_diff = (event_number * percent_diff) / 100;
107     event_diff++;
108
109     if (event_diff < mindiff) {
110         return (event_number + mindiff);
111     } else if (event_diff > maxdiff) {
112         return (event_number + maxdiff);
113     }
114
115     return (event_number + event_diff);
116 }
117
118 /* Update_Hour: done daily */
119 void Update_Hour()
120 {
121     int i, j;
122     int inter;
123
124     /* Print total number of logs received per hour */
125     print_totals();
126
127     /* Hourly update */
128     _RHour[24]++;
129     inter = _RHour[24];
130     if (inter > 7) {
131         inter = 7;
132     }
133
134     for (i = 0; i <= 24; i++) {
135         char _hourly[128]; /* _hourly file */
136
137         FILE *fp;
138
139         if (i != 24) {
140             /* If saved hourly = 0, just copy the current hourly rate */
141             if (_CHour[i] == 0) {
142                 continue;
143             }
144
145             if (_RHour[i] == 0) {
146                 _RHour[i] = _CHour[i] + 20;
147             }
148
149             else {
150                 /* If we had too many errors this day */
151                 if (_daily_errors >= 3) {
152                     _RHour[i] = (((3 * _CHour[i]) + (inter * _RHour[i])) / (inter + 3)) + 25;
153                 }
154
155                 else {
156                     /* The average is going to be the number of interactions +
157                      * the current hourly rate, divided by 4 */
158                     _RHour[i] = ((_CHour[i] + (inter * _RHour[i])) / (inter + 1)) + 5;
159                 }
160             }
161         }
162
163         snprintf(_hourly, 128, "%s/%d", STATQUEUE, i);
164         fp = fopen(_hourly, "w");
165         if (fp) {
166             fprintf(fp, "%d", _RHour[i]);
167             fclose(fp);
168         }
169
170         else {
171             merror(FOPEN_ERROR, "logstats", _hourly, errno, strerror(errno));
172         }
173
174         _CHour[i] = 0; /* Zero the current hour */
175     }
176
177     /* Weekly */
178     for (i = 0; i <= 6; i++) {
179         char _weekly[128];
180         FILE *fp;
181
182         _CWHour[i][24]++;
183         inter = _CWHour[i][24];
184         if (inter > 7) {
185             inter = 7;
186         }
187
188         for (j = 0; j <= 24; j++) {
189             if (j != 24) {
190                 if (_CWHour[i][j] == 0) {
191                     continue;
192                 }
193
194                 if (_RWHour[i][j] == 0) {
195                     _RWHour[i][j] = _CWHour[i][j] + 20;
196                 }
197
198                 else {
199                     if (_daily_errors >= 3) {
200                         _RWHour[i][j] = (((3 * _CWHour[i][j]) + (inter * _RWHour[i][j])) / (inter + 3)) + 25;
201                     } else {
202                         _RWHour[i][j] = ((_CWHour[i][j] + (inter * _RWHour[i][j])) / (inter + 1)) + 5;
203                     }
204                 }
205             }
206
207             snprintf(_weekly, 128, "%s/%d/%d", STATWQUEUE, i, j);
208             fp = fopen(_weekly, "w");
209             if (fp) {
210                 fprintf(fp, "%d", _RWHour[i][j]);
211                 fclose(fp);
212             } else {
213                 merror(FOPEN_ERROR, "logstats", _weekly, errno, strerror(errno));
214             }
215
216             _CWHour[i][j] = 0;
217         }
218     }
219
220     _daily_errors = 0;
221     return;
222 }
223
224 /* Check Hourly stats */
225 int Check_Hour()
226 {
227     _CHour[__crt_hour]++;
228     _CWHour[__crt_wday][__crt_hour]++;
229
230     if (_RHour[24] <= 2) {
231         return (0);
232     }
233
234     /* Checking if any message was already fired for this hour */
235     if ((_daily_errors >= 3) || ((_fired == 1) && (_cignorehour == __crt_hour))) {
236         return (0);
237     }
238
239     else if (_cignorehour != __crt_hour) {
240         _cignorehour = __crt_hour;
241         _fired = 0;
242     }
243
244     /* Check if passed the threshold */
245     if (_RHour[__crt_hour] != 0) {
246         if (_CHour[__crt_hour] > (_RHour[__crt_hour])) {
247             if (_CHour[__crt_hour] > (gethour(_RHour[__crt_hour]))) {
248                 /* snprintf will null terminate */
249                 snprintf(__stats_comment, 191,
250                          "The average number of logs"
251                          " between %d:00 and %d:00 is %d. We "
252                          "reached %d.", __crt_hour, __crt_hour + 1,
253                          _RHour[__crt_hour], _CHour[__crt_hour]);
254
255
256                 _fired = 1;
257                 _daily_errors++;
258                 return (1);
259             }
260         }
261     }
262
263     /* We need to have at least 3 days of stats */
264     if (_RWHour[__crt_wday][24] <= 2) {
265         return (0);
266     }
267
268     /* Check for the hour during a specific day of the week */
269     if (_RWHour[__crt_wday][__crt_hour] != 0) {
270         if (_CWHour[__crt_wday][__crt_hour] > _RWHour[__crt_wday][__crt_hour]) {
271             if (_CWHour[__crt_wday][__crt_hour] >
272                     gethour(_RWHour[__crt_wday][__crt_hour])) {
273                 snprintf(__stats_comment, 191,
274                          "The average number of logs"
275                          " between %d:00 and %d:00 on %s is %d. We"
276                          " reached %d.", __crt_hour, __crt_hour + 1,
277                          weekdays[__crt_wday],
278                          _RWHour[__crt_wday][__crt_hour],
279                          _CWHour[__crt_wday][__crt_hour]);
280
281
282                 _fired = 1;
283                 _daily_errors++;
284                 return (1);
285             }
286         }
287     }
288     return (0);
289 }
290
291 /* Start hourly stats and other necessary variables */
292 int Start_Hour()
293 {
294     int i = 0, j = 0;
295     struct tm *p;
296
297     /* Current time */
298     p = localtime(&c_time);
299
300     /* Other global variables */
301     _fired = 0;
302     _cignorehour = 0;
303
304     today = p->tm_mday;
305     thishour = p->tm_hour;
306     prev_year = p->tm_year + 1900;
307     strncpy(prev_month, l_month[p->tm_mon], 3);
308     prev_month[3] = '\0';
309
310     /* Clear some memory */
311     memset(__stats_comment, '\0', 192);
312
313     /* Get maximum/minimum diffs */
314     maxdiff = getDefine_Int("analysisd",
315                             "stats_maxdiff",
316                             10, 999999);
317
318     mindiff = getDefine_Int("analysisd",
319                             "stats_mindiff",
320                             10, 999999);
321
322     percent_diff = getDefine_Int("analysisd",
323                                  "stats_percent_diff",
324                                  5, 9999);
325
326     /* Last three messages
327      * They are used to keep track of the last
328      * messages received to avoid floods
329      */
330     _lastmsg = NULL;
331     _prevlast = NULL;
332     _pprevlast = NULL;
333
334     /* They should not be null */
335     os_strdup(" ", _lastmsg);
336     os_strdup(" ", _prevlast);
337     os_strdup(" ", _pprevlast);
338
339     /* Create the stat queue directories */
340     if (IsDir(STATWQUEUE) == -1) {
341         if (mkdir(STATWQUEUE, 0770) == -1) {
342             merror("%s: logstat: Unable to create stat queue: %s",
343                    ARGV0, STATWQUEUE);
344             return (-1);
345         }
346     }
347
348     if (IsDir(STATQUEUE) == -1) {
349         if (mkdir(STATQUEUE, 0770) == -1) {
350             merror("%s: logstat: Unable to create stat queue: %s",
351                    ARGV0, STATQUEUE);
352             return (-1);
353         }
354     }
355
356     /* Create store dir */
357     if (IsDir(STATSAVED) == -1) {
358         if (mkdir(STATSAVED, 0770) == -1) {
359             merror("%s: logstat: Unable to create stat directory: %s",
360                    ARGV0, STATSAVED);
361             return (-1);
362         }
363     }
364
365     /* Create hourly directory (24 hour is the stats) */
366     for (i = 0; i <= 24; i++) {
367         char _hourly[128];
368         snprintf(_hourly, 128, "%s/%d", STATQUEUE, i);
369
370         _CHour[i] = 0;
371         if (File_DateofChange(_hourly) < 0) {
372             _RHour[i] = 0;
373         }
374
375         else {
376             FILE *fp;
377             fp = fopen(_hourly, "r");
378             if (!fp) {
379                 _RHour[i] = 0;
380             } else {
381                 if (fscanf(fp, "%d", &_RHour[i]) <= 0) {
382                     _RHour[i] = 0;
383                 }
384
385                 if (_RHour[i] < 0) {
386                     _RHour[i] = 0;
387                 }
388                 fclose(fp);
389             }
390         }
391     }
392
393     /* Create weekly/hourly directories */
394     for (i = 0; i <= 6; i++) {
395         char _weekly[128];
396         snprintf(_weekly, 128, "%s/%d", STATWQUEUE, i);
397         if (IsDir(_weekly) == -1)
398             if (mkdir(_weekly, 0770) == -1) {
399                 merror("%s: logstat: Unable to create stat queue: %s",
400                        ARGV0, _weekly);
401                 return (-1);
402             }
403
404         for (j = 0; j <= 24; j++) {
405             _CWHour[i][j] = 0;
406             snprintf(_weekly, 128, "%s/%d/%d", STATWQUEUE, i, j);
407             if (File_DateofChange(_weekly) < 0) {
408                 _RWHour[i][j] = 0;
409             } else {
410                 FILE *fp;
411                 fp = fopen(_weekly, "r");
412                 if (!fp) {
413                     _RWHour[i][j] = 0;
414                 } else {
415                     if (fscanf(fp, "%d", &_RWHour[i][j]) <= 0) {
416                         _RWHour[i][j] = 0;
417                     }
418
419                     if (_RWHour[i][j] < 0) {
420                         _RWHour[i][j] = 0;
421                     }
422                     fclose(fp);
423                 }
424             }
425         }
426     }
427     return (0);
428 }
429
430 /* Check if the message received is repeated to avoid
431  * floods of the same message
432  */
433 int LastMsg_Stats(const char *log)
434 {
435     if (strcmp(log, _lastmsg) == 0) {
436         return (1);
437     }
438
439     else if (strcmp(log, _prevlast) == 0) {
440         return (1);
441     }
442
443     else if (strcmp(log, _pprevlast) == 0) {
444         return (1);
445     }
446
447     return (0);
448 }
449
450 /* If the message is not repeated, rearrange the last
451  * received messages
452  */
453 void LastMsg_Change(const char *log)
454 {
455     /* Remove the last one */
456     free(_pprevlast);
457
458     /* Move the second to third and the last to second */
459     _pprevlast = _prevlast;
460     _prevlast = _lastmsg;
461
462     os_strdup(log, _lastmsg);
463     return;
464 }
465