source: trunk/src/sh_processcheck.c@ 138

Last change on this file since 138 was 132, checked in by rainer, 17 years ago

Make utility functions thread-safe.

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