xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/roken/simple_exec.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1 /*	$NetBSD: simple_exec.c,v 1.2 2017/01/28 21:31:50 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1998 - 2001, 2004 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_WAIT_H
44 #include <sys/wait.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <errno.h>
50 
51 #include <krb5/roken.h>
52 
53 #define EX_NOEXEC	126
54 #define EX_NOTFOUND	127
55 
56 /* return values:
57    SE_E_UNSPECIFIED   on `unspecified' system errors
58    SE_E_FORKFAILED    on fork failures
59    SE_E_WAITPIDFAILED on waitpid errors
60    SE_E_EXECTIMEOUT   exec timeout
61    0-   is return value from subprocess
62    SE_E_NOEXEC        if the program couldn't be executed
63    SE_E_NOTFOUND      if the program couldn't be found
64    128- is 128 + signal that killed subprocess
65 
66    possible values `func' can return:
67    ((time_t)-2)		exit loop w/o killing child and return
68    			`exec timeout'/-4 from simple_exec
69    ((time_t)-1)		kill child with SIGTERM and wait for child to exit
70    0			don't timeout again
71    n			seconds to next timeout
72    */
73 
74 static int sig_alarm;
75 
76 static RETSIGTYPE
sigtimeout(int sig)77 sigtimeout(int sig)
78 {
79     sig_alarm = 1;
80     SIGRETURN(0);
81 }
82 
83 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
wait_for_process_timed(pid_t pid,time_t (* func)(void *),void * ptr,time_t timeout)84 wait_for_process_timed(pid_t pid, time_t (*func)(void *),
85 		       void *ptr, time_t timeout)
86 {
87     RETSIGTYPE (*old_func)(int sig) = NULL;
88     unsigned int oldtime = 0;
89     int ret;
90 
91     sig_alarm = 0;
92 
93     if (func) {
94 	old_func = signal(SIGALRM, sigtimeout);
95 	oldtime = alarm(timeout);
96     }
97 
98     while(1) {
99 	int status;
100 
101 	while(waitpid(pid, &status, 0) < 0) {
102 	    if (errno != EINTR) {
103 		ret = SE_E_WAITPIDFAILED;
104 		goto out;
105 	    }
106 	    if (func == NULL)
107 		continue;
108 	    if (sig_alarm == 0)
109 		continue;
110 	    timeout = (*func)(ptr);
111 	    if (timeout == (time_t)-1) {
112 		kill(pid, SIGTERM);
113 		continue;
114 	    } else if (timeout == (time_t)-2) {
115 		ret = SE_E_EXECTIMEOUT;
116 		goto out;
117 	    }
118 	    alarm(timeout);
119 	}
120 	if(WIFSTOPPED(status))
121 	    continue;
122 	if(WIFEXITED(status)) {
123 	    ret = WEXITSTATUS(status);
124 	    break;
125 	}
126 	if(WIFSIGNALED(status)) {
127 	    ret = WTERMSIG(status) + 128;
128 	    break;
129 	}
130     }
131  out:
132     if (func) {
133 	signal(SIGALRM, old_func);
134 	alarm(oldtime);
135     }
136     return ret;
137 }
138 
139 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
wait_for_process(pid_t pid)140 wait_for_process(pid_t pid)
141 {
142     return wait_for_process_timed(pid, NULL, NULL, 0);
143 }
144 
145 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
pipe_execv(FILE ** stdin_fd,FILE ** stdout_fd,FILE ** stderr_fd,const char * file,...)146 pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
147 	   const char *file, ...)
148 {
149     int in_fd[2] = {-1, -1};
150     int out_fd[2] = {-1, -1};
151     int err_fd[2] = {-1, -1};
152     pid_t pid;
153     va_list ap;
154     char **argv;
155     int ret = 0;
156 
157     if(stdin_fd != NULL)
158 	ret = pipe(in_fd);
159     if(ret != -1 && stdout_fd != NULL)
160 	ret = pipe(out_fd);
161     if(ret != -1 && stderr_fd != NULL)
162 	ret = pipe(err_fd);
163 
164     if (ret == -1) {
165 	close(in_fd[0]);
166 	close(in_fd[1]);
167 	close(out_fd[0]);
168 	close(out_fd[1]);
169 	close(err_fd[0]);
170 	close(err_fd[1]);
171 	return SE_E_UNSPECIFIED;
172     }
173 
174     pid = fork();
175     switch(pid) {
176     case 0:
177 	va_start(ap, file);
178 	argv = vstrcollect(&ap);
179 	va_end(ap);
180 	if(argv == NULL)
181 	    exit(-1);
182 
183 	/* close pipes we're not interested in */
184 	if(stdin_fd != NULL)
185 	    close(in_fd[1]);
186 	if(stdout_fd != NULL)
187 	    close(out_fd[0]);
188 	if(stderr_fd != NULL)
189 	    close(err_fd[0]);
190 
191 	/* pipe everything caller doesn't care about to /dev/null */
192 	if(stdin_fd == NULL)
193 	    in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
194 	if(stdout_fd == NULL)
195 	    out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
196 	if(stderr_fd == NULL)
197 	    err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
198 
199 	/* move to proper descriptors */
200 	if(in_fd[0] != STDIN_FILENO) {
201 	    dup2(in_fd[0], STDIN_FILENO);
202 	    close(in_fd[0]);
203 	}
204 	if(out_fd[1] != STDOUT_FILENO) {
205 	    dup2(out_fd[1], STDOUT_FILENO);
206 	    close(out_fd[1]);
207 	}
208 	if(err_fd[1] != STDERR_FILENO) {
209 	    dup2(err_fd[1], STDERR_FILENO);
210 	    close(err_fd[1]);
211 	}
212 
213 	closefrom(3);
214 
215 	execv(file, argv);
216 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
217     case -1:
218 	if(stdin_fd != NULL) {
219 	    close(in_fd[0]);
220 	    close(in_fd[1]);
221 	}
222 	if(stdout_fd != NULL) {
223 	    close(out_fd[0]);
224 	    close(out_fd[1]);
225 	}
226 	if(stderr_fd != NULL) {
227 	    close(err_fd[0]);
228 	    close(err_fd[1]);
229 	}
230 	return SE_E_FORKFAILED;
231     default:
232 	if(stdin_fd != NULL) {
233 	    close(in_fd[0]);
234 	    *stdin_fd = fdopen(in_fd[1], "w");
235 	}
236 	if(stdout_fd != NULL) {
237 	    close(out_fd[1]);
238 	    *stdout_fd = fdopen(out_fd[0], "r");
239 	}
240 	if(stderr_fd != NULL) {
241 	    close(err_fd[1]);
242 	    *stderr_fd = fdopen(err_fd[0], "r");
243 	}
244     }
245     return pid;
246 }
247 
248 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execvp_timed(const char * file,char * const args[],time_t (* func)(void *),void * ptr,time_t timeout)249 simple_execvp_timed(const char *file, char *const args[],
250 		    time_t (*func)(void *), void *ptr, time_t timeout)
251 {
252     pid_t pid = fork();
253     switch(pid){
254     case -1:
255 	return SE_E_FORKFAILED;
256     case 0:
257 	execvp(file, args);
258 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
259     default:
260 	return wait_for_process_timed(pid, func, ptr, timeout);
261     }
262 }
263 
264 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execvp(const char * file,char * const args[])265 simple_execvp(const char *file, char *const args[])
266 {
267     return simple_execvp_timed(file, args, NULL, NULL, 0);
268 }
269 
270 /* gee, I'd like a execvpe */
271 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execve_timed(const char * file,char * const args[],char * const envp[],time_t (* func)(void *),void * ptr,time_t timeout)272 simple_execve_timed(const char *file, char *const args[], char *const envp[],
273 		    time_t (*func)(void *), void *ptr, time_t timeout)
274 {
275     pid_t pid = fork();
276     switch(pid){
277     case -1:
278 	return SE_E_FORKFAILED;
279     case 0:
280 	execve(file, args, envp);
281 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
282     default:
283 	return wait_for_process_timed(pid, func, ptr, timeout);
284     }
285 }
286 
287 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execve(const char * file,char * const args[],char * const envp[])288 simple_execve(const char *file, char *const args[], char *const envp[])
289 {
290     return simple_execve_timed(file, args, envp, NULL, NULL, 0);
291 }
292 
293 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execlp(const char * file,...)294 simple_execlp(const char *file, ...)
295 {
296     va_list ap;
297     char **argv;
298     int ret;
299 
300     va_start(ap, file);
301     argv = vstrcollect(&ap);
302     va_end(ap);
303     if(argv == NULL)
304 	return SE_E_UNSPECIFIED;
305     ret = simple_execvp(file, argv);
306     free(argv);
307     return ret;
308 }
309 
310 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execle(const char * file,...)311 simple_execle(const char *file, ... /* ,char *const envp[] */)
312 {
313     va_list ap;
314     char **argv;
315     char *const* envp;
316     int ret;
317 
318     va_start(ap, file);
319     argv = vstrcollect(&ap);
320     envp = va_arg(ap, char **);
321     va_end(ap);
322     if(argv == NULL)
323 	return SE_E_UNSPECIFIED;
324     ret = simple_execve(file, argv, envp);
325     free(argv);
326     return ret;
327 }
328