1 /* @(#) $Id: ./src/rootcheck/check_rc_sys.c, 2011/09/08 dcid Exp $
4 /* Copyright (C) 2009 Trend Micro Inc.
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
15 #include "rootcheck.h"
26 int read_sys_dir(char *dir_name, int do_read);
28 int read_sys_file(char *file_name, int do_read)
36 /* Check for NTFS ADS on Windows */
37 os_check_ads(file_name);
41 if(lstat(file_name, &statbuf) < 0)
44 char op_msg[OS_SIZE_1024 +1];
45 snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file '%s'. "
46 "Hidden from stats, but showing up on readdir. "
47 "Possible kernel level rootkit.",
49 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
56 /* If directory, read the directory */
57 else if(S_ISDIR(statbuf.st_mode))
59 /* Making Darwin happy. for some reason,
60 * when I read /dev/fd, it goes forever on
61 * /dev/fd5, /dev/fd6, etc.. weird
63 if(strstr(file_name, "/dev/fd") != NULL)
66 /* Ignoring /proc directory (it has the size 0). */
67 if(statbuf.st_size == 0)
70 return(read_sys_dir(file_name, do_read));
73 /* Check if the size from stats is the same as when we
76 if(S_ISREG(statbuf.st_mode) && do_read)
78 char buf[OS_SIZE_1024];
81 unsigned long int total = 0;
83 fd = open(file_name, O_RDONLY, 0);
85 /* It may not necessarily open */
88 while ((nr = read(fd, buf, sizeof(buf))) > 0)
94 if(strcmp(file_name, "/dev/bus/usb/.usbfs/devices") == 0)
96 /* Ignore .usbfs/devices. */
99 else if(total != statbuf.st_size)
101 struct stat statbuf2;
103 if((lstat(file_name, &statbuf2) == 0) &&
104 (total != statbuf2.st_size) &&
105 (statbuf.st_size == statbuf2.st_size))
107 char op_msg[OS_SIZE_1024 +1];
108 snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file "
109 "'%s'. File size doesn't match what we found. "
110 "Possible kernel level rootkit.",
112 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
120 /* If has OTHER write and exec permission, alert */
122 if(((statbuf.st_mode & S_IWOTH) == S_IWOTH) &&
123 (S_ISREG(statbuf.st_mode)))
125 if((statbuf.st_mode & S_IXUSR) == S_IXUSR)
128 fprintf(_wx, "%s\n",file_name);
135 fprintf(_ww, "%s\n", file_name);
138 if(statbuf.st_uid == 0)
140 char op_msg[OS_SIZE_1024 +1];
142 snprintf(op_msg, OS_SIZE_1024, "File '%s' is owned by root "
143 "and has written permissions to anyone.",
146 snprintf(op_msg, OS_SIZE_1024, "File '%s' is: \n"
147 " - owned by root,\n"
148 " - has written permissions to anyone.",
151 notify_rk(ALERT_SYSTEM_CRIT, op_msg);
157 else if((statbuf.st_mode & S_ISUID) == S_ISUID)
160 fprintf(_suid,"%s\n", file_name);
170 int read_sys_dir(char *dir_name, int do_read)
173 unsigned int entry_count = 0;
177 struct dirent *entry;
181 char *(dirs_to_doread[]) = { "/bin", "/sbin", "/usr/bin",
182 "/usr/sbin", "/dev", "/etc",
186 if((dir_name == NULL)||(strlen(dir_name) > PATH_MAX))
188 merror("%s: Invalid directory given.",ARGV0);
193 /* Ignoring user-supplied list. */
196 while(rootcheck.ignore[i])
198 if(strcmp(dir_name, rootcheck.ignore[i]) == 0)
209 /* Getting the number of nodes. The total number on opendir
212 if(lstat(dir_name, &statbuf) < 0)
218 /* Currently device id */
219 if(did != statbuf.st_dev)
223 did = statbuf.st_dev;
227 if(!S_ISDIR(statbuf.st_mode))
234 /* Check if the do_read is valid for this directory */
235 while(dirs_to_doread[i])
237 if(strcmp(dir_name, dirs_to_doread[i]) == 0)
249 /* Opening the directory given */
250 dp = opendir(dir_name);
253 if((strcmp(dir_name, "") == 0)&&
265 /* Reading every entry in the directory */
266 while((entry = readdir(dp)) != NULL)
268 char f_name[PATH_MAX +2];
269 struct stat statbuf_local;
271 /* Just ignore . and .. */
272 if((strcmp(entry->d_name,".") == 0) ||
273 (strcmp(entry->d_name,"..") == 0))
279 /* Creating new file + path string */
280 if(strcmp(dir_name, "/") == 0)
282 snprintf(f_name, PATH_MAX +1, "/%s", entry->d_name);
286 snprintf(f_name, PATH_MAX +1, "%s/%s",dir_name, entry->d_name);
289 /* Checking if file is a directory */
290 if(lstat(f_name, &statbuf_local) == 0)
292 /* On all the systems, except darwin, the
293 * link count is only increased on directories.
296 if(S_ISDIR(statbuf_local.st_mode))
298 if(S_ISDIR(statbuf_local.st_mode) ||
299 S_ISREG(statbuf_local.st_mode) ||
300 S_ISLNK(statbuf_local.st_mode))
308 /* Checking every file against the rootkit database */
309 for(i = 0; i<= rk_sys_count; i++)
314 if(strcmp(rk_sys_file[i], entry->d_name) == 0)
316 char op_msg[OS_SIZE_1024 +1];
319 snprintf(op_msg, OS_SIZE_1024, "Rootkit '%s' detected "
320 "by the presence of file '%s/%s'.",
321 rk_sys_name[i], dir_name, rk_sys_file[i]);
323 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
328 if((strcmp(f_name, "/proc") == 0) || (strcmp(f_name, "/sys") == 0))
331 read_sys_file(f_name, do_read);
334 /* Entry count for directory different than the actual
335 * link count from stats.
337 if((entry_count != statbuf.st_nlink) &&
338 ((did_changed == 0) || ((entry_count + 1) != statbuf.st_nlink)))
341 struct stat statbuf2;
342 char op_msg[OS_SIZE_1024 +1];
345 if((lstat(dir_name, &statbuf2) == 0) &&
346 (statbuf2.st_nlink != entry_count))
348 snprintf(op_msg, OS_SIZE_1024, "Files hidden inside directory "
349 "'%s'. Link count does not match number of files "
351 dir_name, entry_count, (int)statbuf.st_nlink);
353 /* Solaris /boot is terrible :) */
355 if(strncmp(dir_name, "/boot", strlen("/boot")) != 0)
357 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
360 #elif Darwin || FreeBSD
361 if(strncmp(dir_name, "/dev", strlen("/dev")) != 0)
363 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
367 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
382 /* check_rc_sys: v0.1
383 * Scan the whole filesystem looking for possible issues
385 void check_rc_sys(char *basedir)
387 char file_path[OS_SIZE_1024 +1];
389 debug1("%s: DEBUG: Starting on check_rc_sys", ARGV0);
393 did = 0; /* device id */
395 snprintf(file_path, OS_SIZE_1024, "%s", basedir);
398 /* Opening output files */
399 if(rootcheck.notify != QUEUE)
401 _wx = fopen("rootcheck-rw-rw-rw-.txt", "w");
402 _ww = fopen("rootcheck-rwxrwxrwx.txt", "w");
403 _suid=fopen("rootcheck-suid-files.txt", "w");
414 /* Scan the whole file system -- may be slow */
415 if(rootcheck.scanall)
418 snprintf(file_path, 3, "%s", "/");
421 read_sys_dir(file_path, rootcheck.readall);
425 /* Scan only specific directories */
431 char *(dirs_to_scan[]) = {"/bin", "/sbin", "/usr/bin",
432 "/usr/sbin", "/dev", "/lib",
433 "/etc", "/root", "/var/log",
434 "/var/mail", "/var/lib", "/var/www",
435 "/usr/lib", "/usr/include",
436 "/tmp", "/boot", "/usr/local",
437 "/var/tmp", "/sys", NULL};
440 char *(dirs_to_scan[]) = {"C:\\WINDOWS", "C:\\Program Files", NULL};
443 for(_i = 0; _i <= 24; _i++)
445 if(dirs_to_scan[_i] == NULL)
449 snprintf(file_path, OS_SIZE_1024, "%s%s",
452 read_sys_dir(file_path, rootcheck.readall);
455 read_sys_dir(dirs_to_scan[_i], rootcheck.readall);
463 char op_msg[OS_SIZE_1024 +1];
464 snprintf(op_msg, OS_SIZE_1024, "No problem found on the system."
465 " Analyzed %d files.", _sys_total);
466 notify_rk(ALERT_OK, op_msg);
469 else if(_wx && _ww && _suid)
471 char op_msg[OS_SIZE_1024 +1];
472 snprintf(op_msg, OS_SIZE_1024, "Check the following files for more "
473 "information:\n%s%s%s",
474 (ftell(_wx) == 0)?"":
475 " rootcheck-rw-rw-rw-.txt (list of world writable files)\n",
476 (ftell(_ww) == 0)?"":
477 " rootcheck-rwxrwxrwx.txt (list of world writtable/executable files)\n",
478 (ftell(_suid) == 0)?"":
479 " rootcheck-suid-files.txt (list of suid files)");
481 notify_rk(ALERT_SYSTEM_ERR, op_msg);
487 unlink("rootcheck-rw-rw-rw-.txt");
494 unlink("rootcheck-rwxrwxrwx.txt");
500 if(ftell(_suid) == 0)
501 unlink("rootcheck-suid-files.txt");