[BACK]Return to exec_script.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Annotation of src/sys/kern/exec_script.c, Revision 1.63.8.1

1.63.8.1! rmind       1: /*     $NetBSD: exec_script.c,v 1.63 2008/11/19 18:36:06 ad 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.63.8.1! rmind      34: __KERNEL_RCSID(0, "$NetBSD: exec_script.c,v 1.63 2008/11/19 18:36:06 ad 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.63      ad         57: MODULE(MODULE_CLASS_MISC, exec_script, NULL);
                     58:
                     59: static struct execsw exec_script_execsw[] = {
                     60:        { SCRIPT_HDR_SIZE,
                     61:          exec_script_makecmds,
                     62:          { NULL },
                     63:          NULL,
                     64:          EXECSW_PRIO_ANY,
                     65:          0,
                     66:          NULL,
                     67:          NULL,
                     68:          NULL,
                     69:          exec_setup_stack },
                     70: };
                     71:
                     72: static int
                     73: exec_script_modcmd(modcmd_t cmd, void *arg)
                     74: {
                     75:
                     76:        switch (cmd) {
                     77:        case MODULE_CMD_INIT:
                     78:                return exec_add(exec_script_execsw,
                     79:                    __arraycount(exec_script_execsw));
                     80:
                     81:        case MODULE_CMD_FINI:
                     82:                return exec_remove(exec_script_execsw,
                     83:                    __arraycount(exec_script_execsw));
                     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;
                     98:         }
                     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.63.8.1! rmind     118:        char *cp, *shellname, *shellarg;
1.61      yamt      119:        size_t shellargp_len;
                    120:        struct exec_fakearg *shellargp;
                    121:        struct exec_fakearg *tmpsap;
1.1       cgd       122:        struct vnode *scriptvp;
                    123: #ifdef SETUIDSCRIPTS
1.18      christos  124:        /* Gcc needs those initialized for spurious uninitialized warning */
                    125:        uid_t script_uid = (uid_t) -1;
                    126:        gid_t script_gid = NOGROUP;
1.1       cgd       127:        u_short script_sbits;
                    128: #endif
                    129:
                    130:        /*
                    131:         * if the magic isn't that of a shell script, or we've already
                    132:         * done shell script processing for this exec, punt on it.
                    133:         */
                    134:        if ((epp->ep_flags & EXEC_INDIR) != 0 ||
                    135:            epp->ep_hdrvalid < EXEC_SCRIPT_MAGICLEN ||
                    136:            strncmp(hdrstr, EXEC_SCRIPT_MAGIC, EXEC_SCRIPT_MAGICLEN))
                    137:                return ENOEXEC;
                    138:
                    139:        /*
                    140:         * check that the shell spec is terminated by a newline,
                    141:         * and that it isn't too large.  Don't modify the
                    142:         * buffer unless we're ready to commit to handling it.
                    143:         * (The latter requirement means that we have to check
                    144:         * for both spaces and tabs later on.)
                    145:         */
1.33      perry     146:        hdrlinelen = min(epp->ep_hdrvalid, SCRIPT_HDR_SIZE);
1.1       cgd       147:        for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; cp < hdrstr + hdrlinelen;
                    148:            cp++) {
                    149:                if (*cp == '\n') {
                    150:                        *cp = '\0';
                    151:                        break;
                    152:                }
                    153:        }
                    154:        if (cp >= hdrstr + hdrlinelen)
                    155:                return ENOEXEC;
                    156:
1.38      matt      157:        /*
                    158:         * If the script has an ELF header, don't exec it.
                    159:         */
                    160:        if (epp->ep_hdrvalid >= sizeof(ELFMAG)-1 &&
                    161:            memcmp(hdrstr, ELFMAG, sizeof(ELFMAG)-1) == 0)
                    162:                return ENOEXEC;
                    163:
1.1       cgd       164:        shellname = NULL;
                    165:        shellarg = NULL;
