1*04e9edb5SMark Johnston /*- 2*04e9edb5SMark Johnston * SPDX-License-Identifier: BSD-2-Clause 3*04e9edb5SMark Johnston * 4*04e9edb5SMark Johnston * Copyright (c) 2018 The FreeBSD Foundation 5*04e9edb5SMark Johnston * 6*04e9edb5SMark Johnston * This software was developed by Mark Johnston under sponsorship from 7*04e9edb5SMark Johnston * the FreeBSD Foundation. 8*04e9edb5SMark Johnston * 9*04e9edb5SMark Johnston * Redistribution and use in source and binary forms, with or without 10*04e9edb5SMark Johnston * modification, are permitted provided that the following conditions are 11*04e9edb5SMark Johnston * met: 12*04e9edb5SMark Johnston * 1. Redistributions of source code must retain the above copyright 13*04e9edb5SMark Johnston * notice, this list of conditions and the following disclaimer. 14*04e9edb5SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 15*04e9edb5SMark Johnston * notice, this list of conditions and the following disclaimer in 16*04e9edb5SMark Johnston * the documentation and/or other materials provided with the distribution. 17*04e9edb5SMark Johnston * 18*04e9edb5SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*04e9edb5SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*04e9edb5SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*04e9edb5SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*04e9edb5SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*04e9edb5SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*04e9edb5SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*04e9edb5SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*04e9edb5SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*04e9edb5SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*04e9edb5SMark Johnston * SUCH DAMAGE. 29*04e9edb5SMark Johnston */ 30*04e9edb5SMark Johnston 31*04e9edb5SMark Johnston #include <sys/cdefs.h> 32*04e9edb5SMark Johnston __FBSDID("$FreeBSD$"); 33*04e9edb5SMark Johnston 34*04e9edb5SMark Johnston #include <sys/types.h> 35*04e9edb5SMark Johnston #include <sys/capsicum.h> 36*04e9edb5SMark Johnston #include <sys/dnv.h> 37*04e9edb5SMark Johnston #include <sys/nv.h> 38*04e9edb5SMark Johnston #include <sys/queue.h> 39*04e9edb5SMark Johnston #include <sys/wait.h> 40*04e9edb5SMark Johnston 41*04e9edb5SMark Johnston #include <net/if.h> 42*04e9edb5SMark Johnston #include <netinet/in.h> 43*04e9edb5SMark Johnston 44*04e9edb5SMark Johnston #include <capsicum_helpers.h> 45*04e9edb5SMark Johnston #include <errno.h> 46*04e9edb5SMark Johnston #include <stdlib.h> 47*04e9edb5SMark Johnston #include <string.h> 48*04e9edb5SMark Johnston #include <unistd.h> 49*04e9edb5SMark Johnston 50*04e9edb5SMark Johnston #include <libcasper.h> 51*04e9edb5SMark Johnston #include <libcasper_service.h> 52*04e9edb5SMark Johnston 53*04e9edb5SMark Johnston #include "rtsold.h" 54*04e9edb5SMark Johnston 55*04e9edb5SMark Johnston /* 56*04e9edb5SMark Johnston * Run the script and return the write end of a pipe to the main process. 57*04e9edb5SMark Johnston * Return -1 and set errno on error. 58*04e9edb5SMark Johnston */ 59*04e9edb5SMark Johnston static int 60*04e9edb5SMark Johnston script_run(char **argv) 61*04e9edb5SMark Johnston { 62*04e9edb5SMark Johnston pid_t pid; 63*04e9edb5SMark Johnston int fd[2], null; 64*04e9edb5SMark Johnston 65*04e9edb5SMark Johnston if (pipe(fd) != 0) 66*04e9edb5SMark Johnston return (-1); 67*04e9edb5SMark Johnston if ((pid = fork()) < 0) 68*04e9edb5SMark Johnston return (-1); 69*04e9edb5SMark Johnston if (pid == 0) { 70*04e9edb5SMark Johnston (void)close(fd[1]); 71*04e9edb5SMark Johnston null = open("/dev/null", O_RDWR); 72*04e9edb5SMark Johnston if (null < 0) 73*04e9edb5SMark Johnston _exit(1); 74*04e9edb5SMark Johnston if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) 75*04e9edb5SMark Johnston _exit(1); 76*04e9edb5SMark Johnston 77*04e9edb5SMark Johnston closefrom(3); 78*04e9edb5SMark Johnston (void)execve(argv[0], argv, NULL); 79*04e9edb5SMark Johnston _exit(1); 80*04e9edb5SMark Johnston } else 81*04e9edb5SMark Johnston (void)close(fd[0]); 82*04e9edb5SMark Johnston 83*04e9edb5SMark Johnston return (fd[1]); 84*04e9edb5SMark Johnston } 85*04e9edb5SMark Johnston 86*04e9edb5SMark Johnston int 87*04e9edb5SMark Johnston cap_script_run(cap_channel_t *cap, const char *const *argv) 88*04e9edb5SMark Johnston { 89*04e9edb5SMark Johnston #ifdef WITH_CASPER 90*04e9edb5SMark Johnston nvlist_t *nvl; 91*04e9edb5SMark Johnston size_t argc; 92*04e9edb5SMark Johnston int error, wfd; 93*04e9edb5SMark Johnston 94*04e9edb5SMark Johnston for (argc = 0; argv[argc] != NULL; argc++) 95*04e9edb5SMark Johnston ; 96*04e9edb5SMark Johnston 97*04e9edb5SMark Johnston nvl = nvlist_create(0); 98*04e9edb5SMark Johnston nvlist_add_string(nvl, "cmd", "script_run"); 99*04e9edb5SMark Johnston nvlist_add_string_array(nvl, "argv", argv, argc); 100*04e9edb5SMark Johnston nvl = cap_xfer_nvlist(cap, nvl); 101*04e9edb5SMark Johnston if (nvl == NULL) 102*04e9edb5SMark Johnston return (-1); 103*04e9edb5SMark Johnston 104*04e9edb5SMark Johnston error = (int)dnvlist_get_number(nvl, "error", 0); 105*04e9edb5SMark Johnston if (error == 0) 106*04e9edb5SMark Johnston wfd = nvlist_take_descriptor(nvl, "fd"); 107*04e9edb5SMark Johnston nvlist_destroy(nvl); 108*04e9edb5SMark Johnston if (error != 0) 109*04e9edb5SMark Johnston errno = error; 110*04e9edb5SMark Johnston return (error == 0 ? wfd : -1); 111*04e9edb5SMark Johnston #else 112*04e9edb5SMark Johnston (void)cap; 113*04e9edb5SMark Johnston return (script_run(__DECONST(char **, argv))); 114*04e9edb5SMark Johnston #endif 115*04e9edb5SMark Johnston } 116*04e9edb5SMark Johnston 117*04e9edb5SMark Johnston /* 118*04e9edb5SMark Johnston * Wait for a child process to exit, and return its status. 119*04e9edb5SMark Johnston * Return -1 and set errno upon error. 120*04e9edb5SMark Johnston */ 121*04e9edb5SMark Johnston static int 122*04e9edb5SMark Johnston script_wait(int *statusp) 123*04e9edb5SMark Johnston { 124*04e9edb5SMark Johnston int error; 125*04e9edb5SMark Johnston 126*04e9edb5SMark Johnston error = wait(statusp); 127*04e9edb5SMark Johnston return (error >= 0 ? 0 : -1); 128*04e9edb5SMark Johnston } 129*04e9edb5SMark Johnston 130*04e9edb5SMark Johnston int 131*04e9edb5SMark Johnston cap_script_wait(cap_channel_t *cap, int *statusp) 132*04e9edb5SMark Johnston { 133*04e9edb5SMark Johnston #ifdef WITH_CASPER 134*04e9edb5SMark Johnston nvlist_t *nvl; 135*04e9edb5SMark Johnston int error; 136*04e9edb5SMark Johnston 137*04e9edb5SMark Johnston nvl = nvlist_create(0); 138*04e9edb5SMark Johnston nvlist_add_string(nvl, "cmd", "script_wait"); 139*04e9edb5SMark Johnston nvl = cap_xfer_nvlist(cap, nvl); 140*04e9edb5SMark Johnston if (nvl == NULL) 141*04e9edb5SMark Johnston return (-1); 142*04e9edb5SMark Johnston 143*04e9edb5SMark Johnston error = (int)dnvlist_get_number(nvl, "error", 0); 144*04e9edb5SMark Johnston if (error == 0) 145*04e9edb5SMark Johnston *statusp = (int)nvlist_get_number(nvl, "status"); 146*04e9edb5SMark Johnston nvlist_destroy(nvl); 147*04e9edb5SMark Johnston if (error != 0) 148*04e9edb5SMark Johnston errno = error; 149*04e9edb5SMark Johnston return (error == 0 ? 0 : -1); 150*04e9edb5SMark Johnston #else 151*04e9edb5SMark Johnston (void)cap; 152*04e9edb5SMark Johnston return (script_wait(statusp)); 153*04e9edb5SMark Johnston #endif 154*04e9edb5SMark Johnston } 155*04e9edb5SMark Johnston 156*04e9edb5SMark Johnston #ifdef WITH_CASPER 157*04e9edb5SMark Johnston static int 158*04e9edb5SMark Johnston script_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 159*04e9edb5SMark Johnston nvlist_t *nvlout) 160*04e9edb5SMark Johnston { 161*04e9edb5SMark Johnston cap_rights_t rights; 162*04e9edb5SMark Johnston const char *const *iargv, *const *scripts; 163*04e9edb5SMark Johnston char **argv; 164*04e9edb5SMark Johnston size_t argc, i, nscripts; 165*04e9edb5SMark Johnston int fd, status; 166*04e9edb5SMark Johnston 167*04e9edb5SMark Johnston if (strcmp(cmd, "script_wait") == 0) { 168*04e9edb5SMark Johnston /* Wait for the result of a previous "script_run" command. */ 169*04e9edb5SMark Johnston if (script_wait(&status) == -1) 170*04e9edb5SMark Johnston return (errno); 171*04e9edb5SMark Johnston nvlist_add_number(nvlout, "status", status); 172*04e9edb5SMark Johnston return (0); 173*04e9edb5SMark Johnston } 174*04e9edb5SMark Johnston if (strcmp(cmd, "script_run") != 0) 175*04e9edb5SMark Johnston return (EINVAL); 176*04e9edb5SMark Johnston 177*04e9edb5SMark Johnston /* 178*04e9edb5SMark Johnston * Validate the argv against the limits specified at initialization 179*04e9edb5SMark Johnston * time. 180*04e9edb5SMark Johnston */ 181*04e9edb5SMark Johnston iargv = nvlist_get_string_array(nvlin, "argv", &argc); 182*04e9edb5SMark Johnston if (argc == 0) 183*04e9edb5SMark Johnston return (EINVAL); 184*04e9edb5SMark Johnston scripts = nvlist_get_string_array(limits, "scripts", &nscripts); 185*04e9edb5SMark Johnston for (i = 0; i < nscripts; i++) 186*04e9edb5SMark Johnston if (strcmp(iargv[0], scripts[i]) == 0) 187*04e9edb5SMark Johnston break; 188*04e9edb5SMark Johnston if (i == nscripts) 189*04e9edb5SMark Johnston return (EINVAL); 190*04e9edb5SMark Johnston 191*04e9edb5SMark Johnston /* 192*04e9edb5SMark Johnston * The nvlist API does not permit NULL pointers in an array, so we have 193*04e9edb5SMark Johnston * to add the nul terminator ourselves. Yuck. 194*04e9edb5SMark Johnston */ 195*04e9edb5SMark Johnston argv = calloc(argc + 1, sizeof(*argv)); 196*04e9edb5SMark Johnston if (argv == NULL) 197*04e9edb5SMark Johnston return (errno); 198*04e9edb5SMark Johnston memcpy(argv, iargv, sizeof(*argv) * argc); 199*04e9edb5SMark Johnston 200*04e9edb5SMark Johnston fd = script_run(argv); 201*04e9edb5SMark Johnston if (fd < 0) 202*04e9edb5SMark Johnston return (errno); 203*04e9edb5SMark Johnston 204*04e9edb5SMark Johnston (void)caph_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE)); 205*04e9edb5SMark Johnston nvlist_move_descriptor(nvlout, "fd", fd); 206*04e9edb5SMark Johnston return (0); 207*04e9edb5SMark Johnston } 208*04e9edb5SMark Johnston 209*04e9edb5SMark Johnston static int 210*04e9edb5SMark Johnston script_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits __unused) 211*04e9edb5SMark Johnston { 212*04e9edb5SMark Johnston const char *name; 213*04e9edb5SMark Johnston void *cookie; 214*04e9edb5SMark Johnston int nvtype; 215*04e9edb5SMark Johnston bool hasscripts; 216*04e9edb5SMark Johnston 217*04e9edb5SMark Johnston /* Limits may only be set once. */ 218*04e9edb5SMark Johnston if (oldlimits != NULL) 219*04e9edb5SMark Johnston return (ENOTCAPABLE); 220*04e9edb5SMark Johnston 221*04e9edb5SMark Johnston cookie = NULL; 222*04e9edb5SMark Johnston hasscripts = false; 223*04e9edb5SMark Johnston while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { 224*04e9edb5SMark Johnston if (nvtype == NV_TYPE_STRING_ARRAY && 225*04e9edb5SMark Johnston strcmp(name, "scripts") == 0) 226*04e9edb5SMark Johnston hasscripts = true; 227*04e9edb5SMark Johnston else 228*04e9edb5SMark Johnston return (EINVAL); 229*04e9edb5SMark Johnston } 230*04e9edb5SMark Johnston if (!hasscripts) 231*04e9edb5SMark Johnston return (EINVAL); 232*04e9edb5SMark Johnston return (0); 233*04e9edb5SMark Johnston } 234*04e9edb5SMark Johnston 235*04e9edb5SMark Johnston CREATE_SERVICE("rtsold.script", script_limit, script_command, 0); 236*04e9edb5SMark Johnston #endif /* WITH_CASPER */ 237