source: trunk/src/sh_extern.c@ 25

Last change on this file since 25 was 22, checked in by rainer, 19 years ago

Minor code revisions.

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