source: trunk/src/sh_extern.c @ 132

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