1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019-2023, Juniper Networks, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/errno.h> 32 #include <sys/mac.h> 33 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <string.h> 37 #include <syslog.h> 38 39 #include <security/mac_grantbylabel/mac_grantbylabel.h> 40 41 #include "libveriexec.h" 42 43 static char * 44 find_interpreter(const char *script) 45 { 46 static const char ws[] = " \t\n\r"; 47 static char buf[MAXPATHLEN+4]; /* allow space for #! etc */ 48 char *cp; 49 int fd; 50 int n; 51 52 cp = NULL; 53 if ((fd = open(script, O_RDONLY)) >= 0) { 54 if ((n = read(fd, buf, sizeof(buf))) > 0) { 55 if (strncmp(buf, "#!", 2) == 0) { 56 buf[sizeof(buf) - 1] = '\0'; 57 cp = &buf[2]; 58 if ((n = strspn(cp, ws)) > 0) 59 cp += n; 60 if ((n = strcspn(cp, ws)) > 0) { 61 cp[n] = '\0'; 62 } else { 63 cp = NULL; 64 } 65 } 66 } 67 close(fd); 68 } 69 return (cp); 70 } 71 72 /** 73 * @brief exec a python or similar script 74 * 75 * Python and similar scripts must normally be signed and 76 * run directly rather than fed to the interpreter which 77 * is not normally allowed to be run directly. 78 * 79 * If direct execv of script fails due to EAUTH 80 * and process has GBL_VERIEXEC syslog event and run via 81 * interpreter. 82 * 83 * If interpreter is NULL look at first block of script 84 * to find ``#!`` magic. 85 * 86 * @prarm[in] interpreter 87 * if NULL, extract from script if necessary 88 * 89 * @prarm[in] argv 90 * argv for execv(2) 91 * argv[0] must be full path. 92 * Python at least requires argv[1] to also be the script path. 93 * 94 * @return 95 * error on failure usually EPERM or EAUTH 96 */ 97 int 98 execv_script(const char *interpreter, char * const *argv) 99 { 100 const char *script; 101 int rc; 102 103 script = argv[0]; 104 if (veriexec_check_path(script) == 0) { 105 rc = execv(script, argv); 106 } 107 /* still here? we might be allowed to run via interpreter */ 108 if (gbl_check_pid(0) & GBL_VERIEXEC) { 109 if (!interpreter) 110 interpreter = find_interpreter(script); 111 if (interpreter) { 112 syslog(LOG_NOTICE, "running %s via %s", 113 script, interpreter); 114 rc = execv(interpreter, argv); 115 } 116 } 117 return (rc); 118 } 119 120 #if defined(MAIN) || defined(UNIT_TEST) 121 #include <sys/wait.h> 122 #include <err.h> 123 124 int 125 main(int argc __unused, char *argv[]) 126 { 127 const char *interp; 128 int c; 129 int s; 130 pid_t child; 131 132 openlog("exec_script", LOG_PID|LOG_PERROR, LOG_DAEMON); 133 134 interp = NULL; 135 while ((c = getopt(argc, argv, "i:")) != -1) { 136 switch (c) { 137 case 'i': 138 interp = optarg; 139 break; 140 default: 141 errx(1, "unknown option: -%c", c); 142 break; 143 } 144 } 145 argc -= optind; 146 argv += optind; 147 /* we need a child */ 148 child = fork(); 149 if (child < 0) 150 err(2, "fork"); 151 if (child == 0) { 152 c = execv_script(interp, argv); 153 err(2, "exec_script(%s,%s)", interp, argv[0]); 154 } 155 c = waitpid(child, &s, 0); 156 printf("%s: exit %d\n", argv[0], WEXITSTATUS(s)); 157 return (0); 158 } 159 #endif 160