source: trunk/src/sh_extern.c@ 131

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

Use thread-safe libc functions.

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