Annotation of src/sys/kern/exec_script.c, Revision 1.72
1.72 ! maxv 1: /* $NetBSD: exec_script.c,v 1.71 2014/06/23 18:06:32 maxv Exp $ */
1.8 cgd 2:
1.1 cgd 3: /*
1.14 cgd 4: * Copyright (c) 1993, 1994, 1996 Christopher G. Demetriou
1.1 cgd 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
1.3 jtc 19: * derived from this software without specific prior written permission
1.1 cgd 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: */
1.30 lukem 32:
33: #include <sys/cdefs.h>
1.72 ! maxv 34: __KERNEL_RCSID(0, "$NetBSD: exec_script.c,v 1.71 2014/06/23 18:06:32 maxv Exp $");
1.1 cgd 35:
36: #if defined(SETUIDSCRIPTS) && !defined(FDSCRIPTS)
37: #define FDSCRIPTS /* Need this for safe set-id scripts. */
38: #endif
39:
40: #include <sys/param.h>
41: #include <sys/systm.h>
42: #include <sys/proc.h>
1.61 yamt 43: #include <sys/kmem.h>
1.1 cgd 44: #include <sys/vnode.h>
45: #include <sys/namei.h>
46: #include <sys/file.h>
1.18 christos 47: #ifdef SETUIDSCRIPTS
48: #include <sys/stat.h>
49: #endif
1.1 cgd 50: #include <sys/filedesc.h>
51: #include <sys/exec.h>
52: #include <sys/resourcevar.h>
1.63 ad 53: #include <sys/module.h>
1.1 cgd 54: #include <sys/exec_script.h>
1.38 matt 55: #include <sys/exec_elf.h>
1.1 cgd 56:
1.67 christos 57: MODULE(MODULE_CLASS_EXEC, exec_script, NULL);
1.63 ad 58:
1.70 christos 59: static struct execsw exec_script_execsw = {
60: .es_hdrsz = SCRIPT_HDR_SIZE,
61: .es_makecmds = exec_script_makecmds,
62: .u = {
63: .elf_probe_func = NULL,
64: },
65: .es_emul = NULL,
66: .es_prio = EXECSW_PRIO_ANY,
67: .es_arglen = 0,
68: .es_copyargs = NULL,
69: .es_setregs = NULL,
70: .es_coredump = NULL,
71: .es_setup_stack = exec_setup_stack,
1.63 ad 72: };
73:
74: static int
75: exec_script_modcmd(modcmd_t cmd, void *arg)
76: {
77:
78: switch (cmd) {
79: case MODULE_CMD_INIT:
1.70 christos 80: return exec_add(&exec_script_execsw, 1);
1.63 ad 81:
82: case MODULE_CMD_FINI:
1.70 christos 83: return exec_remove(&exec_script_execsw, 1);
1.63 ad 84:
85: case MODULE_CMD_AUTOUNLOAD:
86: /*
87: * We don't want to be autounloaded because our use is
88: * transient: no executables with p_execsw equal to
89: * exec_script_execsw will exist, so FINI will never
90: * return EBUSY. However, the system will run scripts
91: * often. Return EBUSY here to prevent this module from
92: * ping-ponging in and out of the kernel.
93: */
94: return EBUSY;
95:
96: default:
97: return ENOTTY;
1.68 maxv 98: }
1.63 ad 99: }
100:
1.1 cgd 101: /*
102: * exec_script_makecmds(): Check if it's an executable shell script.
103: *
104: * Given a proc pointer and an exec package pointer, see if the referent
105: * of the epp is in shell script. If it is, then set thing up so that
106: * the script can be run. This involves preparing the address space
107: * and arguments for the shell which will run the script.
108: *
109: * This function is ultimately responsible for creating a set of vmcmds
110: * which can be used to build the process's vm space and inserting them
111: * into the exec package.
112: */
113: int
1.45 christos 114: exec_script_makecmds(struct lwp *l, struct exec_package *epp)
1.1 cgd 115: {
116: int error, hdrlinelen, shellnamelen, shellarglen;
117: char *hdrstr = epp->ep_hdr;
1.64 dholland 118: char *cp, *shellname, *shellarg;
1.61 yamt 119: size_t shellargp_len;
120: struct exec_fakearg *shellargp;
121: struct exec_fakearg *tmpsap;
1.66 dholland 122: struct pathbuf *shell_pathbuf;
1.1 cgd 123: struct vnode *scriptvp;
124: #ifdef SETUIDSCRIPTS
1.18 christos 125: /* Gcc needs those initialized for spurious uninitialized warning */
126: uid_t script_uid = (uid_t) -1;
127: gid_t script_gid = NOGROUP;
1.1 cgd 128: u_short script_sbits;
129: #endif
130:
131: /*
132: * if the magic isn't that of a shell script, or we've already
133: * done shell script processing for this exec, punt on it.
134: */
135: if ((epp->ep_flags & EXEC_INDIR) != 0 ||
136: epp->ep_hdrvalid < EXEC_SCRIPT_MAGICLEN ||
137: strncmp(hdrstr, EXEC_SCRIPT_MAGIC, EXEC_SCRIPT_MAGICLEN))
138: return ENOEXEC;
139:
140: /*
141: * check that the shell spec is terminated by a newline,
142: * and that it isn't too large. Don't modify the
143: * buffer unless we're ready to commit to handling it.
144: * (The latter requirement means that we have to check
145: * for both spaces and tabs later on.)
146: */
1.33 perry 147: hdrlinelen = min(epp->ep_hdrvalid, SCRIPT_HDR_SIZE);
1.1 cgd 148: for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; cp < hdrstr + hdrlinelen;
149: cp++) {
150: if (*cp == '\n') {
151: *cp = '\0';
152: break;
153: }
154: }
155: if (cp >= hdrstr + hdrlinelen)
156: return ENOEXEC;
157:
158: shellname = NULL;
159: shellarg = NULL;
1.13 christos 160: shellarglen = 0;
1.1 cgd 161:
162: /* strip spaces before the shell name */
163: for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; *cp == ' ' || *cp == '\t';
164: cp++)
165: ;
1.72 ! maxv 166: if (*cp == '\0')
! 167: return ENOEXEC;
1.1 cgd 168:
169: /* collect the shell name; remember it's 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: /*
1.29 thorpej 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
1.54 ad 199: * will need to check PSL_TRACED later, however.
1.1 cgd 200: */
1.17 mycroft 201: script_sbits = epp->ep_vap->va_mode & (S_ISUID | S_ISGID);
1.1 cgd 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 the start. That kills this
213: * method of implementing "safe" set-id and x-only scripts.
214: */
1.19 fvdl 215: vn_lock(epp->ep_vp, LK_EXCLUSIVE | LK_RETRY);
1.59 pooka 216: error = VOP_ACCESS(epp->ep_vp, VREAD, l->l_cred);
1.65 hannken 217: VOP_UNLOCK(epp->ep_vp);
1.14 cgd 218: if (error == EACCES
1.9 cgd 219: #ifdef SETUIDSCRIPTS
220: || script_sbits
221: #endif
222: ) {
1.1 cgd 223: struct file *fp;
224:
1.71 maxv 225: KASSERT(!(epp->ep_flags & EXEC_HASFD));
1.1 cgd 226:
1.62 ad 227: if ((error = fd_allocfile(&fp, &epp->ep_fd)) != 0) {
1.18 christos 228: scriptvp = NULL;
229: shellargp = NULL;
1.4 cgd 230: goto fail;
1.18 christos 231: }
1.1 cgd 232: epp->ep_flags |= EXEC_HASFD;
233: fp->f_type = DTYPE_VNODE;
234: fp->f_ops = &vnops;
1.56 christos 235: fp->f_data = (void *) epp->ep_vp;
1.1 cgd 236: fp->f_flag = FREAD;
1.62 ad 237: fd_affix(curproc, fp, epp->ep_fd);
1.1 cgd 238: }
239: #endif
240:
1.64 dholland 241: /* set up the fake args list */
1.61 yamt 242: shellargp_len = 4 * sizeof(*shellargp);
243: shellargp = kmem_alloc(shellargp_len, KM_SLEEP);
1.1 cgd 244: tmpsap = shellargp;
1.61 yamt 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++;
1.1 cgd 249: if (shellarg != NULL) {
1.61 yamt 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++;
1.1 cgd 254: }
1.61 yamt 255: tmpsap->fa_len = MAXPATHLEN;
256: tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP);
1.1 cgd 257: #ifdef FDSCRIPTS
258: if ((epp->ep_flags & EXEC_HASFD) == 0) {
259: #endif
260: /* normally can't fail, but check for it if diagnostic */
1.64 dholland 261: error = copystr(epp->ep_kname, tmpsap->fa_arg, MAXPATHLEN,
1.71 maxv 262: NULL);
263: KASSERT(error == 0);
1.61 yamt 264: tmpsap++;
1.1 cgd 265: #ifdef FDSCRIPTS
1.61 yamt 266: } else {
267: snprintf(tmpsap->fa_arg, MAXPATHLEN, "/dev/fd/%d", epp->ep_fd);
268: tmpsap++;
269: }
1.1 cgd 270: #endif
1.61 yamt 271: tmpsap->fa_arg = NULL;
1.1 cgd 272:
1.64 dholland 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:
1.1 cgd 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:
1.64 dholland 286: /* try loading the interpreter */
1.66 dholland 287: shell_pathbuf = pathbuf_create(shellname);
288: if (shell_pathbuf == NULL) {
289: error = ENOMEM;
290: } else {
291: error = check_exec(l, epp, shell_pathbuf);
292: pathbuf_destroy(shell_pathbuf);
293: }
1.1 cgd 294:
1.57 dsl 295: /* note that we've clobbered the header */
296: epp->ep_flags |= EXEC_DESTR;
1.64 dholland 297:
1.57 dsl 298: if (error == 0) {
1.1 cgd 299: /*
300: * It succeeded. Unlock the script and
301: * close it if we aren't using it any more.
1.4 cgd 302: * Also, set things up so that the fake args
1.1 cgd 303: * list will be used.
304: */
1.14 cgd 305: if ((epp->ep_flags & EXEC_HASFD) == 0) {
1.20 wrstuden 306: vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
1.58 pooka 307: VOP_CLOSE(scriptvp, FREAD, l->l_cred);
1.20 wrstuden 308: vput(scriptvp);
1.14 cgd 309: }
1.2 cgd 310:
1.1 cgd 311: epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG);
312: epp->ep_fa = shellargp;
1.61 yamt 313: epp->ep_fa_len = shellargp_len;
1.1 cgd 314: #ifdef SETUIDSCRIPTS
315: /*
316: * set thing up so that set-id scripts will be
1.54 ad 317: * handled appropriately. PSL_TRACED will be
1.29 thorpej 318: * checked later when the shell is actually
319: * exec'd.
1.1 cgd 320: */
321: epp->ep_vap->va_mode |= script_sbits;
1.17 mycroft 322: if (script_sbits & S_ISUID)
1.1 cgd 323: epp->ep_vap->va_uid = script_uid;
1.17 mycroft 324: if (script_sbits & S_ISGID)
1.1 cgd 325: epp->ep_vap->va_gid = script_gid;
326: #endif
1.4 cgd 327: return (0);
328: }
329:
1.13 christos 330: #ifdef FDSCRIPTS
1.4 cgd 331: fail:
1.13 christos 332: #endif
1.4 cgd 333:
334: /* kill the opened file descriptor, else close the file */
1.68 maxv 335: if (epp->ep_flags & EXEC_HASFD) {
336: epp->ep_flags &= ~EXEC_HASFD;
337: fd_close(epp->ep_fd);
338: } else if (scriptvp) {
1.20 wrstuden 339: vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
1.58 pooka 340: VOP_CLOSE(scriptvp, FREAD, l->l_cred);
1.20 wrstuden 341: vput(scriptvp);
1.14 cgd 342: }
1.4 cgd 343:
344: /* free the fake arg list, because we're not returning it */
1.18 christos 345: if ((tmpsap = shellargp) != NULL) {
1.61 yamt 346: while (tmpsap->fa_arg != NULL) {
347: kmem_free(tmpsap->fa_arg, tmpsap->fa_len);
1.18 christos 348: tmpsap++;
349: }
1.61 yamt 350: kmem_free(shellargp, shellargp_len);
1.1 cgd 351: }
1.4 cgd 352:
1.68 maxv 353: /*
354: * free any vmspace-creation commands,
355: * and release their references
356: */
357: kill_vmcmds(&epp->ep_vmcmds);
1.1 cgd 358:
1.68 maxv 359: return error;
1.1 cgd 360: }
CVSweb <webmaster@jp.NetBSD.org>