The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/exec_script.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: exec_script.c,v 1.36 2003/06/29 22:31:16 fvdl 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.36 2003/06/29 22:31:16 fvdl Exp $");
   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>
   43 #include <sys/malloc.h>
   44 #include <sys/vnode.h>
   45 #include <sys/namei.h>
   46 #include <sys/file.h>
   47 #ifdef SETUIDSCRIPTS
   48 #include <sys/stat.h>
   49 #endif
   50 #include <sys/filedesc.h>
   51 #include <sys/exec.h>
   52 #include <sys/resourcevar.h>
   53 
   54 #include <sys/exec_script.h>
   55 
   56 /*
   57  * exec_script_makecmds(): Check if it's an executable shell script.
   58  *
   59  * Given a proc pointer and an exec package pointer, see if the referent
   60  * of the epp is in shell script.  If it is, then set thing up so that
   61  * the script can be run.  This involves preparing the address space
   62  * and arguments for the shell which will run the script.
   63  *
   64  * This function is ultimately responsible for creating a set of vmcmds
   65  * which can be used to build the process's vm space and inserting them
   66  * into the exec package.
   67  */
   68 int
   69 exec_script_makecmds(struct proc *p, struct exec_package *epp)
   70 {
   71         int error, hdrlinelen, shellnamelen, shellarglen;
   72         char *hdrstr = epp->ep_hdr;
   73         char *cp, *shellname, *shellarg, *oldpnbuf;
   74         char **shellargp, **tmpsap;
   75         struct vnode *scriptvp;
   76 #ifdef SETUIDSCRIPTS
   77         /* Gcc needs those initialized for spurious uninitialized warning */
   78         uid_t script_uid = (uid_t) -1;
   79         gid_t script_gid = NOGROUP;
   80         u_short script_sbits;
   81 #endif
   82 
   83         /*
   84          * if the magic isn't that of a shell script, or we've already
   85          * done shell script processing for this exec, punt on it.
   86          */
   87         if ((epp->ep_flags & EXEC_INDIR) != 0 ||
   88             epp->ep_hdrvalid < EXEC_SCRIPT_MAGICLEN ||
   89             strncmp(hdrstr, EXEC_SCRIPT_MAGIC, EXEC_SCRIPT_MAGICLEN))
   90                 return ENOEXEC;
   91 
   92         /*
   93          * check that the shell spec is terminated by a newline,
   94          * and that it isn't too large.  Don't modify the
   95          * buffer unless we're ready to commit to handling it.
   96          * (The latter requirement means that we have to check
   97          * for both spaces and tabs later on.)
   98          */
   99         hdrlinelen = min(epp->ep_hdrvalid, SCRIPT_HDR_SIZE);
  100         for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; cp < hdrstr + hdrlinelen;
  101             cp++) {
  102                 if (*cp == '\n') {
  103                         *cp = '\0';
  104                         break;
  105                 }
  106         }
  107         if (cp >= hdrstr + hdrlinelen)
  108                 return ENOEXEC;
  109 
  110         shellname = NULL;
  111         shellarg = NULL;
  112         shellarglen = 0;
  113 
  114         /* strip spaces before the shell name */
  115         for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; *cp == ' ' || *cp == '\t';
  116             cp++)
  117                 ;
  118 
  119         /* collect the shell name; remember it's length for later */
  120         shellname = cp;
  121         shellnamelen = 0;
  122         if (*cp == '\0')
  123                 goto check_shell;
  124         for ( /* cp = cp */ ; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
  125                 shellnamelen++;
  126         if (*cp == '\0')
  127                 goto check_shell;
  128         *cp++ = '\0';
  129 
  130         /* skip spaces before any argument */
  131         for ( /* cp = cp */ ; *cp == ' ' || *cp == '\t'; cp++)
  132                 ;
  133         if (*cp == '\0')
  134                 goto check_shell;
  135 
  136         /*
  137          * collect the shell argument.  everything after the shell name
  138          * is passed as ONE argument; that's the correct (historical)
  139          * behaviour.
  140          */
  141         shellarg = cp;
  142         for ( /* cp = cp */ ; *cp != '\0'; cp++)
  143                 shellarglen++;
  144         *cp++ = '\0';
  145 
  146 check_shell:
  147 #ifdef SETUIDSCRIPTS
  148         /*
  149          * MNT_NOSUID has already taken care of by check_exec,
  150          * so we don't need to worry about it now or later.  We
  151          * will need to check P_TRACED later, however.
  152          */
  153         script_sbits = epp->ep_vap->va_mode & (S_ISUID | S_ISGID);
  154         if (script_sbits != 0) {
  155                 script_uid = epp->ep_vap->va_uid;
  156                 script_gid = epp->ep_vap->va_gid;
  157         }
  158 #endif
  159 #ifdef FDSCRIPTS
  160         /*
  161          * if the script isn't readable, or it's set-id, then we've
  162          * gotta supply a "/dev/fd/..." for the shell to read.
  163          * Note that stupid shells (csh) do the wrong thing, and
  164          * close all open fd's when the start.  That kills this
  165          * method of implementing "safe" set-id and x-only scripts.
  166          */
  167         vn_lock(epp->ep_vp, LK_EXCLUSIVE | LK_RETRY);
  168         error = VOP_ACCESS(epp->ep_vp, VREAD, p->p_ucred, p);
  169         VOP_UNLOCK(epp->ep_vp, 0);
  170         if (error == EACCES
  171 #ifdef SETUIDSCRIPTS
  172             || script_sbits
  173 #endif
  174             ) {
  175                 struct file *fp;
  176 
  177 #if defined(DIAGNOSTIC) && defined(FDSCRIPTS)
  178                 if (epp->ep_flags & EXEC_HASFD)
  179                         panic("exec_script_makecmds: epp already has a fd");
  180 #endif
  181 
  182                 /* falloc() will use the descriptor for us */
  183                 if ((error = falloc(p, &fp, &epp->ep_fd)) != 0) {
  184                         scriptvp = NULL;
  185                         shellargp = NULL;
  186                         goto fail;
  187                 }
  188 
  189                 epp->ep_flags |= EXEC_HASFD;
  190                 fp->f_type = DTYPE_VNODE;
  191                 fp->f_ops = &vnops;
  192                 fp->f_data = (caddr_t) epp->ep_vp;
  193                 fp->f_flag = FREAD;
  194                 FILE_SET_MATURE(fp);
  195                 FILE_UNUSE(fp, p);
  196         }
  197 #endif
  198 
  199         /* set up the parameters for the recursive check_exec() call */
  200         epp->ep_ndp->ni_dirp = shellname;
  201         epp->ep_ndp->ni_segflg = UIO_SYSSPACE;
  202         epp->ep_flags |= EXEC_INDIR;
  203 
  204         /* and set up the fake args list, for later */
  205         MALLOC(shellargp, char **, 4 * sizeof(char *), M_EXEC, M_WAITOK);
  206         tmpsap = shellargp;
  207         MALLOC(*tmpsap, char *, shellnamelen + 1, M_EXEC, M_WAITOK);
  208         strlcpy(*tmpsap++, shellname, shellnamelen + 1);
  209         if (shellarg != NULL) {
  210                 MALLOC(*tmpsap, char *, shellarglen + 1, M_EXEC, M_WAITOK);
  211                 strlcpy(*tmpsap++, shellarg, shellarglen + 1);
  212         }
  213         MALLOC(*tmpsap, char *, MAXPATHLEN, M_EXEC, M_WAITOK);
  214 #ifdef FDSCRIPTS
  215         if ((epp->ep_flags & EXEC_HASFD) == 0) {
  216 #endif
  217                 /* normally can't fail, but check for it if diagnostic */
  218                 error = copyinstr(epp->ep_name, *tmpsap++, MAXPATHLEN,
  219                     (size_t *)0);
  220 #ifdef DIAGNOSTIC
  221                 if (error != 0)
  222                         panic("exec_script: copyinstr couldn't fail");
  223 #endif
  224 #ifdef FDSCRIPTS
  225         } else
  226                 sprintf(*tmpsap++, "/dev/fd/%d", epp->ep_fd);
  227 #endif
  228         *tmpsap = NULL;
  229 
  230         /*
  231          * mark the header we have as invalid; check_exec will read
  232          * the header from the new executable
  233          */
  234         epp->ep_hdrvalid = 0;
  235 
  236         /*
  237          * remember the old vp and pnbuf for later, so we can restore
  238          * them if check_exec() fails.
  239          */
  240         scriptvp = epp->ep_vp;
  241         oldpnbuf = epp->ep_ndp->ni_cnd.cn_pnbuf;
  242 
  243 #ifdef VERIFIED_EXEC
  244         if ((error = check_exec(p, epp, 0)) == 0) {
  245 #else
  246         if ((error = check_exec(p, epp)) == 0) {
  247 #endif
  248                 /* note that we've clobbered the header */
  249                 epp->ep_flags |= EXEC_DESTR|EXEC_HASES;
  250 
  251                 /*
  252                  * It succeeded.  Unlock the script and
  253                  * close it if we aren't using it any more.
  254                  * Also, set things up so that the fake args
  255                  * list will be used.
  256                  */
  257                 if ((epp->ep_flags & EXEC_HASFD) == 0) {
  258                         vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
  259                         VOP_CLOSE(scriptvp, FREAD, p->p_ucred, p);
  260                         vput(scriptvp);
  261                 }
  262 
  263                 /* free the old pathname buffer */
  264                 PNBUF_PUT(oldpnbuf);
  265 
  266                 epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG);
  267                 epp->ep_fa = shellargp;
  268 #ifdef SETUIDSCRIPTS
  269                 /*
  270                  * set thing up so that set-id scripts will be
  271                  * handled appropriately.  P_TRACED will be
  272                  * checked later when the shell is actually
  273                  * exec'd.
  274                  */
  275                 epp->ep_vap->va_mode |= script_sbits;
  276                 if (script_sbits & S_ISUID)
  277                         epp->ep_vap->va_uid = script_uid;
  278                 if (script_sbits & S_ISGID)
  279                         epp->ep_vap->va_gid = script_gid;
  280 #endif
  281                 return (0);
  282         }
  283 
  284         /* XXX oldpnbuf not set for "goto fail" path */
  285         epp->ep_ndp->ni_cnd.cn_pnbuf = oldpnbuf;
  286 #ifdef FDSCRIPTS
  287 fail:
  288 #endif
  289         /* note that we've clobbered the header */
  290         epp->ep_flags |= EXEC_DESTR;
  291 
  292         /* kill the opened file descriptor, else close the file */
  293         if (epp->ep_flags & EXEC_HASFD) {
  294                 epp->ep_flags &= ~EXEC_HASFD;
  295                 (void) fdrelease(p, epp->ep_fd);
  296         } else if (scriptvp) {
  297                 vn_lock(scriptvp, LK_EXCLUSIVE | LK_RETRY);
  298                 VOP_CLOSE(scriptvp, FREAD, p->p_ucred, p);
  299                 vput(scriptvp);
  300         }
  301 
  302         PNBUF_PUT(epp->ep_ndp->ni_cnd.cn_pnbuf);
  303 
  304         /* free the fake arg list, because we're not returning it */
  305         if ((tmpsap = shellargp) != NULL) {
  306                 while (*tmpsap != NULL) {
  307                         FREE(*tmpsap, M_EXEC);
  308                         tmpsap++;
  309                 }
  310                 FREE(shellargp, M_EXEC);
  311         }
  312 
  313         /*
  314          * free any vmspace-creation commands,
  315          * and release their references
  316          */
  317         kill_vmcmds(&epp->ep_vmcmds);
  318 
  319         return error;
  320 }

Cache object: 0bbfb0ac3aafcd9c4e3a969ec4868fd3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.