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