source: trunk/src/sh_extern.c@ 101

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

Fix for ticket #54 (samhain_hide module does not work under kernel 2.6.20).

File size: 31.5 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2000,2004 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#include "config_xor.h"
22
23
24#include <stdio.h>
25#include <string.h>
26#ifdef HAVE_MEMORY_H
27#include <memory.h>
28#endif
29
30/* replace #if 0 by #if 1 and set an appropriate path in front of '/pdbg.'
31 * for debugging
32 */
33#if 0
34#define PDGBFILE "/pdbg."
35#endif
36
37
38#if defined(PDGBFILE)
39static FILE * pdbg = NULL;
40static FILE * pdbgc = NULL;
41#define PDBG_OPEN if (pdbg == NULL) pdbg = fopen(PDGBFILE"main", "a")
42#define PDBG_CLOSE fclose (pdbg); pdbg = NULL
43#define PDBG(arg) fprintf(pdbg, "PDBG: step %d\n", arg); fflush(pdbg)
44#define PDBG_D(arg) fprintf(pdbg, "PDBG: %d\n", arg); fflush(pdbg)
45#define PDBG_S(arg) fprintf(pdbg, "PDBG: %s\n", arg); fflush(pdbg)
46
47#define PDBGC_OPEN if (pdbgc == NULL) pdbgc = fopen(PDGBFILE"child", "a")
48#define PDBGC_CLOSE fclose (pdbgc); pdbgc = NULL
49#define PDBGC(arg) fprintf(pdbgc, "PDBGC: step %d\n", arg); fflush(pdbgc)
50#define PDBGC_D(arg) fprintf(pdbgc, "PDBGC: %d\n", arg); fflush(pdbgc)
51#define PDBGC_S(arg) fprintf(pdbgc, "PDBGC: %s\n", arg); fflush(pdbgc)
52#else
53#define PDBG_OPEN
54#define PDBG_CLOSE
55#define PDBG(arg)
56#define PDBG_D(arg)
57#define PDBG_S(arg)
58#define PDBGC_OPEN
59#define PDBGC_CLOSE
60#define PDBGC(arg)
61#define PDBGC_D(arg)
62#define PDBGC_S(arg)
63#endif
64
65
66#include <stdlib.h>
67#include <pwd.h>
68#include <unistd.h>
69#include <fcntl.h>
70#include <signal.h>
71#include <sys/stat.h>
72#include <sys/types.h>
73#include <errno.h>
74#include <sys/wait.h>
75
76#if TIME_WITH_SYS_TIME
77#include <sys/time.h>
78#include <time.h>
79#else
80#if HAVE_SYS_TIME_H
81#include <sys/time.h>
82#else
83#include <time.h>
84#endif
85#endif
86
87
88#include "samhain.h"
89#include "sh_utils.h"
90#include "sh_unix.h"
91#include "sh_tiger.h"
92#include "sh_extern.h"
93#include "sh_calls.h"
94#define SH_NEED_PWD_GRP 1
95#include "sh_static.h"
96
97
98#undef FIL__
99#define FIL__ _("sh_extern.c")
100
101extern int get_the_fd (SL_TICKET ticket);
102
103/*
104 * -- generic safe popen
105 */
106
107int sh_ext_popen (sh_tas_t * task)
108{
109 long status = 0;
110 int flags;
111 char * tmp;
112 char * tmp2;
113 int errnum;
114 int pipedes[2];
115 FILE * outf = NULL;
116 char * envp[1];
117 char * argp[2];
118
119 char * errfile;
120
121 static int some_error = 0;
122
123#if defined (__linux__)
124 SL_TICKET fd = -1;
125 char pname[128];
126 int pfd = -1;
127#endif
128
129 SL_ENTER(_("sh_ext_popen"));
130
131 /* Linux, HP-UX and FreeBSD will happily accept envp = argp = NULL
132 * Solaris (and probably some other Unices)
133 * needs a valid *envp[] with envp[0] = NULL;
134 * and similarly for argp
135 * OpenBSD finally needs non-null argp[0] ...
136 */
137 argp[0] = task->command;
138 argp[1] = NULL;
139 envp[0] = NULL;
140
141 /*
142 * -- check whether path is trustworthy
143 */
144 if ((uid_t) -1 != task->trusted_users[0])
145 {
146 status = sl_trustfile(task->command, task->trusted_users, NULL);
147 }
148
149 PDBG_OPEN;
150 PDBG_D( (int) status);
151
152 if ( SL_ENONE != status)
153 {
154 PDBG_S("SL_ENONE != status");
155 if (some_error == 0)
156 {
157 tmp = sh_util_safe_name (task->command);
158 errfile = sl_trust_errfile();
159 if (errfile[0] != '\0')
160 {
161 tmp2 = sh_util_safe_name (sl_trust_errfile());
162 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_TRUST2,
163 sl_error_string((int)status), tmp, tmp2);
164 SH_FREE(tmp2);
165 }
166 else
167 {
168 sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_TRUST1,
169 sl_error_string((int)status), tmp);
170 }
171 SH_FREE(tmp);
172 }
173 some_error = 1;
174 SL_RETURN ((-1), _("sh_ext_popen"));
175 }
176
177 PDBG(1);
178
179 /*
180 * -- check whether the checksum is correct; with linux emulate fdexec
181 */
182#if !defined(__linux__) && !defined(SL_DEBUG)
183 if (task->checksum[0] != '\0')
184 {
185 PDBG_S("checksum test");
186 if (0 != sl_strcmp(task->checksum,
187 sh_tiger_hash (task->command, TIGER_FILE, 0))
188 )
189 {
190 PDBG_S("checksum mismatch");
191 if (some_error == 0)
192 {
193 tmp = sh_util_safe_name (task->command);
194 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_HASH, tmp);
195 SH_FREE(tmp);
196 }
197 some_error = 1;
198 SL_RETURN ((-1), _("sh_ext_popen"));
199 }
200 }
201#endif
202
203 some_error = 0;
204
205 PDBG(2);
206
207 /*
208 * -- Create the pipe
209 */
210 if (aud_pipe(FIL__, __LINE__, pipedes) < 0)
211 {
212 PDBG_S("pipe() failure");
213 errnum = errno;
214 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
215 sh_error_message(errnum), _("pipe"));
216 SL_RETURN ((-1), _("sh_ext_popen"));
217 }
218
219 PDBG(3);
220
221 /*
222 * -- Fork
223 */
224 task->pid = aud_fork(FIL__, __LINE__);
225
226 if (task->pid == (pid_t) - 1)
227 {
228 PDBG_S("fork() failure");
229 /*@-usedef@*/
230 (void) close(pipedes[0]);
231 (void) close(pipedes[1]);
232 /*@+usedef@*/
233 errnum = errno;
234 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
235 sh_error_message(errnum), _("fork"));
236 SL_RETURN ((-1), _("sh_ext_popen"));
237 }
238
239 PDBG(4);
240
241 if (task->pid == (pid_t) 0)
242 {
243 /*
244 * -- fork again, if requested
245 */
246 if (S_TRUE == task->fork_twice)
247 {
248 task->pid = aud_fork(FIL__, __LINE__);
249
250 if (task->pid == (pid_t) - 1)
251 {
252 aud__exit (FIL__, __LINE__, EXIT_FAILURE);
253 }
254 }
255
256 if (task->pid == (pid_t) 0)
257 {
258 PDBGC_OPEN;
259 PDBGC(1);
260
261 /*
262 * -- grandchild - make write side of the pipe stdin
263 */
264 if (task->rw == 'w')
265 {
266 if (retry_aud_dup2(FIL__, __LINE__,
267 pipedes[STDIN_FILENO], STDIN_FILENO) < 0)
268 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
269 }
270 else
271 {
272 if (retry_aud_dup2(FIL__, __LINE__,
273 pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
274 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
275 }
276 PDBGC(2);
277
278
279 /* close the pipe descriptors
280 */
281 (void) close (pipedes[STDIN_FILENO]);
282 (void) close (pipedes[STDOUT_FILENO]);
283
284 /* don't leak file descriptors
285 */
286#if !defined(PDGBFILE)
287 sh_unix_closeall (3, task->com_fd); /* in child process */
288#endif
289
290 /* drop root privileges, if possible && requested
291 */
292 if (task->privileged == 0 && 0 == getuid())
293 {
294 PDBGC_S("privileged");
295
296 /* zero priv info
297 */
298 memset(skey, 0, sizeof(sh_key_t));
299
300 (void) aud_setgid(FIL__, __LINE__,(gid_t) task->run_user_gid);
301 (void) aud_setuid(FIL__, __LINE__,(uid_t) task->run_user_uid);
302 /* make sure we cannot get root again
303 */
304 if (aud_setuid(FIL__, __LINE__,0) >= 0)
305 aud__exit(FIL__, __LINE__,EXIT_FAILURE);
306 }
307
308 PDBGC(3);
309 (void) fflush(NULL);
310
311 if (task->rw == 'w')
312 {
313 PDBGC_S("w");
314 (void) fcntl (STDOUT_FILENO, F_SETFD, FD_CLOEXEC);
315 (void) fcntl (STDERR_FILENO, F_SETFD, FD_CLOEXEC);
316 /*
317 freopen(_("/dev/null"), "r+", stderr);
318 freopen(_("/dev/null"), "r+", stdout);
319 */
320 }
321 else
322 {
323 PDBGC_S("r");
324 (void) retry_aud_dup2 (FIL__, __LINE__,
325 STDOUT_FILENO, STDERR_FILENO);
326 (void) fcntl (STDIN_FILENO, F_SETFD, FD_CLOEXEC);
327 /*
328 freopen(_("/dev/null"), "r+", stdin);
329 */
330 }
331
332 PDBGC(4);
333
334
335#if defined(__linux__)
336 /*
337 * -- emulate an fdexec with checksum testing
338 */
339 if (task->checksum[0] != '\0')
340 {
341 PDBGC_S("fexecve");
342 if (task->com_fd != (-1))
343 {
344 pfd = retry_aud_dup(FIL__, __LINE__, task->com_fd);
345 if (pfd < 0)
346 {
347 PDBGC_S("fexecve: dup2 failed");
348 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
349 }
350 }
351 else
352 {
353 fd =
354 sl_open_read(task->command,
355 task->privileged==0 ? SL_NOPRIV : SL_YESPRIV);
356 tiger_fd = fd;
357 if (0 != sl_strcmp(task->checksum,
358 sh_tiger_hash (task->command,
359 TIGER_FD, 0)))
360 {
361 PDBGC_S("fexecve: checksum mismatch");
362 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
363 }
364 pfd = get_the_fd(fd);
365 }
366
367 PDBGC(5);
368 sl_snprintf(pname, sizeof(pname), _("/proc/self/fd/%d"), pfd);
369 if (access(pname, R_OK|X_OK) == 0) /* flawfinder: ignore */
370 {
371 PDBGC(6);
372 PDBGC_CLOSE;
373 fcntl (pfd, F_SETFD, FD_CLOEXEC);
374 retry_aud_execve (FIL__, __LINE__,
375 pname,
376 (task->argc == 0) ? NULL : task->argv,
377 (task->envc == 0) ? NULL : task->envv
378 );
379
380 errnum = errno;
381 PDBGC_OPEN;
382 PDBGC_S(strerror(errnum));
383 PDBGC_S(task->command);
384 PDBGC_S("fexecve: failed");
385 PDBGC_CLOSE;
386 /* failed
387 */
388 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
389 }
390 PDBGC_S("fexecve: not working");
391 /*
392 * procfs not working, go ahead; checksum is tested already
393 */
394 if (fd != -1)
395 sl_close(fd);
396 else if (pfd != -1)
397 close(fd);
398 }
399#endif
400
401 PDBGC_S(" -- non fexecve --");
402 /*
403 * -- execute path if executable
404 */
405 if (0 == access(task->command, R_OK|X_OK)) /* flawfinder: ignore */
406 {
407 PDBGC(5);
408 PDBGC_CLOSE;
409 (void) retry_aud_execve (FIL__, __LINE__,
410 task->command,
411 (task->argc == 0) ? argp : task->argv,
412 (task->envc == 0) ? envp : task->envv
413 );
414 }
415 errnum = errno;
416 PDBGC_OPEN;
417 PDBGC_S(strerror(errnum));
418 PDBGC_S(task->command);
419 PDBGC_S("execve: failed");
420 PDBGC_CLOSE;
421 /* failed
422 */
423 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
424 }
425 /*
426 * if we have forked twice, this is parent::detached_subprocess
427 */
428 if (S_TRUE == task->fork_twice)
429 {
430 aud__exit (FIL__, __LINE__, 0);
431 }
432 }
433
434
435 /*
436 * -- parent; task->pid is child pid; exit status is status of
437 * grandchild if exited
438 */
439 if (S_TRUE == task->fork_twice)
440 {
441 (void) waitpid (task->pid, NULL, 0);
442 }
443
444 PDBG(5);
445 /* open an output stream on top of the write side of the pipe
446 */
447 if (task->rw == 'w')
448 {
449 PDBG_S("is w");
450 (void) close (pipedes[STDIN_FILENO]);
451 (void) retry_fcntl (FIL__, __LINE__, pipedes[STDOUT_FILENO],
452 F_SETFD, FD_CLOEXEC);
453 outf = fdopen (pipedes[STDOUT_FILENO], "w");
454 }
455 else
456 {
457 PDBG_S("is r");
458 (void) close (pipedes[STDOUT_FILENO]);
459 (void) retry_fcntl (FIL__, __LINE__, pipedes[STDIN_FILENO],
460 F_SETFD, FD_CLOEXEC);
461 outf = fdopen (pipedes[STDIN_FILENO], "r");
462 }
463
464 if (outf == NULL)
465 {
466 errnum = errno;
467 PDBG_S("outf == NULL");
468 tmp = sh_util_safe_name (task->command);
469
470 if (task->privileged == 0 && 0 == getuid())
471 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_NOEXEC,
472 (UID_CAST) task->run_user_uid, tmp);
473 else
474 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_NOEXEC,
475 (UID_CAST) getuid(), tmp);
476
477 SH_FREE(tmp);
478
479 (void) aud_kill (FIL__, __LINE__, task->pid, SIGKILL);
480 (void) close (pipedes[STDOUT_FILENO]);
481 (void) close (pipedes[STDIN_FILENO]);
482 (void) waitpid (task->pid, NULL, 0);
483 task->pid = 0;
484
485 SL_RETURN ((-1), _("sh_ext_popen"));
486 }
487
488 if (task->rw == 'w')
489 task->pipeFD = pipedes[STDOUT_FILENO];
490 else
491 task->pipeFD = pipedes[STDIN_FILENO];
492
493 PDBG_D(task->pipeFD);
494
495 task->pipeTI = sl_make_ticket(task->pipeFD, _("pipe"));
496
497 flags = (int) retry_fcntl (FIL__, __LINE__, task->pipeFD, F_GETFL, 0);
498 if (flags != (-1))
499 (void) retry_fcntl (FIL__, __LINE__, task->pipeFD,
500 F_SETFL, flags|O_NONBLOCK);
501 task->pipe = outf;
502
503 PDBG_S("return from popen");
504 PDBG_CLOSE;
505
506 SL_RETURN (0, _("sh_ext_popen"));
507}
508
509/*
510 * -- close the pipe
511 */
512extern int flag_err_debug;
513
514int sh_ext_pclose (sh_tas_t * task)
515{
516 int status = 0;
517 int retry = 0;
518 pid_t retval;
519 char infomsg[256];
520
521 SL_ENTER(_("sh_ext_pclose"));
522
523 PDBGC_OPEN;
524 PDBG_S(" -> pclose");
525 (void) fflush(task->pipe);
526 (void) fclose(task->pipe);
527 if (!SL_ISERROR(task->pipeTI))
528 (void) sl_close(task->pipeTI);
529
530 task->pipe = NULL;
531 task->pipeFD = (-1);
532 task->pipeTI = SL_ETICKET;
533
534 if (S_FALSE == task->fork_twice)
535 {
536 infomsg[0] = '\0';
537
538 nochmal:
539 retval = waitpid(task->pid, &(task->exit_status), WNOHANG|WUNTRACED);
540 /*@-bufferoverflowhigh@*/
541 if (task->pid == retval)
542 {
543#ifndef USE_UNO
544 if (WIFEXITED(task->exit_status) != 0)
545 {
546 task->exit_status = WEXITSTATUS(task->exit_status);
547 if ((flag_err_debug == SL_TRUE) || (task->exit_status != 0))
548 sl_snprintf(infomsg, sizeof(infomsg),
549 _("Subprocess exited normally with status %d"),
550 task->exit_status);
551 }
552 else if (WIFSIGNALED(task->exit_status) != 0)
553 {
554 sl_snprintf(infomsg, sizeof(infomsg),
555 _("Subprocess terminated by signal %d"),
556 WTERMSIG(task->exit_status));
557 task->exit_status = EXIT_FAILURE;
558 }
559 else if (WIFSTOPPED(task->exit_status) != 0)
560 {
561 sl_snprintf(infomsg, sizeof(infomsg),
562 _("Subprocess stopped by signal %d, killing"),
563 WSTOPSIG(task->exit_status));
564 task->exit_status = EXIT_FAILURE;
565 (void) aud_kill (FIL__, __LINE__, task->pid, 9);
566 (void) retry_msleep (0, 30);
567 (void) waitpid (task->pid, NULL, WNOHANG|WUNTRACED);
568 }
569 else
570 {
571 sl_snprintf(infomsg, sizeof(infomsg),
572 _("Subprocess exit status unknown"));
573 task->exit_status = EXIT_FAILURE;
574 }
575#else
576 task->exit_status = EXIT_FAILURE;
577#endif
578 }
579 else if (0 == retval)
580 {
581 if (retry < 3)
582 {
583 ++retry;
584 (void) retry_msleep(0, (retry * 30));
585 goto nochmal;
586 }
587 (void) aud_kill (FIL__, __LINE__, task->pid, 9);
588 sl_snprintf(infomsg, sizeof(infomsg),
589 _("Subprocess not yet exited, killing"));
590 task->exit_status = EXIT_FAILURE;
591 (void) waitpid (task->pid, NULL, 0);
592 }
593 else
594 {
595 sl_snprintf(infomsg, sizeof(infomsg),
596 _("Waitpid returned error %d\n"), errno);
597 task->exit_status = EXIT_FAILURE;
598 }
599 /*@+bufferoverflowhigh@*/
600 status = task->exit_status;
601 if (flag_err_debug == SL_TRUE)
602 {
603 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, task->exit_status,
604 MSG_E_SUBGEN, infomsg, _("sh_ext_pclose"));
605 }
606 else if (status != 0)
607 {
608 sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, task->exit_status,
609 MSG_E_SUBGEN, infomsg, _("sh_ext_pclose"));
610 }
611 }
612
613 task->pid = 0;
614 task->exit_status = 0;
615 PDBG_S(" <--");
616 PDBG_CLOSE;
617 SL_RETURN (status, _("sh_ext_pclose"));
618}
619
620void sh_ext_tas_init (sh_tas_t * tas)
621{
622 int i;
623
624 tas->command = NULL;
625 tas->argc = 0;
626 tas->envc = 0;
627 tas->checksum[0] = '\0';
628 tas->pipeFD = (-1);
629 tas->pipeTI = SL_ETICKET;
630 tas->pid = (pid_t) -1;
631 tas->privileged = 1;
632 tas->pipe = NULL;
633 tas->rw = 'w';
634 tas->exit_status = 0;
635 tas->fork_twice = S_TRUE;
636
637 for (i = 0; i < 32; ++i)
638 {
639 tas->argv[i] = NULL;
640 tas->envv[i] = NULL;
641 tas->trusted_users[i] = (uid_t) -1;
642 }
643
644 tas->run_user_uid = (uid_t) getuid();
645 tas->run_user_gid = (gid_t) getgid();
646
647 tas->com_fd = -1;
648 tas->com_ti = -1;
649 return;
650}
651
652
653int sh_ext_tas_add_envv(sh_tas_t * tas, const char * key, const char * val)
654{
655 size_t sk = 0, sv = 0;
656 int si;
657
658 SL_ENTER(_("sh_ext_tas_add_envv"));
659
660 if (tas == NULL || (key == NULL && val == NULL) ||
661 tas->envc >= 30)
662 {
663 SL_RETURN (-1, _("sh_ext_tas_add_envv"));
664 }
665 if (key != NULL)
666 sk = strlen(key) + 1;
667 if (val != NULL)
668 sv = strlen(val) + 1;
669
670 if (!sl_ok_adds(sk, sv))
671 {
672 SL_RETURN (-1, _("sh_ext_tas_add_envv"));
673 }
674 si = tas->envc;
675 tas->envv[si] = SH_ALLOC(sk + sv);
676
677 if (key != NULL)
678 {
679 (void) sl_strlcpy(tas->envv[si], key, sk+sv);
680 (void) sl_strlcat(tas->envv[si], "=", sk+sv);
681 if (val != NULL)
682 (void) sl_strlcat(tas->envv[si], val, sk+sv);
683 }
684 else
685 (void) sl_strlcpy(tas->envv[si], val, sv);
686
687 ++(tas->envc);
688 SL_RETURN ((tas->envc), _("sh_ext_tas_add_envv"));
689}
690
691int sh_ext_tas_rm_argv(sh_tas_t * tas)
692{
693 int last;
694
695 SL_ENTER(_("sh_ext_tas_rm_argv"));
696 if (tas == NULL || tas->argc == 0)
697 {
698 SL_RETURN (-1, _("sh_ext_tas_rm_argv"));
699 }
700
701 last = (tas->argc - 1);
702 --(tas->argc);
703 SH_FREE(tas->argv[last]);
704 tas->argv[last] = NULL;
705 SL_RETURN ((tas->argc), _("sh_ext_tas_rm_argv"));
706}
707
708int sh_ext_tas_add_argv(sh_tas_t * tas, const char * val)
709{
710 size_t sv = 0;
711 int si;
712
713 SL_ENTER(_("sh_ext_tas_add_argv"));
714
715 if (tas == NULL || val == NULL ||
716 tas->argc >= 30)
717 {
718 SL_RETURN (-1, _("sh_ext_tas_add_argv"));
719 }
720
721 if (val != NULL)
722 sv = strlen(val) + 1;
723
724 si = tas->argc;
725 tas->argv[si] = SH_ALLOC(sv);
726
727 (void) sl_strlcpy(tas->argv[si], val, sv);
728
729 ++(tas->argc);
730 SL_RETURN ((tas->argc), _("sh_ext_tas_add_argv"));
731}
732
733void sh_ext_tas_command(sh_tas_t * tas, const char * command)
734{
735 size_t len = sl_strlen(command);
736 tas->command = SH_ALLOC(len+1);
737 (void) sl_strlcpy(tas->command, command, len+1);
738 return;
739}
740
741void sh_ext_tas_free(sh_tas_t * tas)
742{
743 int i;
744 if (NULL != tas->command) SH_FREE(tas->command);
745
746 for (i = 0; i < 32; ++i)
747 {
748 if (NULL != tas->argv[i]) SH_FREE(tas->argv[i]);
749 if (NULL != tas->envv[i]) SH_FREE(tas->envv[i]);
750 }
751
752 if (tas->com_ti != (-1))
753 {
754 (void) sl_close(tas->com_ti);
755 tas->com_ti = -1;
756 tas->com_fd = -1;
757 }
758
759 return;
760}
761
762
763/* --------------- EXTERN STUFF ------------------- */
764
765#if defined(WITH_EXTERNAL)
766
767typedef struct _sh_com_t
768{
769 char type[4];
770
771 int for_c;
772 char * for_v[32];
773 int fand_c;
774 char * fand_v[32];
775 int fnot_c;
776 char * fnot_v[32];
777 time_t deadtime;
778 time_t last_run;
779
780 sh_tas_t tas;
781
782 struct _sh_com_t * next;
783
784} sh_com_t;
785
786
787static
788void set3 (char * pos, char c1, char c2, char c3)
789{
790 pos[0] = c1;
791 pos[1] = c2;
792 pos[2] = c3;
793 pos[3] = '\0';
794 return;
795}
796
797
798
799/* initialize the external command structure
800 */
801static
802sh_com_t * command_init(void)
803{
804 int i;
805 uid_t ff_euid;
806 sh_com_t * ext_com = NULL;
807
808 SL_ENTER(_("command_init"));
809
810 ext_com = (sh_com_t *) SH_ALLOC(sizeof(sh_com_t));
811
812 if (!ext_com)
813 {
814 SL_RETURN( NULL, ("command_init"));
815 }
816
817 sh_ext_tas_init (&(ext_com->tas));
818
819 (void) sl_get_euid(&ff_euid);
820 ext_com->tas.trusted_users[0] = (uid_t) 0;
821 ext_com->tas.trusted_users[1] = (uid_t) (ff_euid);
822
823 /* ------------------------------------------------- */
824
825 set3(ext_com->type, 'l', 'o', 'g');
826 ext_com->for_c = 0;
827 ext_com->fand_c = 0;
828 ext_com->fnot_c = 0;
829 ext_com->deadtime = 0;
830 ext_com->last_run = 0;
831
832 for (i = 0; i < 32; ++i)
833 {
834 ext_com->for_v[i] = NULL;
835 ext_com->fand_v[i] = NULL;
836 ext_com->fnot_v[i] = NULL;
837 }
838 ext_com->next = NULL;
839
840 SL_RETURN( ext_com, ("command_init"));
841}
842
843/* the list of external commands
844 */
845static sh_com_t * ext_coms = NULL;
846
847/* if -1, allocation of last command has failed,
848 * thus don't fill in options
849 */
850static int ext_failed = -1;
851
852static
853int sh_ext_add_envv(const char * key, const char * val)
854{
855 int retval;
856
857 SL_ENTER(_("sh_ext_add_envv"));
858
859 if (ext_coms == NULL || ext_failed == (-1) ||
860 (key == NULL && val == NULL) ||
861 ext_coms->tas.envc >= 30)
862 {
863 SL_RETURN (-1, _("sh_ext_add_envv"));
864 }
865
866 retval = sh_ext_tas_add_envv(&(ext_coms->tas), key, val);
867
868 if (retval >= 0)
869 retval = 0;
870
871 SL_RETURN (retval, _("sh_ext_add_envv"));
872}
873
874
875
876static
877int sh_ext_init(const char * command)
878{
879 sh_com_t * retval;
880 size_t size;
881
882 SL_ENTER(_("sh_ext_init"));
883
884 if (command == NULL)
885 {
886 SL_RETURN (-1, _("sh_ext_init"));
887 }
888 size = strlen(command);
889 if (command[0] != '/' || size < 2)
890 {
891 SL_RETURN (-1, _("sh_ext_init"));
892 }
893
894 if (NULL == (retval = command_init()))
895 {
896 SL_RETURN (-1, _("sh_ext_init"));
897 }
898
899 sh_ext_tas_command(&(retval->tas), command);
900
901 if (sh.timezone != NULL)
902 {
903 (void) sh_ext_add_envv( "TZ", sh.timezone);
904 }
905
906 retval->next = ext_coms;
907 ext_coms = retval;
908 SL_RETURN (0, _("sh_ext_init"));
909}
910
911static
912int sh_ext_uid (const char * user, /*@out@*/uid_t * uid, /*@out@*/gid_t * gid)
913{
914 struct passwd * tempres;
915
916 SL_ENTER(_("sh_ext_uid"));
917
918 *uid = (uid_t)-1; *gid = (gid_t)-1;
919
920 if (user == NULL)
921 {
922 SL_RETURN (-1, _("sh_ext_uid"));
923 }
924 tempres = sh_getpwnam(user);
925
926 if (NULL != tempres)
927 {
928 *uid = tempres->pw_uid;
929 *gid = tempres->pw_gid;
930 SL_RETURN (0, _("sh_ext_uid"));
931 }
932
933 SL_RETURN (-1, _("sh_ext_uid"));
934}
935
936
937static
938int sh_ext_add (const char * argstring, int * ntok, char * stok[])
939{
940 int i = 0;
941 size_t s;
942 char * p;
943 char * new;
944 size_t len;
945
946 SL_ENTER(_("sh_ext_add"));
947
948 if (NULL == argstring)
949 {
950 SL_RETURN((-1), _("sh_ext_add"));
951 }
952
953 len = strlen(argstring) + 1;
954 new = SH_ALLOC(len);
955 sl_strlcpy(new, argstring, len);
956
957 do
958 {
959 if (i == 0)
960 p = strtok (new, ", \t");
961 else
962 p = strtok (NULL, ", \t");
963 if (p == NULL)
964 break;
965
966 s = strlen(p) + 1;
967 if (stok[i] != NULL)
968 SH_FREE(stok[i]);
969 stok[i] = SH_ALLOC(s);
970 (void) sl_strlcpy(stok[i], p, s);
971
972 ++i;
973 if (i == 30)
974 break;
975 }
976 while (p != NULL);
977
978 *ntok = i;
979 SH_FREE(new);
980
981 SL_RETURN (0, _("sh_ext_add"));
982}
983
984/*********************************************************
985 *
986 * Public functions
987 *
988 *
989 *********************************************************/
990
991/*
992 * -- start a new external command, and add it to the list
993 */
994int sh_ext_setcommand(const char * cmd)
995{
996 int i;
997
998 SL_ENTER(_("sh_ext_setcommand"));
999 if ( (i = sh_ext_init(cmd)) < 0)
1000 ext_failed = -1;
1001 else
1002 ext_failed = 0;
1003 SL_RETURN( i, _("sh_ext_setcommand"));
1004}
1005
1006
1007/*
1008 * -- clean up the command list
1009 */
1010int sh_ext_cleanup(void)
1011{
1012 int i;
1013 sh_com_t * retval;
1014
1015 SL_ENTER(_("sh_ext_cleanup"));
1016
1017 while (ext_coms != NULL)
1018 {
1019 retval = ext_coms;
1020 ext_coms = retval->next;
1021
1022 sh_ext_tas_free (&(retval->tas));
1023
1024 for (i = 0; i < 32; ++i)
1025 {
1026 if (NULL != retval->for_v[i]) SH_FREE(retval->for_v[i]);
1027 if (NULL != retval->fand_v[i]) SH_FREE(retval->fand_v[i]);
1028 if (NULL != retval->fnot_v[i]) SH_FREE(retval->fnot_v[i]);
1029 }
1030
1031 SH_FREE(retval);
1032
1033 }
1034
1035 SL_RETURN (0, _("sh_ext_cleanup"));
1036}
1037
1038/*
1039 * -- add keywords to the OR filter
1040 */
1041int sh_ext_add_or (const char * str)
1042{
1043 if (ext_coms == NULL || ext_failed == (-1))
1044 return (-1);
1045 return (sh_ext_add (str, &(ext_coms->for_c), ext_coms->for_v));
1046}
1047
1048/*
1049 * -- add keywords to the AND filter
1050 */
1051int sh_ext_add_and (const char * str)
1052{
1053 if (ext_coms == NULL || ext_failed == (-1))
1054 return (-1);
1055 return (sh_ext_add (str, &(ext_coms->fand_c), ext_coms->fand_v));
1056}
1057
1058/*
1059 * -- add keywords to the NOT filter
1060 */
1061int sh_ext_add_not (const char * str)
1062{
1063 if (ext_coms == NULL || ext_failed == (-1))
1064 return (-1);
1065 return (sh_ext_add (str, &(ext_coms->fnot_c), ext_coms->fnot_v));
1066}
1067
1068/*
1069 * -- add keywords to the CL argument list
1070 */
1071int sh_ext_add_argv (const char * str)
1072{
1073 if (ext_coms == NULL || ext_failed == (-1))
1074 return (-1);
1075 return (sh_ext_add (str, &(ext_coms->tas.argc), ext_coms->tas.argv));
1076}
1077
1078/*
1079 * -- add a path to the environment
1080 */
1081int sh_ext_add_default (const char * dummy)
1082{
1083 char * p = NULL;
1084 int i;
1085
1086 SL_ENTER(_("sh_ext_add_default"));
1087 if (dummy[0] == 'n' || dummy[0] == 'N' ||
1088 dummy[0] == 'f' || dummy[0] == 'F' || dummy[0] == '0')
1089 {
1090 SL_RETURN(0, _("sh_ext_add_default"));
1091 }
1092 p = sh_unix_getUIDdir (SH_ERR_ERR, (uid_t) ext_coms->tas.run_user_uid);
1093 if (p)
1094 (void) sh_ext_add_envv (_("HOME"), p);
1095 (void) sh_ext_add_envv (_("SHELL"), _("/bin/sh"));
1096 (void) sh_ext_add_envv (_("PATH"), _("/sbin:/usr/sbin:/bin:/usr/bin"));
1097 i = (p == NULL ? (-1) : 0);
1098 SL_RETURN(i, _("sh_ext_add_default"));
1099}
1100
1101/*
1102 * -- add an environment variable
1103 */
1104int sh_ext_add_environ (const char * str)
1105{
1106 int i;
1107
1108 SL_ENTER(_("sh_ext_add_environ"));
1109 i = sh_ext_add_envv (NULL, str);
1110 SL_RETURN(i, _("sh_ext_add_environ"));
1111}
1112
1113/*
1114 * -- set deadtime
1115 */
1116int sh_ext_deadtime (const char * str)
1117{
1118 long deadtime = 0;
1119 char * tail = NULL;
1120
1121 SL_ENTER(_("sh_ext_deadtime"));
1122
1123 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1124 {
1125 SL_RETURN (-1, ("sh_ext_deadtime"));
1126 }
1127 deadtime = strtol(str, &tail, 10);
1128 if (tail == str || deadtime < 0 || deadtime == LONG_MAX)
1129 {
1130 SL_RETURN (-1, ("sh_ext_deadtime"));
1131 }
1132
1133 ext_coms->deadtime = (time_t) deadtime;
1134 SL_RETURN (0, ("sh_ext_deadtime"));
1135}
1136
1137/*
1138 * -- define type
1139 */
1140int sh_ext_type (const char * str)
1141{
1142 SL_ENTER(_("sh_ext_type"));
1143
1144 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1145 {
1146 SL_RETURN((-1), _("sh_ext_type"));
1147 }
1148
1149 if (strlen(str) != 3)
1150 {
1151 SL_RETURN((-1), _("sh_ext_type"));
1152 }
1153
1154 set3(ext_coms->type, str[0], str[1], str[2]);
1155
1156 if (str[0] == 'l' && str[1] == 'o' && str[2] == 'g')
1157 ext_coms->tas.rw = 'w';
1158 else if (str[0] == 's' && str[1] == 'r' && str[2] == 'v')
1159 ext_coms->tas.rw = 'w';
1160 else if (str[0] == 'm' && str[1] == 'o' && str[2] == 'n')
1161 ext_coms->tas.rw = 'r';
1162 else
1163 {
1164 SL_RETURN((-1), _("sh_ext_type"));
1165 }
1166
1167 SL_RETURN(0, _("sh_ext_type"));
1168}
1169
1170
1171
1172/*
1173 * -- define checksum
1174 */
1175int sh_ext_checksum (const char * str)
1176{
1177 SL_ENTER(_("sh_ext_checksum"));
1178 if (ext_coms == NULL || ext_failed == (-1) || str == NULL)
1179 {
1180 SL_RETURN((-1), _("sh_ext_checksum"));
1181 }
1182
1183 if (sl_strlen(str) != KEY_LEN)
1184 {
1185 SL_RETURN((-1), _("sh_ext_checksum"));
1186 }
1187
1188 (void) sl_strlcpy (ext_coms->tas.checksum, str, KEY_LEN+1);
1189
1190 SL_RETURN((0), _("sh_ext_checksum"));
1191}
1192
1193/*
1194 * -- choose privileges
1195 */
1196int sh_ext_priv (const char * c)
1197{
1198
1199 uid_t me_uid;
1200 gid_t me_gid;
1201
1202 SL_ENTER(_("sh_ext_priv"));
1203 if (0 == sh_ext_uid (c, &me_uid, &me_gid))
1204 {
1205 ext_coms->tas.run_user_uid = me_uid;
1206 ext_coms->tas.run_user_gid = me_gid;
1207 if (me_uid != (uid_t) 0)
1208 ext_coms->tas.privileged = 0;
1209 SL_RETURN((0), _("sh_ext_priv"));
1210 }
1211
1212 SL_RETURN (-1, _("sh_ext_priv"));
1213}
1214
1215
1216
1217
1218/*
1219 * -- check filters
1220 */
1221static int sh_ext_filter (char * message, sh_com_t * task)
1222{
1223 int i;
1224 int j = 0;
1225 time_t now_time;
1226
1227 SL_ENTER(_("sh_ext_filter"));
1228
1229 /* Presence of any of these keywords prevents execution.
1230 */
1231 if (task->fnot_c > 0)
1232 {
1233 for (i = 0; i < task->fnot_c; ++i)
1234 {
1235 if (NULL != sl_strstr(message, task->fnot_v[i]))
1236 {
1237 SL_RETURN ((-1), _("sh_ext_filter"));
1238 }
1239 }
1240 }
1241
1242 /* Presence of all of these keywords is required for execution.
1243 */
1244 if (task->fand_c > 0)
1245 {
1246 j = 0;
1247
1248 for (i = 0; i < task->fand_c; ++i)
1249 if (NULL != sl_strstr(message, task->fand_v[i]))
1250 ++j;
1251
1252 if (j != task->fand_c)
1253 {
1254 SL_RETURN ((-1), _("sh_ext_filter"));
1255 }
1256
1257 }
1258
1259 /* Presence of at least one of these keywords is required for execution.
1260 */
1261 if (task->for_c > 0)
1262 {
1263 for (i = 0; i < task->for_c; ++i)
1264 {
1265 if (NULL != sl_strstr(message, task->for_v[i]))
1266 {
1267 goto checkdeadtime;
1268 }
1269 }
1270 SL_RETURN ((-1), _("sh_ext_filter"));
1271 }
1272
1273 checkdeadtime:
1274 if (task->deadtime != (time_t) 0) /* deadtime */
1275 {
1276 now_time = time (NULL);
1277
1278 if (task->last_run == (time_t) 0)
1279 {
1280 task->last_run = now_time;
1281 }
1282 else if ((time_t)(now_time-task->last_run) < task->deadtime)
1283 {
1284 SL_RETURN ((-1), _("sh_ext_filter"));
1285 }
1286 else
1287 {
1288 task->last_run = now_time;
1289 }
1290 }
1291
1292 SL_RETURN ((0), _("sh_ext_filter"));
1293}
1294
1295
1296
1297/*
1298 * -- execute external script/program
1299 */
1300int sh_ext_execute (char t1, char t2, char t3, /*@null@*/char * message,
1301 size_t msg_siz)
1302{
1303 int caperr;
1304 sh_com_t * listval = ext_coms;
1305 int status = 0;
1306 char * tmp;
1307
1308 static int some_error = 0;
1309
1310 struct sigaction new_act;
1311 struct sigaction old_act;
1312
1313 SL_ENTER(_("sh_ext_execute"));
1314
1315 PDBG_OPEN;
1316
1317 if (listval == NULL || message == NULL)
1318 {
1319 SL_RETURN ((-1), _("sh_ext_execute"));
1320 }
1321
1322 PDBG(-1);
1323
1324 if (msg_siz == 0)
1325 msg_siz = sl_strlen(message);
1326
1327
1328 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
1329 */
1330 new_act.sa_handler = SIG_IGN;
1331 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
1332
1333 while (listval != NULL)
1334 {
1335 PDBG(-2);
1336 if (t1 == listval->type[0] &&
1337 t2 == listval->type[1] &&
1338 t3 == listval->type[2] &&
1339 0 == sh_ext_filter (message, listval))
1340 {
1341 PDBG(-3);
1342
1343 if (0 != (caperr = sl_get_cap_sub()))
1344 {
1345 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
1346 sh_error_message (caperr),
1347 _("sl_get_cap_sub"));
1348 }
1349 if (0 == sh_ext_popen (&(listval->tas)))
1350 {
1351 PDBG_OPEN;
1352 PDBG(-4);
1353 if (NULL != listval->tas.pipe && listval->tas.rw == 'w')
1354 {
1355 PDBG(-5);
1356 if (message != NULL)
1357 {
1358 PDBG(-6);
1359 status = (int) write (listval->tas.pipeFD,
1360 message, msg_siz);
1361 if (status >= 0)
1362 status = (int) write (listval->tas.pipeFD, "\n", 1);
1363 }
1364 PDBG_D(status);
1365 if (status >= 0)
1366 status = (int) write (listval->tas.pipeFD, "[", 1);
1367 PDBG_D(status);
1368 if (status >= 0)
1369 status = (int) write (listval->tas.pipeFD, "E", 1);
1370 PDBG_D(status);
1371 if (status >= 0)
1372 status = (int) write (listval->tas.pipeFD, "O", 1);
1373 PDBG_D(status);
1374 if (status >= 0)
1375 status = (int) write (listval->tas.pipeFD, "F", 1);
1376 PDBG_D(status);
1377 if (status >= 0)
1378 status = (int) write (listval->tas.pipeFD, "]", 1);
1379 PDBG_D(status);
1380 if (status >= 0)
1381 status = (int) write (listval->tas.pipeFD, "\n", 1);
1382 PDBG_D(status);
1383 if (status >= 0)
1384 {
1385 some_error = 0;
1386 }
1387 if ((status < 0) && (some_error == 0))
1388 {
1389 some_error = 1;
1390 PDBG_S("some error");
1391 PDBG_D(status);
1392 tmp = sh_util_safe_name (listval->tas.command);
1393
1394 if (tmp)
1395 {
1396 if (listval->tas.privileged == 0 &&
1397 (0 == getuid() || 0 != sl_is_suid()) )
1398 sh_error_handle((-1), FIL__, __LINE__, 0,
1399 MSG_NOEXEC,
1400 (UID_CAST) listval->tas.run_user_uid,
1401 tmp);
1402 else
1403 sh_error_handle((-1), FIL__, __LINE__, 0,
1404 MSG_NOEXEC,
1405 (UID_CAST) getuid(), tmp);
1406
1407 SH_FREE(tmp);
1408 }
1409
1410 }
1411 PDBG(-7);
1412 (void) fflush(listval->tas.pipe);
1413 }
1414 PDBG(-8);
1415 (void) sh_ext_pclose(&(listval->tas));
1416 }
1417 else
1418 {
1419 PDBG_OPEN;
1420 PDBG_S("0 != sh_ext_popen()");
1421 }
1422 if (0 != (caperr = sl_drop_cap_sub()))
1423 {
1424 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
1425 sh_error_message (caperr),
1426 _("sl_drop_cap_sub"));
1427 }
1428
1429 }
1430 listval = listval->next;
1431 }
1432 PDBG_OPEN;
1433 PDBG_S("no more commands");
1434
1435 /* restore old signal handler
1436 */
1437 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
1438 PDBG_S("return");
1439 PDBG_CLOSE;
1440
1441 SL_RETURN ((0), _("sh_ext_execute"));
1442}
1443
1444
1445/* #if defined(WITH_EXTERNAL) */
1446#endif
Note: See TracBrowser for help on using the repository browser.