1 /* Copyright (C) 2009 Trend Micro Inc.
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
11 #include "rootcheck.h"
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);
22 #define RKCL_TYPE_FILE 1
23 #define RKCL_TYPE_REGISTRY 2
24 #define RKCL_TYPE_PROCESS 3
25 #define RKCL_TYPE_DIR 4
27 #define RKCL_COND_ALL 0x001
28 #define RKCL_COND_ANY 0x002
29 #define RKCL_COND_REQ 0x004
30 #define RKCL_COND_INV 0x010
34 char *_rkcl_getrootdir(char *root_dir, int dir_size)
36 char final_file[2048 + 1];
40 final_file[2048] = '\0';
42 ExpandEnvironmentStrings("%WINDIR%", final_file, 2047);
44 tmp = strchr(final_file, '\\');
47 strncpy(root_dir, final_file, dir_size);
55 /* Get next available buffer in file */
56 static char *_rkcl_getfp(FILE *fp, char *buf)
58 while (fgets(buf, OS_SIZE_1024, fp) != NULL) {
61 /* Remove end of line */
62 nbuf = strchr(buf, '\n');
67 /* Assign buf to be used */
70 /* Exclude commented lines or blanked ones */
71 while (*nbuf != '\0') {
72 if (*nbuf == ' ' || *nbuf == '\t') {
75 } else if (*nbuf == '#') {
83 /* Go to next line if empty */
94 static int _rkcl_is_name(const char *buf)
96 if (*buf == '[' && buf[strlen(buf) - 1] == ']') {
102 static int _rkcl_get_vars(OSStore *vars, char *nbuf)
107 /* If not a variable, return 0 */
112 /* Remove semicolon from the end */
113 tmp = strchr(nbuf, ';');
121 tmp = strchr(nbuf, '=');
129 /* Dump the variable options */
130 os_strdup(tmp, var_value);
132 /* Add entry to the storage */
133 OSStore_Put(vars, nbuf, var_value);
137 static char *_rkcl_get_name(char *buf, char *ref, int *condition)
143 /* Check if name is valid */
144 if (!_rkcl_is_name(buf)) {
150 tmp_location = strchr(buf, ']');
154 *tmp_location = '\0';
158 if (*tmp_location != ' ' && tmp_location[1] != '[') {
163 tmp_location2 = strchr(tmp_location, ']');
164 if (!tmp_location2) {
167 *tmp_location2 = '\0';
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;
182 *condition = RKCL_COND_INV;
187 if (*tmp_location2 != ' ' && tmp_location2[1] != '[') {
192 tmp_location = strchr(tmp_location2, ']');
196 *tmp_location = '\0';
199 strncpy(ref, tmp_location2, 255);
201 return (strdup(buf));
204 static char *_rkcl_get_pattern(char *value)
206 while (*value != '\0') {
207 if ((*value == ' ') && (value[1] == '-') &&
208 (value[2] == '>') && (value[3] == ' ')) {
220 static char *_rkcl_get_value(char *buf, int *type)
225 /* Zero type before using it to make sure return is valid
230 value = strchr(buf, ':');
238 tmp_str = strchr(value, ';');
239 if (tmp_str == NULL) {
244 /* Get types - removing negate flag (using later) */
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;
264 int rkcl_get_entry(FILE *fp, const char *msg, OSList *p_list)
266 int type = 0, condition = 0;
268 char buf[OS_SIZE_1024 + 2];
269 char root_dir[OS_SIZE_1024 + 2];
270 char final_file[2048 + 1];
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));
283 /* Get Windows rootdir */
284 _rkcl_getrootdir(root_dir, sizeof(root_dir) - 1);
285 if (root_dir[0] == '\0') {
286 merror(INVALID_ROOTDIR, ARGV0);
290 vars = OSStore_Create();
292 /* We first read all variables -- they must be defined at the top */
295 nbuf = _rkcl_getfp(fp, buf);
300 rc_code = _rkcl_get_vars(vars, nbuf);
303 } else if (rc_code == -1) {
304 merror(INVALID_RKCL_VAR, ARGV0, nbuf);
310 name = _rkcl_get_name(nbuf, ref, &condition);
311 if (name == NULL || condition == RKCL_COND_INV) {
312 merror(INVALID_RKCL_NAME, ARGV0, nbuf);
316 /* Get the real entries */
320 debug2("%s: DEBUG: Checking entry: '%s'.", ARGV0, name);
328 nbuf = _rkcl_getfp(fp, buf);
333 /* First try to get the name, looking for new entries */
334 if (_rkcl_is_name(nbuf)) {
338 /* Get value to look for */
339 value = _rkcl_get_value(nbuf, &type);
341 merror(INVALID_RKCL_VALUE, ARGV0, nbuf);
345 /* Get negate value */
351 /* Check for a file */
352 if (type == RKCL_TYPE_FILE) {
353 char *pattern = NULL;
354 char *f_value = NULL;
356 pattern = _rkcl_get_pattern(value);
359 /* Get any variable */
360 if (value[0] == '$') {
361 f_value = (char *) OSStore_Get(vars, value);
363 merror(INVALID_RKCL_VAR, ARGV0, value);
369 else if (value[0] == '\\') {
370 final_file[0] = '\0';
371 final_file[sizeof(final_file) - 1] = '\0';
373 snprintf(final_file, sizeof(final_file) - 2, "%s%s",
375 f_value = final_file;
377 final_file[0] = '\0';
378 final_file[sizeof(final_file) - 1] = '\0';
380 ExpandEnvironmentStrings(value, final_file,
381 sizeof(final_file) - 2);
382 f_value = final_file;
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);
394 /* Check for a registry entry */
395 else if (type == RKCL_TYPE_REGISTRY) {
397 char *pattern = NULL;
399 /* Look for additional entries in the registry
400 * and a pattern to match.
402 entry = _rkcl_get_pattern(value);
404 pattern = _rkcl_get_pattern(entry);
407 debug2("%s: DEBUG: Checking registry: '%s'.", ARGV0, value);
408 if (is_registry(value, entry, pattern)) {
409 debug2("%s: DEBUG: found registry.", ARGV0);
415 /* Check for a directory */
416 else if (type == RKCL_TYPE_DIR) {
418 char *pattern = NULL;
419 char *f_value = NULL;
422 file = _rkcl_get_pattern(value);
424 merror(INVALID_RKCL_VAR, ARGV0, value);
428 pattern = _rkcl_get_pattern(file);
430 /* Get any variable */
431 if (value[0] == '$') {
432 f_value = (char *) OSStore_Get(vars, value);
434 merror(INVALID_RKCL_VAR, ARGV0, value);
441 /* Check for multiple comma separated directories */
443 f_value = strchr(dir, ',');
450 debug2("%s: Checking dir: %s", ARGV0, dir);
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);
457 debug2("%s: DEBUG: %s => is_nfs=%d, skip_nfs=%d", ARGV0, dir, is_nfs, rootcheck.skip_nfs);
459 if (rk_check_dir(dir, file, pattern)) {
460 debug2("%s: DEBUG: Found dir.", ARGV0);
471 f_value = strchr(dir, ',');
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);
490 /* Switch the values if ! is present */
499 /* Check the conditions */
500 if (condition & RKCL_COND_ANY) {
501 debug2("%s: DEBUG: Condition ANY.", ARGV0);
506 /* Condition for ALL */
507 debug2("%s: DEBUG: Condition ALL.", ARGV0);
508 if (found && (g_found != -1)) {
514 } while (value != NULL);
516 /* Alert if necessary */
519 char op_msg[OS_SIZE_1024 + 1];
520 char **p_alert_msg = rootcheck.alert_msg;
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",
529 snprintf(op_msg, OS_SIZE_1024, "%s %s.%s", msg,
530 name, p_alert_msg[j] ? p_alert_msg[j] : "\0");
533 if ((type == RKCL_TYPE_DIR) || (j == 0)) {
534 notify_rk(ALERT_POLICY_VIOLATION, op_msg);
537 if (p_alert_msg[j]) {
538 free(p_alert_msg[j]);
539 p_alert_msg[j] = NULL;
542 if (!p_alert_msg[j]) {
551 while (rootcheck.alert_msg[j]) {
552 free(rootcheck.alert_msg[j]);
553 rootcheck.alert_msg[j] = NULL;
557 /* Check if this entry is required for the rest of the file */
558 if (condition & RKCL_COND_REQ) {
563 /* End if we don't have anything else */
574 /* Get name already read */
575 name = _rkcl_get_name(nbuf, ref, &condition);
577 merror(INVALID_RKCL_NAME, ARGV0, nbuf);
580 } while (nbuf != NULL);
582 /* Clean up memory */