Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/exec/intp/intp.c
+++ new/usr/src/uts/common/exec/intp/intp.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 * Copyright 2012 Milan Jurik. All rights reserved.
25 25 * Copyright 2016, Joyent, Inc.
26 26 */
27 27
28 28 /* Copyright (c) 1988 AT&T */
29 29 /* All Rights Reserved */
30 30
31 31
32 32 /* from S5R4 1.6 */
33 33
34 34 #include <sys/types.h>
35 35 #include <sys/param.h>
36 36 #include <sys/sysmacros.h>
37 37 #include <sys/signal.h>
38 38 #include <sys/cred.h>
39 39 #include <sys/user.h>
40 40 #include <sys/errno.h>
41 41 #include <sys/vnode.h>
42 42 #include <sys/proc.h>
43 43 #include <sys/cmn_err.h>
44 44 #include <sys/debug.h>
45 45 #include <sys/pathname.h>
46 46 #include <sys/disp.h>
47 47 #include <sys/exec.h>
48 48 #include <sys/kmem.h>
49 49 #include <sys/note.h>
50 50 #include <sys/sdt.h>
51 51 #include <sys/brand.h>
52 52
53 53 /*
54 54 * This is the loadable module wrapper.
55 55 */
56 56 #include <sys/modctl.h>
57 57
58 58 extern int intpexec(struct vnode *, struct execa *, struct uarg *,
59 59 struct intpdata *, int, long *, int, caddr_t, struct cred *, int *);
60 60
61 61 static struct execsw esw = {
62 62 intpmagicstr,
63 63 0,
64 64 2,
65 65 intpexec,
66 66 NULL
67 67 };
68 68
69 69 /*
70 70 * Module linkage information for the kernel.
71 71 */
72 72 extern struct mod_ops mod_execops;
73 73
74 74 static struct modlexec modlexec = {
75 75 &mod_execops, "exec mod for interp", &esw
76 76 };
77 77
78 78 static struct modlinkage modlinkage = {
79 79 MODREV_1, (void *)&modlexec, NULL
80 80 };
81 81
82 82 int
83 83 _init()
84 84 {
85 85 return (mod_install(&modlinkage));
86 86 }
87 87
88 88 int
89 89 _fini()
90 90 {
91 91 return (mod_remove(&modlinkage));
92 92 }
93 93
94 94 int
95 95 _info(struct modinfo *modinfop)
96 96 {
97 97 return (mod_info(&modlinkage, modinfop));
98 98 }
99 99
100 100
101 101 /*
102 102 * Crack open a '#!' line.
103 103 */
104 104 static int
105 105 getintphead(struct vnode *vp, struct intpdata *idatap)
106 106 {
107 107 int error;
108 108 char *cp, *linep = idatap->intp;
109 109 ssize_t resid;
110 110
111 111 /*
112 112 * Read the entire line and confirm that it starts with '#!'.
113 113 */
114 114 if (error = vn_rdwr(UIO_READ, vp, linep, INTPSZ, (offset_t)0,
115 115 UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid))
116 116 return (error);
117 117 if (resid > INTPSZ-2 || linep[0] != '#' || linep[1] != '!')
118 118 return (ENOEXEC);
119 119 /*
120 120 * Blank all white space and find the newline.
121 121 */
122 122 for (cp = &linep[2]; cp < &linep[INTPSZ] && *cp != '\n'; cp++)
123 123 if (*cp == '\t')
124 124 *cp = ' ';
125 125 if (cp >= &linep[INTPSZ])
126 126 return (ENOEXEC);
127 127 ASSERT(*cp == '\n');
128 128 *cp = '\0';
129 129
130 130 /*
131 131 * Locate the beginning and end of the interpreter name. Historically,
132 132 * for illumos and its predecessors, in addition to the name, one
133 133 * additional argument may optionally be included here, to be prepended
134 134 * to the arguments provided on the command line. Thus, for example,
135 135 * you can say
136 136 *
137 137 * #! /usr/bin/awk -f
138 138 *
139 139 * However, handling of interpreter arguments varies across operating
140 140 * systems and other systems allow more than one argument. In
141 141 * particular, Linux allows more than one and delivers all arguments
142 142 * as a single string (argv[1] is "-arg1 -arg2 ..."). We support this
143 143 * style of argument handling as a brand-specific option (setting
144 144 * b_intp_parse_arg to B_FALSE).
145 145 */
146 146 for (cp = &linep[2]; *cp == ' '; cp++)
147 147 ;
148 148 if (*cp == '\0')
149 149 return (ENOEXEC);
150 150 idatap->intp_name[0] = cp;
151 151 while (*cp && *cp != ' ')
152 152 cp++;
153 153 if (*cp == '\0') {
154 154 idatap->intp_arg[0] = NULL;
155 155 } else {
156 156 *cp++ = '\0';
157 157 while (*cp == ' ')
158 158 cp++;
159 159 if (*cp == '\0')
160 160 idatap->intp_arg[0] = NULL;
161 161 else {
162 162 idatap->intp_arg[0] = cp;
163 163 if (!PROC_IS_BRANDED(curproc) ||
164 164 BROP(curproc)->b_intp_parse_arg) {
165 165 while (*cp && *cp != ' ')
166 166 cp++;
167 167 *cp = '\0';
168 168 }
169 169 }
170 170 }
171 171 return (0);
172 172 }
173 173
174 174 /*
175 175 * We support nested interpreters up to a depth of INTP_MAXDEPTH (this value
176 176 * matches the depth on Linux). When a nested interpreter is in use, the
177 177 * previous name and argument must be passed along. We use the intpdata_t
178 178 * name and argument arrays for this. In the normal, non-nested case, only the
179 179 * first element in those arrays will be populated.
180 180 *
181 181 * For setid scripts the "script hole" is a security race condition between
182 182 * when we exec the interpreter and when the interpreter reads the script. We
183 183 * handle this below for the initial script, but we don't allow setid scripts
184 184 * when using nested interpreters. Because gexec only modifies the credentials
185 185 * for a setid script at level 0, then if we come back through for a nested
186 186 * interpreter we know that args->fname will be set (the first script is setid)
187 187 * and we can return an error. If an intermediate nested interpreter is setid
188 188 * then it will not be run with different credentials because of the gexec
189 189 * handling, so it is effectively no longer setid and we don't have to worry
190 190 * about the "script hole".
191 191 */
192 192 int
193 193 intpexec(
194 194 struct vnode *vp,
195 195 struct execa *uap,
196 196 struct uarg *args,
197 197 struct intpdata *idatap,
198 198 int level,
199 199 long *execsz,
200 200 int setid,
201 201 caddr_t exec_file,
202 202 struct cred *cred,
203 203 int *brand_action)
204 204 {
205 205 vnode_t *nvp;
206 206 int error = 0;
207 207 struct intpdata idata;
208 208 struct pathname intppn;
209 209 struct pathname resolvepn;
210 210 char *opath;
211 211 char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */
212 212 int fd = -1;
213 213
214 214 if (level >= INTP_MAXDEPTH) { /* Can't recurse past maxdepth */
215 215 error = ELOOP;
216 216 goto bad;
217 217 }
218 218
219 219 if (level == 0)
220 220 ASSERT(idatap == (struct intpdata *)NULL);
221 221
222 222 bzero(&idata, sizeof (intpdata_t));
223 223
224 224 /*
225 225 * Allocate a buffer to read in the interpreter pathname.
226 226 */
227 227 idata.intp = kmem_alloc(INTPSZ, KM_SLEEP);
228 228 if (error = getintphead(vp, &idata))
229 229 goto fail;
230 230
231 231 /*
232 232 * Look the new vnode up.
233 233 */
234 234 if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &intppn))
235 235 goto fail;
236 236 pn_alloc(&resolvepn);
237 237 if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) {
238 238 pn_free(&resolvepn);
239 239 pn_free(&intppn);
240 240 goto fail;
241 241 }
242 242
243 243 if (level > 0) {
244 244 /*
245 245 * We have a nested interpreter. The previous name(s) and
246 246 * argument(s) need to be passed along. We also keep track
247 247 * of how often this zone uses nested interpreters.
248 248 */
249 249 int i;
250 250
251 251 atomic_inc_32(&curproc->p_zone->zone_nested_intp);
252 252
253 253 ASSERT(idatap != NULL);
254 254 /* since we're shifting up, loop stops one short */
255 255 for (i = 0; i < (INTP_MAXDEPTH - 1); i++) {
256 256 idata.intp_name[i + 1] = idatap->intp_name[i];
257 257 idata.intp_arg[i + 1] = idatap->intp_arg[i];
258 258 }
259 259
260 260 DTRACE_PROBE3(nested__intp, int, level, void *, &idata,
261 261 void *, nvp);
262 262 }
263 263
264 264 opath = args->pathname;
265 265 args->pathname = resolvepn.pn_path;
266 266 /* don't free resolvepn until we are done with args */
267 267 pn_free(&intppn);
268 268
269 269 /*
270 270 * Disallow setuid or additional privilege execution for nested
271 271 * interpreters.
272 272 */
273 273 if (level > 0 && args->fname != NULL) {
274 274 error = ENOEXEC;
275 275 goto done;
276 276 }
277 277
278 278 /*
279 279 * When we're executing a set-uid script resulting in uids
280 280 * mismatching or when we execute with additional privileges,
281 281 * we close the "replace script between exec and open by shell"
282 282 * hole by passing the script as /dev/fd parameter.
283 283 */
284 284 if ((setid & EXECSETID_PRIVS) != 0 ||
285 285 (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) ==
286 286 (EXECSETID_UGIDS|EXECSETID_SETID)) {
287 287 (void) strcpy(devfd, "/dev/fd/");
288 288 if (error = execopen(&vp, &fd))
289 289 goto done;
290 290 numtos(fd, &devfd[8]);
291 291 args->fname = devfd;
292 292 }
293 293
294 294 error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
295 295 brand_action);
296 296
297 297 if (!error) {
298 298 /*
299 299 * Close this executable as the interpreter
300 300 * will open and close it later on.
301 301 */
302 302 (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL);
303 303 }
304 304 done:
305 305 VN_RELE(nvp);
306 306 args->pathname = opath;
307 307 pn_free(&resolvepn);
308 308 fail:
309 309 kmem_free(idata.intp, INTPSZ);
310 310 if (error && fd != -1)
311 311 (void) execclose(fd);
312 312 bad:
313 313 return (error);
314 314 }
|
↓ open down ↓ |
314 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX