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