new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / rootcheck / check_rc_pids.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 #ifndef WIN32
11 #include "shared.h"
12 #include "rootcheck.h"
13
14 /* Prototypes */
15 static int  proc_read(int pid);
16 static int  proc_chdir(int pid);
17 static int  proc_stat(int pid);
18 static void loop_all_pids(const char *ps, pid_t max_pid, int *_errors, int *_total);
19
20 /* Global variables */
21 static int noproc;
22
23
24 /* If /proc is mounted, check to see if the pid is present */
25 static int proc_read(int pid)
26 {
27     char dir[OS_SIZE_1024 + 1];
28
29     if (noproc) {
30         return (0);
31     }
32
33     snprintf(dir, OS_SIZE_1024, "%d", pid);
34     if (isfile_ondir(dir, "/proc")) {
35         return (1);
36     }
37     return (0);
38 }
39
40 /* If /proc is mounted, check to see if the pid is present */
41 static int proc_chdir(int pid)
42 {
43     int ret = 0;
44     char curr_dir[OS_SIZE_1024 + 1];
45     char dir[OS_SIZE_1024 + 1];
46
47     if (noproc) {
48         return (0);
49     }
50     if (getcwd(curr_dir, OS_SIZE_1024) == NULL) {
51         return (0);
52     }
53     if (chdir("/proc") == -1) {
54         return (0);
55     }
56
57     snprintf(dir, OS_SIZE_1024, "/proc/%d", pid);
58     if (chdir(dir) == 0) {
59         ret = 1;
60     }
61
62     /* Returning to the previous directory */
63     if (chdir(curr_dir) == -1) {
64         return (0);
65     }
66
67     return (ret);
68 }
69
70 /* If /proc is mounted, check to see if the pid is present there */
71 static int proc_stat(int pid)
72 {
73     char proc_dir[OS_SIZE_1024 + 1];
74
75     if (noproc) {
76         return (0);
77     }
78
79     snprintf(proc_dir, OS_SIZE_1024, "%s/%d", "/proc", pid);
80
81     if (is_file(proc_dir)) {
82         return (1);
83     }
84
85     return (0);
86 }
87
88 /* Check all the available PIDs for hidden stuff */
89 static void loop_all_pids(const char *ps, pid_t max_pid, int *_errors, int *_total)
90 {
91     int _kill0 = 0;
92     int _kill1 = 0;
93     int _gsid0 = 0;
94     int _gsid1 = 0;
95     int _gpid0 = 0;
96     int _gpid1 = 0;
97     int _ps0 = -1;
98     int _proc_stat  = 0;
99     int _proc_read  = 0;
100     int _proc_chdir = 0;
101
102     pid_t i = 1;
103     pid_t my_pid;
104
105     char command[OS_SIZE_1024 + 1];
106
107     my_pid = getpid();
108
109     for (;; i++) {
110         if ((i <= 0) || (i > max_pid)) {
111             break;
112         }
113
114         (*_total)++;
115
116         _kill0 = 0;
117         _kill1 = 0;
118         _gsid0 = 0;
119         _gsid1 = 0;
120         _gpid0 = 0;
121         _gpid1 = 0;
122         _ps0 = -1;
123
124         /* kill test */
125         if (!((kill(i, 0) == -1) && (errno == ESRCH))) {
126             _kill0 = 1;
127         }
128
129         /* getsid test */
130         if (!((getsid(i) == -1) && (errno == ESRCH))) {
131             _gsid0 = 1;
132         }
133
134         /* getpgid test */
135         if (!((getpgid(i) == -1) && (errno == ESRCH))) {
136             _gpid0 = 1;
137         }
138
139         /* /proc test */
140         _proc_stat = proc_stat(i);
141         _proc_read = proc_read(i);
142         _proc_chdir = proc_chdir(i);
143
144         /* If PID does not exist, move on */
145         if (!_kill0     && !_gsid0     && !_gpid0 &&
146                 !_proc_stat && !_proc_read && !_proc_chdir) {
147             continue;
148         }
149
150         /* Ignore our own pid */
151         if (i == my_pid) {
152             continue;
153         }
154
155         /* Check the number of errors */
156         if ((*_errors) > 15) {
157             char op_msg[OS_SIZE_1024 + 1];
158             snprintf(op_msg, OS_SIZE_1024, "Excessive number of hidden processes"
159                      ". It maybe a false-positive or "
160                      "something really bad is going on.");
161             notify_rk(ALERT_SYSTEM_CRIT, op_msg);
162             return;
163         }
164
165         /* Check if the process appears in ps(1) output */
166         if (*ps) {
167             snprintf(command, OS_SIZE_1024, "%s -p %d > /dev/null 2>&1", ps, (int)i);
168             _ps0 = 0;
169             if (system(command) == 0) {
170                 _ps0 = 1;
171             }
172         }
173
174         /* If we are run in the context of OSSEC-HIDS, sleep here (no rush) */
175 #ifdef OSSECHIDS
176         debug1("%s: DEBUG: pause for %u", ARGV0, rootcheck.tsleep);
177         sleep(rootcheck.tsleep);
178 #endif
179
180         /* Everything fine, move on */
181         if (_ps0 && _kill0 && _gsid0 && _gpid0 && _proc_stat && _proc_read) {
182             continue;
183         }
184
185         /*
186          * If our kill or getsid system call got the PID but ps(1) did not,
187          * find out if the PID is deleted (not used anymore)
188          */
189         if (!((getsid(i) == -1) && (errno == ESRCH))) {
190             _gsid1 = 1;
191         }
192         if (!((kill(i, 0) == -1) && (errno == ESRCH))) {
193             _kill1 = 1;
194         }
195         if (!((getpgid(i) == -1) && (errno == ESRCH))) {
196             _gpid1 = 1;
197         }
198
199         _proc_stat = proc_stat(i);
200         _proc_read = proc_read(i);
201         _proc_chdir = proc_chdir(i);
202
203         /* If it matches, process was terminated in the meantime, so move on */
204         if (!_gsid1 && !_kill1 && !_gpid1 && !_proc_stat &&
205                 !_proc_read && !_proc_chdir) {
206             continue;
207         }
208
209 #ifdef AIX
210         /* Ignore AIX wait and sched programs */
211         if (_gsid0 == _gsid1 &&
212                 _kill0 == _kill1 &&
213                 _gpid0 == _gpid1 &&
214                 _ps0 == 1 &&
215                 _gsid0 == 1 &&
216                 _kill0 == 0) {
217             /* The wait and sched programs do not respond to kill 0.
218              * So if everything else finds it, including ps, getpid, getsid,
219              * but not kill, we can safely ignore on AIX.
220              * A malicious program would specially try to hide from ps.
221              */
222             continue;
223         }
224 #endif
225
226         if (_gsid0 == _gsid1 &&
227                 _kill0 == _kill1 &&
228                 _gsid0 != _kill0) {
229             /* If kill worked, but getsid and getpgid did not, it may
230              * be a defunct process -- ignore.
231              */
232             if (! (_kill0 == 1 && _gsid0 == 0 && _gpid0 == 0) ) {
233                 char op_msg[OS_SIZE_1024 + 1];
234
235                 snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from "
236                          "kill (%d) or getsid (%d). Possible kernel-level"
237                          " rootkit.", (int)i, _kill0, _gsid0);
238                 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
239                 (*_errors)++;
240             }
241         } else if (_kill1 != _gsid1 ||
242                    _gpid1 != _kill1 ||
243                    _gpid1 != _gsid1) {
244             /* See defunct process comment above */
245             if (! (_kill1 == 1 && _gsid0 == 0 && _gpid0 == 0 && _gsid1 == 0) ) {
246                 char op_msg[OS_SIZE_1024 + 1];
247
248                 snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from "
249                          "kill (%d), getsid (%d) or getpgid. Possible "
250                          "kernel-level rootkit.", (int)i, _kill1, _gsid1);
251                 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
252                 (*_errors)++;
253             }
254         } else if (_proc_read != _proc_stat  ||
255                    _proc_read != _proc_chdir ||
256                    _proc_stat != _kill1) {
257             /* Check if the pid is a thread (not showing in /proc */
258             if (!noproc && !check_rc_readproc((int)i)) {
259                 char op_msg[OS_SIZE_1024 + 1];
260
261                 snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from "
262                          "/proc. Possible kernel level rootkit.", (int)i);
263                 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
264                 (*_errors)++;
265             }
266         } else if (_gsid1 && _kill1 && !_ps0) {
267             /* checking if the pid is a thread (not showing on ps */
268             if (!check_rc_readproc((int)i)) {
269                 char op_msg[OS_SIZE_1024 + 1];
270
271                 snprintf(op_msg, OS_SIZE_1024, "Process '%d' hidden from "
272                          "ps. Possible trojaned version installed.",
273                          (int)i);
274                 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
275                 (*_errors)++;
276             }
277         }
278     }
279 }
280
281 /* Scan the whole filesystem looking for possible issues */
282 void check_rc_pids()
283 {
284     int _total = 0;
285     int _errors = 0;
286
287     char ps[OS_SIZE_1024 + 1];
288
289     char proc_0[] = "/proc";
290     char proc_1[] = "/proc/1";
291
292     pid_t max_pid = MAX_PID;
293     noproc = 1;
294
295     /* Checking where ps is */
296     memset(ps, '\0', OS_SIZE_1024 + 1);
297     strncpy(ps, "/bin/ps", OS_SIZE_1024);
298     if (!is_file(ps)) {
299         strncpy(ps, "/usr/bin/ps", OS_SIZE_1024);
300         if (!is_file(ps)) {
301             ps[0] = '\0';
302         }
303     }
304
305     /* Proc is mounted */
306     if (is_file(proc_0) && is_file(proc_1)) {
307         noproc = 0;
308     }
309
310     loop_all_pids(ps, max_pid, &_errors, &_total);
311
312     if (_errors == 0) {
313         char op_msg[OS_SIZE_1024 + 1];
314         snprintf(op_msg, OS_SIZE_1024, "No hidden process by Kernel-level "
315                  "rootkits.\n      %s is not trojaned. "
316                  "Analyzed %d processes.", ps, _total);
317         notify_rk(ALERT_OK, op_msg);
318     }
319
320     return;
321 }
322
323 #else
324 void check_rc_pids()
325 {
326     return;
327 }
328 #endif
329