source: trunk/src/sh_extern.c@ 117

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

Flush before fork (fix for ticket #60), and fix for kcheck on 2.6.21 (ticket #63).

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