source: trunk/src/sh_processcheck.c@ 78

Last change on this file since 78 was 78, checked in by rainer, 18 years ago

Fix for ticket #41 (unable to deactivate processcheck), and minor code cleanup.

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