source: trunk/src/sh_sub.c@ 481

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