1/*	$OpenBSD: exec.c,v 1.25 2023/09/06 03:51:20 jsg Exp $ */
2/*-
3 * Copyright (c) 1991, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/types.h>
32#include <sys/uio.h>
33#include <sys/mman.h>
34
35#include <errno.h>
36#include <limits.h>
37#include <paths.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44int
45execl(const char *name, const char *arg, ...)
46{
47	va_list ap;
48	char **argv;
49	size_t maplen;
50	int save_errno, n, error;
51
52	va_start(ap, arg);
53	n = 1;
54	while (va_arg(ap, char *) != NULL)
55		n++;
56	va_end(ap);
57
58	maplen = (n + 1) * sizeof(*argv);
59	argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
60	    MAP_ANON|MAP_PRIVATE, -1, 0);
61	if (argv == MAP_FAILED)
62		return (-1);
63
64	va_start(ap, arg);
65	n = 1;
66	argv[0] = (char *)arg;
67	while ((argv[n] = va_arg(ap, char *)) != NULL)
68		n++;
69	va_end(ap);
70
71	error = execve(name, argv, environ);
72	save_errno = errno;
73	munmap(argv, maplen);
74	errno = save_errno;
75	return (error);
76}
77DEF_WEAK(execl);
78
79int
80execle(const char *name, const char *arg, ...)
81{
82	va_list ap;
83	char **argv, **envp;
84	size_t maplen;
85	int save_errno, n, error;
86
87	va_start(ap, arg);
88	n = 1;
89	while (va_arg(ap, char *) != NULL)
90		n++;
91	va_end(ap);
92
93	maplen = (n + 1) * sizeof(*argv);
94	argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
95	    MAP_ANON|MAP_PRIVATE, -1, 0);
96	if (argv == MAP_FAILED)
97		return (-1);
98
99	va_start(ap, arg);
100	n = 1;
101	argv[0] = (char *)arg;
102	while ((argv[n] = va_arg(ap, char *)) != NULL)
103		n++;
104	envp = va_arg(ap, char **);
105	va_end(ap);
106
107	error = execve(name, argv, envp);
108	save_errno = errno;
109	munmap(argv, maplen);
110	errno = save_errno;
111	return error;
112}
113
114int
115execlp(const char *name, const char *arg, ...)
116{
117	va_list ap;
118	char **argv;
119	size_t maplen;
120	int save_errno, n, error;
121
122	va_start(ap, arg);
123	n = 1;
124	while (va_arg(ap, char *) != NULL)
125		n++;
126	va_end(ap);
127
128	maplen = (n + 1) * sizeof(*argv);
129	argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
130	    MAP_ANON|MAP_PRIVATE, -1, 0);
131	if (argv == MAP_FAILED)
132		return (-1);
133
134	va_start(ap, arg);
135	n = 1;
136	argv[0] = (char *)arg;
137	while ((argv[n] = va_arg(ap, char *)) != NULL)
138		n++;
139	va_end(ap);
140	error = execvp(name, argv);
141	save_errno = errno;
142	munmap(argv, maplen);
143	errno = save_errno;
144	return error;
145}
146
147int
148execv(const char *name, char *const *argv)
149{
150	(void)execve(name, argv, environ);
151	return (-1);
152}
153
154int
155execvpe(const char *name, char *const *argv, char *const *envp)
156{
157	char **memp;
158	int cnt;
159	size_t lp, ln, curlen;
160	char *p;
161	int eacces = 0;
162	char *bp, *cur, *path, buf[PATH_MAX];
163	size_t maplen;
164	int save_errno;
165
166	/*
167	 * Do not allow null name
168	 */
169	if (name == NULL || *name == '\0') {
170		errno = ENOENT;
171		return (-1);
172 	}
173
174	/* If it's an absolute or relative path name, it's easy. */
175	if (strchr(name, '/')) {
176		bp = (char *)name;
177		cur = path = NULL;
178		goto retry;
179	}
180	bp = buf;
181
182	/* Get the path we're searching. */
183	if (!(path = getenv("PATH")))
184		path = _PATH_DEFPATH;
185
186	curlen = strlen(path) + 1;
187	cur = mmap(NULL, curlen, PROT_WRITE|PROT_READ,
188	    MAP_ANON|MAP_PRIVATE, -1, 0);
189	if (cur == MAP_FAILED)
190		return (-1);
191
192	strlcpy(cur, path, curlen);
193	path = cur;
194	while ((p = strsep(&cur, ":"))) {
195		/*
196		 * It's a SHELL path -- double, leading and trailing colons
197		 * mean the current directory.
198		 */
199		if (!*p) {
200			p = ".";
201			lp = 1;
202		} else
203			lp = strlen(p);
204		ln = strlen(name);
205
206		/*
207		 * If the path is too long complain.  This is a possible
208		 * security issue; given a way to make the path too long
209		 * the user may execute the wrong program.
210		 */
211		if (lp + ln + 2 > sizeof(buf)) {
212			struct iovec iov[3];
213
214			iov[0].iov_base = "execvp: ";
215			iov[0].iov_len = 8;
216			iov[1].iov_base = p;
217			iov[1].iov_len = lp;
218			iov[2].iov_base = ": path too long\n";
219			iov[2].iov_len = 16;
220			(void)writev(STDERR_FILENO, iov, 3);
221			continue;
222		}
223		bcopy(p, buf, lp);
224		buf[lp] = '/';
225		bcopy(name, buf + lp + 1, ln);
226		buf[lp + ln + 1] = '\0';
227
228retry:		(void)execve(bp, argv, envp);
229		switch(errno) {
230		case E2BIG:
231			goto done;
232		case EISDIR:
233		case ELOOP:
234		case ENAMETOOLONG:
235		case ENOENT:
236			break;
237		case ENOEXEC:
238			for (cnt = 0; argv[cnt]; ++cnt)
239				;
240
241			maplen = (cnt + 2) * sizeof(char *);
242			memp = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
243			    MAP_ANON|MAP_PRIVATE, -1, 0);
244			if (memp == MAP_FAILED)
245				goto done;
246
247			memp[0] = "sh";
248			memp[1] = bp;
249			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
250			(void)execve(_PATH_BSHELL, memp, envp);
251			save_errno = errno;
252			munmap(memp, maplen);
253			errno = save_errno;
254			goto done;
255		case ENOMEM:
256			goto done;
257		case ENOTDIR:
258			break;
259		case ETXTBSY:
260			/*
261			 * We used to retry here, but sh(1) doesn't.
262			 */
263			goto done;
264		case EACCES:
265			eacces = 1;
266			break;
267		default:
268			goto done;
269		}
270	}
271	if (eacces)
272		errno = EACCES;
273	else if (!errno)
274		errno = ENOENT;
275done:
276	if (cur) {
277		save_errno = errno;
278		munmap(cur, curlen);
279		errno = save_errno;
280	}
281	return (-1);
282}
283DEF_WEAK(execvpe);
284
285int
286execvp(const char *name, char *const *argv)
287{
288    return execvpe(name, argv, environ);
289}
290DEF_WEAK(execvp);
291
292