]> git.armaanb.net Git - opendoas.git/blob - libopenbsd/execvpe.c
libopenbsd/closefrom.c: sync with sudo
[opendoas.git] / libopenbsd / execvpe.c
1 /*      $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther 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 "includes.h"
32
33 #include <sys/types.h>
34 #include <sys/uio.h>
35
36 #include <errno.h>
37 #include <limits.h>
38 #include <paths.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 int
46 execvpe(const char *name, char *const *argv, char *const *envp)
47 {
48         char **memp;
49         int cnt;
50         size_t lp, ln, len;
51         char *p;
52         int eacces = 0;
53         char *bp, *cur, *path, buf[PATH_MAX];
54
55         /*
56          * Do not allow null name
57          */
58         if (name == NULL || *name == '\0') {
59                 errno = ENOENT;
60                 return (-1);
61         }
62
63         /* If it's an absolute or relative path name, it's easy. */
64         if (strchr(name, '/')) {
65                 bp = (char *)name;
66                 cur = path = NULL;
67                 goto retry;
68         }
69         bp = buf;
70
71         /* Get the path we're searching. */
72         if (!(path = getenv("PATH")))
73                 path = _PATH_DEFPATH;
74         len = strlen(path) + 1;
75         cur = alloca(len);
76         if (cur == NULL) {
77                 errno = ENOMEM;
78                 return (-1);
79         }
80         strlcpy(cur, path, len);
81         path = cur;
82         while ((p = strsep(&cur, ":"))) {
83                 /*
84                  * It's a SHELL path -- double, leading and trailing colons
85                  * mean the current directory.
86                  */
87                 if (!*p) {
88                         p = ".";
89                         lp = 1;
90                 } else
91                         lp = strlen(p);
92                 ln = strlen(name);
93
94                 /*
95                  * If the path is too long complain.  This is a possible
96                  * security issue; given a way to make the path too long
97                  * the user may execute the wrong program.
98                  */
99                 if (lp + ln + 2 > sizeof(buf)) {
100                         struct iovec iov[3];
101
102                         iov[0].iov_base = "execvp: ";
103                         iov[0].iov_len = 8;
104                         iov[1].iov_base = p;
105                         iov[1].iov_len = lp;
106                         iov[2].iov_base = ": path too long\n";
107                         iov[2].iov_len = 16;
108                         (void)writev(STDERR_FILENO, iov, 3);
109                         continue;
110                 }
111                 bcopy(p, buf, lp);
112                 buf[lp] = '/';
113                 bcopy(name, buf + lp + 1, ln);
114                 buf[lp + ln + 1] = '\0';
115
116 retry:          (void)execve(bp, argv, envp);
117                 switch(errno) {
118                 case E2BIG:
119                         goto done;
120                 case EISDIR:
121                 case ELOOP:
122                 case ENAMETOOLONG:
123                 case ENOENT:
124                         break;
125                 case ENOEXEC:
126                         for (cnt = 0; argv[cnt]; ++cnt)
127                                 ;
128                         memp = alloca((cnt + 2) * sizeof(char *));
129                         if (memp == NULL)
130                                 goto done;
131                         memp[0] = "sh";
132                         memp[1] = bp;
133                         bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
134                         (void)execve(_PATH_BSHELL, memp, envp);
135                         goto done;
136                 case ENOMEM:
137                         goto done;
138                 case ENOTDIR:
139                         break;
140                 case ETXTBSY:
141                         /*
142                          * We used to retry here, but sh(1) doesn't.
143                          */
144                         goto done;
145                 case EACCES:
146                         eacces = 1;
147                         break;
148                 default:
149                         goto done;
150                 }
151         }
152         if (eacces)
153                 errno = EACCES;
154         else if (!errno)
155                 errno = ENOENT;
156 done:
157         return (-1);
158 }