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