source: trunk/src/sh_extern.c@ 4

Last change on this file since 4 was 1, checked in by katerina, 19 years ago

Initial import

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