source: trunk/src/sh_processcheck.c@ 184

Last change on this file since 184 was 183, checked in by katerina, 16 years ago

Support for logfile monitoring (ticket #122). Also improved some configure error messages.

File size: 33.6 KB
Line 
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
50#ifdef HAVE_SYS_STATVFS_H
51#include <sys/statvfs.h>
52#endif
53
54
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"
65#include "sh_calls.h"
66#include "sh_pthread.h"
67
68#ifdef SH_USE_PROCESSCHECK
69
70#define FIL__ _("sh_processcheck.c")
71
72#ifdef __linux__
73#define PS_THREADS
74#endif
75
76/* We won't want to build this into yule
77 */
78#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
79
80SH_MUTEX_STATIC(mutex_proc_check, PTHREAD_MUTEX_INITIALIZER);
81
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
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
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;
176static const short SH_PR_STATVSF = 0x0200;
177
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;
182
183/* /proc:
184 * linux: /proc/pid/exe
185 * freebsd: /proc/pid/file
186 * solaris10: /proc/pid/path/a.out
187 */
188static char * get_user_and_path (pid_t pid, char * user, size_t usrlen)
189{
190 extern char * sh_unix_getUIDname (int level, uid_t uid, char * out, size_t len);
191
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
237 tmp = sh_unix_getUIDname (SH_ERR_ALL, sbuf.st_uid, user, usrlen);
238
239 if (!tmp)
240 sl_snprintf (user, usrlen, "%ld", (unsigned long) sbuf.st_uid);
241
242 return buf;
243}
244
245
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
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
403static void check_watchlist (short * res)
404{
405 struct watchlist * list = process_check;
406 char * tmp;
407 size_t indx;
408
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
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 {
422 SH_MUTEX_LOCK(mutex_thread_nolog);
423 tmp = sh_util_safe_name (list->str);
424 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
425 MSG_PCK_MISS,
426 tmp);
427 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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 {
443 SH_MUTEX_LOCK(mutex_thread_nolog);
444 tmp = sh_util_safe_name (list->str);
445 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
446 MSG_PCK_MISS,
447 tmp);
448 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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));
480 SH_MUTEX_LOCK(mutex_thread_nolog);
481 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
482 errbuf, _("sh_processes_add_process"));
483 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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;
579 int retval = -1;
580
581 SL_ENTER(_("sh_prochk_set_maxpid"));
582
583 value = (size_t) strtoul(str, &foo, 0);
584
585 if (*foo == '\0' && SL_TRUE == sl_ok_adds(value, 1)) {
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 {
602 SH_MUTEX_LOCK(mutex_thread_nolog);
603 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
604 _("process check interval"), c);
605 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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{
638 char * ret;
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
649 if (0 == access(PROC_PID_MAX, R_OK)) /* flawfinder: ignore */
650 {
651 if (NULL != (fd = fopen(PROC_PID_MAX, "r")))
652 {
653 str[0] = '\0';
654 ret = fgets(str, 128, fd);
655 if (ret && *str != '\0')
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);
689 if (res & SH_PR_STATVSF) sl_strlcat(list, _(" statvfs"), len);
690 if (res & SH_PR_PS2) sl_strlcat(list, _(" ps(final)"), len);
691 return;
692}
693
694
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;
703 int retval;
704#ifdef HAVE_STATVFS
705 struct statvfs vfsbuf;
706#endif
707#endif
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
725
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
767 do {
768 retval = lstat (path, &buf);
769 } while (retval < 0 && errno == EINTR);
770
771 if (0 == retval)
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
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
796#if !defined(SH_PROFILE)
797 if (0 == chdir(path))
798 {
799 res |= SH_PR_CHDIR; res |= SH_PR_ANY; ++have_checks;
800 do {
801 retval = chdir ("/");
802 } while (retval < 0 && errno == EINTR);
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
815extern int flag_err_debug;
816
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];
827 enum { SKIP_TO_WS, SKIP_WS, SKIP_TO_WS2, SKIP_WS2, GET_NUM, SKIP_END, GET_NUM2 } line;
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 {
851 clearerr(in);
852 continue;
853 }
854#ifdef HOST_IS_OPENBSD
855 else if (errno == ENODEV)
856 {
857 clearerr(in);
858 continue;
859 }
860#endif
861 else
862 {
863 char errbuf[SH_ERRBUF_SIZE];
864
865 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
866 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
867 sh_error_message(errno, errbuf, sizeof(errbuf)),
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';
886 if (flag_err_debug == SL_TRUE)
887 {
888 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
889 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, num,
890 MSG_E_SUBGEN,
891 tstr,
892 _("sh_processes_readps"));
893 }
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;
903 case SKIP_TO_WS:
904 if (!isspace(cc))
905 break;
906 line = SKIP_WS;
907 /* fallthrough */
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 }
920 else if (isspace(cc))
921 {
922#ifdef PS_THREADS
923 num = 0;
924 line = SKIP_WS2;
925#else
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
935 {
936 line = SKIP_TO_WS;
937 break;
938 }
939 case SKIP_TO_WS2:
940 if (!isspace(cc))
941 break;
942 line = SKIP_WS2;
943 /* fallthrough */
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 }
956 else if (isspace(cc))
957 {
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;
964 }
965 else
966 {
967 line = SKIP_TO_WS2;
968 break;
969 }
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;
993 char dir[SH_PATHBUF];
994
995 SL_ENTER(_("sh_processes_runps"));
996
997 sh_ext_tas_init(&task);
998 p = sh_unix_getUIDdir (SH_ERR_ERR, task.run_user_uid, dir, sizeof(dir));
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 {
1021#ifdef PS_THREADS
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 {
1038 /* SH_MUTEX_LOCK(mutex_thread_nolog) is in caller */
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);
1063 sh_ext_tas_free (&task);
1064 SL_RETURN ((retval), _("sh_processes_runps"));
1065}
1066
1067static int sh_process_check_int (short * res)
1068{
1069 volatile size_t i;
1070 size_t j;
1071 char tests[512];
1072 volatile int retval;
1073
1074 pid_t this_pid;
1075
1076 SL_ENTER(_("sh_process_check_int"));
1077
1078 this_pid = getpid();
1079
1080 if (!res)
1081 {
1082 SH_MUTEX_LOCK(mutex_thread_nolog);
1083 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1084 _("Internal error: NULL argument, switching off"),
1085 _("sh_process_check_int"));
1086 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1087 SL_RETURN ((-1), _("sh_process_check_int"));
1088 }
1089
1090 SH_MUTEX_LOCK(mutex_thread_nolog);
1091 retval = sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
1092 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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 }
1098 SH_MUTEX_LOCK(mutex_thread_nolog);
1099 retval += sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
1100 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1101
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
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 {
1136 /* list all tests where the pid was found
1137 */
1138 sh_processes_tlist (tests, sizeof(tests), res[j]);
1139
1140 /*
1141 * case 1: in ps and found
1142 */
1143 if ((res[j] & SH_PR_PS_ANY) && (res[j] & SH_PR_ANY))
1144 {
1145 SH_MUTEX_LOCK(mutex_thread_nolog);
1146 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_OK,
1147 (unsigned long) i, tests);
1148 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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 {
1164 char user[16];
1165 char * aout;
1166 char * safe;
1167
1168 SH_MUTEX_LOCK(mutex_thread_nolog);
1169 aout = get_user_and_path ((pid_t) i, user, sizeof(user));
1170 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1171
1172 if (aout)
1173 {
1174 safe = sh_util_safe_name (aout);
1175 SH_MUTEX_LOCK(mutex_thread_nolog);
1176 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1177 MSG_PCK_P_HIDDEN,
1178 (unsigned long) i, tests, safe, user);
1179 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1180 SH_FREE(safe);
1181 SH_FREE(aout);
1182 }
1183 else
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 }
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 {
1204 SH_MUTEX_LOCK(mutex_thread_nolog);
1205 sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0,
1206 MSG_PCK_FAKE,
1207 (unsigned long) i, tests);
1208 SH_MUTEX_UNLOCK(mutex_thread_nolog);
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 */
1222static int sh_prochk_init_internal(void)
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
1250int sh_prochk_init (struct mod_type * arg)
1251{
1252 if (ShProchkActive == S_FALSE)
1253 return SH_MOD_FAILED;
1254#ifdef HAVE_PTHREAD
1255 if (arg != NULL && arg->initval < 0 &&
1256 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
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}
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{
1282 int status;
1283
1284 SL_ENTER(_("sh_prochk_check"));
1285
1286 SH_MUTEX_LOCK(mutex_proc_check);
1287
1288 status = 0;
1289
1290 if( ShProchkActive != S_FALSE )
1291 {
1292 SH_MUTEX_LOCK(mutex_thread_nolog);
1293 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_CHECK,
1294 (unsigned long) sh_prochk_minpid,
1295 (unsigned long) (sh_prochk_maxpid-1));
1296 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1297
1298 if (sh_prochk_res) {
1299 memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
1300 }
1301 status = sh_process_check_int(sh_prochk_res);
1302
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 }
1313
1314 SH_MUTEX_UNLOCK(mutex_proc_check);
1315
1316 SL_RETURN(status, _("sh_prochk_check"));
1317}
1318
1319/* Free our lists and the associated memory
1320 */
1321int sh_prochk_cleanup(void)
1322{
1323 SL_ENTER(_("sh_prochk_cleanup"));
1324
1325 sh_prochk_reconf();
1326
1327 if (list_missing) {
1328 kill_list(list_missing);
1329 list_missing = NULL;
1330 }
1331 if (list_hidden) {
1332 kill_list(list_hidden);
1333 list_hidden = NULL;
1334 }
1335 if (list_fake) {
1336 kill_list(list_fake);
1337 list_fake = NULL;
1338 }
1339
1340 SL_RETURN(0, _("sh_prochk_cleanup"));
1341}
1342
1343/* Free our lists and the associated memory
1344 */
1345int sh_prochk_reconf(void)
1346{
1347 SL_ENTER(_("sh_prochk_reconf"));
1348
1349 SH_MUTEX_LOCK(mutex_proc_check);
1350 userdef_maxpid = 0;
1351 sh_prochk_maxpid = 0x8000;
1352 sh_prochk_minpid = 0x0001;
1353 sh_prochk_interval = SH_PROCHK_INTERVAL;
1354
1355 sh_prochk_free_list(process_check);
1356 process_check = NULL;
1357 if (sh_prochk_res != NULL)
1358 SH_FREE(sh_prochk_res);
1359 sh_prochk_res = NULL;
1360
1361 if (sh_prochk_psarg)
1362 SH_FREE(sh_prochk_psarg);
1363 sh_prochk_psarg = NULL;
1364 if (sh_prochk_pspath)
1365 SH_FREE(sh_prochk_pspath);
1366 sh_prochk_pspath = NULL;
1367 SH_MUTEX_UNLOCK(mutex_proc_check);
1368
1369 SL_RETURN(0, _("sh_prochk_reconf"));
1370}
1371
1372/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
1373#endif
1374
1375/* #ifdef SH_USE_PROCESSCHECK */
1376#endif
1377
1378
1379#ifdef SH_CUTEST
1380#include "CuTest.h"
1381
1382void Test_processcheck_watchlist_ok (CuTest *tc) {
1383#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1384 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1385 CuAssertTrue(tc,
1386 S_TRUE == is_in_watchlist(" 1 ? 00:00:00 init", 0));
1387 CuAssertTrue(tc,
1388 S_FALSE == is_in_watchlist(" 1 ? 00:00:00 flix", 0));
1389 CuAssertTrue(tc,
1390 S_TRUE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/init -k start -DSSL", 0));
1391 CuAssertTrue(tc,
1392 S_FALSE == is_in_watchlist("25218 ? SNs 0:01 /usr/sbin/apache2 -k start -DSSL", 0));
1393
1394
1395 sh_prochk_free_list(process_check);
1396 process_check = NULL;
1397 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1398
1399 CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
1400 CuAssertTrue(tc, 0 == sh_prochk_add_process("ssh"));
1401 CuAssertTrue(tc, 0 == sh_prochk_add_process("syslog"));
1402 CuAssertTrue(tc, S_TRUE == is_in_watchlist("init", 0));
1403 CuAssertTrue(tc, S_TRUE == is_in_watchlist("ssh", 0));
1404 CuAssertTrue(tc, S_TRUE == is_in_watchlist("syslog", 0));
1405
1406 sh_prochk_free_list(process_check);
1407 process_check = NULL;
1408 CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
1409 CuAssertTrue(tc, S_FALSE == is_in_watchlist("ssh", 0));
1410 CuAssertTrue(tc, S_FALSE == is_in_watchlist("syslog", 0));
1411#else
1412 (void) tc; /* fix compiler warning */
1413#endif
1414 return;
1415}
1416
1417void Test_processcheck_listhandle_ok (CuTest *tc) {
1418#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
1419 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1420 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1421 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1422 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1423
1424 if (list_missing)
1425 kill_list(list_missing);
1426 list_missing = NULL;
1427
1428 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1429 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1430 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1431 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1432
1433 if (list_missing)
1434 kill_list(list_missing);
1435 list_missing = NULL;
1436
1437 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
1438 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1439 CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
1440 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1441
1442 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1443 CuAssertPtrNotNull(tc, list_missing);
1444
1445 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "init", 0));
1446 CuAssertTrue(tc, S_TRUE == is_in_list(&list_missing, "foobar", 0));
1447
1448 CuAssertTrue(tc, 2 == clean_list(&list_missing));
1449 CuAssertPtrNotNull(tc, list_missing);
1450
1451 CuAssertTrue(tc, 0 == clean_list(&list_missing));
1452 CuAssertTrue(tc, NULL == list_missing);
1453#else
1454 (void) tc; /* fix compiler warning */
1455#endif
1456 return;
1457}
1458
1459
1460/* #ifdef SH_CUTEST */
1461#endif
1462
Note: See TracBrowser for help on using the repository browser.