new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / rootcheck / common_rcl.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
13 /* Prototypes */
14 static char *_rkcl_getfp(FILE *fp, char *buf);
15 static int   _rkcl_is_name(const char *buf);
16 static int   _rkcl_get_vars(OSStore *vars, char *nbuf);
17 static char *_rkcl_get_name(char *buf, char *ref, int *condition);
18 static char *_rkcl_get_pattern(char *value);
19 static char *_rkcl_get_value(char *buf, int *type);
20
21 /* Types of values */
22 #define RKCL_TYPE_FILE      1
23 #define RKCL_TYPE_REGISTRY  2
24 #define RKCL_TYPE_PROCESS   3
25 #define RKCL_TYPE_DIR       4
26
27 #define RKCL_COND_ALL       0x001
28 #define RKCL_COND_ANY       0x002
29 #define RKCL_COND_REQ       0x004
30 #define RKCL_COND_INV       0x010
31
32
33 #ifdef WIN32
34 char *_rkcl_getrootdir(char *root_dir, int dir_size)
35 {
36     char final_file[2048 + 1];
37     char *tmp;
38
39     final_file[0] = '\0';
40     final_file[2048] = '\0';
41
42     ExpandEnvironmentStrings("%WINDIR%", final_file, 2047);
43
44     tmp = strchr(final_file, '\\');
45     if (tmp) {
46         *tmp = '\0';
47         strncpy(root_dir, final_file, dir_size);
48         return (root_dir);
49     }
50
51     return (NULL);
52 }
53 #endif
54
55 /* Get next available buffer in file */
56 static char *_rkcl_getfp(FILE *fp, char *buf)
57 {
58     while (fgets(buf, OS_SIZE_1024, fp) != NULL) {
59         char *nbuf;
60
61         /* Remove end of line */
62         nbuf = strchr(buf, '\n');
63         if (nbuf) {
64             *nbuf = '\0';
65         }
66
67         /* Assign buf to be used */
68         nbuf = buf;
69
70         /* Exclude commented lines or blanked ones */
71         while (*nbuf != '\0') {
72             if (*nbuf == ' ' || *nbuf == '\t') {
73                 nbuf++;
74                 continue;
75             } else if (*nbuf == '#') {
76                 *nbuf = '\0';
77                 continue;
78             } else {
79                 break;
80             }
81         }
82
83         /* Go to next line if empty */
84         if (*nbuf == '\0') {
85             continue;
86         }
87
88         return (nbuf);
89     }
90
91     return (NULL);
92 }
93
94 static int _rkcl_is_name(const char *buf)
95 {
96     if (*buf == '[' && buf[strlen(buf) - 1] == ']') {
97         return (1);
98     }
99     return (0);
100 }
101
102 static int _rkcl_get_vars(OSStore *vars, char *nbuf)
103 {
104     char *var_value;
105     char *tmp;
106
107     /* If not a variable, return 0 */
108     if (*nbuf != '$') {
109         return (0);
110     }
111
112     /* Remove semicolon from the end */
113     tmp = strchr(nbuf, ';');
114     if (tmp) {
115         *tmp = '\0';
116     } else {
117         return (-1);
118     }
119
120     /* Get value */
121     tmp = strchr(nbuf, '=');
122     if (tmp) {
123         *tmp = '\0';
124         tmp++;
125     } else {
126         return (-1);
127     }
128
129     /* Dump the variable options */
130     os_strdup(tmp, var_value);
131
132     /* Add entry to the storage */
133     OSStore_Put(vars, nbuf, var_value);
134     return (1);
135 }
136
137 static char *_rkcl_get_name(char *buf, char *ref, int *condition)
138 {
139     char *tmp_location;
140     char *tmp_location2;
141     *condition = 0;
142
143     /* Check if name is valid */
144     if (!_rkcl_is_name(buf)) {
145         return (NULL);
146     }
147
148     /* Set name */
149     buf++;
150     tmp_location = strchr(buf, ']');
151     if (!tmp_location) {
152         return (NULL);
153     }
154     *tmp_location = '\0';
155
156     /* Get condition */
157     tmp_location++;
158     if (*tmp_location != ' ' && tmp_location[1] != '[') {
159         return (NULL);
160     }
161     tmp_location += 2;
162
163     tmp_location2 = strchr(tmp_location, ']');
164     if (!tmp_location2) {
165         return (NULL);
166     }
167     *tmp_location2 = '\0';
168     tmp_location2++;
169
170     /* Get condition */
171     if (strcmp(tmp_location, "all") == 0) {
172         *condition |= RKCL_COND_ALL;
173     } else if (strcmp(tmp_location, "any") == 0) {
174         *condition |= RKCL_COND_ANY;
175     } else if (strcmp(tmp_location, "any required") == 0) {
176         *condition |= RKCL_COND_ANY;
177         *condition |= RKCL_COND_REQ;
178     } else if (strcmp(tmp_location, "all required") == 0) {
179         *condition |= RKCL_COND_ALL;
180         *condition |= RKCL_COND_REQ;
181     } else {
182         *condition = RKCL_COND_INV;
183         return (NULL);
184     }
185
186     /* Get reference */
187     if (*tmp_location2 != ' ' && tmp_location2[1] != '[') {
188         return (NULL);
189     }
190
191     tmp_location2 += 2;
192     tmp_location = strchr(tmp_location2, ']');
193     if (!tmp_location) {
194         return (NULL);
195     }
196     *tmp_location = '\0';
197
198     /* Copy reference */
199     strncpy(ref, tmp_location2, 255);
200
201     return (strdup(buf));
202 }
203
204 static char *_rkcl_get_pattern(char *value)
205 {
206     while (*value != '\0') {
207         if ((*value == ' ') && (value[1] == '-') &&
208                 (value[2] == '>') && (value[3] == ' ')) {
209             *value = '\0';
210             value += 4;
211
212             return (value);
213         }
214         value++;
215     }
216
217     return (NULL);
218 }
219
220 static char *_rkcl_get_value(char *buf, int *type)
221 {
222     char *tmp_str;
223     char *value;
224
225     /* Zero type before using it to make sure return is valid
226      * in case of error.
227      */
228     *type = 0;
229
230     value = strchr(buf, ':');
231     if (value == NULL) {
232         return (NULL);
233     }
234
235     *value = '\0';
236     value++;
237
238     tmp_str = strchr(value, ';');
239     if (tmp_str == NULL) {
240         return (NULL);
241     }
242     *tmp_str = '\0';
243
244     /* Get types - removing negate flag (using later) */
245     if (*buf == '!') {
246         buf++;
247     }
248
249     if (strcmp(buf, "f") == 0) {
250         *type = RKCL_TYPE_FILE;
251     } else if (strcmp(buf, "r") == 0) {
252         *type = RKCL_TYPE_REGISTRY;
253     } else if (strcmp(buf, "p") == 0) {
254         *type = RKCL_TYPE_PROCESS;
255     } else if (strcmp(buf, "d") == 0) {
256         *type = RKCL_TYPE_DIR;
257     } else {
258         return (NULL);
259     }
260
261     return (value);
262 }
263
264 int rkcl_get_entry(FILE *fp, const char *msg, OSList *p_list)
265 {
266     int type = 0, condition = 0;
267     char *nbuf;
268     char buf[OS_SIZE_1024 + 2];
269     char root_dir[OS_SIZE_1024 + 2];
270     char final_file[2048 + 1];
271     char ref[255 + 1];
272     char *value;
273     char *name = NULL;
274     OSStore *vars;
275
276     /* Initialize variables */
277     memset(buf, '\0', sizeof(buf));
278     memset(root_dir, '\0', sizeof(root_dir));
279     memset(final_file, '\0', sizeof(final_file));
280     memset(ref, '\0', sizeof(ref));
281
282 #ifdef WIN32
283     /* Get Windows rootdir */
284     _rkcl_getrootdir(root_dir, sizeof(root_dir) - 1);
285     if (root_dir[0] == '\0') {
286         merror(INVALID_ROOTDIR, ARGV0);
287     }
288 #endif
289     /* Get variables */
290     vars = OSStore_Create();
291
292     /* We first read all variables -- they must be defined at the top */
293     while (1) {
294         int rc_code = 0;
295         nbuf = _rkcl_getfp(fp, buf);
296         if (nbuf == NULL) {
297             goto clean_return;
298         }
299
300         rc_code = _rkcl_get_vars(vars, nbuf);
301         if (rc_code == 0) {
302             break;
303         } else if (rc_code == -1) {
304             merror(INVALID_RKCL_VAR, ARGV0, nbuf);
305             goto clean_return;
306         }
307     }
308
309     /* Get first name */
310     name = _rkcl_get_name(nbuf, ref, &condition);
311     if (name == NULL || condition == RKCL_COND_INV) {
312         merror(INVALID_RKCL_NAME, ARGV0, nbuf);
313         goto clean_return;
314     }
315
316     /* Get the real entries */
317     do {
318         int g_found = 0;
319
320         debug2("%s: DEBUG: Checking entry: '%s'.", ARGV0, name);
321
322         /* Get each value */
323         do {
324             int negate = 0;
325             int found = 0;
326             value = NULL;
327
328             nbuf = _rkcl_getfp(fp, buf);
329             if (nbuf == NULL) {
330                 break;
331             }
332
333             /* First try to get the name, looking for new entries */
334             if (_rkcl_is_name(nbuf)) {
335                 break;
336             }
337
338             /* Get value to look for */
339             value = _rkcl_get_value(nbuf, &type);
340             if (value == NULL) {
341                 merror(INVALID_RKCL_VALUE, ARGV0, nbuf);
342                 goto clean_return;
343             }
344
345             /* Get negate value */
346             if (*value == '!') {
347                 negate = 1;
348                 value++;
349             }
350
351             /* Check for a file */
352             if (type == RKCL_TYPE_FILE) {
353                 char *pattern = NULL;
354                 char *f_value = NULL;
355
356                 pattern = _rkcl_get_pattern(value);
357                 f_value = value;
358
359                 /* Get any variable */
360                 if (value[0] == '$') {
361                     f_value = (char *) OSStore_Get(vars, value);
362                     if (!f_value) {
363                         merror(INVALID_RKCL_VAR, ARGV0, value);
364                         continue;
365                     }
366                 }
367
368 #ifdef WIN32
369                 else if (value[0] == '\\') {
370                     final_file[0] = '\0';
371                     final_file[sizeof(final_file) - 1] = '\0';
372
373                     snprintf(final_file, sizeof(final_file) - 2, "%s%s",
374                              root_dir, value);
375                     f_value = final_file;
376                 } else {
377                     final_file[0] = '\0';
378                     final_file[sizeof(final_file) - 1] = '\0';
379
380                     ExpandEnvironmentStrings(value, final_file,
381                                              sizeof(final_file) - 2);
382                     f_value = final_file;
383                 }
384 #endif
385
386                 debug2("%s: DEBUG: Checking file: '%s'.", ARGV0, f_value);
387                 if (rk_check_file(f_value, pattern)) {
388                     debug1("%s: DEBUG: found file.", ARGV0);
389                     found = 1;
390                 }
391             }
392
393 #ifdef WIN32
394             /* Check for a registry entry */
395             else if (type == RKCL_TYPE_REGISTRY) {
396                 char *entry = NULL;
397                 char *pattern = NULL;
398
399                 /* Look for additional entries in the registry
400                  * and a pattern to match.
401                  */
402                 entry = _rkcl_get_pattern(value);
403                 if (entry) {
404                     pattern = _rkcl_get_pattern(entry);
405                 }
406
407                 debug2("%s: DEBUG: Checking registry: '%s'.", ARGV0, value);
408                 if (is_registry(value, entry, pattern)) {
409                     debug2("%s: DEBUG: found registry.", ARGV0);
410                     found = 1;
411                 }
412
413             }
414 #endif
415             /* Check for a directory */
416             else if (type == RKCL_TYPE_DIR) {
417                 char *file = NULL;
418                 char *pattern = NULL;
419                 char *f_value = NULL;
420                 char *dir = NULL;
421
422                 file = _rkcl_get_pattern(value);
423                 if (!file) {
424                     merror(INVALID_RKCL_VAR, ARGV0, value);
425                     continue;
426                 }
427
428                 pattern = _rkcl_get_pattern(file);
429
430                 /* Get any variable */
431                 if (value[0] == '$') {
432                     f_value = (char *) OSStore_Get(vars, value);
433                     if (!f_value) {
434                         merror(INVALID_RKCL_VAR, ARGV0, value);
435                         continue;
436                     }
437                 } else {
438                     f_value = value;
439                 }
440
441                 /* Check for multiple comma separated directories */
442                 dir = f_value;
443                 f_value = strchr(dir, ',');
444                 if (f_value) {
445                     *f_value = '\0';
446                 }
447
448                 while (dir) {
449
450                     debug2("%s: Checking dir: %s", ARGV0, dir);
451
452                     short is_nfs = IsNFS(dir);
453                     if( is_nfs == 1 && rootcheck.skip_nfs ) {
454                         debug1("%s: DEBUG: rootcheck.skip_nfs enabled and %s is flagged as NFS.", ARGV0, dir);
455                     }
456                     else {
457                         debug2("%s: DEBUG: %s => is_nfs=%d, skip_nfs=%d", ARGV0, dir, is_nfs, rootcheck.skip_nfs);
458
459                         if (rk_check_dir(dir, file, pattern)) {
460                             debug2("%s: DEBUG: Found dir.", ARGV0);
461                             found = 1;
462                         }
463                     }
464
465                     if (f_value) {
466                         *f_value = ',';
467                         f_value++;
468
469                         dir = f_value;
470
471                         f_value = strchr(dir, ',');
472                         if (f_value) {
473                             *f_value = '\0';
474                         }
475                     } else {
476                         dir = NULL;
477                     }
478                 }
479             }
480
481             /* Check for a process */
482             else if (type == RKCL_TYPE_PROCESS) {
483                 debug2("%s: DEBUG: Checking process: '%s'.", ARGV0, value);
484                 if (is_process(value, p_list)) {
485                     debug2("%s: DEBUG: found process.", ARGV0);
486                     found = 1;
487                 }
488             }
489
490             /* Switch the values if ! is present */
491             if (negate) {
492                 if (found) {
493                     found = 0;
494                 } else {
495                     found = 1;
496                 }
497             }
498
499             /* Check the conditions */
500             if (condition & RKCL_COND_ANY) {
501                 debug2("%s: DEBUG: Condition ANY.", ARGV0);
502                 if (found) {
503                     g_found = 1;
504                 }
505             } else {
506                 /* Condition for ALL */
507                 debug2("%s: DEBUG: Condition ALL.", ARGV0);
508                 if (found && (g_found != -1)) {
509                     g_found = 1;
510                 } else {
511                     g_found = -1;
512                 }
513             }
514         } while (value != NULL);
515
516         /* Alert if necessary */
517         if (g_found == 1) {
518             int j = 0;
519             char op_msg[OS_SIZE_1024 + 1];
520             char **p_alert_msg = rootcheck.alert_msg;
521
522             while (1) {
523                 if (ref[0] != '\0') {
524                     snprintf(op_msg, OS_SIZE_1024, "%s %s.%s"
525                              " Reference: %s .", msg, name,
526                              p_alert_msg[j] ? p_alert_msg[j] : "\0",
527                              ref);
528                 } else {
529                     snprintf(op_msg, OS_SIZE_1024, "%s %s.%s", msg,
530                              name, p_alert_msg[j] ? p_alert_msg[j] : "\0");
531                 }
532
533                 if ((type == RKCL_TYPE_DIR) || (j == 0)) {
534                     notify_rk(ALERT_POLICY_VIOLATION, op_msg);
535                 }
536
537                 if (p_alert_msg[j]) {
538                     free(p_alert_msg[j]);
539                     p_alert_msg[j] = NULL;
540                     j++;
541
542                     if (!p_alert_msg[j]) {
543                         break;
544                     }
545                 } else {
546                     break;
547                 }
548             }
549         } else {
550             int j = 0;
551             while (rootcheck.alert_msg[j]) {
552                 free(rootcheck.alert_msg[j]);
553                 rootcheck.alert_msg[j] = NULL;
554                 j++;
555             }
556
557             /* Check if this entry is required for the rest of the file */
558             if (condition & RKCL_COND_REQ) {
559                 goto clean_return;
560             }
561         }
562
563         /* End if we don't have anything else */
564         if (!nbuf) {
565             goto clean_return;
566         }
567
568         /* Clean up name */
569         if (name) {
570             free(name);
571             name = NULL;
572         }
573
574         /* Get name already read */
575         name = _rkcl_get_name(nbuf, ref, &condition);
576         if (!name) {
577             merror(INVALID_RKCL_NAME, ARGV0, nbuf);
578             goto clean_return;
579         }
580     } while (nbuf != NULL);
581
582     /* Clean up memory */
583 clean_return:
584     if (name) {
585         free(name);
586         name = NULL;
587     }
588     OSStore_Free(vars);
589
590     return (1);
591 }
592