source: trunk/src/sh_extern.c@ 132

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

Make utility functions thread-safe.

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