source: trunk/src/sh_extern.c @ 133

Last change on this file since 133 was 133, checked in by rainer, 12 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.