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