source: trunk/src/sh_sub.c@ 555

Last change on this file since 555 was 491, checked in by katerina, 9 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.