1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999 Brian Somers <[email protected]>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sysexits.h>
41 #include <sys/wait.h>
42 #include <sys/stat.h>
43 #include <sys/uio.h>
44 #include <termios.h>
45 #include <unistd.h>
46
47 #include "layer.h"
48 #include "defs.h"
49 #include "mbuf.h"
50 #include "log.h"
51 #include "timer.h"
52 #include "lqr.h"
53 #include "hdlc.h"
54 #include "throughput.h"
55 #include "fsm.h"
56 #include "lcp.h"
57 #include "ccp.h"
58 #include "link.h"
59 #include "async.h"
60 #include "descriptor.h"
61 #include "physical.h"
62 #include "mp.h"
63 #include "chat.h"
64 #include "command.h"
65 #include "auth.h"
66 #include "chap.h"
67 #include "cbcp.h"
68 #include "datalink.h"
69 #include "id.h"
70 #include "main.h"
71 #include "exec.h"
72
73
74 struct execdevice {
75 struct device dev; /* What struct physical knows about */
76 int fd_out; /* output descriptor */
77 };
78
79 #define device2exec(d) ((d)->type == EXEC_DEVICE ? (struct execdevice *)d : NULL)
80
81 unsigned
exec_DeviceSize(void)82 exec_DeviceSize(void)
83 {
84 return sizeof(struct execdevice);
85 }
86
87 static void
exec_Free(struct physical * p)88 exec_Free(struct physical *p)
89 {
90 struct execdevice *dev = device2exec(p->handler);
91
92 if (dev->fd_out != -1)
93 close(dev->fd_out);
94 free(dev);
95 }
96
97 static void
exec_device2iov(struct device * d,struct iovec * iov,int * niov,int maxiov __unused,int * auxfd,int * nauxfd)98 exec_device2iov(struct device *d, struct iovec *iov, int *niov,
99 int maxiov __unused, int *auxfd, int *nauxfd)
100 {
101 struct execdevice *dev;
102 int sz = physical_MaxDeviceSize();
103
104 iov[*niov].iov_base = d = realloc(d, sz);
105 if (d == NULL) {
106 log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
107 AbortProgram(EX_OSERR);
108 }
109 iov[*niov].iov_len = sz;
110 (*niov)++;
111
112 dev = device2exec(d);
113 if (dev->fd_out >= 0) {
114 *auxfd = dev->fd_out;
115 (*nauxfd)++;
116 }
117 }
118
119 static int
exec_RemoveFromSet(struct physical * p,fd_set * r,fd_set * w,fd_set * e)120 exec_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
121 {
122 struct execdevice *dev = device2exec(p->handler);
123 int sets;
124
125 p->handler->removefromset = NULL;
126 sets = physical_RemoveFromSet(p, r, w, e);
127 p->handler->removefromset = exec_RemoveFromSet;
128
129 if (dev->fd_out >= 0) {
130 if (w && FD_ISSET(dev->fd_out, w)) {
131 FD_CLR(dev->fd_out, w);
132 log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, dev->fd_out);
133 sets++;
134 }
135 if (e && FD_ISSET(dev->fd_out, e)) {
136 FD_CLR(dev->fd_out, e);
137 log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, dev->fd_out);
138 sets++;
139 }
140 }
141
142 return sets;
143 }
144
145 static ssize_t
exec_Write(struct physical * p,const void * v,size_t n)146 exec_Write(struct physical *p, const void *v, size_t n)
147 {
148 struct execdevice *dev = device2exec(p->handler);
149 int fd = dev->fd_out == -1 ? p->fd : dev->fd_out;
150
151 return write(fd, v, n);
152 }
153
154 static struct device baseexecdevice = {
155 EXEC_DEVICE,
156 "exec",
157 0,
158 { CD_NOTREQUIRED, 0 },
159 NULL,
160 exec_RemoveFromSet,
161 NULL,
162 NULL,
163 NULL,
164 NULL,
165 NULL,
166 exec_Free,
167 NULL,
168 exec_Write,
169 exec_device2iov,
170 NULL,
171 NULL,
172 NULL
173 };
174
175 struct device *
exec_iov2device(int type,struct physical * p,struct iovec * iov,int * niov,int maxiov __unused,int * auxfd,int * nauxfd)176 exec_iov2device(int type, struct physical *p, struct iovec *iov,
177 int *niov, int maxiov __unused, int *auxfd, int *nauxfd)
178 {
179 if (type == EXEC_DEVICE) {
180 struct execdevice *dev = (struct execdevice *)iov[(*niov)++].iov_base;
181
182 dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
183 if (dev == NULL) {
184 log_Printf(LogALERT, "Failed to allocate memory: %d\n",
185 (int)(sizeof *dev));
186 AbortProgram(EX_OSERR);
187 }
188
189 if (*nauxfd) {
190 dev->fd_out = *auxfd;
191 (*nauxfd)--;
192 } else
193 dev->fd_out = -1;
194
195 /* Refresh function pointers etc */
196 memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
197
198 physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
199 return &dev->dev;
200 }
201
202 return NULL;
203 }
204
205 static int
exec_UpdateSet(struct fdescriptor * d,fd_set * r,fd_set * w,fd_set * e,int * n)206 exec_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
207 {
208 struct physical *p = descriptor2physical(d);
209 struct execdevice *dev = device2exec(p->handler);
210 int result = 0;
211
212 if (w && dev->fd_out >= 0) {
213 FD_SET(dev->fd_out, w);
214 log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, dev->fd_out);
215 result++;
216 w = NULL;
217 }
218
219 if (e && dev->fd_out >= 0) {
220 FD_SET(dev->fd_out, e);
221 log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, dev->fd_out);
222 result++;
223 }
224
225 if (result && *n <= dev->fd_out)
226 *n = dev->fd_out + 1;
227
228 return result + physical_doUpdateSet(d, r, w, e, n, 0);
229 }
230
231 static int
exec_IsSet(struct fdescriptor * d,const fd_set * fdset)232 exec_IsSet(struct fdescriptor *d, const fd_set *fdset)
233 {
234 struct physical *p = descriptor2physical(d);
235 struct execdevice *dev = device2exec(p->handler);
236 int result = dev->fd_out >= 0 && FD_ISSET(dev->fd_out, fdset);
237 result += physical_IsSet(d, fdset);
238
239 return result;
240 }
241
242 struct device *
exec_Create(struct physical * p)243 exec_Create(struct physical *p)
244 {
245 struct execdevice *dev;
246
247 dev = NULL;
248 if (p->fd < 0) {
249 if (*p->name.full == '!') {
250 int fids[2], type;
251
252 if ((dev = malloc(sizeof *dev)) == NULL) {
253 log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
254 p->link.name, strerror(errno));
255 return NULL;
256 }
257 dev->fd_out = -1;
258
259 p->fd--; /* We own the device but maybe can't use it - change fd */
260 type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
261
262 if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) {
263 log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
264 strerror(errno));
265 free(dev);
266 dev = NULL;
267 } else {
268 static int child_status; /* This variable is abused ! */
269 int stat, argc, i, ret, wret, pidpipe[2];
270 pid_t pid, realpid;
271 char *argv[MAXARGS];
272
273 stat = fcntl(fids[0], F_GETFL, 0);
274 if (stat > 0) {
275 stat |= O_NONBLOCK;
276 fcntl(fids[0], F_SETFL, stat);
277 }
278 realpid = getpid();
279 if (pipe(pidpipe) == -1) {
280 log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
281 strerror(errno));
282 close(fids[1]);
283 close(fids[0]);
284 free(dev);
285 dev = NULL;
286 } else switch ((pid = fork())) {
287 case -1:
288 log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
289 strerror(errno));
290 close(pidpipe[0]);
291 close(pidpipe[1]);
292 close(fids[1]);
293 close(fids[0]);
294 break;
295
296 case 0:
297 close(pidpipe[0]);
298 close(fids[0]);
299 timer_TermService();
300 #ifndef NOSUID
301 setuid(ID0realuid());
302 #endif
303
304 child_status = 0;
305 switch ((pid = vfork())) {
306 case 0:
307 close(pidpipe[1]);
308 break;
309
310 case -1:
311 ret = errno;
312 log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
313 strerror(errno));
314 close(pidpipe[1]);
315 _exit(ret);
316
317 default:
318 write(pidpipe[1], &pid, sizeof pid);
319 close(pidpipe[1]);
320 _exit(child_status); /* The error from exec() ! */
321 }
322
323 log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
324
325 if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
326 PARSE_REDUCE|PARSE_NOHASH)) < 0) {
327 log_Printf(LogWARN, "Syntax error in exec command\n");
328 _exit(ESRCH);
329 }
330
331 command_Expand(argv, argc, (char const *const *)argv,
332 p->dl->bundle, 0, realpid);
333
334 dup2(fids[1], STDIN_FILENO);
335 dup2(fids[1], STDOUT_FILENO);
336 dup2(fids[1], STDERR_FILENO);
337 for (i = getdtablesize(); i > STDERR_FILENO; i--)
338 fcntl(i, F_SETFD, 1);
339
340 execvp(*argv, argv);
341 child_status = errno; /* Only works for vfork() */
342 printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
343 _exit(child_status);
344 break;
345
346 default:
347 close(pidpipe[1]);
348 close(fids[1]);
349 if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
350 sizeof p->session_owner)
351 p->session_owner = (pid_t)-1;
352 close(pidpipe[0]);
353 while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
354 ;
355 if (wret == -1) {
356 log_Printf(LogWARN, "Waiting for child process: %s\n",
357 strerror(errno));
358 close(fids[0]);
359 p->session_owner = (pid_t)-1;
360 break;
361 } else if (WIFSIGNALED(stat)) {
362 log_Printf(LogWARN, "Child process received sig %d !\n",
363 WTERMSIG(stat));
364 close(fids[0]);
365 p->session_owner = (pid_t)-1;
366 break;
367 } else if (WIFSTOPPED(stat)) {
368 log_Printf(LogWARN, "Child process received stop sig %d !\n",
369 WSTOPSIG(stat));
370 /* I guess that's ok.... */
371 } else if ((ret = WEXITSTATUS(stat))) {
372 log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
373 strerror(ret));
374 close(fids[0]);
375 p->session_owner = (pid_t)-1;
376 break;
377 }
378 p->fd = fids[0];
379 log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
380 }
381 }
382 }
383 } else {
384 struct stat st;
385
386 if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) {
387 if ((dev = malloc(sizeof *dev)) == NULL)
388 log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
389 p->link.name, strerror(errno));
390 else if (p->fd == STDIN_FILENO) {
391 log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with "
392 "parent (pipe mode)\n", p->link.name);
393 dev->fd_out = dup(STDOUT_FILENO);
394
395 /* Hook things up so that we monitor dev->fd_out */
396 p->desc.UpdateSet = exec_UpdateSet;
397 p->desc.IsSet = exec_IsSet;
398 } else
399 dev->fd_out = -1;
400 }
401 }
402
403 if (dev) {
404 memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
405 physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
406 if (p->cfg.cd.necessity != CD_DEFAULT)
407 log_Printf(LogWARN, "Carrier settings ignored\n");
408 return &dev->dev;
409 }
410
411 return NULL;
412 }
413