source: trunk/src/sh_processcheck.c@ 172

Last change on this file since 172 was 170, checked in by katerina, 17 years ago

Plenty of compiler warnings fixed, SQL query length fixed, doc update.

File size: 33.6 KB
RevLine 
[67]1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2006 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/***************************************************************************
21 *
22 * This file provides a module for samhain to check for hidden/faked/missing
23 * processes on the host.
24 *
25 */
26
27#include "config_xor.h"
28
29#define _XOPEN_SOURCE 500
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <ctype.h>
34#include <string.h>
35#include <sys/types.h>
36#include <dirent.h>
37#include <sys/stat.h>
38#include <signal.h>
39#include <unistd.h>
40
41#ifdef _POSIX_PRIORITY_SCHEDULING
42#include <sched.h>
43#endif
44
45#ifdef HAVE_GETPRIORITY
46#include <errno.h>
47#include <sys/resource.h>
48#endif
49
[78]50#ifdef HAVE_SYS_STATVFS_H
51#include <sys/statvfs.h>
52#endif
53
54
[67]55#ifdef HAVE_REGEX_H
56#include <regex.h>
57#endif
58
59#include "samhain.h"
60#include "sh_modules.h"
61#include "sh_processcheck.h"
62#include "sh_utils.h"
63#include "sh_error.h"
64#include "sh_extern.h"
[78]65#include "sh_calls.h"
[143]66#include "sh_pthread.h"
[67]67
68#ifdef SH_USE_PROCESSCHECK
69
70#define FIL__ _("sh_processcheck.c")
71
72#ifdef __linux__
[143]73#define PS_THREADS
[67]74#endif
75
76/* We won't want to build this into yule
77 */
78#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
79
[143]80SH_MUTEX_STATIC(mutex_proc_check, PTHREAD_MUTEX_INITIALIZER);
81
[67]82/* sh_prochk_maxpid is one more than the largest pid
83 */
84static size_t sh_prochk_minpid = 0x0001;
85static size_t sh_prochk_maxpid = 0x8000;
86static size_t sh_prochk_size = 0;
87
88static int ShProchkActive = S_TRUE;
89static short * sh_prochk_res = NULL;
90
91static char * sh_prochk_pspath = NULL;
92static char * sh_prochk_psarg = NULL;
93
94#define SH_PROCHK_INTERVAL 300
95static time_t sh_prochk_interval = SH_PROCHK_INTERVAL;
96static int sh_prochk_severity = SH_ERR_SEVERE;
97
98
99static int sh_prochk_set_maxpid (const char * str);
100static int sh_prochk_set_minpid (const char * str);
101static int sh_prochk_set_active (const char *str);
102static int sh_prochk_add_process (const char *str);
103static int sh_prochk_set_pspath (const char *str);
104static int sh_prochk_set_psarg (const char *str);
105static int sh_prochk_set_interval(const char *str);
106static int sh_prochk_set_severity(const char *str);
107
108sh_rconf sh_prochk_table[] = {
109 {
110 N_("severityprocesscheck"),
111 sh_prochk_set_severity,
112 },
113 {
114 N_("processcheckexists"),
115 sh_prochk_add_process,
116 },
117 {
118 N_("processcheckactive"),
119 sh_prochk_set_active,
120 },
121 {
122 N_("processcheckminpid"),
123 sh_prochk_set_minpid,
124 },
125 {
126 N_("processcheckmaxpid"),
127 sh_prochk_set_maxpid,
128 },
129 {
130 N_("processcheckpspath"),
131 sh_prochk_set_pspath,
132 },
133 {
134 N_("processcheckpsarg"),
135 sh_prochk_set_psarg,
136 },
137 {
138 N_("processcheckinterval"),
139 sh_prochk_set_interval,
140 },
141 {
142 NULL,
143 NULL
144 }
145};
146
147#define SH_PROC_MISSING 1
148#define SH_PROC_FAKED 2
149#define SH_PROC_HIDDEN 4
150#define SH_PROC_EXISTS 8
151
152#ifndef HAVE_LSTAT
153#define lstat(x,y) stat(x,y)
154#endif /* HAVE_LSTAT */
155
[82]156#if defined(S_IFLNK) && !defined(S_ISLNK)
157#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
158#else
159#if !defined(S_ISLNK)
160#define S_ISLNK(mode) (0)
161#endif
162#endif
163
[67]164static const short SH_PR_PS = 0x0001;
165
166static const short SH_PR_GETSID = 0x0002;
167static const short SH_PR_KILL = 0x0004;
168static const short SH_PR_GETPGID = 0x0008;
169
170static const short SH_PR_LSTAT = 0x0010;
171static const short SH_PR_OPENDIR = 0x0020;
172static const short SH_PR_CHDIR = 0x0040;
173static const short SH_PR_SCHED = 0x0080;
174
175static const short SH_PR_PRIORITY = 0x0100;
[78]176static const short SH_PR_STATVSF = 0x0200;
[67]177
[78]178static const short SH_PR_PS2 = 0x1000;
179static const short SH_PR_PS_ANY = 0x2000;
180static const short SH_PR_ALL = 0x4000;
181static const short SH_PR_ANY = 0x8000;
[67]182
[77]183/* /proc:
184 * linux: /proc/pid/exe
185 * freebsd: /proc/pid/file
186 * solaris10: /proc/pid/path/a.out
187 */
[78]188static char * get_user_and_path (pid_t pid, char * user, size_t usrlen)
189{
[132]190 extern char * sh_unix_getUIDname (int level, uid_t uid, char * out, size_t len);
[77]191
[78]192 char path[128];
193 char * buf;
194 struct stat sbuf;
195 int len;
196 char * tmp;
197
198 sl_snprintf (path, sizeof(path), "/proc/%ld/exe", (unsigned long) pid);
199
200 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
201 {
202 goto linkread;
203 }
204
205 sl_snprintf (path, sizeof(path), "/proc/%ld/file", (unsigned long) pid);
206
207 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
208 {
209 goto linkread;
210 }
211
212 sl_snprintf (path, sizeof(path), "/proc/%ld/path/a.out", (unsigned long) pid);
213
214 if (0 == retry_lstat(FIL__, __LINE__, path, &sbuf) && S_ISLNK(sbuf.st_mode))
215 {
216 goto linkread;
217 }
218
219 return NULL;
220
221 linkread:
222
223 buf = SH_ALLOC(PATH_MAX);
224 len = readlink(path, buf, PATH_MAX); /* flawfinder: ignore */
225 len = (len >= PATH_MAX) ? (PATH_MAX-1) : len;
226
227 if (len > 0)
228 {
229 buf[len] = '\0';
230 }
231 else
232 {
233 SH_FREE(buf);
234 return NULL;
235 }
236
[132]237 tmp = sh_unix_getUIDname (SH_ERR_ALL, sbuf.st_uid, user, usrlen);
[78]238
[132]239 if (!tmp)
[78]240 sl_snprintf (user, usrlen, "%ld", (unsigned long) sbuf.st_uid);
241
242 return buf;
243}
244
245
[67]246struct watchlist {
247 char * str;
248 unsigned long pid;
249#ifdef HAVE_REGEX_H
250 regex_t preg;
251#endif
252 int seen;
253
254 struct watchlist *next;
255};
256
257static struct watchlist * process_check = NULL;
258
259static struct watchlist * list_missing = NULL;
260static struct watchlist * list_fake = NULL;
261static struct watchlist * list_hidden = NULL;
262
263/* recursively remove all list entries
264 */
265static void kill_list (struct watchlist * head)
266{
267 if (head->next)
268 kill_list (head->next);
269
270 if (head->str)
271 SH_FREE(head->str);
272 SH_FREE(head);
273
274 return;
275}
276
277
278/* check the list for old entries; clean out old entries; reset others
279 * Return number of non-obsolete entries
280 */
281static size_t clean_list (struct watchlist ** head_ptr)
282{
283 size_t count = 0;
284 struct watchlist * ptr = *head_ptr;
285 struct watchlist * pre = *head_ptr;
286
287 while (ptr)
288 {
289 if (ptr->seen == S_FALSE) /* obsolete entry */
290 {
291 if (ptr == pre) /* at head */
292 {
293 ptr = pre->next;
294 *head_ptr = pre->next;
295 if (pre->str)
296 SH_FREE(pre->str);
297 SH_FREE(pre);
298 pre = ptr;
299 }
300 else
301 {
302 pre->next = ptr->next;
303 if (ptr->str)
304 SH_FREE(ptr->str);
305 SH_FREE(ptr);
306 ptr = pre->next;
307 }
308 }
309 else
310 {
311 ++count;
312 ptr->seen = S_FALSE; /* reset status */
313 pre = ptr;
314 ptr = ptr->next;
315 }
316 }
317 return count;
318}
319
320/* check if process is in list; if not, add it and return false
321 */
322static int is_in_list (struct watchlist ** head_ptr,
323 char * str, unsigned long pid)
324{
325 struct watchlist * ptr = *head_ptr;
326
327 if (str)
328 {
329 while (ptr)
330 {
331 if (ptr->str && (0 == strcmp(str, ptr->str)))
332 {
333 ptr->seen = S_TRUE;
334 return S_TRUE;
335 }
336 ptr = ptr->next;
337 }
338 }
339 else
340 {
341 while (ptr)
342 {
343 if (ptr->pid == pid)
344 {
345 ptr->seen = S_TRUE;
346 return S_TRUE;
347 }
348 ptr = ptr->next;
349 }
350 }
351
352 ptr = SH_ALLOC(sizeof(struct watchlist));
353
354 if (str)
355 {
356 ptr->str = sh_util_strdup(str);
357 }
358 else
359 {
360 ptr->str = NULL;
361 ptr->pid = pid;
362 }
363 ptr->next = *head_ptr;
364 ptr->seen = S_TRUE;
365 *head_ptr = ptr;
366
367 return S_FALSE;
368}
369
370static int is_in_watchlist (const char *str, unsigned long num)
371{
372 struct watchlist * list = process_check;
373
374 while (list)
375 {
376#ifdef HAVE_REGEX_H
377 if (0 == regexec(&(list->preg), str, 0, NULL, 0))
378 {
379 list->seen = S_TRUE;
380 list->pid = num;
381 return S_TRUE;
382 }
383#else
384 if (strstr(str, list->str))
385 {
386 list->seen = S_TRUE;
387 list->pid = num;
388 return S_TRUE;
389 }
390#endif
391 list = list->next;
392 }
393 return S_FALSE;
394}
395
[170]396/* These variables are not used anywhere. They only exist
397 * to assign &userold, &user to them, which keeps gcc from
398 * putting them into a register, and avoids the 'clobbered
399 * by longjmp' warning. And no, 'volatile' proved insufficient.
400 */
401static void * sh_dummy_watchlist = NULL;
402
[67]403static void check_watchlist (short * res)
404{
405 struct watchlist * list = process_check;
406 char * tmp;
407 size_t indx;
408
[170]409 /* Take the address to keep gcc from putting them into registers.
410 * Avoids the 'clobbered by longjmp' warning.
411 */
412 sh_dummy_watchlist = (void*) &list;
413
[67]414 while (list)
415 {
416 if (list->seen == S_FALSE)
417 {
418 /* avoid repetition of messages
419 */
420 if (S_FALSE == is_in_list(&list_missing, list->str, 0))
421 {
[143]422 SH_MUTEX_LOCK(mutex_thread_nolog);
[170]423 tmp = sh_util_safe_name (list->str);
[67]424 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
425 MSG_PCK_MISS,
[170]426 tmp);
[143]427 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]428 }
429 }
430 else
431 {
432 indx = list->pid - sh_prochk_minpid;
433
434 if (list->pid < sh_prochk_maxpid && list->pid >= sh_prochk_minpid &&
435 ((res[indx] & SH_PR_ANY) == 0) && /* not found */
436 ((res[indx] & SH_PR_PS) != 0) && /* seen in first ps */
437 ((res[indx] & SH_PR_PS2) != 0)) /* seen in second ps */
438 {
439 /* fake process, thus considered missing
440 */
441 if (S_FALSE == is_in_list(&list_missing, list->str, 0))
442 {
[170]443 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]444 tmp = sh_util_safe_name (list->str);
445 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
446 MSG_PCK_MISS,
447 tmp);
[143]448 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]449 SH_FREE(tmp);
450 }
451 }
452 list->seen = S_FALSE;
453 }
454 list = list->next;
455 }
456}
457
458/* Add 'str' to the list of watched processes for which
459 * existence should be checked.
460 */
461int sh_prochk_add_process (const char *str)
462{
463 struct watchlist *new;
464 int status;
465 char errbuf[256];
466
467 SL_ENTER(_("sh_prochk_add_process"));
468
469 if( str == NULL )
470 SL_RETURN(-1, _("sh_prochk_add_process") );
471
472 new = SH_ALLOC(sizeof(struct watchlist));
473 new->next = process_check;
474 new->str = sh_util_strdup(str);
475#ifdef HAVE_REGEX_H
476 status = regcomp(&(new->preg), str, REG_NOSUB|REG_EXTENDED);
477 if (status != 0)
478 {
479 regerror(status, &(new->preg), errbuf, sizeof(errbuf));
[143]480 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]481 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
[143]482 errbuf, _("sh_processes_add_process"));
483 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]484 SH_FREE(new->str);
485 SH_FREE(new);
486 SL_RETURN(-1, _("sh_prochk_add_process") );
487 }
488#endif
489 new->pid = 0;
490 new->seen = S_FALSE;
491
492 process_check = new;
493 SL_RETURN(0, _("sh_prochk_add_process") );
494}
495
496/* severity
497 */
498int sh_prochk_set_severity (const char * c)
499{
500 char tmp[32];
501 tmp[0] = '='; tmp[1] = '\0';
502 sl_strlcat (tmp, c, 32);
503 return sh_error_set_level (tmp, &sh_prochk_severity);
504}
505
506
507
508/* Path to ps
509 */
510int sh_prochk_set_pspath(const char *str)
511{
512 SL_ENTER(_("sh_prochk_set_pspath"));
513
514 if (!str && ('/' != str[0]))
515 SL_RETURN((-1), _("sh_prochk_set_psarg"));
516 if (sh_prochk_pspath)
517 SH_FREE(sh_prochk_pspath);
518 sh_prochk_pspath = sh_util_strdup (str);
519
520 SL_RETURN((0), _("sh_prochk_set_pspath"));
521
522}
523
524/* argument for ps
525 */
526int sh_prochk_set_psarg(const char *str)
527{
528 SL_ENTER(_("sh_prochk_set_psarg"));
529
530 if (sh_prochk_psarg)
531 SH_FREE(sh_prochk_psarg);
532 sh_prochk_psarg = sh_util_strdup (str);
533
534 SL_RETURN((0), _("sh_prochk_set_psarg"));
535
536}
537
538
539/* Decide if we're active.
540 */
541int sh_prochk_set_active(const char *str)
542{
543 int value;
544
545 SL_ENTER(_("sh_prochk_set_active"));
546
547 value = sh_util_flagval(str, &ShProchkActive);
548
549 SL_RETURN((value), _("sh_prochk_set_active"));
550}
551
552/* Minimum PID
553 */
554int sh_prochk_set_minpid(const char * str)
555{
556 size_t value;
557 char * foo;
558 int retval = 0;
559
560 SL_ENTER(_("sh_prochk_set_minpid"));
561
562 value = (size_t) strtoul(str, &foo, 0);
563 if (*foo != '\0')
564 retval = -1;
565 else
566 sh_prochk_minpid = value;
567
568 SL_RETURN((retval), _("sh_prochk_set_minpid"));
569}
570
571/* Maximum PID
572 */
573static int userdef_maxpid = 0;
574
575int sh_prochk_set_maxpid(const char * str)
576{
577 size_t value;
578 char * foo;
[78]579 int retval = -1;
[67]580
581 SL_ENTER(_("sh_prochk_set_maxpid"));
582
583 value = (size_t) strtoul(str, &foo, 0);
[78]584
585 if (*foo == '\0' && SL_TRUE == sl_ok_adds(value, 1)) {
[67]586 sh_prochk_maxpid = value + 1;
587 userdef_maxpid = 1;
588 }
589
590 SL_RETURN((retval), _("sh_prochk_set_maxpid"));
591}
592
593int sh_prochk_set_interval (const char * c)
594{
595 int retval = 0;
596 long val;
597
598 SL_ENTER(_("sh_prochk_set_interval"));
599 val = strtol (c, (char **)NULL, 10);
600 if (val <= 0)
601 {
[143]602 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]603 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
604 _("process check interval"), c);
[143]605 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]606 retval = -1;
607 }
608
609 sh_prochk_interval = (time_t) val;
610 SL_RETURN(0, _("sh_prochk_set_interval"));
611}
612
613
614
615/* Recurse to the end of the list and then free the data as we return
616 * back up towards the start, making sure to free any strdupped strings
617 */
618static void sh_prochk_free_list(struct watchlist *head)
619{
620 if ( head != NULL )
621 {
622 sh_prochk_free_list(head->next);
623 if (head->str)
624 SH_FREE(head->str);
625#ifdef HAVE_REGEX_H
626 regfree(&(head->preg));
627#endif
628 SH_FREE(head);
629 }
630 return;
631}
632
633#if defined(__linux__)
634#define PROC_PID_MAX _("/proc/sys/kernel/pid_max")
635
636static int proc_max_pid (size_t * procpid)
637{
[170]638 char * ret;
[67]639 unsigned long pid;
640 FILE * fd;
641 char str[128];
642 char * ptr;
643
644 SL_ENTER(_("proc_max_pid"));
645
646 if (userdef_maxpid != 0)
647 SL_RETURN((-1), _("proc_max_pid"));
648
[73]649 if (0 == access(PROC_PID_MAX, R_OK)) /* flawfinder: ignore */
[67]650 {
651 if (NULL != (fd = fopen(PROC_PID_MAX, "r")))
652 {
653 str[0] = '\0';
[170]654 ret = fgets(str, 128, fd);
655 if (ret && *str != '\0')
[67]656 {
657 pid = strtoul(str, &ptr, 0);
658 if (*ptr == '\0' || *ptr == '\n')
659 {
660 fclose(fd);
661 *procpid = (size_t) pid;
662 SL_RETURN(0, _("proc_max_pid"));
663 }
664 }
665 fclose(fd);
666 }
667 }
668 SL_RETURN((-1), _("proc_max_pid"));
669}
670#else
671static int proc_max_pid(size_t * dummy)
672{
673 (void) dummy;
674 return -1;
675}
676#endif
677
678static void sh_processes_tlist (char * list, size_t len, short res)
679{
680 if (res & SH_PR_PS) sl_strlcat(list, _(" ps(initial)"), len);
681 if (res & SH_PR_CHDIR) sl_strlcat(list, _(" chdir"), len);
682 if (res & SH_PR_OPENDIR) sl_strlcat(list, _(" opendir"), len);
683 if (res & SH_PR_LSTAT) sl_strlcat(list, _(" lstat"), len);
684 if (res & SH_PR_PRIORITY) sl_strlcat(list, _(" getpriority"), len);
685 if (res & SH_PR_SCHED) sl_strlcat(list, _(" sched_getparam"), len);
686 if (res & SH_PR_GETSID) sl_strlcat(list, _(" getsid"), len);
687 if (res & SH_PR_GETPGID) sl_strlcat(list, _(" getpgid"), len);
688 if (res & SH_PR_KILL) sl_strlcat(list, _(" kill"), len);
[78]689 if (res & SH_PR_STATVSF) sl_strlcat(list, _(" statvfs"), len);
[67]690 if (res & SH_PR_PS2) sl_strlcat(list, _(" ps(final)"), len);
691 return;
692}
693
[78]694
[67]695static short sh_processes_check (pid_t pid, short res)
696{
697 int have_checks = 0;
698 int need_checks = 0;
699#ifdef HAVE_PROCFS
700 char path[128];
701 struct stat buf;
702 DIR * dir;
[144]703 int retval;
[78]704#ifdef HAVE_STATVFS
705 struct statvfs vfsbuf;
706#endif
[82]707#endif
[67]708
709#if !defined(sun) && !defined(__sun) && !defined(__sun__)
710#ifdef _POSIX_PRIORITY_SCHEDULING
711 struct sched_param p;
712#endif
713#endif
714
715 if (0 == kill(pid, 0))
716 {
717 res |= SH_PR_KILL; res |= SH_PR_ANY; ++have_checks;
718 ++need_checks;
719 }
720 else if (errno != EPERM)
721 {
722 ++need_checks;
723 }
724
[78]725
[67]726#ifdef HAVE_GETPGID
727 if ((pid_t)-1 != getpgid(pid))
728 {
729 res |= SH_PR_GETPGID; res |= SH_PR_ANY; ++have_checks;
730 }
731 ++need_checks;
732#endif
733
734#ifdef HAVE_GETSID
735 if ((pid_t)-1 != getsid(pid))
736 {
737 res |= SH_PR_GETSID; res |= SH_PR_ANY; ++have_checks;
738 }
739 ++need_checks;
740#endif
741
742 /* sched_getparam() is broken on solaris 10, may segfault in librt
743 */
744#if !defined(sun) && !defined(__sun) && !defined(__sun__)
745#ifdef _POSIX_PRIORITY_SCHEDULING
746 if (0 == sched_getparam (pid, &p))
747 {
748 res |= SH_PR_SCHED; res |= SH_PR_ANY; ++have_checks;
749 }
750 ++need_checks;
751#endif
752#endif
753
754#ifdef HAVE_GETPRIORITY
755 errno = 0;
756 if (((-1) == getpriority (PRIO_PROCESS, (int) pid)) && (errno == ESRCH));
757 else
758 {
759 res |= SH_PR_PRIORITY; res |= SH_PR_ANY; ++have_checks;
760 }
761 ++need_checks;
762#endif
763
764#ifdef HAVE_PROCFS
765 sl_snprintf (path, sizeof(path), "/proc/%ld", (unsigned long) pid);
766
[144]767 do {
768 retval = lstat (path, &buf);
769 } while (retval < 0 && errno == EINTR);
770
771 if (0 == retval)
[67]772 {
773 res |= SH_PR_LSTAT; res |= SH_PR_ANY; ++have_checks;
774 }
775 ++need_checks;
776
777 if (NULL != (dir = opendir(path)))
778 {
779 res |= SH_PR_OPENDIR; res |= SH_PR_ANY; ++have_checks;
780 closedir(dir);
781 }
782 ++need_checks;
783
[78]784#ifdef HAVE_STATVFS
785 do {
786 retval = statvfs (path, &vfsbuf);
787 } while (retval < 0 && errno == EINTR);
788
789 if (0 == retval)
790 {
791 res |= SH_PR_STATVSF; res |= SH_PR_ANY; ++have_checks;
792 }
793 ++need_checks;
794#endif
795
[67]796#if !defined(SH_PROFILE)
797 if (0 == chdir(path))
798 {
799 res |= SH_PR_CHDIR; res |= SH_PR_ANY; ++have_checks;
[144]800 do {
801 retval = chdir ("/");
802 } while (retval < 0 && errno == EINTR);
[67]803 }
804 ++need_checks;
805#endif
806#endif
807
808 if (have_checks == need_checks)
809 {
810 res |= SH_PR_ALL;
811 }
812 return res;
813}
814
[102]815extern int flag_err_debug;
816
[67]817static int sh_processes_readps (FILE * in, short * res,
818 char * str, size_t len,
819 short flag, pid_t pid)
820{
821 int cc;
822 unsigned int lnum = 0;
823 unsigned long num = 0;
824 char c;
825 unsigned int pos = 0;
826 char tstr[256];
[103]827 enum { SKIP_TO_WS, SKIP_WS, SKIP_TO_WS2, SKIP_WS2, GET_NUM, SKIP_END, GET_NUM2 } line;
[67]828
829 SL_ENTER(_("sh_processes_readps"));
830
831 if (!in) {
832 SL_RETURN((-1), _("sh_processes_readps"));
833 }
834
835 tstr[(sizeof(tstr)-1)] = '\0';
836 tstr[0] = '\0';
837 line = SKIP_END; /* Skip 1st line */
838
839 do
840 {
841 cc = fgetc(in);
842
843 if (EOF == cc)
844 {
845 if (feof(in))
846 {
847 break;
848 }
849 else if (errno == EAGAIN)
850 {
[169]851 clearerr(in);
[67]852 continue;
853 }
[169]854#ifdef HOST_IS_OPENBSD
855 else if (errno == ENODEV)
856 {
857 clearerr(in);
858 continue;
859 }
860#endif
[67]861 else
862 {
[132]863 char errbuf[SH_ERRBUF_SIZE];
864
[143]865 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
[67]866 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]867 sh_error_message(errno, errbuf, sizeof(errbuf)),
[67]868 _("sh_processes_readps"));
869 break;
870 }
871 }
872
873 c = (char) cc;
874
875 if (pos < (sizeof(tstr)-1))
876 {
877 tstr[pos] = c; ++pos;
878 }
879
880 switch(line)
881 {
882 case SKIP_END:
883 if (c == '\n')
884 {
885 tstr[pos-1] = '\0';
[102]886 if (flag_err_debug == SL_TRUE)
887 {
[143]888 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
889 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, num,
890 MSG_E_SUBGEN,
[102]891 tstr,
892 _("sh_processes_readps"));
893 }
[67]894 /* fprintf(stderr, "<%ld> %s\n", num, tstr); */
895 line = SKIP_WS; pos = 0;
896 if (str != NULL && num == (unsigned long) pid)
897 sl_strlcpy(str, tstr, len);
898 if (lnum != 0)
899 is_in_watchlist (tstr, num);
900 ++lnum;
901 }
902 break;
[103]903 case SKIP_TO_WS:
904 if (!isspace(cc))
905 break;
906 line = SKIP_WS;
907 /* fallthrough */
[67]908 case SKIP_WS:
909 if (isspace(cc))
910 break;
911 num = 0;
912 line = GET_NUM;
913 /* fallthrough */
914 case GET_NUM:
915 if (isdigit(cc))
916 {
917 num = num * 10 + (c - '0');
918 break;
919 }
[103]920 else if (isspace(cc))
921 {
[143]922#ifdef PS_THREADS
[103]923 num = 0;
924 line = SKIP_WS2;
[67]925#else
[103]926 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
927 {
928 res[num - sh_prochk_minpid] |= flag;
929 }
930 line = SKIP_END;
931#endif
932 break;
933 }
934 else
[67]935 {
[103]936 line = SKIP_TO_WS;
937 break;
[67]938 }
[103]939 case SKIP_TO_WS2:
940 if (!isspace(cc))
941 break;
942 line = SKIP_WS2;
943 /* fallthrough */
[67]944 case SKIP_WS2:
945 if (isspace(cc))
946 break;
947 num = 0;
948 line = GET_NUM2;
949 /* fallthrough */
950 case GET_NUM2:
951 if (isdigit(cc))
952 {
953 num = num * 10 + (c - '0');
954 break;
955 }
[103]956 else if (isspace(cc))
[67]957 {
[103]958 if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
959 {
960 res[num - sh_prochk_minpid] |= flag;
961 }
962 line = SKIP_END;
963 break;
[67]964 }
[103]965 else
966 {
967 line = SKIP_TO_WS2;
968 break;
969 }
[67]970 default:
971 SL_RETURN ((-1), _("sh_processes_readps"));
972 }
973 } while (1);
974
975 if (ferror(in))
976 {
977 SL_RETURN ((-1), _("sh_processes_readps"));
978 }
979
980 SL_RETURN ((0), _("sh_processes_readps"));
981}
982
983static int sh_processes_runps (short * res, char * str, size_t len,
984 short flag, pid_t pid)
985{
986 sh_tas_t task;
987
988 int status = 0;
989 char * p;
990 struct sigaction new_act;
991 struct sigaction old_act;
992 int retval = 0;
[132]993 char dir[SH_PATHBUF];
[67]994
995 SL_ENTER(_("sh_processes_runps"));
996
997 sh_ext_tas_init(&task);
[132]998 p = sh_unix_getUIDdir (SH_ERR_ERR, task.run_user_uid, dir, sizeof(dir));
[67]999 if (p)
1000 {
1001 (void) sh_ext_tas_add_envv (&task, _("HOME"), p);
1002 }
1003 (void) sh_ext_tas_add_envv (&task, _("SHELL"),
1004 _("/bin/sh"));
1005 (void) sh_ext_tas_add_envv (&task, _("PATH"),
1006 _("/sbin:/usr/sbin:/bin:/usr/bin"));
1007 if (sh.timezone != NULL)
1008 {
1009 (void) sh_ext_tas_add_envv(&task, "TZ", sh.timezone);
1010 }
1011
1012 if (!sh_prochk_pspath)
1013 sh_ext_tas_command(&task, PSPATH);
1014 else
1015 sh_ext_tas_command(&task, sh_prochk_pspath);
1016
1017 (void) sh_ext_tas_add_argv(&task, _("ps"));
1018
1019 if (!sh_prochk_psarg)
1020 {
[143]1021#ifdef PS_THREADS
[67]1022 (void) sh_ext_tas_add_argv(&task, _("-eT"));
1023#else
1024 (void) sh_ext_tas_add_argv(&task, PSARG);
1025#endif
1026 }
1027 else
1028 {
1029 (void) sh_ext_tas_add_argv(&task, sh_prochk_psarg);
1030 }
1031
1032 task.rw = 'r';
1033 task.fork_twice = S_FALSE;
1034
1035 status = sh_ext_popen(&task);
1036 if (status != 0)
1037 {
[143]1038 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
[67]1039 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
1040 _("Could not open pipe"), _("sh_processes_runps"));
1041 SL_RETURN ((-1), _("sh_processes_runps"));
1042 }
1043
1044 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
1045 */
1046 new_act.sa_handler = SIG_IGN;
1047 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
1048
1049 /* read from the open pipe
1050 */
1051 if (task.pipe != NULL)
1052 {
1053 retval = sh_processes_readps (task.pipe, res, str, len, flag, pid);
1054 }
1055
1056 /* restore old signal handler
1057 */
1058 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
1059
1060 /* close pipe and return exit status
1061 */
1062 (void) sh_ext_pclose(&task);
[103]1063 sh_ext_tas_free (&task);
[67]1064 SL_RETURN ((retval), _("sh_processes_runps"));
1065}
1066
1067static int sh_process_check_int (short * res)
1068{
[170]1069 volatile size_t i;
1070 size_t j;
[67]1071 char tests[512];
[170]1072 volatile int retval;
[67]1073
1074 pid_t this_pid;
1075
1076 SL_ENTER(_("sh_process_check_int"));
1077
1078 this_pid = getpid();
1079
1080 if (!res)
1081 {
[143]1082 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]1083 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
[169]1084 _("Internal error: NULL argument, switching off"),
[67]1085 _("sh_process_check_int"));
[143]1086 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]1087 SL_RETURN ((-1), _("sh_process_check_int"));
1088 }
1089
[143]1090 SH_MUTEX_LOCK(mutex_thread_nolog);
[169]1091 retval = sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
[143]1092 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]1093 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1094 {
1095 j = i - sh_prochk_minpid;
1096 res[j] = sh_processes_check ((pid_t) i, res[j]);
1097 }
[143]1098 SH_MUTEX_LOCK(mutex_thread_nolog);
[169]1099 retval += sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
[143]1100 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]1101
[169]1102 if (retval != 0)
1103 {
1104 SH_MUTEX_LOCK(mutex_thread_nolog);
1105 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1106 _("Failed to run ps, switching off"),
1107 _("sh_process_check_int"));
1108 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1109 SL_RETURN ((-1), _("sh_process_check_int"));
1110 }
1111
[67]1112 /* Evaluate results
1113 */
1114 for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
1115 {
1116 /* don't check the current process
1117 */
1118 if (i == (size_t) this_pid)
1119 continue;
1120
1121 j = i - sh_prochk_minpid;
1122
1123 if (((res[j] & SH_PR_PS) != 0) || ((res[j] & SH_PR_PS2) != 0))
1124 {
1125 res[j] |= SH_PR_PS_ANY;
1126 }
1127 else
1128 {
1129 res[j] &= ~SH_PR_PS_ANY;
1130 }
1131
1132 tests[0] = '\0';
1133
1134 if ((res[j] & SH_PR_ANY) || (res[j] & SH_PR_PS_ANY))
1135 {
[102]1136 /* list all tests where the pid was found
1137 */
[67]1138 sh_processes_tlist (tests, sizeof(tests), res[j]);
[102]1139
[67]1140 /*
1141 * case 1: in ps and found
1142 */
1143 if ((res[j] & SH_PR_PS_ANY) && (res[j] & SH_PR_ANY))
1144 {
[143]1145 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]1146 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_OK,
1147 (unsigned long) i, tests);
[143]1148 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]1149 }
1150
1151 /*
1152 * case 2: not in ps and found
1153 */
1154 else if ((res[j] & SH_PR_PS_ANY) == 0)
1155 {
1156 res[j] = sh_processes_check ((pid_t) i, 0);
1157 /*
1158 * if still there, it is real and hidden
1159 */
1160 if (res[j] & SH_PR_ANY)
1161 {
1162 if (S_FALSE == is_in_list(&list_hidden, NULL, i))
1163 {
[78]1164 char user[16];
1165 char * aout;
1166 char * safe;
1167
[143]1168 SH_MUTEX_LOCK(mutex_thread_nolog);
[78]1169 aout = get_user_and_path ((pid_t) i, user, sizeof(user));
[143]1170 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[78]1171
1172 if (aout)
1173 {
1174 safe = sh_util_safe_name (aout);
[143]1175 SH_MUTEX_LOCK(mutex_thread_nolog);
[78]1176 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1177 MSG_PCK_P_HIDDEN,
1178 (unsigned long) i, tests, safe, user);
[143]1179 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[78]1180 SH_FREE(safe);
1181 SH_FREE(aout);
1182 }
1183 else
[143]1184 {
1185 SH_MUTEX_LOCK(mutex_thread_nolog);
1186 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1187 MSG_PCK_HIDDEN,
1188 (unsigned long) i, tests);
1189 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1190 }
[67]1191 }
1192 }
1193 }
1194
1195 /*
1196 * case 3: in ps, but not found
1197 */
1198 else
1199 {
1200 if (((res[j] & SH_PR_PS) != 0) && ((res[j] & SH_PR_PS2) != 0))
1201 {
1202 if (S_FALSE == is_in_list(&list_fake, NULL, i))
1203 {
[143]1204 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]1205 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1206 MSG_PCK_FAKE,
1207 (unsigned long) i, tests);
[143]1208 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[67]1209 }
1210 }
1211 }
1212 }
1213 } /* loop end */
1214
1215 check_watchlist (res);
1216
1217 SL_RETURN (0, _("sh_process_check_int"));
1218}
1219
1220/* Initialise.
1221 */
[170]1222static int sh_prochk_init_internal(void)
[67]1223{
1224 SL_ENTER(_("sh_prochk_init"));
1225
1226 (void) proc_max_pid (&sh_prochk_maxpid);
1227
1228 if (sh_prochk_minpid > sh_prochk_maxpid)
1229 ShProchkActive = S_FALSE;
1230
1231 /* We need to free anything allocated by the configuration functions if
1232 * we find that the module is to be left inactive - otherwise _reconf()
1233 * won't quite work.
1234 */
1235 if( ShProchkActive == S_FALSE )
1236 {
1237 sh_prochk_free_list(process_check);
1238 process_check = NULL;
1239 SL_RETURN(-1, _("sh_prochk_init"));
1240 }
1241
1242 sh_prochk_size = sh_prochk_maxpid - sh_prochk_minpid;
1243
1244 sh_prochk_res = SH_ALLOC(sizeof(short) * sh_prochk_size);
1245 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1246
1247 SL_RETURN(0, _("sh_prochk_init"));
1248}
1249
[143]1250int sh_prochk_init (struct mod_type * arg)
1251{
1252 if (ShProchkActive == S_FALSE)
1253 return SH_MOD_FAILED;
1254#ifdef HAVE_PTHREAD
[144]1255 if (arg != NULL && arg->initval < 0 &&
1256 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
[143]1257 {
1258 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1259 return SH_MOD_THREAD;
1260 else
1261 return SH_MOD_FAILED;
1262 }
1263#endif
1264 return sh_prochk_init_internal();
1265}
[67]1266
1267int sh_prochk_timer(time_t tcurrent)
1268{
1269 static time_t lastcheck = 0;
1270
1271 SL_ENTER(_("sh_prochk_timer"));
1272 if ((time_t) (tcurrent - lastcheck) >= sh_prochk_interval)
1273 {
1274 lastcheck = tcurrent;
1275 SL_RETURN((-1), _("sh_prochk_timer"));
1276 }
1277 SL_RETURN(0, _("sh_prochk_timer"));
1278}
1279
1280int sh_prochk_check(void)
1281{
[170]1282 int status;
[67]1283
1284 SL_ENTER(_("sh_prochk_check"));
1285
[143]1286 SH_MUTEX_LOCK(mutex_proc_check);
[170]1287
1288 status = 0;
1289
[67]1290 if( ShProchkActive != S_FALSE )
1291 {
[143]1292 SH_MUTEX_LOCK(mutex_thread_nolog);
[67]1293 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_CHECK,
1294 (unsigned long) sh_prochk_minpid,
1295 (unsigned long) (sh_prochk_maxpid-1));
[143]1296 SH_MUTEX_UNLOCK(mutex_thread_nolog);
[103]1297
1298 if (sh_prochk_res) {
1299 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1300 }
[67]1301 status = sh_process_check_int(sh_prochk_res);
[103]1302
[67]1303 if (status != 0)
1304 ShProchkActive = S_FALSE;
1305
1306 /* clean out old entries which are not marked
1307 * as missing/hidden/fake anymore
1308 */
1309 clean_list (&list_missing);
1310 clean_list (&list_hidden);
1311 clean_list (&list_fake);
1312 }
[143]1313 SH_MUTEX_UNLOCK(mutex_proc_check);
[67]1314
1315 SL_RETURN(status, _("sh_prochk_check"));
1316}
1317
1318/* Free our lists and the associated memory
1319 */
1320int sh_prochk_cleanup(void)
1321{
1322 SL_ENTER(_("sh_prochk_cleanup"));
1323
1324 sh_prochk_reconf();
1325
1326 if (list_missing) {
1327 kill_list(list_missing);
1328 list_missing = NULL;
1329 }
1330 if (list_hidden) {
1331 kill_list(list_hidden);
1332 list_hidden = NULL;
1333 }
1334 if (list_fake) {
1335 kill_list(list_fake);
1336 list_fake = NULL;
1337 }
1338
1339 SL_RETURN(0, _("sh_prochk_cleanup"));
1340}
1341
1342/* Free our lists and the associated memory
1343 */
1344int sh_prochk_reconf(void)
1345{
1346 SL_ENTER(_("sh_prochk_reconf"));
1347
[143]1348 SH_MUTEX_LOCK(mutex_proc_check);
[67]1349 userdef_maxpid = 0;
1350 sh_prochk_maxpid = 0x8000;
[144]1351 sh_prochk_minpid = 0x0001;
[67]1352 sh_prochk_interval = SH_PROCHK_INTERVAL;
1353
1354 sh_prochk_free_list(process_check);
1355 process_check = NULL;
1356 if (sh_prochk_res != NULL)
1357 SH_FREE(sh_prochk_res);
1358 sh_prochk_res = NULL;
1359
1360 if (sh_prochk_psarg)
1361 SH_FREE(sh_prochk_psarg);
1362 sh_prochk_psarg = NULL;
1363 if (sh_prochk_pspath)
1364 SH_FREE(sh_prochk_pspath);
1365 sh_prochk_pspath = NULL;
[143]1366 SH_MUTEX_UNLOCK(mutex_proc_check);
[67]1367
1368 SL_RETURN(0, _("sh_prochk_reconf"));
1369}
1370
1371/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
1372#endif
1373
1374/* #ifdef SH_USE_PROCESSCHECK */
1375#endif
1376
1377
1378#ifdef SH_CUTEST
1379#include "CuTest.h"
1380
1381void Test_processcheck_watchlist_ok (CuTest *tc) {
1382#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1383 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1384 CuAssertTrue(tc,
1385 S_TRUE == is_in_watchlist(" 1 ? 00:00:00 init", 0));
1386 CuAssertTrue(tc,
1387 S_FALSE == is_in_watchlist(" 1 ? 00:00:00 flix", 0));
1388 CuAssertTrue(tc,
1389 S_TRUE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/init -k start -DSSL", 0));
1390 CuAssertTrue(tc,
1391 S_FALSE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/apache2 -k start -DSSL", 0));
1392
1393
1394 sh_prochk_free_list(process_check);
1395 process_check = NULL;
1396 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1397
1398 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1399 CuAssertTrue(tc, 0 == sh_prochk_add_process("ssh"));
1400 CuAssertTrue(tc, 0 == sh_prochk_add_process("syslog"));
1401 CuAssertTrue(tc, S_TRUE == is_in_watchlist("init", 0));
1402 CuAssertTrue(tc, S_TRUE == is_in_watchlist("ssh", 0));
1403 CuAssertTrue(tc, S_TRUE == is_in_watchlist("syslog", 0));
1404
1405 sh_prochk_free_list(process_check);
1406 process_check = NULL;
1407 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1408 CuAssertTrue(tc, S_FALSE == is_in_watchlist("ssh", 0));
1409 CuAssertTrue(tc, S_FALSE == is_in_watchlist("syslog", 0));
1410#else
1411 (void) tc; /* fix compiler warning */
1412#endif
1413 return;
1414}
1415
1416void Test_processcheck_listhandle_ok (CuTest *tc) {
1417#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1418 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1419 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1420 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1421 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1422
1423 if (list_missing)
1424 kill_list(list_missing);
1425 list_missing = NULL;
1426
1427 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1428 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1429 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1430 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1431
1432 if (list_missing)
1433 kill_list(list_missing);
1434 list_missing = NULL;
1435
1436 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1437 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1438 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1439 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1440
1441 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1442 CuAssertPtrNotNull(tc, list_missing);
1443
1444 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1445 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1446
1447 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1448 CuAssertPtrNotNull(tc, list_missing);
1449
1450 CuAssertTrue(tc, 0 == clean_list(&list_missing));
1451 CuAssertTrue(tc, NULL == list_missing);
1452#else
1453 (void) tc; /* fix compiler warning */
1454#endif
1455 return;
1456}
1457
1458
1459/* #ifdef SH_CUTEST */
1460#endif
1461
Note: See TracBrowser for help on using the repository browser.