source: trunk/src/sh_extern.c@ 148

Last change on this file since 148 was 133, checked in by rainer, 17 years ago

Reentrant checksum/hash functions.

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