1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright 2015, Joyent, Inc.
  26  */
  27 
  28 #include <sys/types.h>
  29 #include <sys/param.h>
  30 #include <sys/sysmacros.h>
  31 #include <sys/signal.h>
  32 #include <sys/cred.h>
  33 #include <sys/user.h>
  34 #include <sys/errno.h>
  35 #include <sys/vnode.h>
  36 #include <sys/proc.h>
  37 #include <sys/cmn_err.h>
  38 #include <sys/debug.h>
  39 #include <sys/pathname.h>
  40 #include <sys/disp.h>
  41 #include <sys/exec.h>
  42 #include <sys/kmem.h>
  43 #include <sys/note.h>
  44 
  45 /*
  46  * This is the loadable module wrapper.
  47  */
  48 #include <sys/modctl.h>
  49 
  50 /* Local prototypes */
  51 static int
  52 shbinexec(
  53         struct vnode *vp,
  54         struct execa *uap,
  55         struct uarg *args,
  56         struct intpdata *idatap,
  57         int level,
  58         long *execsz,
  59         int setid,
  60         caddr_t exec_file,
  61         struct cred *cred,
  62         int *brand_action);
  63 
  64 #define SHBIN_CNTL(x)   ((x)&037)
  65 #define SHBINMAGIC_LEN  4
  66 extern char shbinmagicstr[];
  67 
  68 /*
  69  * Our list where we may find a copy of ksh93. The ordering is:
  70  * 1. 64bit (may not be installed or not supported in hardware)
  71  * 2. 32bit
  72  * 3. Use /sbin/ksh93 when /usr is not available
  73  *
  74  * ([1] and [2] explicitly bypass /usr/bin/ksh93 to avoid the
  75  * isaexec overhead).
  76  */
  77 static char *shell_list[] =
  78 {
  79 /* Bypass /usr/bin/ksh93 (which is "isaexec") for performance */
  80 #if defined(__sparc)
  81         "/usr/bin/sparcv9/ksh93",
  82         "/usr/bin/sparcv7/ksh93",
  83 #elif defined(__amd64)
  84         "/usr/bin/amd64/ksh93",
  85         "/usr/bin/i86/ksh93",
  86 #elif defined(__i386)
  87         "/usr/bin/i86/ksh93",
  88 #else
  89 #error "Unrecognized platform/CPU (use /usr/bin/ksh93 when in doubt)."
  90 #endif
  91         "/sbin/ksh93",
  92         NULL
  93 };
  94 
  95 static struct execsw esw = {
  96         shbinmagicstr,
  97         0,
  98         SHBINMAGIC_LEN,
  99         shbinexec,
 100         NULL
 101 };
 102 
 103 /*
 104  * Module linkage information for the kernel.
 105  */
 106 extern struct mod_ops mod_execops;
 107 
 108 static struct modlexec modlexec = {
 109         &mod_execops, "exec mod for shell binaries (ksh93)", &esw
 110 };
 111 
 112 static struct modlinkage modlinkage = {
 113         MODREV_1, (void *)&modlexec, NULL
 114 };
 115 
 116 int
 117 _init(void)
 118 {
 119         return (mod_install(&modlinkage));
 120 }
 121 
 122 int
 123 _fini(void)
 124 {
 125         return (mod_remove(&modlinkage));
 126 }
 127 
 128 int
 129 _info(struct modinfo *modinfop)
 130 {
 131         return (mod_info(&modlinkage, modinfop));
 132 }
 133 
 134 static int
 135 checkshbinmagic(struct vnode *vp)
 136 {
 137         int error;
 138         char linep[SHBINMAGIC_LEN];
 139         ssize_t resid;
 140 
 141         /*
 142          * Read the entire line and confirm that it starts with the magic
 143          * sequence for compiled ksh93 shell scripts.
 144          */
 145         if (error = vn_rdwr(UIO_READ, vp, linep, sizeof (linep), (offset_t)0,
 146             UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid))
 147                 return (error);
 148 
 149         if (memcmp(linep, shbinmagicstr, SHBINMAGIC_LEN) != 0)
 150                 return (ENOEXEC);
 151 
 152         return (0);
 153 }
 154 
 155 static int
 156 shbinexec(
 157         struct vnode *vp,
 158         struct execa *uap,
 159         struct uarg *args,
 160         struct intpdata *idatap,
 161         int level,
 162         long *execsz,
 163         int setid,
 164         caddr_t exec_file,
 165         struct cred *cred,
 166         int *brand_action)
 167 {
 168         _NOTE(ARGUNUSED(brand_action))
 169         vnode_t *nvp;
 170         int error = 0;
 171         struct intpdata idata;
 172         struct pathname intppn;
 173         struct pathname resolvepn;
 174         char *opath;
 175         char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */
 176         int fd = -1;
 177         int i;
 178 
 179         if (level) {            /* Can't recurse */
 180                 error = ENOEXEC;
 181                 goto bad;
 182         }
 183 
 184         ASSERT(idatap == (struct intpdata *)NULL);
 185 
 186         /*
 187          * Check whether the executable has the correct magic value.
 188          */
 189         if (error = checkshbinmagic(vp))
 190                 goto fail;
 191 
 192         pn_alloc(&resolvepn);
 193 
 194         /*
 195          * Travel the list of shells and look for one which is available...
 196          */
 197         for (i = 0; shell_list[i] != NULL; i++) {
 198                 error = pn_get(shell_list[i], UIO_SYSSPACE, &intppn);
 199                 if (error != 0) {
 200                         break;
 201                 }
 202 
 203                 error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp);
 204                 if (!error) {
 205                         /* Found match */
 206                         break;
 207                 }
 208 
 209                 /* No match found ? Then continue with the next item... */
 210                 pn_free(&intppn);
 211         }
 212 
 213         if (error) {
 214                 pn_free(&resolvepn);
 215                 goto fail;
 216         }
 217 
 218         /*
 219          * Setup interpreter data
 220          * "--" is passed to mark the end-of-arguments before adding
 221          * the scripts file name, preventing problems when a
 222          * a script's name starts with a '-' character.
 223          */
 224         idata.intp = NULL;
 225         idata.intp_name[0] = shell_list[i];
 226         idata.intp_arg[0] = "--";
 227 
 228         opath = args->pathname;
 229         args->pathname = resolvepn.pn_path;
 230         /* don't free resolvepn until we are done with args */
 231         pn_free(&intppn);
 232 
 233         /*
 234          * When we're executing a set-uid script resulting in uids
 235          * mismatching or when we execute with additional privileges,
 236          * we close the "replace script between exec and open by shell"
 237          * hole by passing the script as /dev/fd parameter.
 238          */
 239         if ((setid & EXECSETID_PRIVS) != 0 ||
 240             (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) ==
 241             (EXECSETID_UGIDS|EXECSETID_SETID)) {
 242                 (void) strcpy(devfd, "/dev/fd/");
 243                 if (error = execopen(&vp, &fd))
 244                         goto done;
 245                 numtos(fd, &devfd[8]);
 246                 args->fname = devfd;
 247         }
 248 
 249         error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
 250             EBA_NONE);
 251 
 252         if (!error) {
 253                 /*
 254                  * Close this script as the sh interpreter
 255                  * will open and close it later on.
 256                  */
 257                 (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL);
 258         }
 259 done:
 260         VN_RELE(nvp);
 261         args->pathname = opath;
 262         pn_free(&resolvepn);
 263 fail:
 264         if (error && fd != -1)
 265                 (void) execclose(fd);
 266 bad:
 267         return (error);
 268 }