1 /* $NetBSD: exec_script.c,v 1.83 2021/05/03 10:25:14 fcambus Exp $ */ 2 3 /* 4 * Copyright (c) 1993, 1994, 1996 Christopher G. Demetriou 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Christopher G. Demetriou. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: exec_script.c,v 1.83 2021/05/03 10:25:14 fcambus Exp $"); 35 36 #ifdef _KERNEL_OPT 37 #include "opt_script.h" 38 #endif 39 40 #if defined(SETUIDSCRIPTS) && !defined(FDSCRIPTS) 41 #define FDSCRIPTS /* Need this for safe set-id scripts. */ 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/proc.h> 47 #include <sys/kmem.h> 48 #include <sys/vnode.h> 49 #include <sys/namei.h> 50 #include <sys/file.h> 51 #ifdef SETUIDSCRIPTS 52 #include <sys/stat.h> 53 #endif 54 #include <sys/filedesc.h> 55 #include <sys/exec.h> 56 #include <sys/resourcevar.h> 57 #include <sys/module.h> 58 #include <sys/exec_script.h> 59 #include <sys/exec_elf.h> 60 61 MODULE(MODULE_CLASS_EXEC, exec_script, NULL); 62 63 static struct execsw exec_script_execsw = { 64 .es_hdrsz = SCRIPT_HDR_SIZE, 65 .es_makecmds = exec_script_makecmds, 66 .u = { 67 .elf_probe_func = NULL, 68 }, 69 .es_emul = NULL, 70 .es_prio = EXECSW_PRIO_ANY, 71 .es_arglen = 0, 72 .es_copyargs = NULL, 73 .es_setregs = NULL, 74 .es_coredump = NULL, 75 .es_setup_stack = exec_setup_stack, 76 }; 77 78 static int 79 exec_script_modcmd(modcmd_t cmd, void *arg) 80 { 81 82 switch (cmd) { 83 case MODULE_CMD_INIT: 84 return exec_add(&exec_script_execsw, 1); 85 86 case MODULE_CMD_FINI: 87 return exec_remove(&exec_script_execsw, 1); 88 89 case MODULE_CMD_AUTOUNLOAD: 90 /* 91 * We don't want to be autounloaded because our use is 92 * transient: no executables with p_execsw equal to 93 * exec_script_execsw will exist, so FINI will never 94 * return EBUSY. However, the system will run scripts 95 * often. Return EBUSY here to prevent this module from 96 * ping-ponging in and out of the kernel. 97 */ 98 return EBUSY; 99 100 default: 101 return ENOTTY; 102 } 103 } 104 105 /* 106 * exec_script_makecmds(): Check if it's an executable shell script. 107 * 108 * Given a proc pointer and an exec package pointer, see if the referent 109 * of the epp is in shell script. If it is, then set thing up so that 110 * the script can be run. This involves preparing the address space 111 * and arguments for the shell which will run the script. 112 * 113 * This function is ultimately responsible for creating a set of vmcmds 114 * which can be used to build the process's vm space and inserting them 115 * into the exec package. 116 */ 117 int 118 exec_script_makecmds(struct lwp *l, struct exec_package *epp) 119 { 120 int error, hdrlinelen, shellnamelen, shellarglen; 121 char *hdrstr = epp->ep_hdr; 122 char *cp, *shellname, *shellarg; 123 size_t shellargp_len; 124 struct exec_fakearg *shellargp; 125 struct exec_fakearg *tmpsap; 126 struct pathbuf *shell_pathbuf; 127 struct vnode *scriptvp; 128 #ifdef SETUIDSCRIPTS 129 /* Gcc needs those initialized for spurious uninitialized warning */ 130 uid_t script_uid = (uid_t) -1; 131 gid_t script_gid = NOGROUP; 132 u_short script_sbits; 133 #endif 134 135 /* 136 * if the magic isn't that of a shell script, or we've already 137 * done shell script processing for this exec, punt on it. 138 */ 139 if ((epp->ep_flags & EXEC_INDIR) != 0 || 140 epp->ep_hdrvalid < EXEC_SCRIPT_MAGICLEN || 141 strncmp(hdrstr, EXEC_SCRIPT_MAGIC, EXEC_SCRIPT_MAGICLEN)) 142 return ENOEXEC; 143 144 /* 145 * Check that the shell spec is terminated by a newline, and that 146 * it isn't too large. 147 */ 148 hdrlinelen = uimin(epp->ep_hdrvalid, SCRIPT_HDR_SIZE); 149 for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; cp < hdrstr + hdrlinelen; 150 cp++) { 151 if (*cp == '\n') { 152 *cp = '\0'; 153 break; 154 } 155 } 156 if (cp >= hdrstr + hdrlinelen) 157 return ENOEXEC; 158 159 /* strip spaces before the shell name */ 160 for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; *cp == ' ' || *cp == '\t'; 161 cp++) 162 ; 163 if (*cp == '\0') 164 return ENOEXEC; 165 166 shellarg = NULL; 167 shellarglen = 0; 168 169 /* collect the shell name; remember its length for later */ 170 shellname = cp; 171 shellnamelen = 0; 172 for ( /* cp = cp */ ; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++) 173 shellnamelen++; 174 if (*cp == '\0') 175 goto check_shell; 176 *cp++ = '\0'; 177 178 /* skip spaces before any argument */ 179 for ( /* cp = cp */ ; *cp == ' ' || *cp == '\t'; cp++) 180 ; 181 if (*cp == '\0') 182 goto check_shell; 183 184 /* 185 * collect the shell argument. everything after the shell name 186 * is passed as ONE argument; that's the correct (historical) 187 * behaviour. 188 */ 189 shellarg = cp; 190 for ( /* cp = cp */ ; *cp != '\0'; cp++) 191 shellarglen++; 192 *cp++ = '\0'; 193 194 check_shell: 195 #ifdef SETUIDSCRIPTS 196 /* 197 * MNT_NOSUID has already taken care of by check_exec, 198 * so we don't need to worry about it now or later. We 199 * will need to check PSL_TRACED later, however. 200 */ 201 script_sbits = epp->ep_vap->va_mode & (S_ISUID | S_ISGID); 202 if (script_sbits != 0) { 203 script_uid = epp->ep_vap->va_uid; 204 script_gid = epp->ep_vap->va_gid; 205 } 206 #endif 207 #ifdef FDSCRIPTS 208 /* 209 * if the script isn't readable, or it's set-id, then we've 210 * gotta supply a "/dev/fd/..." for the shell to read. 211 * Note that stupid shells (csh) do the wrong thing, and 212 * close all open fd's when they start. That kills this 213 * method of implementing "safe" set-id and x-only scripts. 214 */ 215 vn_lock(epp->ep_vp, LK_SHARED | LK_RETRY); 216 error = VOP_ACCESS(epp->ep_vp, VREAD, l->l_cred); 217 VOP_UNLOCK(epp->ep_vp); 218 if (error == EACCES 219 #ifdef SETUIDSCRIPTS 220 || script_sbits 221 #endif 222 ) { 223 struct file *fp; 224 225 KASSERT(!(epp->ep_flags & EXEC_HASFD)); 226 227 if ((error = fd_allocfile(&fp, &epp->ep_fd)) != 0) { 228 scriptvp = NULL; 229 shellargp = NULL; 230 goto fail; 231 } 232 epp->ep_flags |= EXEC_HASFD; 233 fp->f_type = DTYPE_VNODE; 234 fp->f_ops = &vnops; 235 fp->f_vnode = epp->ep_vp; 236 fp->f_flag = FREAD; 237 fd_affix(curproc, fp, epp->ep_fd); 238 } 239 #endif 240 241 /* set up the fake args list */ 242 shellargp_len = 4 * sizeof(*shellargp); 243 shellargp = kmem_alloc(shellargp_len, KM_SLEEP); 244 tmpsap = shellargp; 245 tmpsap->fa_len = shellnamelen + 1; 246 tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP); 247 strlcpy(tmpsap->fa_arg, shellname, tmpsap->fa_len); 248 tmpsap++; 249 if (shellarg != NULL) { 250 tmpsap->fa_len = shellarglen + 1; 251 tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP); 252 strlcpy(tmpsap->fa_arg, shellarg, tmpsap->fa_len); 253 tmpsap++; 254 } 255 tmpsap->fa_len = MAXPATHLEN; 256 tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP); 257 #ifdef FDSCRIPTS 258 if ((epp->ep_flags & EXEC_HASFD) == 0) { 259 #endif 260 /* normally can't fail, but check for it if diagnostic */ 261 error = copystr(epp->ep_kname, tmpsap->fa_arg, MAXPATHLEN, 262 NULL); 263 KASSERT(error == 0); 264 tmpsap++; 265 #ifdef FDSCRIPTS 266 } else { 267 snprintf(tmpsap->fa_arg, MAXPATHLEN, "/dev/fd/%d", epp->ep_fd); 268 tmpsap++; 269 } 270 #endif 271 tmpsap->fa_arg = NULL; 272 273 /* Save the old vnode so we can clean it up later. */ 274 scriptvp = epp->ep_vp; 275 epp->ep_vp = NULL; 276 277 /* Note that we're trying recursively. */ 278 epp->ep_flags |= EXEC_INDIR; 279 280 /* 281 * mark the header we have as invalid; check_exec will read 282 * the header from the new executable 283 */ 284 epp->ep_hdrvalid = 0; 285 286 /* try loading the interpreter */ 287 if ((error = exec_makepathbuf(l, shellname, UIO_SYSSPACE, 288 &shell_pathbuf, NULL)) == 0) { 289 error = check_exec(l, epp, shell_pathbuf, NULL); 290 pathbuf_destroy(shell_pathbuf); 291 } 292 293 /* note that we've clobbered the header */ 294 epp->ep_flags |= EXEC_DESTR; 295 296 if (error == 0) { 297 /* 298 * It succeeded. Unlock the script and 299 * close it if we aren't using it any more. 300 * Also, set things up so that the fake args 301 * list will be used. 302 */ 303 if ((epp->ep_flags & EXEC_HASFD) == 0) { 304 vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY); 305 VOP_CLOSE(scriptvp, FREAD, l->l_cred); 306 vput(scriptvp); 307 } 308 309 epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG); 310 epp->ep_fa = shellargp; 311 epp->ep_fa_len = shellargp_len; 312 #ifdef SETUIDSCRIPTS 313 /* 314 * set thing up so that set-id scripts will be 315 * handled appropriately. PSL_TRACED will be 316 * checked later when the shell is actually 317 * exec'd. 318 */ 319 epp->ep_vap->va_mode |= script_sbits; 320 if (script_sbits & S_ISUID) 321 epp->ep_vap->va_uid = script_uid; 322 if (script_sbits & S_ISGID) 323 epp->ep_vap->va_gid = script_gid; 324 #endif 325 return (0); 326 } 327 328 #ifdef FDSCRIPTS 329 fail: 330 #endif 331 332 /* kill the opened file descriptor, else close the file */ 333 if (epp->ep_flags & EXEC_HASFD) { 334 epp->ep_flags &= ~EXEC_HASFD; 335 fd_close(epp->ep_fd); 336 } else if (scriptvp) { 337 vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY); 338 VOP_CLOSE(scriptvp, FREAD, l->l_cred); 339 vput(scriptvp); 340 } 341 342 /* free the fake arg list, because we're not returning it */ 343 if ((tmpsap = shellargp) != NULL) { 344 while (tmpsap->fa_arg != NULL) { 345 kmem_free(tmpsap->fa_arg, tmpsap->fa_len); 346 tmpsap++; 347 } 348 kmem_free(shellargp, shellargp_len); 349 } 350 351 /* 352 * free any vmspace-creation commands, 353 * and release their references 354 */ 355 kill_vmcmds(&epp->ep_vmcmds); 356 357 return error; 358 } 359