-/* @(#) $Id: ./src/rootcheck/check_rc_sys.c, 2011/09/08 dcid Exp $
- */
-
/* Copyright (C) 2009 Trend Micro Inc.
* All right reserved.
*
* Foundation
*/
-
#include "shared.h"
#include "rootcheck.h"
-int _sys_errors;
-int _sys_total;
-dev_t did;
+/* Prototypes */
+static int read_sys_file(const char *file_name, int do_read);
+static int read_sys_dir(const char *dir_name, int do_read);
-FILE *_wx;
-FILE *_ww;
-FILE *_suid;
+/* Global variables */
+static int _sys_errors;
+static int _sys_total;
+static dev_t did;
+static FILE *_wx;
+static FILE *_ww;
+static FILE *_suid;
-/** Prototypes **/
-int read_sys_dir(char *dir_name, int do_read);
-int read_sys_file(char *file_name, int do_read)
+static int read_sys_file(const char *file_name, int do_read)
{
struct stat statbuf;
_sys_total++;
-
- #ifdef WIN32
+#ifdef WIN32
/* Check for NTFS ADS on Windows */
os_check_ads(file_name);
- #endif
-
-
- if(lstat(file_name, &statbuf) < 0)
- {
- #ifndef WIN32
- char op_msg[OS_SIZE_1024 +1];
+#endif
+ if (lstat(file_name, &statbuf) < 0) {
+#ifndef WIN32
+ char op_msg[OS_SIZE_1024 + 1];
snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file '%s'. "
- "Hidden from stats, but showing up on readdir. "
- "Possible kernel level rootkit.",
- file_name);
+ "Hidden from stats, but showing up on readdir. "
+ "Possible kernel level rootkit.",
+ file_name);
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
_sys_errors++;
-
- #endif
- return(-1);
+#endif
+ return (-1);
}
/* If directory, read the directory */
- else if(S_ISDIR(statbuf.st_mode))
- {
- /* Making Darwin happy. for some reason,
+ else if (S_ISDIR(statbuf.st_mode)) {
+ /* Make Darwin happy. For some reason,
* when I read /dev/fd, it goes forever on
* /dev/fd5, /dev/fd6, etc.. weird
*/
- if(strstr(file_name, "/dev/fd") != NULL)
- return(0);
+ if (strstr(file_name, "/dev/fd") != NULL) {
+ return (0);
+ }
- /* Ignoring /proc directory (it has the size 0). */
- if(statbuf.st_size == 0)
- return(0);
+ /* Ignore the /proc directory (it has size 0) */
+ if (statbuf.st_size == 0) {
+ return (0);
+ }
- return(read_sys_dir(file_name, do_read));
+ return (read_sys_dir(file_name, do_read));
}
- /* Check if the size from stats is the same as when we
- * read the file
- */
- if(S_ISREG(statbuf.st_mode) && do_read)
- {
+ /* Check if the size from stats is the same as when we read the file */
+ if (S_ISREG(statbuf.st_mode) && do_read) {
char buf[OS_SIZE_1024];
int fd;
- int nr;
- unsigned long int total = 0;
+ ssize_t nr;
+ long int total = 0;
fd = open(file_name, O_RDONLY, 0);
/* It may not necessarily open */
- if(fd >= 0)
- {
- while ((nr = read(fd, buf, sizeof(buf))) > 0)
- {
+ if (fd >= 0) {
+ while ((nr = read(fd, buf, sizeof(buf))) > 0) {
total += nr;
}
close(fd);
- if(strcmp(file_name, "/dev/bus/usb/.usbfs/devices") == 0)
- {
- /* Ignore .usbfs/devices. */
- }
-
- else if(total != statbuf.st_size)
- {
+ if (strcmp(file_name, "/dev/bus/usb/.usbfs/devices") == 0) {
+ /* Ignore .usbfs/devices */
+ } else if (total != statbuf.st_size) {
struct stat statbuf2;
- if((lstat(file_name, &statbuf2) == 0) &&
- (total != statbuf2.st_size) &&
- (statbuf.st_size == statbuf2.st_size))
- {
- char op_msg[OS_SIZE_1024 +1];
+ if ((lstat(file_name, &statbuf2) == 0) &&
+ (total != statbuf2.st_size) &&
+ (statbuf.st_size == statbuf2.st_size)) {
+ char op_msg[OS_SIZE_1024 + 1];
snprintf(op_msg, OS_SIZE_1024, "Anomaly detected in file "
- "'%s'. File size doesn't match what we found. "
- "Possible kernel level rootkit.",
- file_name);
+ "'%s'. File size doesn't match what we found. "
+ "Possible kernel level rootkit.",
+ file_name);
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
_sys_errors++;
}
}
}
-
/* If has OTHER write and exec permission, alert */
- #ifndef WIN32
- if(((statbuf.st_mode & S_IWOTH) == S_IWOTH) &&
- (S_ISREG(statbuf.st_mode)))
- {
- if((statbuf.st_mode & S_IXUSR) == S_IXUSR)
- {
- if(_wx)
- fprintf(_wx, "%s\n",file_name);
+#ifndef WIN32
+ if ((statbuf.st_mode & S_IWOTH) == S_IWOTH && S_ISREG(statbuf.st_mode)) {
+ if ((statbuf.st_mode & S_IXUSR) == S_IXUSR) {
+ if (_wx) {
+ fprintf(_wx, "%s\n", file_name);
+ }
_sys_errors++;
- }
- else
- {
- if(_ww)
+ } else {
+ if (_ww) {
fprintf(_ww, "%s\n", file_name);
+ }
}
- if(statbuf.st_uid == 0)
- {
- char op_msg[OS_SIZE_1024 +1];
- #ifdef OSSECHIDS
+ if (statbuf.st_uid == 0) {
+ char op_msg[OS_SIZE_1024 + 1];
+#ifdef OSSECHIDS
snprintf(op_msg, OS_SIZE_1024, "File '%s' is owned by root "
- "and has written permissions to anyone.",
- file_name);
- #else
+ "and has write permissions to anyone.", file_name);
+#else
snprintf(op_msg, OS_SIZE_1024, "File '%s' is: \n"
- " - owned by root,\n"
- " - has written permissions to anyone.",
- file_name);
- #endif
+ " - owned by root,\n"
+ " - has write permissions to anyone.",
+ file_name);
+#endif
notify_rk(ALERT_SYSTEM_CRIT, op_msg);
}
_sys_errors++;
+ } else if ((statbuf.st_mode & S_ISUID) == S_ISUID) {
+ if (_suid) {
+ fprintf(_suid, "%s\n", file_name);
+ }
}
-
- else if((statbuf.st_mode & S_ISUID) == S_ISUID)
- {
- if(_suid)
- fprintf(_suid,"%s\n", file_name);
- }
- #endif
-
- return(0);
+#endif /* WIN32 */
+ return (0);
}
-/* read_dir v0.1
- *
- */
-int read_sys_dir(char *dir_name, int do_read)
+static int read_sys_dir(const char *dir_name, int do_read)
{
int i = 0;
unsigned int entry_count = 0;
int did_changed = 0;
DIR *dp;
-
- struct dirent *entry;
- struct stat statbuf;
-
- #ifndef WIN32
- char *(dirs_to_doread[]) = { "/bin", "/sbin", "/usr/bin",
- "/usr/sbin", "/dev", "/etc",
- "/boot", NULL };
- #endif
-
- if((dir_name == NULL)||(strlen(dir_name) > PATH_MAX))
- {
- merror("%s: Invalid directory given.",ARGV0);
- return(-1);
+ struct dirent *entry;
+ struct stat statbuf;
+ short is_nfs;
+ short skip_fs;
+
+#ifndef WIN32
+ const char *(dirs_to_doread[]) = { "/bin", "/sbin", "/usr/bin",
+ "/usr/sbin", "/dev", "/etc",
+ "/boot", NULL
+ };
+#endif
+
+ if ((dir_name == NULL) || (strlen(dir_name) > PATH_MAX)) {
+ merror("%s: Invalid directory given.", ARGV0);
+ return (-1);
}
-
- /* Ignoring user-supplied list. */
- if(rootcheck.ignore)
- {
- while(rootcheck.ignore[i])
- {
- if(strcmp(dir_name, rootcheck.ignore[i]) == 0)
- {
- return(1);
+ /* Ignore user-supplied list */
+ if (rootcheck.ignore) {
+ while (rootcheck.ignore[i]) {
+ if (strcmp(dir_name, rootcheck.ignore[i]) == 0) {
+ return (1);
}
i++;
}
i = 0;
}
-
+ /* Should we check for NFS? */
+ if(rootcheck.skip_nfs)
+ {
+ is_nfs = IsNFS(dir_name);
+ if(is_nfs != 0)
+ {
+ // Error will be -1, and 1 means skipped
+ return(is_nfs);
+ }
+ }
/* Getting the number of nodes. The total number on opendir
* must be the same
return(-1);
}
-
- /* Currently device id */
- if(did != statbuf.st_dev)
- {
- if(did != 0)
+ /* Current device id */
+ if (did != statbuf.st_dev) {
+ if (did != 0) {
did_changed = 1;
+ }
did = statbuf.st_dev;
}
-
- if(!S_ISDIR(statbuf.st_mode))
- {
- return(-1);
+ if (!S_ISDIR(statbuf.st_mode)) {
+ return (-1);
}
-
- #ifndef WIN32
+#ifndef WIN32
/* Check if the do_read is valid for this directory */
- while(dirs_to_doread[i])
- {
- if(strcmp(dir_name, dirs_to_doread[i]) == 0)
- {
+ while (dirs_to_doread[i]) {
+ if (strcmp(dir_name, dirs_to_doread[i]) == 0) {
do_read = 1;
break;
}
i++;
}
- #else
+#else
do_read = 0;
- #endif
-
+#endif
- /* Opening the directory given */
+ /* Open the directory */
dp = opendir(dir_name);
- if(!dp)
- {
- if((strcmp(dir_name, "") == 0)&&
- (dp = opendir("/")))
- {
+ if (!dp) {
+ if ((strcmp(dir_name, "") == 0) &&
+ (dp = opendir("/"))) {
/* ok */
- }
- else
- {
- return(-1);
+ } else {
+ return (-1);
}
}
-
- /* Reading every entry in the directory */
- while((entry = readdir(dp)) != NULL)
- {
- char f_name[PATH_MAX +2];
+ /* Read every entry in the directory */
+ while ((entry = readdir(dp)) != NULL) {
+ char f_name[PATH_MAX + 2];
struct stat statbuf_local;
- /* Just ignore . and .. */
- if((strcmp(entry->d_name,".") == 0) ||
- (strcmp(entry->d_name,"..") == 0))
- {
+ /* Ignore . and .. */
+ if ((strcmp(entry->d_name, ".") == 0) ||
+ (strcmp(entry->d_name, "..") == 0)) {
entry_count++;
continue;
}
- /* Creating new file + path string */
- if(strcmp(dir_name, "/") == 0)
- {
- snprintf(f_name, PATH_MAX +1, "/%s", entry->d_name);
- }
- else
- {
- snprintf(f_name, PATH_MAX +1, "%s/%s",dir_name, entry->d_name);
+ /* Create new file + path string */
+ if (strcmp(dir_name, "/") == 0) {
+ snprintf(f_name, PATH_MAX + 1, "/%s", entry->d_name);
+ } else {
+ snprintf(f_name, PATH_MAX + 1, "%s/%s", dir_name, entry->d_name);
}
- /* Checking if file is a directory */
- if(lstat(f_name, &statbuf_local) == 0)
- {
- /* On all the systems, except darwin, the
- * link count is only increased on directories.
+ /* Check if file is a directory */
+ if (lstat(f_name, &statbuf_local) == 0) {
+ /* On all the systems except Darwin, the
+ * link count is only increased on directories
*/
- #ifndef Darwin
- if(S_ISDIR(statbuf_local.st_mode))
- #else
- if(S_ISDIR(statbuf_local.st_mode) ||
- S_ISREG(statbuf_local.st_mode) ||
- S_ISLNK(statbuf_local.st_mode))
- #endif
+#ifndef Darwin
+ if (S_ISDIR(statbuf_local.st_mode))
+#else
+ if (S_ISDIR(statbuf_local.st_mode) ||
+ S_ISREG(statbuf_local.st_mode)
+ /* No S_ISLNK on Windows */
+#ifndef WIN32
+ || S_ISLNK(statbuf_local.st_mode)
+#endif
+ )
+#endif
{
entry_count++;
}
}
-
- /* Checking every file against the rootkit database */
- for(i = 0; i<= rk_sys_count; i++)
- {
- if(!rk_sys_file[i])
+ /* Check every file against the rootkit database */
+ for (i = 0; i <= rk_sys_count; i++) {
+ if (!rk_sys_file[i]) {
break;
+ }
- if(strcmp(rk_sys_file[i], entry->d_name) == 0)
- {
- char op_msg[OS_SIZE_1024 +1];
+ if (strcmp(rk_sys_file[i], entry->d_name) == 0) {
+ char op_msg[OS_SIZE_1024 + 1];
_sys_errors++;
snprintf(op_msg, OS_SIZE_1024, "Rootkit '%s' detected "
- "by the presence of file '%s/%s'.",
- rk_sys_name[i], dir_name, rk_sys_file[i]);
+ "by the presence of file '%s/%s'.",
+ rk_sys_name[i], dir_name, rk_sys_file[i]);
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
}
}
- /* Ignoring /proc */
- if((strcmp(f_name, "/proc") == 0) || (strcmp(f_name, "/sys") == 0))
+ /* Ignore the /proc and /sys filesystems */
+ if ((strcmp(f_name, "/proc") == 0) || (strcmp(f_name, "/sys") == 0)) {
continue;
+ }
read_sys_file(f_name, do_read);
}
+ /* skip further test because the FS cant deliver the stats (btrfs link count always is 1) */
+ skip_fs = skipFS(dir_name);
+ if(skip_fs != 0)
+ {
+ // Error will be -1, and 1 means skipped
+ closedir(dp);
+ return(0);
+ }
+
/* Entry count for directory different than the actual
- * link count from stats.
+ * link count from stats
*/
- if((entry_count != statbuf.st_nlink) &&
- ((did_changed == 0) || ((entry_count + 1) != statbuf.st_nlink)))
- {
- #ifndef WIN32
+ if ((entry_count != (unsigned) statbuf.st_nlink) &&
+ ((did_changed == 0) || ((entry_count + 1) != (unsigned) statbuf.st_nlink))) {
+#ifndef WIN32
struct stat statbuf2;
- char op_msg[OS_SIZE_1024 +1];
+ char op_msg[OS_SIZE_1024 + 1];
-
- if((lstat(dir_name, &statbuf2) == 0) &&
- (statbuf2.st_nlink != entry_count))
- {
+ if ((lstat(dir_name, &statbuf2) == 0) &&
+ (statbuf2.st_nlink != entry_count)) {
snprintf(op_msg, OS_SIZE_1024, "Files hidden inside directory "
- "'%s'. Link count does not match number of files "
- "(%d,%d).",
- dir_name, entry_count, (int)statbuf.st_nlink);
+ "'%s'. Link count does not match number of files "
+ "(%d,%d).",
+ dir_name, entry_count, (int)statbuf.st_nlink);
/* Solaris /boot is terrible :) */
- #ifdef SOLARIS
- if(strncmp(dir_name, "/boot", strlen("/boot")) != 0)
- {
+#ifdef SOLARIS
+ if (strncmp(dir_name, "/boot", strlen("/boot")) != 0) {
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
_sys_errors++;
}
- #elif Darwin || FreeBSD
- if(strncmp(dir_name, "/dev", strlen("/dev")) != 0)
- {
+#elif defined(Darwin) || defined(FreeBSD)
+ if (strncmp(dir_name, "/dev", strlen("/dev")) != 0) {
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
_sys_errors++;
}
- #else
+#else
notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
-
_sys_errors++;
- #endif
+#endif
}
-
- #endif
+#endif /* WIN32 */
}
closedir(dp);
- return(0);
+ return (0);
}
-
-/* check_rc_sys: v0.1
- * Scan the whole filesystem looking for possible issues
- */
-void check_rc_sys(char *basedir)
+/* Scan the whole filesystem looking for possible issues */
+void check_rc_sys(const char *basedir)
{
- char file_path[OS_SIZE_1024 +1];
+ char file_path[OS_SIZE_1024 + 1];
debug1("%s: DEBUG: Starting on check_rc_sys", ARGV0);
snprintf(file_path, OS_SIZE_1024, "%s", basedir);
-
- /* Opening output files */
- if(rootcheck.notify != QUEUE)
- {
+ /* Open output files */
+ if (rootcheck.notify != QUEUE) {
_wx = fopen("rootcheck-rw-rw-rw-.txt", "w");
_ww = fopen("rootcheck-rwxrwxrwx.txt", "w");
- _suid=fopen("rootcheck-suid-files.txt", "w");
- }
- else
- {
+ _suid = fopen("rootcheck-suid-files.txt", "w");
+ } else {
_wx = NULL;
_ww = NULL;
_suid = NULL;
}
-
-
- /* Scan the whole file system -- may be slow */
- if(rootcheck.scanall)
- {
- #ifndef WIN32
+ if (rootcheck.scanall) {
+ /* Scan the whole file system -- may be slow */
+#ifndef WIN32
snprintf(file_path, 3, "%s", "/");
- #endif
-
+#endif
read_sys_dir(file_path, rootcheck.readall);
- }
-
-
- /* Scan only specific directories */
- else
- {
- int _i = 0;
-
- #ifndef WIN32
- char *(dirs_to_scan[]) = {"/bin", "/sbin", "/usr/bin",
- "/usr/sbin", "/dev", "/lib",
- "/etc", "/root", "/var/log",
- "/var/mail", "/var/lib", "/var/www",
- "/usr/lib", "/usr/include",
- "/tmp", "/boot", "/usr/local",
- "/var/tmp", "/sys", NULL};
-
- #else
- char *(dirs_to_scan[]) = {"C:\\WINDOWS", "C:\\Program Files", NULL};
- #endif
-
- for(_i = 0; _i <= 24; _i++)
- {
- if(dirs_to_scan[_i] == NULL)
- break;
-
- #ifndef WIN32
+ } else {
+ /* Scan only specific directories */
+ int _i;
+#ifndef WIN32
+ const char *(dirs_to_scan[]) = {"/bin", "/sbin", "/usr/bin",
+ "/usr/sbin", "/dev", "/lib",
+ "/etc", "/root", "/var/log",
+ "/var/mail", "/var/lib", "/var/www",
+ "/usr/lib", "/usr/include",
+ "/tmp", "/boot", "/usr/local",
+ "/var/tmp", "/sys", NULL
+ };
+
+#else
+ const char *(dirs_to_scan[]) = {"C:\\WINDOWS", "C:\\Program Files", NULL};
+#endif
+
+ _i = 0;
+ while (dirs_to_scan[_i] != NULL) {
+#ifndef WIN32
snprintf(file_path, OS_SIZE_1024, "%s%s",
- basedir,
- dirs_to_scan[_i]);
+ basedir,
+ dirs_to_scan[_i]);
read_sys_dir(file_path, rootcheck.readall);
- #else
+#else
read_sys_dir(dirs_to_scan[_i], rootcheck.readall);
- #endif
+#endif
+ _i++;
}
}
- if(_sys_errors == 0)
- {
- char op_msg[OS_SIZE_1024 +1];
+ if (_sys_errors == 0) {
+ char op_msg[OS_SIZE_1024 + 1];
snprintf(op_msg, OS_SIZE_1024, "No problem found on the system."
- " Analyzed %d files.", _sys_total);
+ " Analyzed %d files.", _sys_total);
notify_rk(ALERT_OK, op_msg);
}
- else if(_wx && _ww && _suid)
- {
- char op_msg[OS_SIZE_1024 +1];
+ else if (_wx && _ww && _suid) {
+ char op_msg[OS_SIZE_1024 + 1];
snprintf(op_msg, OS_SIZE_1024, "Check the following files for more "
- "information:\n%s%s%s",
- (ftell(_wx) == 0)?"":
- " rootcheck-rw-rw-rw-.txt (list of world writable files)\n",
- (ftell(_ww) == 0)?"":
- " rootcheck-rwxrwxrwx.txt (list of world writtable/executable files)\n",
- (ftell(_suid) == 0)?"":
- " rootcheck-suid-files.txt (list of suid files)");
+ "information:\n%s%s%s",
+ (ftell(_wx) == 0) ? "" :
+ " rootcheck-rw-rw-rw-.txt (list of world writable files)\n",
+ (ftell(_ww) == 0) ? "" :
+ " rootcheck-rwxrwxrwx.txt (list of world writtable/executable files)\n",
+ (ftell(_suid) == 0) ? "" :
+ " rootcheck-suid-files.txt (list of suid files)");
notify_rk(ALERT_SYSTEM_ERR, op_msg);
}
- if(_wx)
- {
- if(ftell(_wx) == 0)
+ if (_wx) {
+ if (ftell(_wx) == 0) {
unlink("rootcheck-rw-rw-rw-.txt");
+ }
fclose(_wx);
}
- if(_ww)
- {
- if(ftell(_ww) == 0)
+ if (_ww) {
+ if (ftell(_ww) == 0) {
unlink("rootcheck-rwxrwxrwx.txt");
+ }
fclose(_ww);
}
- if(_suid)
- {
- if(ftell(_suid) == 0)
+ if (_suid) {
+ if (ftell(_suid) == 0) {
unlink("rootcheck-suid-files.txt");
+ }
fclose(_suid);
}
return;
}
-/* EOF */