104e9edb5SMark Johnston /*- 204e9edb5SMark Johnston * SPDX-License-Identifier: BSD-2-Clause 304e9edb5SMark Johnston * 404e9edb5SMark Johnston * Copyright (c) 2018 The FreeBSD Foundation 504e9edb5SMark Johnston * 604e9edb5SMark Johnston * This software was developed by Mark Johnston under sponsorship from 704e9edb5SMark Johnston * the FreeBSD Foundation. 804e9edb5SMark Johnston * 904e9edb5SMark Johnston * Redistribution and use in source and binary forms, with or without 1004e9edb5SMark Johnston * modification, are permitted provided that the following conditions are 1104e9edb5SMark Johnston * met: 1204e9edb5SMark Johnston * 1. Redistributions of source code must retain the above copyright 1304e9edb5SMark Johnston * notice, this list of conditions and the following disclaimer. 1404e9edb5SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 1504e9edb5SMark Johnston * notice, this list of conditions and the following disclaimer in 1604e9edb5SMark Johnston * the documentation and/or other materials provided with the distribution. 1704e9edb5SMark Johnston * 1804e9edb5SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1904e9edb5SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2004e9edb5SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2104e9edb5SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2204e9edb5SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2304e9edb5SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2404e9edb5SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2504e9edb5SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2604e9edb5SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2704e9edb5SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2804e9edb5SMark Johnston * SUCH DAMAGE. 2904e9edb5SMark Johnston */ 3004e9edb5SMark Johnston 3104e9edb5SMark Johnston #include <sys/cdefs.h> 3204e9edb5SMark Johnston __FBSDID("$FreeBSD$"); 3304e9edb5SMark Johnston 3404e9edb5SMark Johnston #include <sys/types.h> 3504e9edb5SMark Johnston #include <sys/capsicum.h> 3604e9edb5SMark Johnston #include <sys/dnv.h> 3704e9edb5SMark Johnston #include <sys/nv.h> 3804e9edb5SMark Johnston #include <sys/queue.h> 3904e9edb5SMark Johnston #include <sys/wait.h> 4004e9edb5SMark Johnston 4104e9edb5SMark Johnston #include <net/if.h> 4204e9edb5SMark Johnston #include <netinet/in.h> 4304e9edb5SMark Johnston 4404e9edb5SMark Johnston #include <capsicum_helpers.h> 4504e9edb5SMark Johnston #include <errno.h> 4604e9edb5SMark Johnston #include <stdlib.h> 4704e9edb5SMark Johnston #include <string.h> 4804e9edb5SMark Johnston #include <unistd.h> 4904e9edb5SMark Johnston 5004e9edb5SMark Johnston #include <libcasper.h> 5104e9edb5SMark Johnston #include <libcasper_service.h> 5204e9edb5SMark Johnston 5304e9edb5SMark Johnston #include "rtsold.h" 5404e9edb5SMark Johnston 5504e9edb5SMark Johnston /* 5604e9edb5SMark Johnston * Run the script and return the write end of a pipe to the main process. 5704e9edb5SMark Johnston * Return -1 and set errno on error. 5804e9edb5SMark Johnston */ 5904e9edb5SMark Johnston static int 6004e9edb5SMark Johnston script_run(char **argv) 6104e9edb5SMark Johnston { 6204e9edb5SMark Johnston pid_t pid; 6304e9edb5SMark Johnston int fd[2], null; 6404e9edb5SMark Johnston 6504e9edb5SMark Johnston if (pipe(fd) != 0) 6604e9edb5SMark Johnston return (-1); 6704e9edb5SMark Johnston if ((pid = fork()) < 0) 6804e9edb5SMark Johnston return (-1); 6904e9edb5SMark Johnston if (pid == 0) { 7004e9edb5SMark Johnston (void)close(fd[1]); 7104e9edb5SMark Johnston null = open("/dev/null", O_RDWR); 7204e9edb5SMark Johnston if (null < 0) 7304e9edb5SMark Johnston _exit(1); 7404e9edb5SMark Johnston if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) 7504e9edb5SMark Johnston _exit(1); 7604e9edb5SMark Johnston 7704e9edb5SMark Johnston closefrom(3); 7804e9edb5SMark Johnston (void)execve(argv[0], argv, NULL); 7904e9edb5SMark Johnston _exit(1); 8004e9edb5SMark Johnston } else 8104e9edb5SMark Johnston (void)close(fd[0]); 8204e9edb5SMark Johnston 8304e9edb5SMark Johnston return (fd[1]); 8404e9edb5SMark Johnston } 8504e9edb5SMark Johnston 8604e9edb5SMark Johnston int 8704e9edb5SMark Johnston cap_script_run(cap_channel_t *cap, const char *const *argv) 8804e9edb5SMark Johnston { 8904e9edb5SMark Johnston #ifdef WITH_CASPER 9004e9edb5SMark Johnston nvlist_t *nvl; 9104e9edb5SMark Johnston size_t argc; 9204e9edb5SMark Johnston int error, wfd; 9304e9edb5SMark Johnston 9404e9edb5SMark Johnston for (argc = 0; argv[argc] != NULL; argc++) 9504e9edb5SMark Johnston ; 9604e9edb5SMark Johnston 9704e9edb5SMark Johnston nvl = nvlist_create(0); 9804e9edb5SMark Johnston nvlist_add_string(nvl, "cmd", "script_run"); 9904e9edb5SMark Johnston nvlist_add_string_array(nvl, "argv", argv, argc); 10004e9edb5SMark Johnston nvl = cap_xfer_nvlist(cap, nvl); 10104e9edb5SMark Johnston if (nvl == NULL) 10204e9edb5SMark Johnston return (-1); 10304e9edb5SMark Johnston 10404e9edb5SMark Johnston error = (int)dnvlist_get_number(nvl, "error", 0); 10504e9edb5SMark Johnston if (error == 0) 10604e9edb5SMark Johnston wfd = nvlist_take_descriptor(nvl, "fd"); 10704e9edb5SMark Johnston nvlist_destroy(nvl); 10804e9edb5SMark Johnston if (error != 0) 10904e9edb5SMark Johnston errno = error; 11004e9edb5SMark Johnston return (error == 0 ? wfd : -1); 11104e9edb5SMark Johnston #else 11204e9edb5SMark Johnston (void)cap; 11304e9edb5SMark Johnston return (script_run(__DECONST(char **, argv))); 11404e9edb5SMark Johnston #endif 11504e9edb5SMark Johnston } 11604e9edb5SMark Johnston 11704e9edb5SMark Johnston /* 11804e9edb5SMark Johnston * Wait for a child process to exit, and return its status. 11904e9edb5SMark Johnston * Return -1 and set errno upon error. 12004e9edb5SMark Johnston */ 12104e9edb5SMark Johnston static int 12204e9edb5SMark Johnston script_wait(int *statusp) 12304e9edb5SMark Johnston { 12404e9edb5SMark Johnston int error; 12504e9edb5SMark Johnston 12604e9edb5SMark Johnston error = wait(statusp); 12704e9edb5SMark Johnston return (error >= 0 ? 0 : -1); 12804e9edb5SMark Johnston } 12904e9edb5SMark Johnston 13004e9edb5SMark Johnston int 13104e9edb5SMark Johnston cap_script_wait(cap_channel_t *cap, int *statusp) 13204e9edb5SMark Johnston { 13304e9edb5SMark Johnston #ifdef WITH_CASPER 13404e9edb5SMark Johnston nvlist_t *nvl; 13504e9edb5SMark Johnston int error; 13604e9edb5SMark Johnston 13704e9edb5SMark Johnston nvl = nvlist_create(0); 13804e9edb5SMark Johnston nvlist_add_string(nvl, "cmd", "script_wait"); 13904e9edb5SMark Johnston nvl = cap_xfer_nvlist(cap, nvl); 14004e9edb5SMark Johnston if (nvl == NULL) 14104e9edb5SMark Johnston return (-1); 14204e9edb5SMark Johnston 14304e9edb5SMark Johnston error = (int)dnvlist_get_number(nvl, "error", 0); 14404e9edb5SMark Johnston if (error == 0) 14504e9edb5SMark Johnston *statusp = (int)nvlist_get_number(nvl, "status"); 14604e9edb5SMark Johnston nvlist_destroy(nvl); 14704e9edb5SMark Johnston if (error != 0) 14804e9edb5SMark Johnston errno = error; 14904e9edb5SMark Johnston return (error == 0 ? 0 : -1); 15004e9edb5SMark Johnston #else 15104e9edb5SMark Johnston (void)cap; 15204e9edb5SMark Johnston return (script_wait(statusp)); 15304e9edb5SMark Johnston #endif 15404e9edb5SMark Johnston } 15504e9edb5SMark Johnston 15604e9edb5SMark Johnston #ifdef WITH_CASPER 15704e9edb5SMark Johnston static int 15804e9edb5SMark Johnston script_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 15904e9edb5SMark Johnston nvlist_t *nvlout) 16004e9edb5SMark Johnston { 16104e9edb5SMark Johnston cap_rights_t rights; 16204e9edb5SMark Johnston const char *const *iargv, *const *scripts; 16304e9edb5SMark Johnston char **argv; 16404e9edb5SMark Johnston size_t argc, i, nscripts; 165*331b84b5SEric van Gyzen int error, fd, status; 16604e9edb5SMark Johnston 16704e9edb5SMark Johnston if (strcmp(cmd, "script_wait") == 0) { 16804e9edb5SMark Johnston /* Wait for the result of a previous "script_run" command. */ 16904e9edb5SMark Johnston if (script_wait(&status) == -1) 17004e9edb5SMark Johnston return (errno); 17104e9edb5SMark Johnston nvlist_add_number(nvlout, "status", status); 17204e9edb5SMark Johnston return (0); 17304e9edb5SMark Johnston } 17404e9edb5SMark Johnston if (strcmp(cmd, "script_run") != 0) 17504e9edb5SMark Johnston return (EINVAL); 17604e9edb5SMark Johnston 17704e9edb5SMark Johnston /* 17804e9edb5SMark Johnston * Validate the argv against the limits specified at initialization 17904e9edb5SMark Johnston * time. 18004e9edb5SMark Johnston */ 18104e9edb5SMark Johnston iargv = nvlist_get_string_array(nvlin, "argv", &argc); 18204e9edb5SMark Johnston if (argc == 0) 18304e9edb5SMark Johnston return (EINVAL); 18404e9edb5SMark Johnston scripts = nvlist_get_string_array(limits, "scripts", &nscripts); 18504e9edb5SMark Johnston for (i = 0; i < nscripts; i++) 18604e9edb5SMark Johnston if (strcmp(iargv[0], scripts[i]) == 0) 18704e9edb5SMark Johnston break; 18804e9edb5SMark Johnston if (i == nscripts) 18904e9edb5SMark Johnston return (EINVAL); 19004e9edb5SMark Johnston 19104e9edb5SMark Johnston /* 19204e9edb5SMark Johnston * The nvlist API does not permit NULL pointers in an array, so we have 19304e9edb5SMark Johnston * to add the nul terminator ourselves. Yuck. 19404e9edb5SMark Johnston */ 19504e9edb5SMark Johnston argv = calloc(argc + 1, sizeof(*argv)); 19604e9edb5SMark Johnston if (argv == NULL) 19704e9edb5SMark Johnston return (errno); 19804e9edb5SMark Johnston memcpy(argv, iargv, sizeof(*argv) * argc); 19904e9edb5SMark Johnston 20004e9edb5SMark Johnston fd = script_run(argv); 201*331b84b5SEric van Gyzen error = errno; 202*331b84b5SEric van Gyzen free(argv); 20304e9edb5SMark Johnston if (fd < 0) 204*331b84b5SEric van Gyzen return (error); 20504e9edb5SMark Johnston 20604e9edb5SMark Johnston (void)caph_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE)); 20704e9edb5SMark Johnston nvlist_move_descriptor(nvlout, "fd", fd); 20804e9edb5SMark Johnston return (0); 20904e9edb5SMark Johnston } 21004e9edb5SMark Johnston 21104e9edb5SMark Johnston static int 212a792802fSMark Johnston script_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 21304e9edb5SMark Johnston { 21404e9edb5SMark Johnston const char *name; 21504e9edb5SMark Johnston void *cookie; 21604e9edb5SMark Johnston int nvtype; 21704e9edb5SMark Johnston bool hasscripts; 21804e9edb5SMark Johnston 21904e9edb5SMark Johnston /* Limits may only be set once. */ 22004e9edb5SMark Johnston if (oldlimits != NULL) 22104e9edb5SMark Johnston return (ENOTCAPABLE); 22204e9edb5SMark Johnston 22304e9edb5SMark Johnston cookie = NULL; 22404e9edb5SMark Johnston hasscripts = false; 22504e9edb5SMark Johnston while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { 22604e9edb5SMark Johnston if (nvtype == NV_TYPE_STRING_ARRAY && 22704e9edb5SMark Johnston strcmp(name, "scripts") == 0) 22804e9edb5SMark Johnston hasscripts = true; 22904e9edb5SMark Johnston else 23004e9edb5SMark Johnston return (EINVAL); 23104e9edb5SMark Johnston } 23204e9edb5SMark Johnston if (!hasscripts) 23304e9edb5SMark Johnston return (EINVAL); 23404e9edb5SMark Johnston return (0); 23504e9edb5SMark Johnston } 23604e9edb5SMark Johnston 23704e9edb5SMark Johnston CREATE_SERVICE("rtsold.script", script_limit, script_command, 0); 23804e9edb5SMark Johnston #endif /* WITH_CASPER */ 239