/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 1993, David Greenman4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/vnode.h>30#include <sys/proc.h>31#include <sys/sbuf.h>32#include <sys/systm.h>33#include <sys/sysproto.h>34#include <sys/exec.h>35#include <sys/imgact.h>36#include <sys/kernel.h>3738#if BYTE_ORDER == LITTLE_ENDIAN39#define SHELLMAGIC 0x2123 /* #! */40#else41#define SHELLMAGIC 0x232142#endif4344/*45* At the time of this writing, MAXSHELLCMDLEN == PAGE_SIZE. This is46* significant because the caller has only mapped in one page of the47* file we're reading.48*/49#if MAXSHELLCMDLEN > PAGE_SIZE50#error "MAXSHELLCMDLEN is larger than a single page!"51#endif5253/*54* MAXSHELLCMDLEN must be at least MAXINTERP plus the size of the `#!'55* prefix and terminating newline.56*/57CTASSERT(MAXSHELLCMDLEN >= MAXINTERP + 3);5859/**60* Shell interpreter image activator. An interpreter name beginning at61* imgp->args->begin_argv is the minimal successful exit requirement.62*63* If the given file is a shell-script, then the first line will start64* with the two characters `#!' (aka SHELLMAGIC), followed by the name65* of the shell-interpreter to run, followed by zero or more tokens.66*67* The interpreter is then started up such that it will see:68* arg[0] -> The name of interpreter as specified after `#!' in the69* first line of the script. The interpreter name must70* not be longer than MAXSHELLCMDLEN bytes.71* arg[1] -> *If* there are any additional tokens on the first line,72* then we add a new arg[1], which is a copy of the rest of73* that line. The copy starts at the first token after the74* interpreter name. We leave it to the interpreter to75* parse the tokens in that value.76* arg[x] -> the full pathname of the script. This will either be77* arg[2] or arg[1], depending on whether or not tokens78* were found after the interpreter name.79* arg[x+1] -> all the arguments that were specified on the original80* command line.81*82* This processing is described in the execve(2) man page.83*/8485/*86* HISTORICAL NOTE: From 1993 to mid-2005, FreeBSD parsed out the tokens as87* found on the first line of the script, and setup each token as a separate88* value in arg[]. This extra processing did not match the behavior of other89* OS's, and caused a few subtle problems. For one, it meant the kernel was90* deciding how those values should be parsed (wrt characters for quoting or91* comments, etc), while the interpreter might have other rules for parsing.92* It also meant the interpreter had no way of knowing which arguments came93* from the first line of the shell script, and which arguments were specified94* by the user on the command line. That extra processing was dropped in the95* 6.x branch on May 28, 2005 (matching __FreeBSD_version 600029).96*/97int98exec_shell_imgact(struct image_params *imgp)99{100const char *image_header = imgp->image_header;101const char *ihp, *interpb, *interpe, *maxp, *optb, *opte, *fname;102int error, offset;103size_t length;104struct vattr vattr;105struct sbuf *sname;106107/* a shell script? */108if (((const short *)image_header)[0] != SHELLMAGIC)109return (-1);110111/*112* Don't allow a shell script to be the shell for a shell113* script. :-)114*/115if (imgp->interpreted & IMGACT_SHELL)116return (ENOEXEC);117118imgp->interpreted |= IMGACT_SHELL;119120/*121* At this point we have the first page of the file mapped.122* However, we don't know how far into the page the contents are123* valid -- the actual file might be much shorter than the page.124* So find out the file size.125*/126error = VOP_GETATTR(imgp->vp, &vattr, imgp->proc->p_ucred);127if (error)128return (error);129130/*131* Copy shell name and arguments from image_header into a string132* buffer.133*/134maxp = &image_header[MIN(vattr.va_size, MAXSHELLCMDLEN)];135ihp = &image_header[2];136137/*138* Find the beginning and end of the interpreter_name. If the139* line does not include any interpreter, or if the name which140* was found is too long, we bail out.141*/142while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))143ihp++;144interpb = ihp;145while (ihp < maxp && ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n')146&& (*ihp != '\0')))147ihp++;148interpe = ihp;149if (interpb == interpe)150return (ENOEXEC);151if (interpe - interpb >= MAXINTERP)152return (ENAMETOOLONG);153154/*155* Find the beginning of the options (if any), and the end-of-line.156* Then trim the trailing blanks off the value. Note that some157* other operating systems do *not* trim the trailing whitespace...158*/159while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))160ihp++;161optb = ihp;162while (ihp < maxp && ((*ihp != '\n') && (*ihp != '\0')))163ihp++;164opte = ihp;165if (opte == maxp)166return (ENOEXEC);167while (--ihp > optb && ((*ihp == ' ') || (*ihp == '\t')))168opte = ihp;169170if (imgp->args->fname != NULL) {171fname = imgp->args->fname;172sname = NULL;173} else {174sname = sbuf_new_auto();175sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd);176sbuf_finish(sname);177fname = sbuf_data(sname);178}179180/*181* We need to "pop" (remove) the present value of arg[0], and "push"182* either two or three new values in the arg[] list. To do this,183* we first shift all the other values in the `begin_argv' area to184* provide the exact amount of room for the values added. Set up185* `offset' as the number of bytes to be added to the `begin_argv'186* area, and 'length' as the number of bytes being removed.187*/188offset = interpe - interpb + 1; /* interpreter */189if (opte > optb) /* options (if any) */190offset += opte - optb + 1;191offset += strlen(fname) + 1; /* fname of script */192length = (imgp->args->argc == 0) ? 0 :193strlen(imgp->args->begin_argv) + 1; /* bytes to delete */194195error = exec_args_adjust_args(imgp->args, length, offset);196if (error != 0) {197if (sname != NULL)198sbuf_delete(sname);199return (error);200}201202/*203* If there was no arg[0] when we started, then the interpreter_name204* is adding an argument (instead of replacing the arg[0] we started205* with). And we're always adding an argument when we include the206* full pathname of the original script.207*/208if (imgp->args->argc == 0)209imgp->args->argc = 1;210imgp->args->argc++;211212/*213* The original arg[] list has been shifted appropriately. Copy in214* the interpreter name and options-string.215*/216length = interpe - interpb;217bcopy(interpb, imgp->args->begin_argv, length);218*(imgp->args->begin_argv + length) = '\0';219offset = length + 1;220if (opte > optb) {221length = opte - optb;222bcopy(optb, imgp->args->begin_argv + offset, length);223*(imgp->args->begin_argv + offset + length) = '\0';224offset += length + 1;225imgp->args->argc++;226}227228/*229* Finally, add the filename onto the end for the interpreter to230* use and copy the interpreter's name to imgp->interpreter_name231* for exec to use.232*/233error = copystr(fname, imgp->args->begin_argv + offset,234imgp->args->stringspace, NULL);235236if (error == 0)237imgp->interpreter_name = imgp->args->begin_argv;238239if (sname != NULL)240sbuf_delete(sname);241return (error);242}243244/*245* Tell kern_execve.c about it, with a little help from the linker.246*/247static struct execsw shell_execsw = {248.ex_imgact = exec_shell_imgact,249.ex_name = "#!"250};251EXEC_SET(shell, shell_execsw);252253254