datapath: Avoid pointer arithmetic on possibly-NULL pointer.
[sliver-openvswitch.git] / secchan / executer.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include "executer.h"
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <fnmatch.h>
39 #include <poll.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "dynamic-string.h"
47 #include "fatal-signal.h"
48 #include "openflow/nicira-ext.h"
49 #include "ofpbuf.h"
50 #include "openflow/openflow.h"
51 #include "poll-loop.h"
52 #include "rconn.h"
53 #include "secchan.h"
54 #include "socket-util.h"
55 #include "util.h"
56 #include "vconn.h"
57
58 #define THIS_MODULE VLM_executer
59 #include "vlog.h"
60
61 #define MAX_CHILDREN 8
62
63 struct child {
64     /* Information about child process. */
65     char *name;                 /* argv[0] passed to child. */
66     pid_t pid;                  /* Child's process ID. */
67
68     /* For sending a reply to the controller when the child dies. */
69     struct relay *relay;
70     uint32_t xid;               /* Transaction ID used by controller. */
71
72     /* We read up to MAX_OUTPUT bytes of output and send them back to the
73      * controller when the child dies. */
74 #define MAX_OUTPUT 4096
75     int output_fd;              /* FD from which to read child's output. */
76     uint8_t *output;            /* Output data. */
77     size_t output_size;         /* Number of bytes of output data so far. */
78 };
79
80 struct executer {
81     const struct settings *s;
82
83     /* Children. */
84     struct child children[MAX_CHILDREN];
85     size_t n_children;
86
87     /* File descriptors. */
88     int wait_fd;                /* Pipe FD for wakeup when on SIGCHLD. */
89     int null_fd;                /* FD for /dev/null. */
90 };
91
92 static void send_child_status(struct relay *, uint32_t xid, uint32_t status,
93                               const void *data, size_t size);
94 static void send_child_message(struct relay *, uint32_t xid, uint32_t status,
95                                const char *message);
96
97 /* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
98  * access control list in the format described for --command-acl in
99  * secchan(8). */
100 static bool
101 executer_is_permitted(const char *acl_, const char *cmd)
102 {
103     char *acl, *save_ptr, *pattern;
104     bool allowed, denied;
105
106     /* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
107     if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
108                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
109         VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
110                   "characters", cmd);
111         return false;
112     }
113
114     /* Check 'cmd' against 'acl'. */
115     acl = xstrdup(acl_);
116     save_ptr = acl;
117     allowed = denied = false;
118     while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
119         if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
120             allowed = true;
121         } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
122             denied = true;
123         }
124     }
125     free(acl);
126
127     /* Check the command white/blacklisted state. */
128     if (allowed && !denied) {
129         VLOG_WARN("permitting command execution: \"%s\" is whitelisted", cmd);
130     } else if (allowed && denied) {
131         VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
132                   "and whitelisted", cmd);
133     } else if (!allowed) {
134         VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
135     } else if (denied) {
136         VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
137     }
138     return allowed && !denied;
139 }
140
141 static bool
142 executer_remote_packet_cb(struct relay *r, void *e_)
143 {
144     struct executer *e = e_;
145     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
146     struct nicira_header *request;
147     char **argv;
148     char *args;
149     char *exec_file = NULL;
150     int max_fds;
151     struct stat s;
152     size_t args_size;
153     size_t argc;
154     size_t i;
155     pid_t pid;
156     int output_fds[2];
157
158     /* Check for NXT_COMMAND_REQUEST vendor extension. */
159     if (msg->size < sizeof(struct nicira_header)) {
160         return false;
161     }
162     request = msg->data;
163     if (request->header.type != OFPT_VENDOR
164         || request->vendor != htonl(NX_VENDOR_ID)
165         || request->subtype != htonl(NXT_COMMAND_REQUEST)) {
166         return false;
167     }
168
169     /* Verify limit on children not exceeded.
170      * XXX should probably kill children when the connection drops? */
171     if (e->n_children >= MAX_CHILDREN) {
172         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
173                            "too many child processes");
174         VLOG_WARN("limit of %d child processes reached, dropping request",
175                   MAX_CHILDREN);
176         return false;
177     }
178
179     /* Copy argument buffer, adding a null terminator at the end.  Now every
180      * argument is null-terminated, instead of being merely null-delimited. */
181     args_size = msg->size - sizeof *request;
182     args = xmemdup0((const void *) (request + 1), args_size);
183
184     /* Count arguments. */
185     argc = 0;
186     for (i = 0; i <= args_size; i++) {
187         argc += args[i] == '\0';
188     }
189
190     /* Set argv[*] to point to each argument. */
191     argv = xmalloc((argc + 1) * sizeof *argv);
192     argv[0] = args;
193     for (i = 1; i < argc; i++) {
194         argv[i] = strchr(argv[i - 1], '\0') + 1;
195     }
196     argv[argc] = NULL;
197
198     /* Check permissions. */
199     if (!executer_is_permitted(e->s->command_acl, argv[0])) {
200         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
201                            "command not allowed");
202         goto done;
203     }
204
205     /* Find the executable. */
206     exec_file = xasprintf("%s/%s", e->s->command_dir, argv[0]);
207     if (stat(exec_file, &s)) {
208         VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
209         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
210                            "command not allowed");
211         goto done;
212     }
213     if (!S_ISREG(s.st_mode)) {
214         VLOG_WARN("\"%s\" is not a regular file", exec_file);
215         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
216                            "command not allowed");
217         goto done;
218     }
219     argv[0] = exec_file;
220
221     /* Arrange to capture output. */
222     if (pipe(output_fds)) {
223         VLOG_WARN("pipe failed: %s", strerror(errno));
224         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
225                            "internal error (pipe)");
226         goto done;
227     }
228
229     pid = fork();
230     if (!pid) {
231         /* Running in child.
232          * XXX should run in new process group so that we can signal all
233          * subprocesses at once?  Would also want to catch fatal signals and
234          * kill them at the same time though. */
235         fatal_signal_fork();
236         dup2(e->null_fd, 0);
237         dup2(output_fds[1], 1);
238         dup2(e->null_fd, 2);
239         max_fds = get_max_fds();
240         for (i = 3; i < max_fds; i++) {
241             close(i);
242         }
243         if (chdir(e->s->command_dir)) {
244             printf("could not change directory to \"%s\": %s",
245                    e->s->command_dir, strerror(errno));
246             exit(EXIT_FAILURE);
247         }
248         execv(argv[0], argv);
249         printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
250         exit(EXIT_FAILURE);
251     } else if (pid > 0) {
252         /* Running in parent. */
253         struct child *child;
254
255         VLOG_WARN("started \"%s\" subprocess", argv[0]);
256         send_child_status(r, request->header.xid, NXT_STATUS_STARTED, NULL, 0);
257         child = &e->children[e->n_children++];
258         child->name = xstrdup(argv[0]);
259         child->pid = pid;
260         child->relay = r;
261         child->xid = request->header.xid;
262         child->output_fd = output_fds[0];
263         child->output = xmalloc(MAX_OUTPUT);
264         child->output_size = 0;
265         set_nonblocking(output_fds[0]);
266         close(output_fds[1]);
267     } else {
268         VLOG_WARN("fork failed: %s", strerror(errno));
269         send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
270                            "internal error (fork)");
271         close(output_fds[0]);
272         close(output_fds[1]);
273     }
274
275 done:
276     free(exec_file);
277     free(args);
278     free(argv);
279     return true;
280 }
281
282 static void
283 send_child_status(struct relay *relay, uint32_t xid, uint32_t status,
284                   const void *data, size_t size)
285 {
286     if (relay) {
287         struct nx_command_reply *r;
288         struct ofpbuf *buffer;
289
290         r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
291         r->nxh.vendor = htonl(NX_VENDOR_ID);
292         r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
293         r->status = htonl(status);
294         ofpbuf_put(buffer, data, size);
295         update_openflow_length(buffer);
296         if (rconn_send(relay->halves[HALF_REMOTE].rconn, buffer, NULL)) {
297             ofpbuf_delete(buffer);
298         }
299     }
300 }
301
302 static void
303 send_child_message(struct relay *relay, uint32_t xid, uint32_t status,
304                    const char *message)
305 {
306     send_child_status(relay, xid, status, message, strlen(message));
307 }
308
309 /* 'child' died with 'status' as its return code.  Deal with it. */
310 static void
311 child_terminated(struct child *child, int status)
312 {
313     struct ds ds;
314     uint32_t ofp_status;
315
316     /* Log how it terminated. */
317     ds_init(&ds);
318     if (WIFEXITED(status)) {
319         ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
320     } else if (WIFSIGNALED(status)) {
321         const char *name = NULL;
322 #ifdef HAVE_STRSIGNAL
323         name = strsignal(WTERMSIG(status));
324 #endif
325         ds_put_format(&ds, "by signal %d", WTERMSIG(status));
326         if (name) {
327             ds_put_format(&ds, " (%s)", name);
328         }
329     }
330     if (WCOREDUMP(status)) {
331         ds_put_cstr(&ds, " (core dumped)");
332     }
333     VLOG_WARN("child process \"%s\" with pid %ld terminated %s",
334               child->name, (long int) child->pid, ds_cstr(&ds));
335     ds_destroy(&ds);
336
337     /* Send a status message back to the controller that requested the
338      * command. */
339     if (WIFEXITED(status)) {
340         ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
341     } else if (WIFSIGNALED(status)) {
342         ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
343     } else {
344         ofp_status = NXT_STATUS_UNKNOWN;
345     }
346     if (WCOREDUMP(status)) {
347         ofp_status |= NXT_STATUS_COREDUMP;
348     }
349     send_child_status(child->relay, child->xid, ofp_status,
350                       child->output, child->output_size);
351 }
352
353 /* Read output from 'child' and append it to its output buffer. */
354 static void
355 poll_child(struct child *child)
356 {
357     ssize_t n;
358
359     if (child->output_fd < 0) {
360         return;
361     }
362
363     do {
364         n = read(child->output_fd, child->output + child->output_size,
365                  MAX_OUTPUT - child->output_size);
366     } while (n < 0 && errno == EINTR);
367     if (n > 0) {
368         child->output_size += n;
369         if (child->output_size < MAX_OUTPUT) {
370             return;
371         }
372     } else if (n < 0 && errno == EAGAIN) {
373         return;
374     }
375     close(child->output_fd);
376     child->output_fd = -1;
377 }
378
379 static void
380 executer_periodic_cb(void *e_)
381 {
382     struct executer *e = e_;
383     char buffer[MAX_CHILDREN];
384     size_t i;
385
386     if (!e->n_children) {
387         return;
388     }
389
390     /* Read output from children. */
391     for (i = 0; i < e->n_children; i++) {
392         struct child *child = &e->children[i];
393         poll_child(child);
394     }
395
396     /* If SIGCHLD was received, reap dead children. */
397     if (read(e->wait_fd, buffer, sizeof buffer) <= 0) {
398         return;
399     }
400     for (;;) {
401         int status;
402         pid_t pid;
403
404         /* Get dead child in 'pid' and its return code in 'status'. */
405         pid = waitpid(WAIT_ANY, &status, WNOHANG);
406         if (pid < 0 && errno == EINTR) {
407             continue;
408         } else if (pid <= 0) {
409             return;
410         }
411
412         /* Find child with given 'pid' and drop it from the list. */
413         for (i = 0; i < e->n_children; i++) {
414             struct child *child = &e->children[i];
415             if (child->pid == pid) {
416                 poll_child(child);
417                 child_terminated(child, status);
418                 free(child->name);
419                 free(child->output);
420                 *child = e->children[--e->n_children];
421                 goto found;
422             }
423         }
424         VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
425     found:;
426     }
427
428 }
429
430 static void
431 executer_wait_cb(void *e_)
432 {
433     struct executer *e = e_;
434     if (e->n_children) {
435         size_t i;
436
437         /* Wake up on SIGCHLD. */
438         poll_fd_wait(e->wait_fd, POLLIN);
439
440         /* Wake up when we get output from a child. */
441         for (i = 0; i < e->n_children; i++) {
442             struct child *child = &e->children[i];
443             if (child->output_fd >= 0) {
444                 poll_fd_wait(e->wait_fd, POLLIN);
445             }
446         }
447     }
448 }
449
450 static void
451 executer_closing_cb(struct relay *r, void *e_)
452 {
453     struct executer *e = e_;
454     size_t i;
455
456     /* If any of our children was connected to 'r', then disconnect it so we
457      * don't try to reference a dead connection when the process terminates
458      * later.
459      * XXX kill the children started by 'r'? */
460     for (i = 0; i < e->n_children; i++) {
461         if (e->children[i].relay == r) {
462             e->children[i].relay = NULL;
463         }
464     }
465 }
466
467 static int child_fd;
468
469 static void
470 sigchld_handler(int signr UNUSED)
471 {
472     write(child_fd, "", 1);
473 }
474
475 static struct hook_class executer_hook_class = {
476     NULL,                       /* local_packet_cb */
477     executer_remote_packet_cb,  /* remote_packet_cb */
478     executer_periodic_cb,       /* periodic_cb */
479     executer_wait_cb,           /* wait_cb */
480     executer_closing_cb,        /* closing_cb */
481 };
482
483 void
484 executer_start(struct secchan *secchan, const struct settings *settings)
485 {
486     struct executer *e;
487     struct sigaction sa;
488     int fds[2], null_fd;
489
490     /* Create pipe for notifying us that SIGCHLD was invoked. */
491     if (pipe(fds)) {
492         ofp_fatal(errno, "pipe failed");
493     }
494     set_nonblocking(fds[0]);
495     set_nonblocking(fds[1]);
496     child_fd = fds[1];
497
498     /* Open /dev/null. */
499     null_fd = open("/dev/null", O_RDWR);
500     if (null_fd < 0) {
501         ofp_fatal(errno, "could not open /dev/null");
502     }
503
504     /* Set up signal handler. */
505     memset(&sa, 0, sizeof sa);
506     sa.sa_handler = sigchld_handler;
507     sigemptyset(&sa.sa_mask);
508     sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
509     if (sigaction(SIGCHLD, &sa, NULL)) {
510         ofp_fatal(errno, "sigaction(SIGCHLD) failed");
511     }
512
513     /* Add hook. */
514     e = xcalloc(1, sizeof *e);
515     e->s = settings;
516     e->n_children = 0;
517     e->wait_fd = fds[0];
518     e->null_fd = null_fd;
519     add_hook(secchan, &executer_hook_class, e);
520 }