novi upstream verzije 2.8.3
[ossec-hids.git] / src / rootcheck / common.c
1 /* @(#) $Id: ./src/rootcheck/common.c, 2011/09/08 dcid Exp $
2  */
3
4 /* Copyright (C) 2009 Trend Micro Inc.
5  * All right reserved.
6  *
7  * This program is a free software; you can redistribute it
8  * and/or modify it under the terms of the GNU General Public
9  * License (version 2) as published by the FSF - Free Software
10  * Foundation
11  *
12  * License details at the LICENSE file included with OSSEC or
13  * online at: http://www.ossec.net/main/license/ .
14  */
15
16
17 #include "shared.h"
18 #include "rootcheck.h"
19 #include "os_regex/os_regex.h"
20
21
22
23 /** Checks if the specified string is already in the array.
24  */
25 int _is_str_in_array(char **ar, char *str)
26 {
27     while(*ar)
28     {
29         if(strcmp(*ar, str) == 0)
30         {
31             return(1);
32         }
33         ar++;
34     }
35     return(0);
36 }
37
38
39
40 /** int rk_check_dir(char *dir, char *file, char *pattern)
41  */
42 int rk_check_dir(char *dir, char *file, char *pattern)
43 {
44     int ret_code = 0;
45     char f_name[PATH_MAX +2];
46     struct dirent *entry;
47     struct stat statbuf_local;
48     DIR *dp = NULL;
49
50
51     f_name[PATH_MAX +1] = '\0';
52
53
54     dp = opendir(dir);
55     if(!dp)
56         return(0);
57
58
59     while((entry = readdir(dp)) != NULL)
60     {
61         /* Just ignore . and ..  */
62         if((strcmp(entry->d_name,".") == 0) ||
63            (strcmp(entry->d_name,"..") == 0))
64         {
65             continue;
66         }
67
68
69         /* Creating new file + path string */
70         snprintf(f_name, PATH_MAX +1, "%s/%s",dir, entry->d_name);
71
72
73         /* Checking if the read entry, matches the provided file name. */
74         if(strncasecmp(file, "r:", 2) == 0)
75         {
76             if(OS_Regex(file +2, entry->d_name))
77             {
78                 if(rk_check_file(f_name, pattern))
79                 {
80                     ret_code = 1;
81                 }
82             }
83         }
84
85         /* Trying without regex. */
86         else
87         {
88             if(OS_Match2(file, entry->d_name))
89             {
90                 if(rk_check_file(f_name, pattern))
91                 {
92                     ret_code = 1;
93                 }
94             }
95         }
96
97
98         /* Checking if file is a directory */
99         if(lstat(f_name, &statbuf_local) == 0)
100         {
101             if(S_ISDIR(statbuf_local.st_mode))
102             {
103                 if(rk_check_dir(f_name, file, pattern))
104                 {
105                     ret_code = 1;
106                 }
107             }
108         }
109     }
110
111     closedir(dp);
112     return(ret_code);
113
114 }
115
116
117
118 /** int rk_check_file(char *value, char *pattern)
119  */
120 int rk_check_file(char *file, char *pattern)
121 {
122     char *split_file;
123     int full_negate = 0;
124     int pt_result = 0;
125
126     FILE *fp;
127     char buf[OS_SIZE_2048 +1];
128
129
130     /* If string is null, we don't match */
131     if(file == NULL)
132     {
133         return(0);
134     }
135
136
137     /* Checking if the file is divided */
138     split_file = strchr(file, ',');
139     if(split_file)
140     {
141         *split_file = '\0';
142         split_file++;
143     }
144
145
146     /* Getting each file */
147     do
148     {
149
150
151         /* If we don't have a pattern, just check if the file/dir is there */
152         if(pattern == NULL)
153         {
154             if(is_file(file))
155             {
156                 int i = 0;
157                 char _b_msg[OS_SIZE_1024 +1];
158
159                 _b_msg[OS_SIZE_1024] = '\0';
160                 snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
161                          file);
162
163                 /* Already present. */
164                 if(_is_str_in_array(rootcheck.alert_msg, _b_msg))
165                 {
166                     return(1);
167                 }
168
169                 while(rootcheck.alert_msg[i] && (i < 255))
170                     i++;
171
172                 if(!rootcheck.alert_msg[i])
173                     os_strdup(_b_msg, rootcheck.alert_msg[i]);
174
175                 return(1);
176             }
177         }
178
179         else
180         {
181             full_negate = pt_check_negate(pattern);
182             /* Checking for a content in the file */
183             debug1("checking file: %s", file);
184             fp = fopen(file, "r");
185             if(fp)
186             {
187
188                 debug1(" starting new file: %s", file);
189                 buf[OS_SIZE_2048] = '\0';
190                 while(fgets(buf, OS_SIZE_2048, fp) != NULL)
191                 {
192                     char *nbuf;
193
194                     /* Removing end of line */
195                     nbuf = strchr(buf, '\n');
196                     if(nbuf)
197                     {
198                         *nbuf = '\0';
199                     }
200
201
202                     #ifdef WIN32
203                     /* Removing end of line */
204                     nbuf = strchr(buf, '\r');
205                     if(nbuf)
206                     {
207                         *nbuf = '\0';
208                     }
209                     #endif
210
211
212                     /* Matched */
213                     pt_result = pt_matches(buf, pattern);
214                     debug1("Buf == \"%s\"", buf);
215                     debug1("Pattern == \"%s\"", pattern);
216                     debug1("pt_result == %d and full_negate == %d", pt_result, full_negate);
217                     if((pt_result == 1 && full_negate == 0) )
218                     {
219                         debug1("alerting file %s on line %s", file, buf);
220                         int i = 0;
221                         char _b_msg[OS_SIZE_1024 +1];
222
223
224                         /* Closing the file before dealing with the alert. */
225                         fclose(fp);
226
227                         /* Generating the alert itself. */
228                         _b_msg[OS_SIZE_1024] = '\0';
229                         snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
230                                  file);
231
232                         /* Already present. */
233                         if(_is_str_in_array(rootcheck.alert_msg, _b_msg))
234                         {
235                             return(1);
236                         }
237
238                         while(rootcheck.alert_msg[i] && (i < 255))
239                             i++;
240
241                         if(!rootcheck.alert_msg[i])
242                         os_strdup(_b_msg, rootcheck.alert_msg[i]);
243
244                         return(1);
245                     }
246                     else if((pt_result == 0 && full_negate == 1) )
247                     {
248                         /* found a full+negate match so no longer need to search
249                          * break out of loop and amke sure the full negate does
250                          * not alertin
251                          */
252                         debug1("found a complete match for full_negate");
253                         full_negate = 0;
254                         break;
255                     }
256                 }
257
258                 fclose(fp);
259
260                 if(full_negate == 1)
261                 {
262                     debug1("full_negate alerting - file %s",file);
263                     int i = 0;
264                     char _b_msg[OS_SIZE_1024 +1];
265
266                     /* Generating the alert itself. */
267                     _b_msg[OS_SIZE_1024] = '\0';
268                     snprintf(_b_msg, OS_SIZE_1024, " File: %s.",
269                              file);
270
271                     /* Already present. */
272                     if(_is_str_in_array(rootcheck.alert_msg, _b_msg))
273                     {
274                         return(1);
275                     }
276
277                     while(rootcheck.alert_msg[i] && (i < 255))
278                         i++;
279
280                     if(!rootcheck.alert_msg[i])
281                     os_strdup(_b_msg, rootcheck.alert_msg[i]);
282
283                     return(1);
284                 }
285             }
286         }
287
288         if(split_file)
289         {
290             file = split_file;
291             split_file = strchr(split_file, ',');
292             if(split_file)
293             {
294                 split_file++;
295             }
296         }
297
298
299     }while(split_file);
300
301
302     return(0);
303 }
304
305
306 /** int pt_check_negate(char *pattern)
307  * Checks if the patterns is all negate values and if so returns 1
308  * else return 0
309  */
310 int pt_check_negate(char *pattern)
311 {
312     char *mypattern = NULL;
313     os_strdup(pattern, mypattern);
314     char *tmp_pt = mypattern;
315     char *tmp_pattern = mypattern;
316
317
318     while(tmp_pt != NULL)
319     {
320         /* We first look for " && " */
321         tmp_pt = strchr(tmp_pattern, ' ');
322         if(tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ')
323         {
324             *tmp_pt = '\0';
325             tmp_pt += 4;
326         }
327         else
328         {
329             tmp_pt = NULL;
330         }
331
332         if(*tmp_pattern != '!')
333         {
334             free(mypattern);
335             return 0;
336         }
337
338         tmp_pattern = tmp_pt;
339     }
340
341     debug1("pattern: %s is fill_negate",pattern);
342     free(mypattern);
343     return(1);
344 }
345
346 /** int pt_matches(char *str, char *pattern)
347  * Checks if the specific pattern is present on str.
348  * A pattern can be preceeded by:
349  *                                =: (for equal) - default - strcasecmp
350  *                                r: (for ossec regexes)
351  *                                >: (for strcmp greater)
352  *                                <: (for strcmp  lower)
353  *
354  * Multiple patterns can be specified by using " && " between them.
355  * All of them must match for it to return true.
356  */
357 int pt_matches(char *str, char *pattern)
358 {
359     int neg = 0;
360     int ret_code = 0;
361     char *tmp_pt = pattern;
362     char *tmp_ret = NULL;
363
364
365     /* If string we null, we don't match */
366     if(str == NULL)
367     {
368         return(0);
369     }
370
371     while(tmp_pt != NULL)
372     {
373         /* We first look for " && " */
374         tmp_pt = strchr(pattern, ' ');
375         if(tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ')
376         {
377             /* Marking pointer to clean it up */
378             tmp_ret = tmp_pt;
379
380             *tmp_pt = '\0';
381             tmp_pt += 4;
382         }
383         else
384         {
385             tmp_pt = NULL;
386         }
387
388
389         /* Checking for negate values */
390         neg = 0;
391         ret_code = 0;
392         if(*pattern == '!')
393         {
394             pattern++;
395             neg = 1;
396         }
397
398
399         /* Doing strcasecmp */
400         if(strncasecmp(pattern, "=:", 2) == 0)
401         {
402             pattern += 2;
403             if(strcasecmp(pattern, str) == 0)
404             {
405                 ret_code = 1;
406             }
407         }
408         else if(strncasecmp(pattern, "r:", 2) == 0)
409         {
410             pattern += 2;
411             if(OS_Regex(pattern, str))
412             {
413                 debug1("pattern: %s matches %s.",pattern, str);
414                 ret_code = 1;
415             }
416         }
417         else if(strncasecmp(pattern, "<:", 2) == 0)
418         {
419             pattern += 2;
420             if(strcmp(pattern, str) < 0)
421             {
422                 ret_code = 1;
423             }
424         }
425         else if(strncasecmp(pattern, ">:", 2) == 0)
426         {
427             pattern += 2;
428             if(strcmp(pattern, str) > 0)
429             {
430                 ret_code = 1;
431             }
432         }
433         else
434         {
435             #ifdef WIN32
436             char final_file[2048 +1];
437
438             /* Try to get Windows variable */
439             if(*pattern == '%')
440             {
441                 final_file[0] = '\0';
442                 final_file[2048] = '\0';
443
444                 ExpandEnvironmentStrings(pattern, final_file, 2047);
445             }
446             else
447             {
448                 strncpy(final_file, pattern, 2047);
449             }
450
451             /* Comparing against the expanded variable */
452             if(strcasecmp(final_file, str) == 0)
453             {
454                 ret_code = 1;
455             }
456
457             #else
458             if(strcasecmp(pattern, str) == 0)
459             {
460                 ret_code = 1;
461             }
462
463             #endif
464         }
465
466         /* Fixing tmp_ret entry */
467         if(tmp_ret != NULL)
468         {
469             *tmp_ret = ' ';
470             tmp_ret = NULL;
471         }
472
473
474         /* If we have "!", return true if we don't match */
475         if(neg == 1)
476         {
477             if(ret_code)
478             {
479                 ret_code = 0;
480                 break;
481             }
482         }
483         else
484         {
485             if(!ret_code)
486             {
487                 ret_code = 0;
488                 break;
489             }
490         }
491
492         ret_code = 1;
493         pattern = tmp_pt;
494     }
495
496     return(ret_code);
497 }
498
499
500
501 /** char *normalize_string
502  * Normalizes a string, removing white spaces and tabs
503  * from the begining and the end of it.
504  */
505 char *normalize_string(char *str)
506 {
507     unsigned int str_sz = strlen(str);
508     // return zero-length str as is
509     if (str_sz == 0) {
510        return str;
511     } else {
512         str_sz--;
513     }
514     // remove trailing spaces
515     while(str[str_sz] == ' ' || str[str_sz] == '\t')
516     {
517         if(str_sz == 0)
518             break;
519
520         str[str_sz--] = '\0';
521     }
522     // ignore leading spaces
523     while(*str != '\0')
524     {
525         if(*str == ' ' || *str == '\t')
526         {
527             str++;
528         }
529         else
530         {
531             break;
532         }
533     }
534
535     return(str);
536 }
537
538
539
540
541
542 /** int isfile_ondir(char *file, char *dir)
543  * Checks is 'file' is present on 'dir' using readdir
544  */
545 int isfile_ondir(char *file, char *dir)
546 {
547     DIR *dp = NULL;
548     struct dirent *entry;
549     dp = opendir(dir);
550
551     if(!dp)
552         return(0);
553
554     while((entry = readdir(dp)) != NULL)
555     {
556         if(strcmp(entry->d_name, file) == 0)
557         {
558             closedir(dp);
559             return(1);
560         }
561     }
562
563     closedir(dp);
564     return(0);
565 }
566
567
568
569 /* is_file: Check if the file is present
570  * by different attempts (to try to avoid syscall hidding).
571  */
572 int is_file(char *file_name)
573 {
574     int ret = 0;
575
576     struct stat statbuf;
577     FILE *fp = NULL;
578     DIR *dp = NULL;
579
580
581     #ifndef WIN32
582
583     char curr_dir[1024];
584
585     char *file_dirname;
586     char *file_basename;
587
588
589     curr_dir[1023] = '\0';
590
591     if(!getcwd(curr_dir, 1022))
592     {
593         return(0);
594     }
595
596     /* Getting dir name */
597     file_basename = strrchr(file_name, '/');
598     if(!file_basename)
599     {
600         merror("%s: RK: Invalid file name: %s!", ARGV0, file_name);
601         return(0);
602     }
603
604
605     /* If file_basename == file_name, then the file
606      * only has one slash at the beginning.
607      */
608     if(file_basename != file_name)
609     {
610         /* Dir name and base name are now set */
611         *file_basename = '\0';
612         file_basename++;
613         file_dirname = file_name;
614
615         /** chdir test **/
616         if(chdir(file_dirname) == 0)
617         {
618             if(chdir(file_basename) == 0)
619             {
620                 ret = 1;
621             }
622             /* Checking errno (if file exists, but it is not
623              * a directory.
624              */
625             else if(errno == ENOTDIR)
626             {
627                 ret = 1;
628             }
629
630             /** Trying open dir **/
631             dp = opendir(file_basename);
632             if(dp)
633             {
634                 closedir(dp);
635                 ret = 1;
636             }
637             else if(errno == ENOTDIR)
638             {
639                 ret = 1;
640             }
641
642             /* Returning to the previous directory */
643             chdir(curr_dir);
644         }
645
646
647         file_basename--;
648         *file_basename = '/';
649
650     }
651     else
652     {
653         if(chdir(file_name) == 0)
654         {
655             ret = 1;
656
657             /* Returning to the previous directory */
658             chdir(curr_dir);
659         }
660         else if(errno == ENOTDIR)
661         {
662             ret = 1;
663         }
664     }
665
666     #else
667     dp = opendir(file_name);
668     if(dp)
669     {
670         closedir(dp);
671         ret = 1;
672     }
673
674     #endif /* WIN32 */
675
676
677     /* Trying other calls */
678     if( (stat(file_name, &statbuf) < 0) &&
679         #ifndef WIN32
680         (access(file_name, F_OK) < 0) &&
681         #endif
682         ((fp = fopen(file_name, "r")) == NULL))
683     {
684         return(ret);
685     }
686
687     /* must close it over here */
688     if(fp)
689         fclose(fp);
690
691     return(1);
692 }
693
694
695
696 /*  del_plist:. Deletes the process list
697  */
698 int del_plist(void *p_list_p)
699 {
700     OSList *p_list = (OSList *)p_list_p;
701     OSListNode *l_node;
702     OSListNode *p_node = NULL;
703
704     if(p_list == NULL)
705     {
706         return(0);
707     }
708
709     l_node = OSList_GetFirstNode(p_list);
710     while(l_node)
711     {
712         Proc_Info *pinfo;
713
714         pinfo = (Proc_Info *)l_node->data;
715
716         if(pinfo->p_name)
717         {
718             free(pinfo->p_name);
719         }
720
721         if(pinfo->p_path)
722         {
723             free(pinfo->p_path);
724         }
725
726         free(l_node->data);
727
728         if(p_node)
729         {
730             free(p_node);
731             p_node = NULL;
732         }
733         p_node = l_node;
734
735         l_node = OSList_GetNextNode(p_list);
736     }
737
738     if(p_node)
739     {
740         free(p_node);
741         p_node = NULL;
742     }
743
744     free(p_list);
745
746     return(1);
747 }
748
749
750
751 /* is_process: Check is a process is running.
752  */
753 int is_process(char *value, void *p_list_p)
754 {
755     OSList *p_list = (OSList *)p_list_p;
756     OSListNode *l_node;
757     if(p_list == NULL)
758     {
759         return(0);
760     }
761     if(!value)
762     {
763         return(0);
764     }
765
766
767     l_node = OSList_GetFirstNode(p_list);
768     while(l_node)
769     {
770         Proc_Info *pinfo;
771
772         pinfo = (Proc_Info *)l_node->data;
773
774         /* Checking if value matches */
775         if(pt_matches(pinfo->p_path, value))
776         {
777             int i = 0;
778             char _b_msg[OS_SIZE_1024 +1];
779
780             _b_msg[OS_SIZE_1024] = '\0';
781
782             snprintf(_b_msg, OS_SIZE_1024, " Process: %s.",
783                      pinfo->p_path);
784
785             /* Already present. */
786             if(_is_str_in_array(rootcheck.alert_msg, _b_msg))
787             {
788                 return(1);
789             }
790
791             while(rootcheck.alert_msg[i] && (i< 255))
792                 i++;
793
794             if(!rootcheck.alert_msg[i])
795                 os_strdup(_b_msg, rootcheck.alert_msg[i]);
796
797             return(1);
798         }
799
800         l_node = OSList_GetNextNode(p_list);
801     }
802
803     return(0);
804
805 }
806
807
808
809 /* EOF */