source: trunk/src/sh_processcheck.c@ 149

Last change on this file since 149 was 144, checked in by rainer, 17 years ago

Fix some more queer compile problems.

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