Imported Upstream version 2.7
[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     char *tmp_ret = NULL;
317
318
319     while(tmp_pt != NULL)
320     {
321         /* We first look for " && " */
322         tmp_pt = strchr(tmp_pattern, ' ');
323         if(tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ')
324         {
325             /* Marking pointer to clean it up */
326             tmp_ret = tmp_pt;
327
328             *tmp_pt = '\0';
329             tmp_pt += 4;
330         }
331         else
332         {
333             tmp_pt = NULL;
334         }
335
336         if(*tmp_pattern != '!')
337         {
338             free(mypattern);
339             return 0;
340         }
341
342         tmp_pattern = tmp_pt;
343     }
344
345     debug1("pattern: %s is fill_negate",pattern);
346     free(mypattern);
347     return(1);
348 }
349
350 /** int pt_matches(char *str, char *pattern)
351  * Checks if the specific pattern is present on str.
352  * A pattern can be preceeded by:
353  *                                =: (for equal) - default - strcasecmp
354  *                                r: (for ossec regexes)
355  *                                >: (for strcmp greater)
356  *                                <: (for strcmp  lower)
357  *
358  * Multiple patterns can be specified by using " && " between them.
359  * All of them must match for it to return true.
360  */
361 int pt_matches(char *str, char *pattern)
362 {
363     int neg = 0;
364     int ret_code = 0;
365     char *tmp_pt = pattern;
366     char *tmp_ret = NULL;
367
368
369     /* If string we null, we don't match */
370     if(str == NULL)
371     {
372         return(0);
373     }
374
375     while(tmp_pt != NULL)
376     {
377         /* We first look for " && " */
378         tmp_pt = strchr(pattern, ' ');
379         if(tmp_pt && tmp_pt[1] == '&' && tmp_pt[2] == '&' && tmp_pt[3] == ' ')
380         {
381             /* Marking pointer to clean it up */
382             tmp_ret = tmp_pt;
383
384             *tmp_pt = '\0';
385             tmp_pt += 4;
386         }
387         else
388         {
389             tmp_pt = NULL;
390         }
391
392
393         /* Checking for negate values */
394         neg = 0;
395         ret_code = 0;
396         if(*pattern == '!')
397         {
398             pattern++;
399             neg = 1;
400         }
401
402
403         /* Doing strcasecmp */
404         if(strncasecmp(pattern, "=:", 2) == 0)
405         {
406             pattern += 2;
407             if(strcasecmp(pattern, str) == 0)
408             {
409                 ret_code = 1;
410             }
411         }
412         else if(strncasecmp(pattern, "r:", 2) == 0)
413         {
414             pattern += 2;
415             if(OS_Regex(pattern, str))
416             {
417                 debug1("pattern: %s matches %s.",pattern, str);
418                 ret_code = 1;
419             }
420         }
421         else if(strncasecmp(pattern, "<:", 2) == 0)
422         {
423             pattern += 2;
424             if(strcmp(pattern, str) < 0)
425             {
426                 ret_code = 1;
427             }
428         }
429         else if(strncasecmp(pattern, ">:", 2) == 0)
430         {
431             pattern += 2;
432             if(strcmp(pattern, str) > 0)
433             {
434                 ret_code = 1;
435             }
436         }
437         else
438         {
439             #ifdef WIN32
440             char final_file[2048 +1];
441
442             /* Try to get Windows variable */
443             if(*pattern == '%')
444             {
445                 final_file[0] = '\0';
446                 final_file[2048] = '\0';
447
448                 ExpandEnvironmentStrings(pattern, final_file, 2047);
449             }
450             else
451             {
452                 strncpy(final_file, pattern, 2047);
453             }
454
455             /* Comparing against the expanded variable */
456             if(strcasecmp(final_file, str) == 0)
457             {
458                 ret_code = 1;
459             }
460
461             #else
462             if(strcasecmp(pattern, str) == 0)
463             {
464                 ret_code = 1;
465             }
466
467             #endif
468         }
469
470         /* Fixing tmp_ret entry */
471         if(tmp_ret != NULL)
472         {
473             *tmp_ret = ' ';
474             tmp_ret = NULL;
475         }
476
477
478         /* If we have "!", return true if we don't match */
479         if(neg == 1)
480         {
481             if(ret_code)
482             {
483                 ret_code = 0;
484                 break;
485             }
486         }
487         else
488         {
489             if(!ret_code)
490             {
491                 ret_code = 0;
492                 break;
493             }
494         }
495
496         ret_code = 1;
497         pattern = tmp_pt;
498     }
499
500     return(ret_code);
501 }
502
503
504
505 /** char *normalize_string
506  * Normalizes a string, removing white spaces and tabs
507  * from the begining and the end of it.
508  */
509 char *normalize_string(char *str)
510 {
511     unsigned int str_sz = strlen(str);
512     // return zero-length str as is
513     if (str_sz == 0) {
514        return str;
515     } else {
516         str_sz--;
517     }
518     // remove trailing spaces
519     while(str[str_sz] == ' ' || str[str_sz] == '\t')
520     {
521         if(str_sz == 0)
522             break;
523
524         str[str_sz--] = '\0';
525     }
526     // ignore leading spaces
527     while(*str != '\0')
528     {
529         if(*str == ' ' || *str == '\t')
530         {
531             str++;
532         }
533         else
534         {
535             break;
536         }
537     }
538
539     return(str);
540 }
541
542
543
544
545
546 /** int isfile_ondir(char *file, char *dir)
547  * Checks is 'file' is present on 'dir' using readdir
548  */
549 int isfile_ondir(char *file, char *dir)
550 {
551     DIR *dp = NULL;
552     struct dirent *entry;
553     dp = opendir(dir);
554
555     if(!dp)
556         return(0);
557
558     while((entry = readdir(dp)) != NULL)
559     {
560         if(strcmp(entry->d_name, file) == 0)
561         {
562             closedir(dp);
563             return(1);
564         }
565     }
566
567     closedir(dp);
568     return(0);
569 }
570
571
572
573 /* is_file: Check if the file is present
574  * by different attempts (to try to avoid syscall hidding).
575  */
576 int is_file(char *file_name)
577 {
578     int ret = 0;
579
580     struct stat statbuf;
581     FILE *fp = NULL;
582     DIR *dp = NULL;
583
584
585     #ifndef WIN32
586
587     char curr_dir[1024];
588
589     char *file_dirname;
590     char *file_basename;
591
592
593     curr_dir[1023] = '\0';
594
595     if(!getcwd(curr_dir, 1022))
596     {
597         return(0);
598     }
599
600     /* Getting dir name */
601     file_basename = strrchr(file_name, '/');
602     if(!file_basename)
603     {
604         merror("%s: RK: Invalid file name: %s!", ARGV0, file_name);
605         return(0);
606     }
607
608
609     /* If file_basename == file_name, then the file
610      * only has one slash at the beginning.
611      */
612     if(file_basename != file_name)
613     {
614         /* Dir name and base name are now set */
615         *file_basename = '\0';
616         file_basename++;
617         file_dirname = file_name;
618
619         /** chdir test **/
620         if(chdir(file_dirname) == 0)
621         {
622             if(chdir(file_basename) == 0)
623             {
624                 ret = 1;
625             }
626             /* Checking errno (if file exists, but it is not
627              * a directory.
628              */
629             else if(errno == ENOTDIR)
630             {
631                 ret = 1;
632             }
633
634             /** Trying open dir **/
635             dp = opendir(file_basename);
636             if(dp)
637             {
638                 closedir(dp);
639                 ret = 1;
640             }
641             else if(errno == ENOTDIR)
642             {
643                 ret = 1;
644             }
645
646             /* Returning to the previous directory */
647             chdir(curr_dir);
648         }
649
650
651         file_basename--;
652         *file_basename = '/';
653
654     }
655     else
656     {
657         if(chdir(file_name) == 0)
658         {
659             ret = 1;
660
661             /* Returning to the previous directory */
662             chdir(curr_dir);
663         }
664         else if(errno == ENOTDIR)
665         {
666             ret = 1;
667         }
668     }
669
670     #else
671     dp = opendir(file_name);
672     if(dp)
673     {
674         closedir(dp);
675         ret = 1;
676     }
677
678     #endif /* WIN32 */
679
680
681     /* Trying other calls */
682     if( (stat(file_name, &statbuf) < 0) &&
683         #ifndef WIN32
684         (access(file_name, F_OK) < 0) &&
685         #endif
686         ((fp = fopen(file_name, "r")) == NULL))
687     {
688         return(ret);
689     }
690
691     /* must close it over here */
692     if(fp)
693         fclose(fp);
694
695     return(1);
696 }
697
698
699
700 /*  del_plist:. Deletes the process list
701  */
702 int del_plist(void *p_list_p)
703 {
704     OSList *p_list = (OSList *)p_list_p;
705     OSListNode *l_node;
706     OSListNode *p_node = NULL;
707
708     if(p_list == NULL)
709     {
710         return(0);
711     }
712
713     l_node = OSList_GetFirstNode(p_list);
714     while(l_node)
715     {
716         Proc_Info *pinfo;
717
718         pinfo = (Proc_Info *)l_node->data;
719
720         if(pinfo->p_name)
721         {
722             free(pinfo->p_name);
723         }
724
725         if(pinfo->p_path)
726         {
727             free(pinfo->p_path);
728         }
729
730         free(l_node->data);
731
732         if(p_node)
733         {
734             free(p_node);
735             p_node = NULL;
736         }
737         p_node = l_node;
738
739         l_node = OSList_GetNextNode(p_list);
740     }
741
742     if(p_node)
743     {
744         free(p_node);
745         p_node = NULL;
746     }
747
748     free(p_list);
749
750     return(1);
751 }
752
753
754
755 /* is_process: Check is a process is running.
756  */
757 int is_process(char *value, void *p_list_p)
758 {
759     OSList *p_list = (OSList *)p_list_p;
760     OSListNode *l_node;
761     if(p_list == NULL)
762     {
763         return(0);
764     }
765     if(!value)
766     {
767         return(0);
768     }
769
770
771     l_node = OSList_GetFirstNode(p_list);
772     while(l_node)
773     {
774         Proc_Info *pinfo;
775
776         pinfo = (Proc_Info *)l_node->data;
777
778         /* Checking if value matches */
779         if(pt_matches(pinfo->p_path, value))
780         {
781             int i = 0;
782             char _b_msg[OS_SIZE_1024 +1];
783
784             _b_msg[OS_SIZE_1024] = '\0';
785
786             snprintf(_b_msg, OS_SIZE_1024, " Process: %s.",
787                      pinfo->p_path);
788
789             /* Already present. */
790             if(_is_str_in_array(rootcheck.alert_msg, _b_msg))
791             {
792                 return(1);
793             }
794
795             while(rootcheck.alert_msg[i] && (i< 255))
796                 i++;
797
798             if(!rootcheck.alert_msg[i])
799                 os_strdup(_b_msg, rootcheck.alert_msg[i]);
800
801             return(1);
802         }
803
804         l_node = OSList_GetNextNode(p_list);
805     }
806
807     return(0);
808
809 }
810
811
812
813 /* EOF */