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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
28
29
30 /* from S5R4 1.6 */
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/sysmacros.h>
35 #include <sys/signal.h>
36 #include <sys/cred.h>
37 #include <sys/user.h>
38 #include <sys/errno.h>
39 #include <sys/vnode.h>
40 #include <sys/proc.h>
41 #include <sys/cmn_err.h>
42 #include <sys/debug.h>
43 #include <sys/pathname.h>
44 #include <sys/disp.h>
45 #include <sys/exec.h>
46 #include <sys/kmem.h>
47 #include <sys/note.h>
48
49 /*
50 * This is the loadable module wrapper.
51 */
52 #include <sys/modctl.h>
53
54 extern int intpexec();
55
56 static struct execsw esw = {
57 intpmagicstr,
58 0,
59 2,
60 intpexec,
61 NULL
62 };
63
64 /*
65 * Module linkage information for the kernel.
66 */
67 extern struct mod_ops mod_execops;
68
69 static struct modlexec modlexec = {
70 &mod_execops, "exec mod for interp", &esw
71 };
72
73 static struct modlinkage modlinkage = {
74 MODREV_1, (void *)&modlexec, NULL
75 };
76
77 int
_init()78 _init()
79 {
80 return (mod_install(&modlinkage));
81 }
82
83 int
_fini()84 _fini()
85 {
86 return (mod_remove(&modlinkage));
87 }
88
89 int
_info(struct modinfo * modinfop)90 _info(struct modinfo *modinfop)
91 {
92 return (mod_info(&modlinkage, modinfop));
93 }
94
95
96 /*
97 * Crack open a '#!' line.
98 */
99 static int
getintphead(struct vnode * vp,struct intpdata * idatap)100 getintphead(struct vnode *vp, struct intpdata *idatap)
101 {
102 int error;
103 char *cp, *linep = idatap->intp;
104 ssize_t resid;
105
106 /*
107 * Read the entire line and confirm that it starts with '#!'.
108 */
109 if (error = vn_rdwr(UIO_READ, vp, linep, INTPSZ, (offset_t)0,
110 UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid))
111 return (error);
112 if (resid > INTPSZ-2 || linep[0] != '#' || linep[1] != '!')
113 return (ENOEXEC);
114 /*
115 * Blank all white space and find the newline.
116 */
117 for (cp = &linep[2]; cp < &linep[INTPSZ] && *cp != '\n'; cp++)
118 if (*cp == '\t')
119 *cp = ' ';
120 if (cp >= &linep[INTPSZ])
121 return (ENOEXEC);
122 ASSERT(*cp == '\n');
123 *cp = '\0';
124
125 /*
126 * Locate the beginning and end of the interpreter name.
127 * In addition to the name, one additional argument may
128 * optionally be included here, to be prepended to the
129 * arguments provided on the command line. Thus, for
130 * example, you can say
131 *
132 * #! /usr/bin/awk -f
133 */
134 for (cp = &linep[2]; *cp == ' '; cp++)
135 ;
136 if (*cp == '\0')
137 return (ENOEXEC);
138 idatap->intp_name = cp;
139 while (*cp && *cp != ' ')
140 cp++;
141 if (*cp == '\0')
142 idatap->intp_arg = NULL;
143 else {
144 *cp++ = '\0';
145 while (*cp == ' ')
146 cp++;
147 if (*cp == '\0')
148 idatap->intp_arg = NULL;
149 else {
150 idatap->intp_arg = cp;
151 while (*cp && *cp != ' ')
152 cp++;
153 *cp = '\0';
154 }
155 }
156 return (0);
157 }
158
159 int
intpexec(struct vnode * vp,struct execa * uap,struct uarg * args,struct intpdata * idatap,int level,long * execsz,int setid,caddr_t exec_file,struct cred * cred,int brand_action)160 intpexec(
161 struct vnode *vp,
162 struct execa *uap,
163 struct uarg *args,
164 struct intpdata *idatap,
165 int level,
166 long *execsz,
167 int setid,
168 caddr_t exec_file,
169 struct cred *cred,
170 int brand_action)
171 {
172 _NOTE(ARGUNUSED(brand_action))
173 vnode_t *nvp;
174 int error = 0;
175 struct intpdata idata;
176 struct pathname intppn;
177 struct pathname resolvepn;
178 char *opath;
179 char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */
180 int fd = -1;
181
182 if (level) { /* Can't recurse */
183 error = ENOEXEC;
184 goto bad;
185 }
186
187 ASSERT(idatap == (struct intpdata *)NULL);
188
189 /*
190 * Allocate a buffer to read in the interpreter pathname.
191 */
192 idata.intp = kmem_alloc(INTPSZ, KM_SLEEP);
193 if (error = getintphead(vp, &idata))
194 goto fail;
195
196 /*
197 * Look the new vnode up.
198 */
199 if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &intppn))
200 goto fail;
201 pn_alloc(&resolvepn);
202 if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) {
203 pn_free(&resolvepn);
204 pn_free(&intppn);
205 goto fail;
206 }
207 opath = args->pathname;
208 args->pathname = resolvepn.pn_path;
209 /* don't free resolvepn until we are done with args */
210 pn_free(&intppn);
211
212 /*
213 * When we're executing a set-uid script resulting in uids
214 * mismatching or when we execute with additional privileges,
215 * we close the "replace script between exec and open by shell"
216 * hole by passing the script as /dev/fd parameter.
217 */
218 if ((setid & EXECSETID_PRIVS) != 0 ||
219 (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) ==
220 (EXECSETID_UGIDS|EXECSETID_SETID)) {
221 (void) strcpy(devfd, "/dev/fd/");
222 if (error = execopen(&vp, &fd))
223 goto done;
224 numtos(fd, &devfd[8]);
225 args->fname = devfd;
226 }
227
228 error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
229 EBA_NONE);
230
231 if (!error) {
232 /*
233 * Close this executable as the interpreter
234 * will open and close it later on.
235 */
236 (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL);
237 }
238 done:
239 VN_RELE(nvp);
240 args->pathname = opath;
241 pn_free(&resolvepn);
242 fail:
243 kmem_free(idata.intp, INTPSZ);
244 if (error && fd != -1)
245 (void) execclose(fd);
246 bad:
247 return (error);
248 }
249