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.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 }

Cache object: 5efdfad3efdc5026ff9328bf5a1cd90d


[ 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.