new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / rootcheck / common.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 "rootcheck.h"
12 #include "os_regex/os_regex.h"
13
14 /* Prototypes */
15 static int _is_str_in_array(char *const *ar, const char *str);
16
17
18 /* Check if the specified string is already in the array */
19 static int _is_str_in_array(char *const *ar, const char *str)
20 {
21     while (*ar) {
22         if (strcmp(*ar, str) == 0) {
23             return (1);
24         }
25         ar++;
26     }
27     return (0);
28 }
29
30 int rk_check_dir(const char *dir, const char *file, char *pattern)
31 {
32     int ret_code = 0;
33     char f_name[PATH_MAX + 2];
34     struct dirent *entry;
35     struct stat statbuf_local;
36     DIR *dp = NULL;
37
38     f_name[PATH_MAX + 1] = '\0';
39
40     dp = opendir(dir);
41     if (!dp) {
42         return (0);
43     }
44
45     while ((entry = readdir(dp)) != NULL) {
46         /* Ignore . and ..  */
47         if ((strcmp(entry->d_name, ".") == 0) ||
48                 (strcmp(entry->d_name, "..") == 0)) {
49             continue;
50         }
51
52         /* Create new file + path string */
53         snprintf(f_name, PATH_MAX + 1, "%s/%s", dir, entry->d_name);
54
55         /* Check if the read entry matches the provided file name */
56         if (strncasecmp(file, "r:", 2) == 0) {
57             if (OS_Regex(file + 2, entry->d_name)) {
58                 if (rk_check_file(f_name, pattern)) {
59                     ret_code = 1;
60                 }
61             }
62         } else {
63             /* ... otherwise try without regex */
64             if (OS_Match2(file, entry->d_name)) {
65                 if (rk_check_file(f_name, pattern)) {
66                     ret_code = 1;
67                 }
68             }
69         }
70
71         /* Check if file is a directory */
72         if (lstat(f_name, &statbuf_local) == 0) {
73             if (S_ISDIR(statbuf_local.st_mode)) {
74                 if (rk_check_dir(f_name, file, pattern)) {
75                     ret_code = 1;
76                 }
77             }
78         }
79     }
80
81     closedir(dp);
82     return (ret_code);
83
84 }
85
86 int rk_check_file(char *file, char *pattern)
87 {
88     char *split_file;
89     int full_negate = 0;
90     int pt_result = 0;
91     FILE *fp;
92     char buf[OS_SIZE_2048 + 1];
93
94     if (file == NULL) {
95         return (0);
96     }
97
98     /* Check if the file is divided */
99     split_file = strchr(file, ',');
100     if (split_file) {
101         *split_file = '\0';
102         split_file++;
103     }
104
105     /* Get each file */
106     do {
107         /* If we don't have a pattern, just check if the file/dir is there */
108         if (pattern == NULL) {
109             if (is_file(file)) {
110                 int i = 0;
111                 char _b_msg[OS_SIZE_1024 + 1];
112
113                 _b_msg[OS_SIZE_1024] = '\0';
114                 snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
115                          file);
116
117                 /* Already present */
118                 if (_is_str_in_array(rootcheck.alert_msg, _b_msg)) {
119                     return (1);
120                 }
121
122                 while (rootcheck.alert_msg[i] && (i < 255)) {
123                     i++;
124                 }
125
126                 if (!rootcheck.alert_msg[i]) {
127                     os_strdup(_b_msg, rootcheck.alert_msg[i]);
128                 }
129
130                 return (1);
131             }
132         } else {
133             full_negate = pt_check_negate(pattern);
134             /* Check for content in the file */
135             debug1("checking file: %s", file);
136             fp = fopen(file, "r");
137             if (fp) {
138
139                 debug1(" starting new file: %s", file);
140                 buf[OS_SIZE_2048] = '\0';
141                 while (fgets(buf, OS_SIZE_2048, fp) != NULL) {
142                     char *nbuf;
143
144                     /* Remove end of line */
145                     nbuf = strchr(buf, '\n');
146                     if (nbuf) {
147                         *nbuf = '\0';
148                     }
149 #ifdef WIN32
150                     /* Remove end of line */
151                     nbuf = strchr(buf, '\r');
152                     if (nbuf) {
153                         *nbuf = '\0';
154                     }
155 #endif
156                     /* Matched */
157                     pt_result = pt_matches(buf, pattern);
158                     debug1("Buf == \"%s\"", buf);
159                     debug1("Pattern == \"%s\"", pattern);
160                     debug1("pt_result == %d and full_negate == %d", pt_result, full_negate);
161                     if ((pt_result == 1 && full_negate == 0) ) {
162                         debug1("alerting file %s on line %s", file, buf);
163                         int i = 0;
164                         char _b_msg[OS_SIZE_1024 + 1];
165
166                         /* Close the file before dealing with the alert */
167                         fclose(fp);
168
169                         /* Generate the alert itself */
170                         _b_msg[OS_SIZE_1024] = '\0';
171                         snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
172                                  file);
173
174                         /* Already present */
175                         if (_is_str_in_array(rootcheck.alert_msg, _b_msg)) {
176                             return (1);
177                         }
178
179                         while (rootcheck.alert_msg[i] && (i < 255)) {
180                             i++;
181                         }
182
183                         if (!rootcheck.alert_msg[i]) {
184                             os_strdup(_b_msg, rootcheck.alert_msg[i]);
185                         }
186
187                         return (1);
188                     } else if ((pt_result == 0 && full_negate == 1) ) {
189                         /* Found a full+negate match so no longer need to search
190                          * break out of loop and make sure the full negate does
191                          * not alert.
192                          */
193                         debug1("found a complete match for full_negate");
194                         full_negate = 0;
195                         break;
196                     }
197                 }
198
199                 fclose(fp);
200
201                 if (full_negate == 1) {
202                     debug1("full_negate alerting - file %s", file);
203                     int i = 0;
204                     char _b_msg[OS_SIZE_1024 + 1];
205
206                     /* Generate the alert itself */
207                     _b_msg[OS_SIZE_1024] = '\0';
208                     snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
209                              file);
210
211                     /* Already present */
212                     if (_is_str_in_array(rootcheck.alert_msg, _b_msg)) {
213                         return (1);
214                     }
215
216                     while (rootcheck.alert_msg[i] && (i < 255)) {
217                         i++;
218                     }
219
220                     if (!rootcheck.alert_msg[i]) {
221                         os_strdup(_b_msg, rootcheck.alert_msg[i]);
222                     }
223
224                     return (1);
225                 }
226             }
227         }
228
229         if (split_file) {
230             file = split_file;
231             split_file = strchr(split_file, ',');
232             if (split_file) {
233                 split_file++;
234             }
235         }
236
237
238     } while (split_file);
239
240     return (0);
241 }
242
243 /* Check if the pattern is all negate values */
244 int pt_check_negate(const char *pattern)
245 {
246     char *mypattern = NULL;
247     os_strdup(pattern, mypattern);
248     char *tmp_pt = mypattern;
249     char *tmp_pattern = mypattern;
250
251     while (tmp_pt != NULL) {
252         /* First look for " && " */
253         tmp_pt = strchr(tmp_pattern, ' ');
254         if (tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ') {
255             *tmp_pt = '\0';
256             tmp_pt += 4;
257         } else {
258             tmp_pt = NULL;
259         }
260
261         if (*tmp_pattern != '!') {
262             free(mypattern);
263             return 0;
264         }
265
266         tmp_pattern = tmp_pt;
267     }
268
269     debug1("pattern: %s is fill_negate", pattern);
270     free(mypattern);
271     return (1);
272 }
273
274 /* Checks if the specific pattern is present on str.
275  * A pattern can be preceded by:
276  *                                =: (for equal) - default - strcasecmp
277  *                                r: (for ossec regexes)
278  *                                >: (for strcmp greater)
279  *                                <: (for strcmp  lower)
280  *
281  * Multiple patterns can be specified by using " && " between them.
282  * All of them must match for it to return true.
283  */
284 int pt_matches(const char *str, char *pattern)
285 {
286     int neg = 0;
287     int ret_code = 0;
288     char *tmp_pt = pattern;
289     char *tmp_ret = NULL;
290
291     if (str == NULL) {
292         return (0);
293     }
294
295     while (tmp_pt != NULL) {
296         /* First look for " && " */
297         tmp_pt = strchr(pattern, ' ');
298         if (tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ') {
299             /* Mark pointer to clean it up */
300             tmp_ret = tmp_pt;
301
302             *tmp_pt = '\0';
303             tmp_pt += 4;
304         } else {
305             tmp_pt = NULL;
306         }
307
308         /* Check for negate values */
309         neg = 0;
310         ret_code = 0;
311         if (*pattern == '!') {
312             pattern++;
313             neg = 1;
314         }
315
316         /* Do the actual comparison */
317         if (strncasecmp(pattern, "=:", 2) == 0) {
318             pattern += 2;
319             if (strcasecmp(pattern, str) == 0) {
320                 ret_code = 1;
321             }
322         } else if (strncasecmp(pattern, "r:", 2) == 0) {
323             pattern += 2;
324             if (OS_Regex(pattern, str)) {
325                 debug1("pattern: %s matches %s.", pattern, str);
326                 ret_code = 1;
327             }
328         } else if (strncasecmp(pattern, "<:", 2) == 0) {
329             pattern += 2;
330             if (strcmp(pattern, str) < 0) {
331                 ret_code = 1;
332             }
333         } else if (strncasecmp(pattern, ">:", 2) == 0) {
334             pattern += 2;
335             if (strcmp(pattern, str) > 0) {
336                 ret_code = 1;
337             }
338         } else {
339 #ifdef WIN32
340             char final_file[2048 + 1];
341
342             /* Try to get Windows variable */
343             if (*pattern == '%') {
344                 final_file[0] = '\0';
345                 final_file[2048] = '\0';
346
347                 ExpandEnvironmentStrings(pattern, final_file, 2047);
348             } else {
349                 strncpy(final_file, pattern, 2047);
350             }
351
352             /* Compare against the expanded variable */
353             if (strcasecmp(final_file, str) == 0) {
354                 ret_code = 1;
355             }
356 #else
357             if (strcasecmp(pattern, str) == 0) {
358                 ret_code = 1;
359             }
360 #endif
361         }
362         /* Fix tmp_ret entry */
363         if (tmp_ret != NULL) {
364             *tmp_ret = ' ';
365             tmp_ret = NULL;
366         }
367
368         /* If we have "!", return true if we don't match */
369         if (neg == 1) {
370             if (ret_code) {
371                 ret_code = 0;
372                 break;
373             }
374         } else {
375             if (!ret_code) {
376                 ret_code = 0;
377                 break;
378             }
379         }
380
381         ret_code = 1;
382         pattern = tmp_pt;
383     }
384
385     return (ret_code);
386 }
387
388 /* Normalizes a string, removing white spaces and tabs
389  * from the beginning and the end of it.
390  */
391 char *normalize_string(char *str)
392 {
393     size_t str_sz = strlen(str);
394     /* Return zero-length str as is */
395     if (str_sz == 0) {
396         return str;
397     } else {
398         str_sz--;
399     }
400     /* Remove trailing spaces */
401     while (str[str_sz] == ' ' || str[str_sz] == '\t') {
402         if (str_sz == 0) {
403             break;
404         }
405
406         str[str_sz--] = '\0';
407     }
408     /* ignore leading spaces */
409     while (*str != '\0') {
410         if (*str == ' ' || *str == '\t') {
411             str++;
412         } else {
413             break;
414         }
415     }
416
417     return (str);
418 }
419
420 /* Check if 'file' is present on 'dir' using readdir */
421 int isfile_ondir(const char *file, const char *dir)
422 {
423     DIR *dp = NULL;
424     struct dirent *entry;
425     dp = opendir(dir);
426
427     if (!dp) {
428         return (0);
429     }
430
431     while ((entry = readdir(dp)) != NULL) {
432         if (strcmp(entry->d_name, file) == 0) {
433             closedir(dp);
434             return (1);
435         }
436     }
437
438     closedir(dp);
439     return (0);
440 }
441
442 /* Check if the file is present using several methods
443  * to avoid being tricked by syscall hiding
444  */
445 int is_file(char *file_name)
446 {
447     int ret = 0;
448     struct stat statbuf;
449     FILE *fp = NULL;
450     DIR *dp = NULL;
451
452 #ifndef WIN32
453     char curr_dir[1024];
454     char *file_dirname;
455     char *file_basename;
456
457     curr_dir[1023] = '\0';
458
459     if (!getcwd(curr_dir, 1022)) {
460         return (0);
461     }
462
463     /* Get dir name */
464     file_basename = strrchr(file_name, '/');
465     if (!file_basename) {
466         merror("%s: RK: Invalid file name: %s!", ARGV0, file_name);
467         return (0);
468     }
469
470     /* If file_basename == file_name, then the file
471      * only has one slash at the beginning
472      */
473     if (file_basename != file_name) {
474         /* Dir name and base name are now set */
475         *file_basename = '\0';
476         file_basename++;
477         file_dirname = file_name;
478
479         /* chdir test */
480         if (chdir(file_dirname) == 0) {
481             if (chdir(file_basename) == 0) {
482                 ret = 1;
483             }
484             /* Check errno (if file exists, but it is not
485              * a directory.
486              */
487             else if (errno == ENOTDIR) {
488                 ret = 1;
489             }
490
491             /* Trying open dir */
492             dp = opendir(file_basename);
493             if (dp) {
494                 closedir(dp);
495                 ret = 1;
496             } else if (errno == ENOTDIR) {
497                 ret = 1;
498             }
499
500             /* Return to the previous directory */
501             if (chdir(curr_dir) == -1) {
502                 merror(CHDIR_ERROR, ARGV0, curr_dir, errno, strerror(errno));
503                 return (0);
504             }
505         }
506
507         file_basename--;
508         *file_basename = '/';
509     } else {
510         if (chdir(file_name) == 0) {
511             ret = 1;
512
513             /* Return to the previous directory */
514             if (chdir(curr_dir) == -1) {
515                 merror(CHDIR_ERROR, ARGV0, curr_dir, errno, strerror(errno));
516                 return (0);
517             }
518         } else if (errno == ENOTDIR) {
519             ret = 1;
520         }
521     }
522
523 #else
524     dp = opendir(file_name);
525     if (dp) {
526         closedir(dp);
527         ret = 1;
528     }
529 #endif /* WIN32 */
530     /* Trying other calls */
531     if ( (stat(file_name, &statbuf) < 0) &&
532 #ifndef WIN32
533             (access(file_name, F_OK) < 0) &&
534 #endif
535             ((fp = fopen(file_name, "r")) == NULL)) {
536         return (ret);
537     }
538
539     if (fp) {
540         fclose(fp);
541     }
542
543     return (1);
544 }
545
546 /* Delete the process list */
547 int del_plist(OSList *p_list)
548 {
549     OSListNode *l_node;
550     OSListNode *p_node = NULL;
551
552     if (p_list == NULL) {
553         return (0);
554     }
555
556     l_node = OSList_GetFirstNode(p_list);
557     while (l_node) {
558         Proc_Info *pinfo;
559
560         pinfo = (Proc_Info *)l_node->data;
561
562         if (pinfo->p_name) {
563             free(pinfo->p_name);
564         }
565
566         if (pinfo->p_path) {
567             free(pinfo->p_path);
568         }
569
570         free(l_node->data);
571
572         if (p_node) {
573             free(p_node);
574             p_node = NULL;
575         }
576         p_node = l_node;
577
578         l_node = OSList_GetNextNode(p_list);
579     }
580
581     if (p_node) {
582         free(p_node);
583         p_node = NULL;
584     }
585
586     free(p_list);
587
588     return (1);
589 }
590
591 /* Check if a process is running */
592 int is_process(char *value, OSList *p_list)
593 {
594     OSListNode *l_node;
595     if (p_list == NULL) {
596         return (0);
597     }
598     if (!value) {
599         return (0);
600     }
601
602     l_node = OSList_GetFirstNode(p_list);
603     while (l_node) {
604         Proc_Info *pinfo;
605
606         pinfo = (Proc_Info *)l_node->data;
607
608         /* Check if value matches */
609         if (pt_matches(pinfo->p_path, value)) {
610             int i = 0;
611             char _b_msg[OS_SIZE_1024 + 1];
612
613             _b_msg[OS_SIZE_1024] = '\0';
614
615             snprintf(_b_msg, OS_SIZE_1024, " Process: %s.",
616                      pinfo->p_path);
617
618             /* Already present */
619             if (_is_str_in_array(rootcheck.alert_msg, _b_msg)) {
620                 return (1);
621             }
622
623             while (rootcheck.alert_msg[i] && (i < 255)) {
624                 i++;
625             }
626
627             if (!rootcheck.alert_msg[i]) {
628                 os_strdup(_b_msg, rootcheck.alert_msg[i]);
629             }
630
631             return (1);
632         }
633
634         l_node = OSList_GetNextNode(p_list);
635     }
636
637     return (0);
638 }
639