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: stable/11/usr.sbin/ppp/exec.c 330449 2018-03-05 07:26:05Z eadler $
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
74struct 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
81unsigned
82exec_DeviceSize(void)
83{
84  return sizeof(struct execdevice);
85}
86
87static void
88exec_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
97static void
98exec_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
119static int
120exec_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
145static ssize_t
146exec_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
154static 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
175struct device *
176exec_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
205static int
206exec_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
231static int
232exec_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
242struct device *
243exec_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