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