]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/libntp/work_fork.c
Fix ntp multiple vulnerabilities.
[FreeBSD/releng/10.2.git] / contrib / ntp / libntp / work_fork.c
1 /*
2  * work_fork.c - fork implementation for blocking worker child.
3  */
4 #include <config.h>
5 #include "ntp_workimpl.h"
6
7 #ifdef WORK_FORK
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <signal.h>
11 #include <sys/wait.h>
12
13 #include "iosignal.h"
14 #include "ntp_stdlib.h"
15 #include "ntp_malloc.h"
16 #include "ntp_syslog.h"
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_assert.h"
20 #include "ntp_unixtime.h"
21 #include "ntp_worker.h"
22
23 /* === variables === */
24         int                     worker_process;
25         addremove_io_fd_func    addremove_io_fd;
26 static  volatile int            worker_sighup_received;
27
28 /* === function prototypes === */
29 static  void            fork_blocking_child(blocking_child *);
30 static  RETSIGTYPE      worker_sighup(int);
31 static  void            send_worker_home_atexit(void);
32 static  void            cleanup_after_child(blocking_child *);
33
34 /* === functions === */
35 /*
36  * exit_worker()
37  *
38  * On some systems _exit() is preferred to exit() for forked children.
39  * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
40  * recommends _exit() to avoid double-flushing C runtime stream buffers
41  * and also to avoid calling the parent's atexit() routines in the
42  * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
43  * bypasses CRT cleanup, fflush() files we know might have output
44  * buffered.
45  */
46 void
47 exit_worker(
48         int     exitcode
49         )
50 {
51         if (syslog_file != NULL)
52                 fflush(syslog_file);
53         fflush(stdout);
54         fflush(stderr);
55         WORKER_CHILD_EXIT (exitcode);   /* space before ( required */
56 }
57
58
59 static RETSIGTYPE
60 worker_sighup(
61         int sig
62         )
63 {
64         if (SIGHUP == sig)
65                 worker_sighup_received = 1;
66 }
67
68
69 int
70 worker_sleep(
71         blocking_child *        c,
72         time_t                  seconds
73         )
74 {
75         u_int sleep_remain;
76
77         sleep_remain = (u_int)seconds;
78         do {
79                 if (!worker_sighup_received)
80                         sleep_remain = sleep(sleep_remain);
81                 if (worker_sighup_received) {
82                         TRACE(1, ("worker SIGHUP with %us left to sleep",
83                                   sleep_remain));
84                         worker_sighup_received = 0;
85                         return -1;
86                 }
87         } while (sleep_remain);
88
89         return 0;
90 }
91
92
93 void
94 interrupt_worker_sleep(void)
95 {
96         u_int                   idx;
97         blocking_child *        c;
98         int                     rc;
99
100         for (idx = 0; idx < blocking_children_alloc; idx++) {
101                 c = blocking_children[idx];
102
103                 if (NULL == c || c->reusable == TRUE)
104                         continue;
105
106                 rc = kill(c->pid, SIGHUP);
107                 if (rc < 0)
108                         msyslog(LOG_ERR,
109                                 "Unable to signal HUP to wake child pid %d: %m",
110                                 c->pid);
111         }
112 }
113
114
115 /*
116  * harvest_child_status() runs in the parent.
117  */
118 static void
119 harvest_child_status(
120         blocking_child *        c
121         )
122 {
123         if (c->pid)
124         {
125                 /* Wait on the child so it can finish terminating */
126                 if (waitpid(c->pid, NULL, 0) == c->pid)
127                         TRACE(4, ("harvested child %d\n", c->pid));
128                 else msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
129         }
130 }
131
132 /*
133  * req_child_exit() runs in the parent.
134  */
135 int
136 req_child_exit(
137         blocking_child *        c
138         )
139 {
140         if (-1 != c->req_write_pipe) {
141                 close(c->req_write_pipe);
142                 c->req_write_pipe = -1;
143                 return 0;
144         }
145         /* Closing the pipe forces the child to exit */
146         harvest_child_status(c);
147         return -1;
148 }
149
150
151 /*
152  * cleanup_after_child() runs in parent.
153  */
154 static void
155 cleanup_after_child(
156         blocking_child *        c
157         )
158 {
159         harvest_child_status(c);
160         if (-1 != c->resp_read_pipe) {
161                 (*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
162                 close(c->resp_read_pipe);
163                 c->resp_read_pipe = -1;
164         }
165         c->pid = 0;
166         c->resp_read_ctx = NULL;
167         DEBUG_INSIST(-1 == c->req_read_pipe);
168         DEBUG_INSIST(-1 == c->resp_write_pipe);
169         c->reusable = TRUE;
170 }
171
172
173 static void
174 send_worker_home_atexit(void)
175 {
176         u_int                   idx;
177         blocking_child *        c;
178
179         if (worker_process)
180                 return;
181
182         for (idx = 0; idx < blocking_children_alloc; idx++) {
183                 c = blocking_children[idx];
184                 if (NULL == c)
185                         continue;
186                 req_child_exit(c);
187         }
188 }
189
190
191 int
192 send_blocking_req_internal(
193         blocking_child *        c,
194         blocking_pipe_header *  hdr,
195         void *                  data
196         )
197 {
198         int octets;
199         int rc;
200
201         DEBUG_REQUIRE(hdr != NULL);
202         DEBUG_REQUIRE(data != NULL);
203         DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
204
205         if (-1 == c->req_write_pipe) {
206                 fork_blocking_child(c);
207                 DEBUG_INSIST(-1 != c->req_write_pipe);
208         }
209
210         octets = sizeof(*hdr);
211         rc = write(c->req_write_pipe, hdr, octets);
212
213         if (rc == octets) {
214                 octets = hdr->octets - sizeof(*hdr);
215                 rc = write(c->req_write_pipe, data, octets);
216
217                 if (rc == octets)
218                         return 0;
219         }
220
221         if (rc < 0)
222                 msyslog(LOG_ERR,
223                         "send_blocking_req_internal: pipe write: %m");
224         else
225                 msyslog(LOG_ERR,
226                         "send_blocking_req_internal: short write %d of %d",
227                         rc, octets);
228
229         /* Fatal error.  Clean up the child process.  */
230         req_child_exit(c);
231         exit(1);        /* otherwise would be return -1 */
232 }
233
234
235 blocking_pipe_header *
236 receive_blocking_req_internal(
237         blocking_child *        c
238         )
239 {
240         blocking_pipe_header    hdr;
241         blocking_pipe_header *  req;
242         int                     rc;
243         long                    octets;
244
245         DEBUG_REQUIRE(-1 != c->req_read_pipe);
246
247         req = NULL;
248
249         do {
250                 rc = read(c->req_read_pipe, &hdr, sizeof(hdr));
251         } while (rc < 0 && EINTR == errno);
252
253         if (rc < 0) {
254                 msyslog(LOG_ERR,
255                         "receive_blocking_req_internal: pipe read %m");
256         } else if (0 == rc) {
257                 TRACE(4, ("parent closed request pipe, child %d terminating\n",
258                           c->pid));
259         } else if (rc != sizeof(hdr)) {
260                 msyslog(LOG_ERR,
261                         "receive_blocking_req_internal: short header read %d of %lu",
262                         rc, (u_long)sizeof(hdr));
263         } else {
264                 INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
265                 req = emalloc(hdr.octets);
266                 memcpy(req, &hdr, sizeof(*req));
267                 octets = hdr.octets - sizeof(hdr);
268                 rc = read(c->req_read_pipe, (char *)req + sizeof(*req),
269                           octets);
270
271                 if (rc < 0)
272                         msyslog(LOG_ERR,
273                                 "receive_blocking_req_internal: pipe data read %m");
274                 else if (rc != octets)
275                         msyslog(LOG_ERR,
276                                 "receive_blocking_req_internal: short read %d of %ld",
277                                 rc, octets);
278                 else if (BLOCKING_REQ_MAGIC != req->magic_sig)
279                         msyslog(LOG_ERR,
280                                 "receive_blocking_req_internal: packet header mismatch (0x%x)",
281                                 req->magic_sig);
282                 else
283                         return req;
284         }
285
286         if (req != NULL)
287                 free(req);
288
289         return NULL;
290 }
291
292
293 int
294 send_blocking_resp_internal(
295         blocking_child *        c,
296         blocking_pipe_header *  resp
297         )
298 {
299         long    octets;
300         int     rc;
301
302         DEBUG_REQUIRE(-1 != c->resp_write_pipe);
303
304         octets = resp->octets;
305         rc = write(c->resp_write_pipe, resp, octets);
306         free(resp);
307
308         if (octets == rc)
309                 return 0;
310
311         if (rc < 0)
312                 TRACE(1, ("send_blocking_resp_internal: pipe write %m\n"));
313         else
314                 TRACE(1, ("send_blocking_resp_internal: short write %d of %ld\n",
315                           rc, octets));
316
317         return -1;
318 }
319
320
321 blocking_pipe_header *
322 receive_blocking_resp_internal(
323         blocking_child *        c
324         )
325 {
326         blocking_pipe_header    hdr;
327         blocking_pipe_header *  resp;
328         int                     rc;
329         long                    octets;
330
331         DEBUG_REQUIRE(c->resp_read_pipe != -1);
332
333         resp = NULL;
334         rc = read(c->resp_read_pipe, &hdr, sizeof(hdr));
335
336         if (rc < 0) {
337                 TRACE(1, ("receive_blocking_resp_internal: pipe read %m\n"));
338         } else if (0 == rc) {
339                 /* this is the normal child exited indication */
340         } else if (rc != sizeof(hdr)) {
341                 TRACE(1, ("receive_blocking_resp_internal: short header read %d of %lu\n",
342                           rc, (u_long)sizeof(hdr)));
343         } else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
344                 TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
345                           hdr.magic_sig));
346         } else {
347                 INSIST(sizeof(hdr) < hdr.octets &&
348                        hdr.octets < 16 * 1024);
349                 resp = emalloc(hdr.octets);
350                 memcpy(resp, &hdr, sizeof(*resp));
351                 octets = hdr.octets - sizeof(hdr);
352                 rc = read(c->resp_read_pipe,
353                           (char *)resp + sizeof(*resp),
354                           octets);
355
356                 if (rc < 0)
357                         TRACE(1, ("receive_blocking_resp_internal: pipe data read %m\n"));
358                 else if (rc < octets)
359                         TRACE(1, ("receive_blocking_resp_internal: short read %d of %ld\n",
360                                   rc, octets));
361                 else
362                         return resp;
363         }
364
365         cleanup_after_child(c);
366
367         if (resp != NULL)
368                 free(resp);
369
370         return NULL;
371 }
372
373
374 #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
375 void
376 fork_deferred_worker(void)
377 {
378         u_int                   idx;
379         blocking_child *        c;
380
381         REQUIRE(droproot && root_dropped);
382
383         for (idx = 0; idx < blocking_children_alloc; idx++) {
384                 c = blocking_children[idx];
385                 if (NULL == c)
386                         continue;
387                 if (-1 != c->req_write_pipe && 0 == c->pid)
388                         fork_blocking_child(c);
389         }
390 }
391 #endif
392
393
394 static void
395 fork_blocking_child(
396         blocking_child *        c
397         )
398 {
399         static int      atexit_installed;
400         static int      blocking_pipes[4] = { -1, -1, -1, -1 };
401         int             rc;
402         int             was_pipe;
403         int             is_pipe;
404         int             saved_errno = 0;
405         int             childpid;
406         int             keep_fd;
407         int             fd;
408
409         /*
410          * parent and child communicate via a pair of pipes.
411          * 
412          * 0 child read request
413          * 1 parent write request
414          * 2 parent read response
415          * 3 child write response
416          */
417         if (-1 == c->req_write_pipe) {
418                 rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
419                 if (0 != rc) {
420                         saved_errno = errno;
421                 } else {
422                         rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
423                         if (0 != rc) {
424                                 saved_errno = errno;
425                                 close(blocking_pipes[0]);
426                                 close(blocking_pipes[1]);
427                         } else {
428                                 INSIST(was_pipe == is_pipe);
429                         }
430                 }
431                 if (0 != rc) {
432                         errno = saved_errno;
433                         msyslog(LOG_ERR, "unable to create worker pipes: %m");
434                         exit(1);
435                 }
436
437                 /*
438                  * Move the descriptors the parent will keep open out of the
439                  * low descriptors preferred by C runtime buffered FILE *.
440                  */
441                 c->req_write_pipe = move_fd(blocking_pipes[1]);
442                 c->resp_read_pipe = move_fd(blocking_pipes[2]);
443                 /*
444                  * wake any worker child on orderly shutdown of the
445                  * daemon so that it can notice the broken pipes and
446                  * go away promptly.
447                  */
448                 if (!atexit_installed) {
449                         atexit(&send_worker_home_atexit);
450                         atexit_installed = TRUE;
451                 }
452         }
453
454 #if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
455         /* defer the fork until after root is dropped */
456         if (droproot && !root_dropped)
457                 return;
458 #endif
459         if (syslog_file != NULL)
460                 fflush(syslog_file);
461         fflush(stdout);
462         fflush(stderr);
463
464         signal_no_reset(SIGCHLD, SIG_IGN);
465
466         childpid = fork();
467         if (-1 == childpid) {
468                 msyslog(LOG_ERR, "unable to fork worker: %m");
469                 exit(1);
470         }
471
472         if (childpid) {
473                 /* this is the parent */
474                 TRACE(1, ("forked worker child (pid %d)\n", childpid));
475                 c->pid = childpid;
476                 c->ispipe = is_pipe;
477
478                 /* close the child's pipe descriptors. */
479                 close(blocking_pipes[0]);
480                 close(blocking_pipes[3]);
481
482                 memset(blocking_pipes, -1, sizeof(blocking_pipes));
483
484                 /* wire into I/O loop */
485                 (*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
486
487                 return;         /* parent returns */
488         }
489
490         /*
491          * The parent gets the child pid as the return value of fork().
492          * The child must work for it.
493          */
494         c->pid = getpid();
495         worker_process = TRUE;
496
497         /*
498          * In the child, close all files except stdin, stdout, stderr,
499          * and the two child ends of the pipes.
500          */
501         DEBUG_INSIST(-1 == c->req_read_pipe);
502         DEBUG_INSIST(-1 == c->resp_write_pipe);
503         c->req_read_pipe = blocking_pipes[0];
504         c->resp_write_pipe = blocking_pipes[3];
505
506         kill_asyncio(0);
507         closelog();
508         if (syslog_file != NULL) {
509                 fclose(syslog_file);
510                 syslog_file = NULL;
511                 syslogit = TRUE;
512         }
513         keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
514         for (fd = 3; fd < keep_fd; fd++)
515                 if (fd != c->req_read_pipe && 
516                     fd != c->resp_write_pipe)
517                         close(fd);
518         close_all_beyond(keep_fd);
519         /*
520          * We get signals from refclock serial I/O on NetBSD in the
521          * worker if we do not reset SIGIO's handler to the default.
522          * It is not conditionalized for NetBSD alone because on
523          * systems where it is not needed, it is harmless, and that
524          * allows us to handle unknown others with NetBSD behavior.
525          * [Bug 1386]
526          */
527 #if defined(USE_SIGIO)
528         signal_no_reset(SIGIO, SIG_DFL);
529 #elif defined(USE_SIGPOLL)
530         signal_no_reset(SIGPOLL, SIG_DFL);
531 #endif
532         signal_no_reset(SIGHUP, worker_sighup);
533         init_logging("ntp_intres", 0, FALSE);
534         setup_logfile(NULL);
535
536         /*
537          * And now back to the portable code
538          */
539         exit_worker(blocking_child_common(c));
540 }
541
542
543 void worker_global_lock(int inOrOut)
544 {
545         (void)inOrOut;
546 }
547
548 #else   /* !WORK_FORK follows */
549 char work_fork_nonempty_compilation_unit;
550 #endif