]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/libntp/ntp_worker.c
Fix ntp multiple vulnerabilities.
[FreeBSD/releng/10.2.git] / contrib / ntp / libntp / ntp_worker.c
1 /*
2  * ntp_worker.c
3  */
4 #include <config.h>
5 #include "ntp_workimpl.h"
6
7 #ifdef WORKER
8
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <signal.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 "intreswork.h"
22
23
24 #define CHILD_MAX_IDLE  (3 * 60)        /* seconds, idle worker limit */
25
26 blocking_child **       blocking_children;
27 size_t                  blocking_children_alloc;
28 int                     worker_per_query;       /* boolean */
29 int                     intres_req_pending;
30 volatile u_int          blocking_child_ready_seen;
31 volatile u_int          blocking_child_ready_done;
32
33
34 #ifndef HAVE_IO_COMPLETION_PORT
35 /*
36  * pipe_socketpair()
37  *
38  * Provides an AF_UNIX socketpair on systems which have them, otherwise
39  * pair of unidirectional pipes.
40  */
41 int
42 pipe_socketpair(
43         int     caller_fds[2],
44         int *   is_pipe
45         )
46 {
47         int     rc;
48         int     fds[2];
49         int     called_pipe;
50
51 #ifdef HAVE_SOCKETPAIR
52         rc = socketpair(AF_UNIX, SOCK_STREAM, 0, &fds[0]);
53 #else
54         rc = -1;
55 #endif
56
57         if (-1 == rc) {
58                 rc = pipe(&fds[0]);
59                 called_pipe = TRUE;
60         } else {
61                 called_pipe = FALSE;
62         }
63
64         if (-1 == rc)
65                 return rc;
66
67         caller_fds[0] = fds[0];
68         caller_fds[1] = fds[1];
69         if (is_pipe != NULL)
70                 *is_pipe = called_pipe;
71
72         return 0;
73 }
74
75
76 /*
77  * close_all_except()
78  *
79  * Close all file descriptors except the given keep_fd.
80  */
81 void
82 close_all_except(
83         int keep_fd
84         )
85 {
86         int fd;
87
88         for (fd = 0; fd < keep_fd; fd++)
89                 close(fd);
90
91         close_all_beyond(keep_fd);
92 }
93
94
95 /*
96  * close_all_beyond()
97  *
98  * Close all file descriptors after the given keep_fd, which is the
99  * highest fd to keep open.
100  */
101 void
102 close_all_beyond(
103         int keep_fd
104         )
105 {
106 # ifdef HAVE_CLOSEFROM
107         closefrom(keep_fd + 1);
108 # elif defined(F_CLOSEM)
109         /*
110          * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
111          * by Eric Agar (saves us from doing 32767 system
112          * calls)
113          */
114         if (fcntl(keep_fd + 1, F_CLOSEM, 0) == -1)
115                 msyslog(LOG_ERR, "F_CLOSEM(%d): %m", keep_fd + 1);
116 # else  /* !HAVE_CLOSEFROM && !F_CLOSEM follows */
117         int fd;
118         int max_fd;
119
120         max_fd = GETDTABLESIZE();
121         for (fd = keep_fd + 1; fd < max_fd; fd++)
122                 close(fd);
123 # endif /* !HAVE_CLOSEFROM && !F_CLOSEM */
124 }
125 #endif  /* HAVE_IO_COMPLETION_PORT */
126
127
128 u_int
129 available_blocking_child_slot(void)
130 {
131         const size_t    each = sizeof(blocking_children[0]);
132         u_int           slot;
133         size_t          prev_alloc;
134         size_t          new_alloc;
135         size_t          prev_octets;
136         size_t          octets;
137
138         for (slot = 0; slot < blocking_children_alloc; slot++) {
139                 if (NULL == blocking_children[slot])
140                         return slot;
141                 if (blocking_children[slot]->reusable) {
142                         blocking_children[slot]->reusable = FALSE;
143                         return slot;
144                 }
145         }
146
147         prev_alloc = blocking_children_alloc;
148         prev_octets = prev_alloc * each;
149         new_alloc = blocking_children_alloc + 4;
150         octets = new_alloc * each;
151         blocking_children = erealloc_zero(blocking_children, octets,
152                                           prev_octets);
153         blocking_children_alloc = new_alloc;
154
155         /* assume we'll never have enough workers to overflow u_int */
156         return (u_int)prev_alloc;
157 }
158
159
160 int
161 queue_blocking_request(
162         blocking_work_req       rtype,
163         void *                  req,
164         size_t                  reqsize,
165         blocking_work_callback  done_func,
166         void *                  context
167         )
168 {
169         static u_int            intres_slot = UINT_MAX;
170         u_int                   child_slot;
171         blocking_child *        c;
172         blocking_pipe_header    req_hdr;
173
174         req_hdr.octets = sizeof(req_hdr) + reqsize;
175         req_hdr.magic_sig = BLOCKING_REQ_MAGIC;
176         req_hdr.rtype = rtype;
177         req_hdr.done_func = done_func;
178         req_hdr.context = context;
179
180         child_slot = UINT_MAX;
181         if (worker_per_query || UINT_MAX == intres_slot ||
182             blocking_children[intres_slot]->reusable)
183                 child_slot = available_blocking_child_slot();
184         if (!worker_per_query) {
185                 if (UINT_MAX == intres_slot)
186                         intres_slot = child_slot;
187                 else
188                         child_slot = intres_slot;
189                 if (0 == intres_req_pending)
190                         intres_timeout_req(0);
191         }
192         intres_req_pending++;
193         INSIST(UINT_MAX != child_slot);
194         c = blocking_children[child_slot];
195         if (NULL == c) {
196                 c = emalloc_zero(sizeof(*c));
197 #ifdef WORK_FORK
198                 c->req_read_pipe = -1;
199                 c->req_write_pipe = -1;
200 #endif
201 #ifdef WORK_PIPE
202                 c->resp_read_pipe = -1;
203                 c->resp_write_pipe = -1;
204 #endif
205                 blocking_children[child_slot] = c;
206         }
207         req_hdr.child_idx = child_slot;
208
209         return send_blocking_req_internal(c, &req_hdr, req);
210 }
211
212
213 int queue_blocking_response(
214         blocking_child *                c,
215         blocking_pipe_header *          resp,
216         size_t                          respsize,
217         const blocking_pipe_header *    req
218         )
219 {
220         resp->octets = respsize;
221         resp->magic_sig = BLOCKING_RESP_MAGIC;
222         resp->rtype = req->rtype;
223         resp->context = req->context;
224         resp->done_func = req->done_func;
225
226         return send_blocking_resp_internal(c, resp);
227 }
228
229
230 void
231 process_blocking_resp(
232         blocking_child *        c
233         )
234 {
235         blocking_pipe_header *  resp;
236         void *                  data;
237
238         /*
239          * On Windows send_blocking_resp_internal() may signal the
240          * blocking_response_ready event multiple times while we're
241          * processing a response, so always consume all available
242          * responses before returning to test the event again.
243          */
244 #ifdef WORK_THREAD
245         do {
246 #endif
247                 resp = receive_blocking_resp_internal(c);
248                 if (NULL != resp) {
249                         DEBUG_REQUIRE(BLOCKING_RESP_MAGIC ==
250                                       resp->magic_sig);
251                         data = (char *)resp + sizeof(*resp);
252                         intres_req_pending--;
253                         (*resp->done_func)(resp->rtype, resp->context,
254                                            resp->octets - sizeof(*resp),
255                                            data);
256                         free(resp);
257                 }
258 #ifdef WORK_THREAD
259         } while (NULL != resp);
260 #endif
261         if (!worker_per_query && 0 == intres_req_pending)
262                 intres_timeout_req(CHILD_MAX_IDLE);
263         else if (worker_per_query)
264                 req_child_exit(c);
265 }
266
267 void
268 harvest_blocking_responses(void)
269 {
270         size_t          idx;
271         blocking_child* cp;
272         u_int           scseen, scdone;
273
274         scseen = blocking_child_ready_seen;
275         scdone = blocking_child_ready_done;
276         if (scdone != scseen) {
277                 blocking_child_ready_done = scseen;
278                 for (idx = 0; idx < blocking_children_alloc; idx++) {
279                         cp = blocking_children[idx];
280                         if (NULL == cp)
281                                 continue;
282                         scseen = cp->resp_ready_seen;
283                         scdone = cp->resp_ready_done;
284                         if (scdone != scseen) {
285                                 cp->resp_ready_done = scseen;
286                                 process_blocking_resp(cp);
287                         }
288                 }
289         }
290 }
291
292
293 /*
294  * blocking_child_common runs as a forked child or a thread
295  */
296 int
297 blocking_child_common(
298         blocking_child  *c
299         )
300 {
301         int say_bye;
302         blocking_pipe_header *req;
303
304         say_bye = FALSE;
305         while (!say_bye) {
306                 req = receive_blocking_req_internal(c);
307                 if (NULL == req) {
308                         say_bye = TRUE;
309                         continue;
310                 }
311
312                 DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == req->magic_sig);
313
314                 switch (req->rtype) {
315                 case BLOCKING_GETADDRINFO:
316                         if (blocking_getaddrinfo(c, req))
317                                 say_bye = TRUE;
318                         break;
319
320                 case BLOCKING_GETNAMEINFO:
321                         if (blocking_getnameinfo(c, req))
322                                 say_bye = TRUE;
323                         break;
324
325                 default:
326                         msyslog(LOG_ERR, "unknown req %d to blocking worker", req->rtype);
327                         say_bye = TRUE;
328                 }
329
330                 free(req);
331         }
332
333         return 0;
334 }
335
336
337 /*
338  * worker_idle_timer_fired()
339  *
340  * The parent starts this timer when the last pending response has been
341  * received from the child, making it idle, and clears the timer when a
342  * request is dispatched to the child.  Once the timer expires, the
343  * child is sent packing.
344  *
345  * This is called when worker_idle_timer is nonzero and less than or
346  * equal to current_time.
347  */
348 void
349 worker_idle_timer_fired(void)
350 {
351         u_int                   idx;
352         blocking_child *        c;
353
354         DEBUG_REQUIRE(0 == intres_req_pending);
355
356         intres_timeout_req(0);
357         for (idx = 0; idx < blocking_children_alloc; idx++) {
358                 c = blocking_children[idx];
359                 if (NULL == c)
360                         continue;
361                 req_child_exit(c);
362         }
363 }
364
365
366 #else   /* !WORKER follows */
367 int ntp_worker_nonempty_compilation_unit;
368 #endif