source: trunk/src/sh_sub.c @ 481

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

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

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          for (i = 0; i < NSIG; ++i)
187            sigaction(i, &act, NULL);
188          SH_SETSIGMASK(SIG_UNBLOCK, &signal_set_new, NULL);
189
190          wait_for_command();
191         
192          _exit(0);
193        }
194      else if (res > 0)
195        {
196          /* Parent process. */
197          int sflags;
198
199          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
200
201          close (parent2child[0]);
202          close (child2parent[1]);
203         
204          sflags = fcntl(parent2child[1], F_GETFL, 0);
205          fcntl(parent2child[1], F_SETFL, sflags | O_NONBLOCK);
206          sflags = fcntl(child2parent[0], F_GETFL, 0);
207          fcntl(child2parent[0], F_SETFL, sflags | O_NONBLOCK);
208
209          sh_child_pid = res;
210        }
211      else
212        {
213          /* Failure. */
214
215          SH_SETSIGMASK(SIG_SETMASK, &signal_set_old, NULL);
216
217          close (parent2child[0]);
218          close (parent2child[1]);
219
220          close (child2parent[0]);
221          close (child2parent[1]);
222         
223          retval = -1;
224        }
225    }
226
227 out:
228  ; /* 'label at end of compound statement' */
229  SH_MUTEX_UNLOCK(mutex_sub);
230  return retval;
231}
232
233#define  SH_SUB_BUF (PIPE_BUF-1)
234struct sh_sub_in {
235  char   command;
236  char   path[SH_SUB_BUF];
237};
238
239struct sh_sub_out {
240  int retval;
241  int errnum;
242  struct stat sbuf;
243};
244
245#define SH_COM_STAT  0
246#define SH_COM_LSTAT 1
247
248static ssize_t sh_sub_write(int fd, const void *buf, size_t count)
249{
250  const char * mbuf = (const char *) buf;
251  ssize_t rcount;
252  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
253  int tti = 1; 
254
255  do {
256
257    rcount = write(fd, mbuf, count);
258    if (rcount > 0) 
259      {
260        count -= rcount; mbuf += rcount; --ttl;
261      }
262
263    if (count > 0)
264      {
265        if (ttl > 0)
266          {
267            retry_msleep(0, tti);
268            tti *= 9;
269          }
270        else
271          {
272            return -1;
273          }
274      }
275  } while (count > 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
276
277  if (count > 0)
278    return -1;
279  return 0;
280}
281
282static void wait_for_command()
283{
284  int               ret;
285  struct pollfd     fds;
286  struct sh_sub_in  inbuf;
287  struct sh_sub_out outbuf;
288
289  fds.fd     = parent2child[0];
290  fds.events = POLLIN;
291
292  do {
293
294    do {
295      ret = poll(&fds, 1, -1);
296    } while (ret < 0 && errno == EINTR);
297
298    if (ret > 0)
299      {
300        ret = sh_sub_read(parent2child[0], &inbuf, sizeof(inbuf));
301
302        if (ret == 0)
303          {
304            if (inbuf.command == SH_COM_LSTAT)
305              {
306                do { 
307                  outbuf.retval = lstat(inbuf.path, &(outbuf.sbuf)); 
308                } while (outbuf.retval < 0 && errno == EAGAIN);
309              }
310            else
311              {
312                do { 
313                  outbuf.retval = stat(inbuf.path, &(outbuf.sbuf)); 
314                } while (outbuf.retval < 0 && errno == EAGAIN);
315              }
316
317            outbuf.errnum = errno;
318
319            ret = sh_sub_write(child2parent[1], &outbuf, sizeof(outbuf));
320            if (ret < 0)
321              {
322                return;
323              }
324          }
325        else /* sh_sub_read() < 0 */
326          {
327            return;
328          }
329      }
330  } while (1 == 1);
331}
332
333#ifndef ETIMEDOUT
334#define ETIMEDOUT EIO
335#endif
336
337static ssize_t sh_sub_read(int fd, void *buf, size_t count)
338{
339  char * mbuf = (char *) buf;
340  ssize_t rcount;
341  int ttl = 5; /* 0, 1, 9, 81, 729 millisec */
342  int tti = 1; 
343
344  do {
345    rcount = read(fd, mbuf, count);
346
347    if (rcount > 0) 
348      {
349        count -= rcount; mbuf += rcount; --ttl;
350      }
351
352    if (count > 0)
353      {
354        if (ttl > 0)
355          {
356            retry_msleep(0, tti);
357            tti *= 9;
358          }
359        else
360          {
361            if (rcount >= 0) 
362              errno = ETIMEDOUT;
363            return -1;
364          }
365      }
366  } while (count > 0 && 
367           (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));
368
369  if (count > 0)
370    return -1;
371
372  return 0;
373}
374
375#ifdef SH_SUB_DBG
376#include <stdarg.h>
377static void debug_it (const char *fmt, ...)
378{
379  char msg[256];
380  va_list ap;
381
382  int fd = open("debug.it", O_CREAT|O_WRONLY|O_APPEND, 0666);
383
384  va_start(ap, fmt);
385  vsnprintf(msg, sizeof(msg), fmt, ap);  /* flawfinder: ignore */
386  va_end(ap);
387
388  write(fd, msg, strlen(msg));
389  write(fd, "\n", 1);
390  close(fd);
391  return;
392}
393#endif
394
395static int sh_sub_stat_int(const char *path, struct stat *buf, char command)
396{
397  int retval;
398  volatile int sflag = 0;
399  struct sh_sub_in  inbuf;
400  struct sh_sub_out outbuf;
401  struct pollfd     pfds;
402
403  size_t len = strlen(path) + 1;
404
405  if (len > SH_SUB_BUF)
406    {
407      if (command == SH_COM_LSTAT)
408        {
409          do { 
410            retval = lstat(path, buf); 
411          } while (retval < 0 && errno == EAGAIN);
412
413          return retval;
414        }
415      else
416        {
417          do { 
418            retval = stat(path, buf); 
419          } while (retval < 0 && errno == EAGAIN);
420
421          return retval;
422        }
423    }
424
425  sl_strlcpy(inbuf.path, path, SH_SUB_BUF);
426  inbuf.command = command;
427
428 start:
429
430#ifdef SH_SUB_DBG
431  debug_it("%d sh_child_pid %d\n", (int)getpid(), (int) sh_child_pid);
432#endif
433
434  if (sh_child_pid == -1)
435    sh_create_sub();
436
437#ifdef SH_SUB_DBG
438  debug_it("%d stat_sub %s (%d)\n", (int)getpid(), inbuf.path, (int) sh_child_pid);
439#endif
440
441  SH_MUTEX_LOCK(mutex_sub_work);
442
443  retval = sh_sub_write(parent2child[1], &inbuf, sizeof(inbuf));
444  if (retval < 0)
445    {
446      int error = errno;
447      sh_kill_sub();
448      errno = error;
449      sflag = 1;
450      goto end;
451    }
452
453#ifdef SH_SUB_DBG
454  debug_it("%d stat_sub polling..\n", (int)getpid());
455#endif
456
457  pfds.fd     = child2parent[0];
458  pfds.events = POLLIN;
459
460  do {
461    retval = poll(&pfds, 1, 300 * 1000);
462  } while (retval < 0 && errno == EINTR);
463
464  if (retval <= 0)
465    {
466      int error = errno;
467      sh_kill_sub();
468      errno = (retval == 0) ? ETIMEDOUT : error;
469      sflag = -1;
470      goto end;
471    }
472
473#ifdef SH_SUB_DBG
474  debug_it("%d stat_sub reading..\n", (int)getpid());
475#endif
476
477  retval = sh_sub_read (child2parent[0], &outbuf, sizeof(outbuf));
478  if (retval < 0)
479    {
480      int error = errno;
481      sh_kill_sub();
482      errno = error;
483      sflag = 1;
484      goto end;
485    }
486
487 end:
488  ; /* 'label at end of compound statement' */
489  SH_MUTEX_UNLOCK(mutex_sub_work);
490
491  if      (sflag == 0)
492    {
493#ifdef SH_SUB_DBG
494      debug_it("%d stat_sub done..\n", (int)getpid());
495#endif
496      memcpy(buf, &(outbuf.sbuf), sizeof(struct stat));
497      errno = outbuf.errnum;
498      return outbuf.retval;
499    }
500  else if (sflag == 1)
501    {
502#ifdef SH_SUB_DBG
503      debug_it("%d stat_sub error..\n", (int)getpid());
504#endif
505      /* could not read, thus subprocess may have gone */
506      sflag = 0;
507      goto start;
508    }
509
510  return -1;
511}
512
513int sh_sub_stat (const char *path, struct stat *buf)
514{
515  return sh_sub_stat_int(path, buf, SH_COM_STAT);
516}
517
518int sh_sub_lstat(const char *path, struct stat *buf)
519{
520  return sh_sub_stat_int(path, buf, SH_COM_LSTAT);
521}
522
Note: See TracBrowser for help on using the repository browser.