source: web/old/remctl-2.14/server/commands.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 19.8 KB
Line 
1/*
2 * Running commands.
3 *
4 * These are the functions for running external commands under remctld and
5 * calling the appropriate protocol functions to deal with the output.
6 *
7 * Written by Russ Allbery <rra@stanford.edu>
8 * Based on work by Anton Ushakov
9 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
10 *     Board of Trustees, Leland Stanford Jr. University
11 *
12 * See LICENSE for licensing terms.
13 */
14
15#include <config.h>
16#include <portable/system.h>
17#include <portable/uio.h>
18
19#include <errno.h>
20#include <fcntl.h>
21#ifdef HAVE_SYS_SELECT_H
22# include <sys/select.h>
23#endif
24#include <sys/time.h>
25#include <sys/wait.h>
26
27#include <server/internal.h>
28#include <util/util.h>
29
30/* Data structure used to hold details about a running process. */
31struct process {
32    bool reaped;                /* Whether we've reaped the process. */
33    int fds[2];                 /* Array of file descriptors for output. */
34    int stdin_fd;               /* File descriptor for standard input. */
35    struct iovec *input;        /* Data to pass on standard input. */
36    pid_t pid;                  /* Process ID of child. */
37    int status;                 /* Exit status. */
38};
39
40
41/*
42 * Processes the input to and output from an external program.  Takes the
43 * client struct and a struct representing the running process.  Feeds input
44 * data to the process on standard input and reads from all the streams as
45 * output is available, stopping when they all reach EOF.
46 *
47 * For protocol v2 and higher, we can send the output immediately as we get
48 * it.  For protocol v1, we instead accumulate the output in the buffer stored
49 * in our client struct, and will send it out later in conjunction with the
50 * exit status.
51 *
52 * Returns true on success, false on failure.
53 */
54static int
55server_process_output(struct client *client, struct process *process)
56{
57    char junk[BUFSIZ];
58    char *p;
59    size_t offset = 0;
60    size_t left = MAXBUFFER;
61    ssize_t status[2], instatus;
62    int i, maxfd, fd, result;
63    fd_set readfds, writefds;
64    struct timeval timeout;
65
66    /* If we haven't allocated an output buffer, do so now. */
67    if (client->output == NULL)
68        client->output = xmalloc(MAXBUFFER);
69    p = client->output;
70
71    /*
72     * Initialize read status for standard output and standard error and write
73     * status for standard input to the process.  Non-zero says that we keep
74     * trying to read or write.
75     */
76    status[0] = -1;
77    status[1] = -1;
78    instatus = (process->input != NULL ? -1 : 0);
79
80    /*
81     * Now, loop while we have input.  We no longer have input if the return
82     * status of read is 0 on all file descriptors.  At that point, we break
83     * out of the loop.
84     *
85     * Exceptionally, however, we want to catch the case where our child
86     * process ran some other command that didn't close its inherited standard
87     * output and error and then exited itself.  This is not uncommon with
88     * init scripts that start poorly-written daemons.  Once our child process
89     * is finished, we're done, even if standard output and error from the
90     * child process aren't closed yet.  To catch this case, call waitpid with
91     * the WNOHANG flag each time through the select loop and decide we're
92     * done as soon as our child has exited.
93     *
94     * Meanwhile, if we have input data, then as long as we've not gotten an
95     * EPIPE error from sending input data to the process we keep writing
96     * input data as select indicates the process can receive it.  However, we
97     * don't care if we've sent all input data before the process says it's
98     * done and exits.
99     */
100    while (!process->reaped) {
101        FD_ZERO(&readfds);
102        maxfd = -1;
103        for (i = 0; i < 2; i++) {
104            if (status[i] != 0) {
105                if (process->fds[i] > maxfd)
106                    maxfd = process->fds[i];
107                FD_SET(process->fds[i], &readfds);
108            }
109        }
110        if (instatus != 0) {
111            FD_ZERO(&writefds);
112            if (process->stdin_fd > maxfd)
113                maxfd = process->stdin_fd;
114            FD_SET(process->stdin_fd, &writefds);
115        }
116        if (maxfd == -1)
117            break;
118
119        /*
120         * We want to wait until either our child exits or until we get data
121         * on its output file descriptors.  Normally, the SIGCHLD signal from
122         * the child exiting would break us out of our select loop.  However,
123         * the child could exit between the waitpid call and the select call,
124         * in which case select could block forever since there's nothing to
125         * wake it up.
126         *
127         * The POSIX-correct way of doing this is to block SIGCHLD and then
128         * use pselect instead of select with a signal mask that allows
129         * SIGCHLD.  This allows SIGCHLD from the exiting child process to
130         * reliably interrupt pselect without race conditions from the child
131         * exiting before pselect is called.
132         *
133         * Unfortunately, Linux didn't implement a proper pselect until 2.6.16
134         * and the glibc wrapper that emulates it leaves us open to exactly
135         * the race condition we're trying to avoid.  This unfortunately
136         * leaves us with no choice but to set a timeout and wake up every
137         * five seconds to see if our child died.  (The wait time is arbitrary
138         * but makes the test suite less annoying.)
139         *
140         * If we see that the child has already exited, do one final poll of
141         * our output file descriptors and then call the command finished.
142         */
143        timeout.tv_sec = 5;
144        timeout.tv_usec = 0;
145        if (waitpid(process->pid, &process->status, WNOHANG) > 0) {
146            process->reaped = true;
147            timeout.tv_sec = 0;
148        }
149        if (instatus != 0)
150            result = select(maxfd + 1, &readfds, &writefds, NULL, &timeout);
151        else
152            result = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
153        if (result < 0 && errno != EINTR) {
154            syswarn("select failed");
155            server_send_error(client, ERROR_INTERNAL, "Internal failure");
156            goto fail;
157        }
158
159        /*
160         * If we can still write and our child selected for writing, send as
161         * much data as we can.
162         */
163        if (instatus != 0 && FD_ISSET(process->stdin_fd, &writefds)) {
164            instatus = write(process->stdin_fd,
165                             (char *) process->input->iov_base + offset,
166                             process->input->iov_len - offset);
167            if (instatus < 0) {
168                if (errno == EPIPE)
169                    instatus = 0;
170                else if (errno != EINTR && errno != EAGAIN) {
171                    syswarn("write failed");
172                    server_send_error(client, ERROR_INTERNAL,
173                                      "Internal failure");
174                    goto fail;
175                }
176            }
177            offset += instatus;
178            if (offset >= process->input->iov_len) {
179                close(process->stdin_fd);
180                instatus = 0;
181            }
182        }
183
184        /*
185         * Iterate through each set file descriptor and read its output.  If
186         * we're using protocol version one, we append all the output together
187         * into the buffer.  Otherwise, we send an output token for each bit
188         * of output as we see it.
189         */
190        for (i = 0; i < 2; i++) {
191            fd = process->fds[i];
192            if (!FD_ISSET(fd, &readfds))
193                continue;
194            if (client->protocol == 1) {
195                if (left > 0) {
196                    status[i] = read(fd, p, left);
197                    if (status[i] < 0 && (errno != EINTR && errno != EAGAIN))
198                        goto readfail;
199                    else if (status[i] > 0) {
200                        p += status[i];
201                        left -= status[i];
202                    }
203                } else {
204                    status[i] = read(fd, junk, sizeof(junk));
205                    if (status[i] < 0 && (errno != EINTR && errno != EAGAIN))
206                        goto readfail;
207                }
208            } else {
209                status[i] = read(fd, client->output, MAXBUFFER);
210                if (status[i] < 0 && (errno != EINTR && errno != EAGAIN))
211                    goto readfail;
212                if (status[i] > 0) {
213                    client->outlen = status[i];
214                    if (!server_v2_send_output(client, i + 1))
215                        goto fail;
216                }
217            }
218        }
219    }
220    if (client->protocol == 1)
221        client->outlen = p - client->output;
222    return 1;
223
224readfail:
225    syswarn("read failed");
226    server_send_error(client, ERROR_INTERNAL, "Internal failure");
227fail:
228    return 0;
229}
230
231
232/*
233 * Process an incoming command.  Check the configuration files and the ACL
234 * file, and if appropriate, forks off the command.  Takes the argument vector
235 * and the user principal, and a buffer into which to put the output from the
236 * executable or any error message.  Returns 0 on success and a negative
237 * integer on failure.
238 *
239 * Using the command and the subcommand, the following argument, a lookup in
240 * the conf data structure is done to find the command executable and acl
241 * file.  If the conf file, and subsequently the conf data structure contains
242 * an entry for this command with subcommand equal to "ALL", that is a
243 * wildcard match for any given subcommand.  The first argument is then
244 * replaced with the actual program name to be executed.
245 *
246 * After checking the acl permissions, the process forks and the child execv's
247 * the command with pipes arranged to gather output. The parent waits for the
248 * return code and gathers stdout and stderr pipes.
249 */
250void
251server_run_command(struct client *client, struct config *config,
252                   struct iovec **argv)
253{
254    char *program;
255    char *path = NULL;
256    char *command = NULL;
257    char *subcommand = NULL;
258    struct confline *cline = NULL;
259    int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
260    char **req_argv = NULL;
261    size_t count, i, j, stdin_arg;
262    bool ok;
263    int fd;
264    struct process process = { 0, { 0, 0 }, 0, NULL, -1, 0 };
265    const char *user = client->user;
266
267    /*
268     * We need at least one argument.  This is also rejected earlier when
269     * parsing the command and checking argc, but may as well be sure.
270     */
271    if (argv[0] == NULL) {
272        notice("empty command from user %s", user);
273        server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token");
274        goto done;
275    }
276
277    /*
278     * Neither the command nor the subcommand may ever contain nuls.
279     * Arguments may only contain nuls if they're the argument being passed on
280     * standard input.
281     */
282    for (i = 0; argv[i] != NULL && i < 2; i++) {
283        if (memchr(argv[i]->iov_base, '\0', argv[i]->iov_len)) {
284            notice("%s from user %s contains nul octet",
285                   (i == 0) ? "command" : "subcommand", user);
286            server_send_error(client, ERROR_BAD_COMMAND,
287                              "Invalid command token");
288            goto done;
289        }
290    }
291
292    /* We need the command and subcommand as nul-terminated strings. */
293    command = xstrndup(argv[0]->iov_base, argv[0]->iov_len);
294    if (argv[1] != NULL)
295        subcommand = xstrndup(argv[1]->iov_base, argv[1]->iov_len);
296
297    /*
298     * Look up the command and the ACL file from the conf file structure in
299     * memory.  Commands with no subcommand argument will only match lines
300     * with the ALL wildcard.
301     */
302    for (i = 0; i < config->count; i++) {
303        cline = config->rules[i];
304        if (strcmp(cline->command, command) == 0) {
305            if (strcmp(cline->subcommand, "ALL") == 0
306                || (subcommand != NULL
307                    && strcmp(cline->subcommand, subcommand) == 0)) {
308                path = cline->program;
309                break;
310            }
311        }
312    }
313
314    /*
315     * Arguments may only contain nuls if they're the argument being passed on
316     * standard input.
317     */
318    for (i = 1; argv[i] != NULL; i++) {
319        if ((long) i == cline->stdin_arg)
320            continue;
321        if (argv[i + 1] == NULL && cline->stdin_arg == -1)
322            continue;
323        if (memchr(argv[i]->iov_base, '\0', argv[i]->iov_len)) {
324            notice("argument %d from user %s contains nul octet", i, user);
325            server_send_error(client, ERROR_BAD_COMMAND,
326                              "Invalid command token");
327            goto done;
328        }
329    }
330
331    /* Log after we look for command so we can get potentially get logmask. */
332    server_log_command(argv, path == NULL ? NULL : cline, user);
333
334    /*
335     * Check the command, aclfile, and the authorization of this client to
336     * run this command.
337     */
338    if (path == NULL) {
339        notice("unknown command %s%s%s from user %s", command,
340               (subcommand == NULL) ? "" : " ",
341               (subcommand == NULL) ? "" : subcommand, user);
342        server_send_error(client, ERROR_UNKNOWN_COMMAND, "Unknown command");
343        goto done;
344    }
345    if (!server_config_acl_permit(cline, user)) {
346        notice("access denied: user %s, command %s%s%s", user, command,
347               (subcommand == NULL) ? "" : " ",
348               (subcommand == NULL) ? "" : subcommand);
349        server_send_error(client, ERROR_ACCESS, "Access denied");
350        goto done;
351    }
352
353    /* Get ready to assemble the argv of the command. */
354    for (count = 0; argv[count] != NULL; count++)
355        ;
356    req_argv = xmalloc((count + 1) * sizeof(char *));
357
358    /*
359     * Get the real program name, and use it as the first argument in argv
360     * passed to the command.  Then build the rest of the argv for the
361     * command, splicing out the argument we're passing on stdin (if any).
362     */
363    program = strrchr(path, '/');
364    if (program == NULL)
365        program = path;
366    else
367        program++;
368    req_argv[0] = program;
369    if (cline->stdin_arg == -1)
370        stdin_arg = count - 1;
371    else
372        stdin_arg = (size_t) cline->stdin_arg;
373    for (i = 1, j = 1; i < count; i++) {
374        if (i == stdin_arg) {
375            process.input = argv[i];
376            continue;
377        }
378        if (argv[i]->iov_len == 0)
379            req_argv[j] = xstrdup("");
380        else
381            req_argv[j] = xstrndup(argv[i]->iov_base, argv[i]->iov_len);
382        j++;
383    }
384    req_argv[j] = NULL;
385
386    /*
387     * These pipes are used for communication with the child process that
388     * actually runs the command.
389     */
390    if (pipe(stdout_pipe) != 0 || pipe(stderr_pipe) != 0) {
391        syswarn("cannot create pipes");
392        server_send_error(client, ERROR_INTERNAL, "Internal failure");
393        goto done;
394    }
395    if (process.input != NULL && pipe(stdin_pipe) != 0) {
396        syswarn("cannot create stdin pipe");
397        server_send_error(client, ERROR_INTERNAL, "Internal failure");
398        goto done;
399    }
400
401    /*
402     * Flush output before forking, mostly in case -S was given and we've
403     * therefore been writing log messages to standard output that may not
404     * have been flushed yet.
405     */
406    fflush(stdout);
407    process.pid = fork();
408    switch (process.pid) {
409    case -1:
410        syswarn("cannot fork");
411        server_send_error(client, ERROR_INTERNAL, "Internal failure");
412        goto done;
413
414    /* In the child. */
415    case 0:
416        dup2(stdout_pipe[1], 1);
417        close(stdout_pipe[0]);
418        close(stdout_pipe[1]);
419        dup2(stderr_pipe[1], 2);
420        close(stderr_pipe[0]);
421        close(stderr_pipe[1]);
422
423        /*
424         * Set up stdin pipe if we have input data.
425         *
426         * If we don't have input data, child doesn't need stdin at all, but
427         * just closing it causes problems for puppet.  Reopen on /dev/null
428         * instead.  Ignore failure here, since it probably won't matter and
429         * worst case is that we leave stdin closed.
430         */
431        if (process.input != NULL) {
432            dup2(stdin_pipe[0], 0);
433            close(stdin_pipe[0]);
434            close(stdin_pipe[1]);
435        } else {
436            close(0);
437            fd = open("/dev/null", O_RDONLY);
438            if (fd > 0) {
439                dup2(fd, 0);
440                close(fd);
441            }
442        }
443
444        /*
445         * Older versions of MIT Kerberos left the replay cache file open
446         * across exec.  Newer versions correctly set it close-on-exec, but
447         * close our low-numbered file descriptors anyway for older versions.
448         * We're just trying to get the replay cache, so we don't have to go
449         * very high.
450         */
451        for (fd = 3; fd < 16; fd++)
452            close(fd);
453
454        /*
455         * Put the authenticated principal and other connection information in
456         * the environment.  REMUSER is for backwards compatibility with
457         * earlier versions of remctl.
458         */
459        if (setenv("REMUSER", client->user, 1) < 0) {
460            syswarn("cannot set REMUSER in environment");
461            exit(-1);
462        }
463        if (setenv("REMOTE_USER", client->user, 1) < 0) {
464            syswarn("cannot set REMOTE_USER in environment");
465            exit(-1);
466        }
467        if (setenv("REMOTE_ADDR", client->ipaddress, 1) < 0) {
468            syswarn("cannot set REMOTE_ADDR in environment");
469            exit(-1);
470        }
471        if (client->hostname != NULL) {
472            if (setenv("REMOTE_HOST", client->hostname, 1) < 0) {
473                syswarn("cannot set REMOTE_HOST in environment");
474                exit(-1);
475            }
476        }
477
478        /* Run the command. */
479        execv(path, req_argv);
480
481        /*
482         * This happens only if the exec fails.  Print out an error message to
483         * the stderr pipe and fail; that's the best that we can do.
484         */
485        fprintf(stderr, "Cannot execute: %s\n", strerror(errno));
486        exit(-1);
487
488    /* In the parent. */
489    default:
490        close(stdout_pipe[1]);
491        close(stderr_pipe[1]);
492        if (process.input != NULL)
493            close(stdin_pipe[0]);
494
495        /*
496         * Unblock the read ends of the output pipes, to enable us to read
497         * from both iteratively, and unblock the write end of the input pipe
498         * if we have one so that we don't block when feeding data to our
499         * child.
500         */
501        fdflag_nonblocking(stdout_pipe[0], true);
502        fdflag_nonblocking(stderr_pipe[0], true);
503        if (process.input != NULL)
504            fdflag_nonblocking(stdin_pipe[1], true);
505
506        /*
507         * This collects output from both pipes iteratively, while the child
508         * is executing, and processes it.  It also sends input data if we
509         * have any.
510         */
511        process.fds[0] = stdout_pipe[0];
512        process.fds[1] = stderr_pipe[0];
513        if (process.input != NULL)
514            process.stdin_fd = stdin_pipe[1];
515        ok = server_process_output(client, &process);
516        close(process.fds[0]);
517        close(process.fds[1]);
518        if (process.input != NULL)
519            close(process.stdin_fd);
520        if (!process.reaped)
521            waitpid(process.pid, &process.status, 0);
522        if (WIFEXITED(process.status))
523            process.status = (signed int) WEXITSTATUS(process.status);
524        else
525            process.status = -1;
526        if (ok) {
527            if (client->protocol == 1)
528                server_v1_send_output(client, process.status);
529            else
530                server_v2_send_status(client, process.status);
531        }
532    }
533
534 done:
535    if (command != NULL)
536        free(command);
537    if (subcommand != NULL)
538        free(subcommand);
539    if (req_argv != NULL) {
540        i = 1;
541        while (req_argv[i] != NULL) {
542            free(req_argv[i]);
543            i++;
544        }
545        free(req_argv);
546    }
547}
548
549
550/*
551 * Free a command, represented as a NULL-terminated array of pointers to iovec
552 * structs.
553 */
554void
555server_free_command(struct iovec **command)
556{
557    struct iovec **arg;
558
559    for (arg = command; *arg != NULL; arg++) {
560        if ((*arg)->iov_base != NULL)
561            free((*arg)->iov_base);
562        free(*arg);
563    }
564    free(command);
565}
Note: See TracBrowser for help on using the repository browser.