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 2019 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 size_t *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(__x86) 84 "/usr/bin/amd64/ksh93", 85 "/usr/bin/i86/ksh93", 86 #else 87 #error "Unrecognized platform/CPU (use /usr/bin/ksh93 when in doubt)." 88 #endif 89 "/sbin/ksh93", 90 NULL 91 }; 92 93 static struct execsw esw = { 94 shbinmagicstr, 95 0, 96 SHBINMAGIC_LEN, 97 shbinexec, 98 NULL 99 }; 100 101 /* 102 * Module linkage information for the kernel. 103 */ 104 extern struct mod_ops mod_execops; 105 106 static struct modlexec modlexec = { 107 &mod_execops, "exec mod for shell binaries (ksh93)", &esw 108 }; 109 110 static struct modlinkage modlinkage = { 111 MODREV_1, (void *)&modlexec, NULL 112 }; 113 114 int 115 _init(void) 116 { 117 return (mod_install(&modlinkage)); 118 } 119 120 int 121 _fini(void) 122 { 123 return (mod_remove(&modlinkage)); 124 } 125 126 int 127 _info(struct modinfo *modinfop) 128 { 129 return (mod_info(&modlinkage, modinfop)); 130 } 131 132 static int 133 checkshbinmagic(struct vnode *vp) 134 { 135 int error; 136 char linep[SHBINMAGIC_LEN]; 137 ssize_t resid; 138 139 /* 140 * Read the entire line and confirm that it starts with the magic 141 * sequence for compiled ksh93 shell scripts. 142 */ 143 if (error = vn_rdwr(UIO_READ, vp, linep, sizeof (linep), (offset_t)0, 144 UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) 145 return (error); 146 147 if (memcmp(linep, shbinmagicstr, SHBINMAGIC_LEN) != 0) 148 return (ENOEXEC); 149 150 return (0); 151 } 152 153 static int 154 shbinexec( 155 struct vnode *vp, 156 struct execa *uap, 157 struct uarg *args, 158 struct intpdata *idatap, 159 int level, 160 size_t *execsz, 161 int setid, 162 caddr_t exec_file, 163 struct cred *cred, 164 int brand_action) 165 { 166 _NOTE(ARGUNUSED(brand_action)) 167 vnode_t *nvp; 168 int error = 0; 169 struct intpdata idata; 170 struct pathname intppn; 171 struct pathname resolvepn; 172 char *opath; 173 char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */ 174 int fd = -1; 175 int i; 176 177 if (level) { /* Can't recurse */ 178 error = ENOEXEC; 179 goto bad; 180 } 181 182 ASSERT(idatap == (struct intpdata *)NULL); 183 184 /* 185 * Check whether the executable has the correct magic value. 186 */ 187 if (error = checkshbinmagic(vp)) 188 goto fail; 189 190 pn_alloc(&resolvepn); 191 192 /* 193 * Travel the list of shells and look for one which is available... 194 */ 195 for (i = 0; shell_list[i] != NULL; i++) { 196 error = pn_get(shell_list[i], UIO_SYSSPACE, &intppn); 197 if (error != 0) { 198 break; 199 } 200 201 error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp); 202 if (!error) { 203 /* Found match */ 204 break; 205 } 206 207 /* No match found ? Then continue with the next item... */ 208 pn_free(&intppn); 209 } 210 211 if (error) { 212 pn_free(&resolvepn); 213 goto fail; 214 } 215 216 /* 217 * Set up interpreter data 218 * "--" is passed to mark the end-of-arguments before adding 219 * the script's file name, preventing problems when a 220 * script's name starts with a '-' character. 221 */ 222 bzero(&idata, sizeof (intpdata_t)); 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 } 268