xref: /freebsd/usr.sbin/ppp/exec.c (revision 0d66206fff44f864ea8a4b220c3a53b4caa959a0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
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
82 exec_DeviceSize(void)
83 {
84   return sizeof(struct execdevice);
85 }
86 
87 static void
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
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
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
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 *
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
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
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 *
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