1.13      christos  166:        shellarglen = 0;
1.1       cgd       167:
                    168:        /* strip spaces before the shell name */
                    169:        for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; *cp == ' ' || *cp == '\t';
                    170:            cp++)
                    171:                ;
                    172:
                    173:        /* collect the shell name; remember it's length for later */
                    174:        shellname = cp;
                    175:        shellnamelen = 0;
                    176:        if (*cp == '\0')
                    177:                goto check_shell;
                    178:        for ( /* cp = cp */ ; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
                    179:                shellnamelen++;
                    180:        if (*cp == '\0')
                    181:                goto check_shell;
                    182:        *cp++ = '\0';
                    183:
                    184:        /* skip spaces before any argument */
                    185:        for ( /* cp = cp */ ; *cp == ' ' || *cp == '\t'; cp++)
                    186:                ;
                    187:        if (*cp == '\0')
                    188:                goto check_shell;
                    189:
                    190:        /*
                    191:         * collect the shell argument.  everything after the shell name
                    192:         * is passed as ONE argument; that's the correct (historical)
                    193:         * behaviour.
                    194:         */
                    195:        shellarg = cp;
                    196:        for ( /* cp = cp */ ; *cp != '\0'; cp++)
                    197:                shellarglen++;
                    198:        *cp++ = '\0';
                    199:
                    200: check_shell:
                    201: #ifdef SETUIDSCRIPTS
                    202:        /*
1.29      thorpej   203:         * MNT_NOSUID has already taken care of by check_exec,
                    204:         * so we don't need to worry about it now or later.  We
1.54      ad        205:         * will need to check PSL_TRACED later, however.
1.1       cgd       206:         */
1.17      mycroft   207:        script_sbits = epp->ep_vap->va_mode & (S_ISUID | S_ISGID);
1.1       cgd       208:        if (script_sbits != 0) {
                    209:                script_uid = epp->ep_vap->va_uid;
                    210:                script_gid = epp->ep_vap->va_gid;
                    211:        }
                    212: #endif
                    213: #ifdef FDSCRIPTS
                    214:        /*
                    215:         * if the script isn't readable, or it's set-id, then we've
                    216:         * gotta supply a "/dev/fd/..." for the shell to read.
                    217:         * Note that stupid shells (csh) do the wrong thing, and
                    218:         * close all open fd's when the start.  That kills this
                    219:         * method of implementing "safe" set-id and x-only scripts.
                    220:         */
1.19      fvdl      221:        vn_lock(epp->ep_vp, LK_EXCLUSIVE | LK_RETRY);
1.59      pooka     222:        error = VOP_ACCESS(epp->ep_vp, VREAD, l->l_cred);
1.19      fvdl      223:        VOP_UNLOCK(epp->ep_vp, 0);
1.14      cgd       224:        if (error == EACCES
1.9       cgd       225: #ifdef SETUIDSCRIPTS
                    226:            || script_sbits
                    227: #endif
                    228:            ) {
1.1       cgd       229:                struct file *fp;
                    230:
                    231: #if defined(DIAGNOSTIC) && defined(FDSCRIPTS)
                    232:                if (epp->ep_flags & EXEC_HASFD)
                    233:                        panic("exec_script_makecmds: epp already has a fd");
                    234: #endif
                    235:
1.62      ad        236:                if ((error = fd_allocfile(&fp, &epp->ep_fd)) != 0) {
1.18      christos  237:                        scriptvp = NULL;
                    238:                        shellargp = NULL;
1.4       cgd       239:                        goto fail;
1.18      christos  240:                }
1.1       cgd       241:                epp->ep_flags |= EXEC_HASFD;
                    242:                fp->f_type = DTYPE_VNODE;
                    243:                fp->f_ops = &vnops;
1.56      christos  244:                fp->f_data = (void *) epp->ep_vp;
1.1       cgd       245:                fp->f_flag = FREAD;
1.62      ad        246:                fd_affix(curproc, fp, epp->ep_fd);
1.1       cgd       247:        }
                    248: #endif
                    249:
1.63.8.1! rmind     250:        /* set up the fake args list */
1.61      yamt      251:        shellargp_len = 4 * sizeof(*shellargp);
                    252:        shellargp = kmem_alloc(shellargp_len, KM_SLEEP);
1.1       cgd       253:        tmpsap = shellargp;
1.61      yamt      254:        tmpsap->fa_len = shellnamelen + 1;
                    255:        tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP);
                    256:        strlcpy(tmpsap->fa_arg, shellname, tmpsap->fa_len);
                    257:        tmpsap++;
1.1       cgd       258:        if (shellarg != NULL) {
1.61      yamt      259:                tmpsap->fa_len = shellarglen + 1;
                    260:                tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP);
                    261:                strlcpy(tmpsap->fa_arg, shellarg, tmpsap->fa_len);
                    262:                tmpsap++;
1.1       cgd       263:        }
1.61      yamt      264:        tmpsap->fa_len = MAXPATHLEN;
                    265:        tmpsap->fa_arg = kmem_alloc(tmpsap->fa_len, KM_SLEEP);
1.1       cgd       266: #ifdef FDSCRIPTS
                    267:        if ((epp->ep_flags & EXEC_HASFD) == 0) {
                    268: #endif
                    269:                /* normally can't fail, but check for it if diagnostic */
1.63.8.1! rmind     270:                error = copystr(epp->ep_kname, tmpsap->fa_arg, MAXPATHLEN,
1.11      mycroft   271:                    (size_t *)0);
1.61      yamt      272:                tmpsap++;
1.1       cgd       273: #ifdef DIAGNOSTIC
                    274:                if (error != 0)
1.63.8.1! rmind     275:                        panic("exec_script: copystr couldn't fail");
1.1       cgd       276: #endif
                    277: #ifdef FDSCRIPTS
1.61      yamt      278:        } else {
                    279:                snprintf(tmpsap->fa_arg, MAXPATHLEN, "/dev/fd/%d", epp->ep_fd);
                    280:                tmpsap++;
                    281:        }
