source: trunk/src/sh_sub.c

Last change on this file was 491, checked in by katerina, 6 years ago

Fix for ticket #389 (libwrap) and #390 (update on FreeBSD).

File size: 10.5 KB
Line 
1/* SAMHAIN file system integrity testing                                   */
2/* Copyright (C) 2011 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#include "config_xor.h"
21
22/* 0->1 for debug */ 
23#if 0
24#define SH_SUB_DBG 1
25#endif
26
27#ifndef NULL
28#if !defined(__cplusplus)
29#define NULL ((void*)0)
30#else
31#define NULL (0)
32#endif
33#endif
34
35
36#include <stdio.h>
37#include <string.h>
38#include <errno.h>
39#include <limits.h>
40
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/wait.h>
44#include <unistd.h>
45#include <fcntl.h>
46#include <poll.h>
47
48#include "samhain.h"
49#include "sh_pthread.h"
50
51#ifndef HAVE_LSTAT
52#define lstat stat
53#endif
54
55#define FIL__ _("sh_sub.c")
56
57static pid_t sh_child_pid = -1;
58static pid_t sh_wait_ret  =  1;
59
60static int parent2child[2];
61static int child2parent[2];
62
63SH_MUTEX_STATIC(mutex_sub,      PTHREAD_MUTEX_INITIALIZER);
64SH_MUTEX_STATIC(mutex_sub_work, PTHREAD_MUTEX_INITIALIZER);
65
66static void wait_for_command();
67static ssize_t sh_sub_read(int fd, void *buf, size_t count);
68
69void sh_kill_sub()
70{
71  SH_MUTEX_LOCK(mutex_sub);
72
73  if (sh_child_pid != -1)
74    {
75      int status;
76#ifdef WCONTINUED
77      int wflags = WNOHANG|WUNTRACED|WCONTINUED;
78#else
79      int wflags = WNOHANG|WUNTRACED;
80#endif
81
82      close (parent2child[1]);
83      close (child2parent[0]);
84
85      /* Let's be rude. */
86      kill(sh_child_pid, SIGKILL);
87
88      retry_msleep(1,0);
89
90      if (sh_wait_ret == 0)
91        sh_wait_ret = waitpid(          -1, &status, wflags);
92      else
93        sh_wait_ret = waitpid(sh_child_pid, &status, wflags);
94
95      sh_child_pid = -1;
96    }
97
98  SH_MUTEX_UNLOCK(mutex_sub);
99  return;
100}
101
102static int sh_create_sub()
103{
104  pid_t res;
105  volatile int   retval = 0;
106
107  SH_MUTEX_LOCK(mutex_sub);
108
109#if !defined(O_NONBLOCK)
110#if defined(O_NDELAY)
111#define O_NONBLOCK  O_NDELAY
112#else
113#define O_NONBLOCK  0
114#endif
115#endif
116
117  if (sh_child_pid == -1)
118    {
119      sigset_t signal_set_new;
120      sigset_t signal_set_old;
121
122      sigfillset ( &signal_set_new );
123      sigemptyset( &signal_set_old );
124
125      /* Create pipes. */
126      res = pipe (parent2child);
127      if (res == 0)
128        res = pipe (child2parent);
129     
130      if (res != 0)
131        goto out;
132
133      SH_SETSIGMASK(SIG_BLOCK, &signal_set_new, &signal_set_old);
134
135      res = fork();
136     
137      if (res == 0)
138        {
139          /* Child process. */
140#ifdef _SC_OPEN_MAX
141          int fdlimit = sysconf (_SC_OPEN_MAX);
142#else
143#ifdef OPEN_MAX
144          int fdlimit = OPEN_MAX;
145#else
146          int fdlimit = _POSIX_OPEN_MAX;
147#endif
148#endif
149          int sflags, i, fd = 0;
150          struct sigaction act;
151
152          /* zero private information
153           */
154          memset(skey, 0, sizeof(sh_key_t)); 
155
156          close (parent2child[1]);
157          close (child2parent[0]);
158         
159          sflags = fcntl(parent2child[0], F_GETFL, 0);
160          fcntl(parent2child[0], F_SETFL, sflags | O_NONBLOCK);
161          sflags = fcntl(child2parent[1], F_GETFL, 0);
162          fcntl(child2parent[1], F_SETFL, sflags | O_NONBLOCK);
163
164          /* close inherited file descriptors
165           */
166          if (fdlimit < 0) 
167            fdlimit = 20;  /* POSIX lower limit */
168          while (fd < fdlimit)
169            {
170              if (fd != parent2child[0] && fd != child2parent[1])
171                close(fd);
172              ++fd;
173            }
174
175          /*
176          for (i = 0; i < 3; ++i)
177            {
178              if ( fcntl(i, F_GETFL, 0) == (-1))
179                (void) open(_("/dev/null"), O_RDWR, 0);
180            }
181          */
182
183          /* reset signal handling
184           */
185          act.sa_handler = SIG_DFL;
186          act.sa_flags   = 0;
187          sigemptyset( &act.sa_mask );
188         
189          for (i = 1; i < NSIG; ++i)
190            sigaction(i, &act, NULL);
191          SH_SETSIGMASK(SIG_UNBLOCK, &signal_set_new, NULL);
192
193          wait_for_command();
194         
195          _exit(0);
196        }
197      else if (res > 0)
198        {
199          /* Parent process. */
200          int sflags;
201
202          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
203
204          close (parent2child[0]);
205          close (child2parent[1]);
206         
207          sflags = fcntl(parent2child[1], F_GETFL, 0);
208          fcntl(parent2child[1], F_SETFL, sflags | O_NONBLOCK);
209          sflags = fcntl(child2parent[0], F_GETFL, 0);
210          fcntl(child2parent[0], F_SETFL, sflags | O_NONBLOCK);
211
212          sh_child_pid = res;
213        }
214      else
215        {
216          /* Failure. */
217
218          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
219
220          close (parent2child[0]);
221          close (parent2child[1]);
222
223          close (child2parent[0]);
224          close (child2parent[1]);
225         
226          retval = -1;
227        }
228    }
229
230 out:
231  ; /* 'label at end of compound statement' */
232  SH_MUTEX_UNLOCK(mutex_sub);
233  return retval;
234}
235
236#define  SH_SUB_BUF (PIPE_BUF-1)
237struct sh_sub_in {
238  char   command;
239  char   path[SH_SUB_BUF];
240};
241
242struct sh_sub_out {
243  int retval;
244  int errnum;
245  struct stat sbuf;
246};
247
248#define SH_COM_STAT  0
249#define SH_COM_LSTAT 1
250
251static ssize_t sh_sub_write(int fd, const void *buf, size_t count)
252{
253  const char * mbuf = (const char *) buf;
254  ssize_t rcount;
255  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
256  int tti = 1; 
257
258  do {
259
260    rcount = write(fd, mbuf, count);
261    if (rcount > 0) 
262      {
263        count -= rcount; mbuf += rcount; --ttl;
264      }
265
266    if (count > 0)
267      {
268        if (ttl > 0)
269          {
270            retry_msleep(0, tti);
271            tti *= 9;
272          }
273        else
274          {
275            return -1;
276          }
277      }
278  } while (count > 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
279
280  if (count > 0)
281    return -1;
282  return 0;
283}
284
285static void wait_for_command()
286{
287  int               ret;
288  struct pollfd     fds;
289  struct sh_sub_in  inbuf;
290  struct sh_sub_out outbuf;
291
292  fds.fd     = parent2child[0];
293  fds.events = POLLIN;
294
295  do {
296
297    do {
298      ret = poll(&fds, 1, -1);
299    } while (ret < 0 && errno == EINTR);
300
301    if (ret > 0)
302      {
303        ret = sh_sub_read(parent2child[0], &inbuf, sizeof(inbuf));
304
305        if (ret == 0)
306          {
307            if (inbuf.command == SH_COM_LSTAT)
308              {
309                do { 
310                  outbuf.retval = lstat(inbuf.path, &(outbuf.sbuf)); 
311                } while (outbuf.retval < 0 && errno == EAGAIN);
312              }
313            else
314              {
315                do { 
316                  outbuf.retval = stat(inbuf.path, &(outbuf.sbuf)); 
317                } while (outbuf.retval < 0 && errno == EAGAIN);
318              }
319
320            outbuf.errnum = errno;
321
322            ret = sh_sub_write(child2parent[1], &outbuf, sizeof(outbuf));
323            if (ret < 0)
324              {
325                return;
326              }
327          }
328        else /* sh_sub_read() < 0 */
329          {
330            return;
331          }
332      }
333  } while (1 == 1);
334}
335
336#ifndef ETIMEDOUT
337#define ETIMEDOUT EIO
338#endif
339
340static ssize_t sh_sub_read(int fd, void *buf, size_t count)
341{
342  char * mbuf = (char *) buf;
343  ssize_t rcount;
344  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
345  int tti = 1; 
346
347  do {
348    rcount = read(fd, mbuf, count);
349
350    if (rcount > 0) 
351      {
352        count -= rcount; mbuf += rcount; --ttl;
353      }
354
355    if (count > 0)
356      {
357        if (ttl > 0)
358          {
359            retry_msleep(0, tti);
360            tti *= 9;
361          }
362        else
363          {
364            if (rcount >= 0) 
365              errno = ETIMEDOUT;
366            return -1;
367          }
368      }
369  } while (count > 0 && 
370           (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
371
372  if (count > 0)
373    return -1;
374
375  return 0;
376}
377
378#ifdef SH_SUB_DBG
379#include <stdarg.h>
380static void debug_it (const char *fmt, ...)
381{
382  char msg[256];
383  va_list ap;
384
385  int fd = open("debug.it", O_CREAT|O_WRONLY|O_APPEND, 0666);
386
387  va_start(ap, fmt);
388  vsnprintf(msg, sizeof(msg), fmt, ap);  /* flawfinder: ignore */
389  va_end(ap);
390
391  write(fd, msg, strlen(msg));
392  write(fd, "\n", 1);
393  close(fd);
394  return;
395}
396#endif
397
398static int sh_sub_stat_int(const char *path, struct stat *buf, char command)
399{
400  int retval;
401  volatile int sflag = 0;
402  struct sh_sub_in  inbuf;
403  struct sh_sub_out outbuf;
404  struct pollfd     pfds;
405
406  size_t len = strlen(path) + 1;
407
408  if (len > SH_SUB_BUF)
409    {
410      if (command == SH_COM_LSTAT)
411        {
412          do { 
413            retval = lstat(path, buf); 
414          } while (retval < 0 && errno == EAGAIN);
415
416          return retval;
417        }
418      else
419        {
420          do { 
421            retval = stat(path, buf); 
422          } while (retval < 0 && errno == EAGAIN);
423
424          return retval;
425        }
426    }
427
428  sl_strlcpy(inbuf.path, path, SH_SUB_BUF);
429  inbuf.command = command;
430
431 start:
432
433#ifdef SH_SUB_DBG
434  debug_it("%d sh_child_pid %d\n", (int)getpid(), (int) sh_child_pid);
435#endif
436
437  if (sh_child_pid == -1)
438    sh_create_sub();
439
440#ifdef SH_SUB_DBG
441  debug_it("%d stat_sub %s (%d)\n", (int)getpid(), inbuf.path, (int) sh_child_pid);
442#endif
443
444  SH_MUTEX_LOCK(mutex_sub_work);
445
446  retval = sh_sub_write(parent2child[1], &inbuf, sizeof(inbuf));
447  if (retval < 0)
448    {
449      int error = errno;
450      sh_kill_sub();
451      errno = error;
452      sflag = 1;
453      goto end;
454    }
455
456#ifdef SH_SUB_DBG
457  debug_it("%d stat_sub polling..\n", (int)getpid());
458#endif
459
460  pfds.fd     = child2parent[0];
461  pfds.events = POLLIN;
462
463  do {
464    retval = poll(&pfds, 1, 300 * 1000);
465  } while (retval < 0 && errno == EINTR);
466
467  if (retval <= 0)
468    {
469      int error = errno;
470      sh_kill_sub();
471      errno = (retval == 0) ? ETIMEDOUT : error;
472      sflag = -1;
473      goto end;
474    }
475
476#ifdef SH_SUB_DBG
477  debug_it("%d stat_sub reading..\n", (int)getpid());
478#endif
479
480  retval = sh_sub_read (child2parent[0], &outbuf, sizeof(outbuf));
481  if (retval < 0)
482    {
483      int error = errno;
484      sh_kill_sub();
485      errno = error;
486      sflag = 1;
487      goto end;
488    }
489
490 end:
491  ; /* 'label at end of compound statement' */
492  SH_MUTEX_UNLOCK(mutex_sub_work);
493
494  if      (sflag == 0)
495    {
496#ifdef SH_SUB_DBG
497      debug_it("%d stat_sub done..\n", (int)getpid());
498#endif
499      memcpy(buf, &(outbuf.sbuf), sizeof(struct stat));
500      errno = outbuf.errnum;
501      return outbuf.retval;
502    }
503  else if (sflag == 1)
504    {
505#ifdef SH_SUB_DBG
506      debug_it("%d stat_sub error..\n", (int)getpid());
507#endif
508      /* could not read, thus subprocess may have gone */
509      sflag = 0;
510      goto start;
511    }
512
513  return -1;
514}
515
516int sh_sub_stat (const char *path, struct stat *buf)
517{
518  return sh_sub_stat_int(path, buf, SH_COM_STAT);
519}
520
521int sh_sub_lstat(const char *path, struct stat *buf)
522{
523  return sh_sub_stat_int(path, buf, SH_COM_LSTAT);
524}
525
Note: See TracBrowser for help on using the repository browser.