1.1       cgd       282: #endif
1.61      yamt      283:        tmpsap->fa_arg = NULL;
1.1       cgd       284:
1.63.8.1! rmind     285:        /* Save the old vnode so we can clean it up later. */
        !           286:        scriptvp = epp->ep_vp;
        !           287:        epp->ep_vp = NULL;
        !           288:
        !           289:        /* Note that we're trying recursively. */
        !           290:        epp->ep_flags |= EXEC_INDIR;
        !           291:
1.1       cgd       292:        /*
                    293:         * mark the header we have as invalid; check_exec will read
                    294:         * the header from the new executable
                    295:         */
                    296:        epp->ep_hdrvalid = 0;
                    297:
1.63.8.1! rmind     298:        /* try loading the interpreter */
        !           299:        error = check_exec(l, epp, shellname);
1.1       cgd       300:
1.57      dsl       301:        /* note that we've clobbered the header */
                    302:        epp->ep_flags |= EXEC_DESTR;
1.63.8.1! rmind     303:
1.57      dsl       304:        if (error == 0) {
1.1       cgd       305:                /*
                    306:                 * It succeeded.  Unlock the script and
                    307:                 * close it if we aren't using it any more.
1.4       cgd       308:                 * Also, set things up so that the fake args
1.1       cgd       309:                 * list will be used.
                    310:                 */
1.14      cgd       311:                if ((epp->ep_flags & EXEC_HASFD) == 0) {
1.20      wrstuden  312:                        vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
1.58      pooka     313:                        VOP_CLOSE(scriptvp, FREAD, l->l_cred);
1.20      wrstuden  314:                        vput(scriptvp);
1.14      cgd       315:                }
1.2       cgd       316:
1.1       cgd       317:                epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG);
                    318:                epp->ep_fa = shellargp;
1.61      yamt      319:                epp->ep_fa_len = shellargp_len;
1.1       cgd       320: #ifdef SETUIDSCRIPTS
                    321:                /*
                    322:                 * set thing up so that set-id scripts will be
1.54      ad        323:                 * handled appropriately.  PSL_TRACED will be
1.29      thorpej   324:                 * checked later when the shell is actually
                    325:                 * exec'd.
1.1       cgd       326:                 */
                    327:                epp->ep_vap->va_mode |= script_sbits;
1.17      mycroft   328:                if (script_sbits & S_ISUID)
1.1       cgd       329:                        epp->ep_vap->va_uid = script_uid;
1.17      mycroft   330:                if (script_sbits & S_ISGID)
1.1       cgd       331:                        epp->ep_vap->va_gid = script_gid;
                    332: #endif
1.4       cgd       333:                return (0);
                    334:        }
                    335:
1.13      christos  336: #ifdef FDSCRIPTS
1.4       cgd       337: fail:
1.13      christos  338: #endif
1.4       cgd       339:
                    340:        /* kill the opened file descriptor, else close the file */
                    341:         if (epp->ep_flags & EXEC_HASFD) {
                    342:                 epp->ep_flags &= ~EXEC_HASFD;
1.62      ad        343:                 fd_close(epp->ep_fd);
1.18      christos  344:         } else if (scriptvp) {
1.20      wrstuden  345:                vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
1.58      pooka     346:                VOP_CLOSE(scriptvp, FREAD, l->l_cred);
1.20      wrstuden  347:                vput(scriptvp);
1.14      cgd       348:        }
1.4       cgd       349:
                    350:        /* free the fake arg list, because we're not returning it */
1.18      christos  351:        if ((tmpsap = shellargp) != NULL) {
1.61      yamt      352:                while (tmpsap->fa_arg != NULL) {
                    353:                        kmem_free(tmpsap->fa_arg, tmpsap->fa_len);
1.18      christos  354:                        tmpsap++;
                    355:                }
1.61      yamt      356:                kmem_free(shellargp, shellargp_len);
1.1       cgd       357:        }
1.4       cgd       358:
                    359:         /*
                    360:          * free any vmspace-creation commands,
                    361:          * and release their references
                    362:          */
                    363:         kill_vmcmds(&epp->ep_vmcmds);
1.1       cgd       364:
1.4       cgd       365:         return error;
1.1       cgd       366: }

CVSweb <webmaster@jp.NetBSD.org>