146686Sbrian/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
446686Sbrian * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
546686Sbrian * All rights reserved.
646686Sbrian *
746686Sbrian * Redistribution and use in source and binary forms, with or without
846686Sbrian * modification, are permitted provided that the following conditions
946686Sbrian * are met:
1046686Sbrian * 1. Redistributions of source code must retain the above copyright
1146686Sbrian *    notice, this list of conditions and the following disclaimer.
1246686Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1346686Sbrian *    notice, this list of conditions and the following disclaimer in the
1446686Sbrian *    documentation and/or other materials provided with the distribution.
1546686Sbrian *
1646686Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1746686Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1846686Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1946686Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2046686Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2146686Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2246686Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2346686Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2446686Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2546686Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2646686Sbrian * SUCH DAMAGE.
2746686Sbrian *
2850479Speter * $FreeBSD: stable/11/usr.sbin/ppp/exec.c 330449 2018-03-05 07:26:05Z eadler $
2946686Sbrian */
3046686Sbrian
3146686Sbrian#include <sys/param.h>
3246686Sbrian#include <sys/socket.h>
3346686Sbrian#include <sys/un.h>
3446686Sbrian
3546686Sbrian#include <errno.h>
3646686Sbrian#include <fcntl.h>
3746686Sbrian#include <stdio.h>
3846686Sbrian#include <stdlib.h>
3946686Sbrian#include <string.h>
40196514Sbrian#include <sysexits.h>
4146686Sbrian#include <sys/wait.h>
42196514Sbrian#include <sys/stat.h>
4347769Sbrian#include <sys/uio.h>
4446686Sbrian#include <termios.h>
4546686Sbrian#include <unistd.h>
4646686Sbrian
4746686Sbrian#include "layer.h"
4846686Sbrian#include "defs.h"
4946686Sbrian#include "mbuf.h"
5046686Sbrian#include "log.h"
5146686Sbrian#include "timer.h"
5246686Sbrian#include "lqr.h"
5346686Sbrian#include "hdlc.h"
5446686Sbrian#include "throughput.h"
5546686Sbrian#include "fsm.h"
5646686Sbrian#include "lcp.h"
5746686Sbrian#include "ccp.h"
5846686Sbrian#include "link.h"
5946686Sbrian#include "async.h"
6046686Sbrian#include "descriptor.h"
6146686Sbrian#include "physical.h"
6246686Sbrian#include "mp.h"
6346686Sbrian#include "chat.h"
6446686Sbrian#include "command.h"
6546686Sbrian#include "auth.h"
6646686Sbrian#include "chap.h"
6746686Sbrian#include "cbcp.h"
6846686Sbrian#include "datalink.h"
6955253Sbrian#include "id.h"
70196514Sbrian#include "main.h"
7146686Sbrian#include "exec.h"
7246686Sbrian
73196514Sbrian
74196514Sbrianstruct execdevice {
75196514Sbrian  struct device dev;		/* What struct physical knows about */
76196514Sbrian  int fd_out;			/* output descriptor */
77196514Sbrian};
78196514Sbrian
79196514Sbrian#define device2exec(d) ((d)->type == EXEC_DEVICE ? (struct execdevice *)d : NULL)
80196514Sbrian
81196514Sbrianunsigned
82196514Sbrianexec_DeviceSize(void)
83196514Sbrian{
84196514Sbrian  return sizeof(struct execdevice);
85196514Sbrian}
86196514Sbrian
87196514Sbrianstatic void
88196514Sbrianexec_Free(struct physical *p)
89196514Sbrian{
90196514Sbrian  struct execdevice *dev = device2exec(p->handler);
91196514Sbrian
92196514Sbrian  if (dev->fd_out != -1)
93196514Sbrian    close(dev->fd_out);
94196514Sbrian  free(dev);
95196514Sbrian}
96196514Sbrian
97196514Sbrianstatic void
98196514Sbrianexec_device2iov(struct device *d, struct iovec *iov, int *niov,
99196514Sbrian               int maxiov __unused, int *auxfd, int *nauxfd)
100196514Sbrian{
101196514Sbrian  struct execdevice *dev;
102196514Sbrian  int sz = physical_MaxDeviceSize();
103196514Sbrian
104196514Sbrian  iov[*niov].iov_base = d = realloc(d, sz);
105196514Sbrian  if (d == NULL) {
106196514Sbrian    log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
107196514Sbrian    AbortProgram(EX_OSERR);
108196514Sbrian  }
109196514Sbrian  iov[*niov].iov_len = sz;
110196514Sbrian  (*niov)++;
111196514Sbrian
112196514Sbrian  dev = device2exec(d);
113196514Sbrian  if (dev->fd_out >= 0) {
114196514Sbrian    *auxfd = dev->fd_out;
115196514Sbrian    (*nauxfd)++;
116196514Sbrian  }
117196514Sbrian}
118196514Sbrian
119196514Sbrianstatic int
120196514Sbrianexec_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
121196514Sbrian{
122196514Sbrian  struct execdevice *dev = device2exec(p->handler);
123196514Sbrian  int sets;
124196514Sbrian
125196514Sbrian  p->handler->removefromset = NULL;
126196514Sbrian  sets = physical_RemoveFromSet(p, r, w, e);
127196514Sbrian  p->handler->removefromset = exec_RemoveFromSet;
128196514Sbrian
129196514Sbrian  if (dev->fd_out >= 0) {
130196514Sbrian    if (w && FD_ISSET(dev->fd_out, w)) {
131196514Sbrian      FD_CLR(dev->fd_out, w);
132196514Sbrian      log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, dev->fd_out);
133196514Sbrian      sets++;
134196514Sbrian    }
135196514Sbrian    if (e && FD_ISSET(dev->fd_out, e)) {
136196514Sbrian      FD_CLR(dev->fd_out, e);
137196514Sbrian      log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, dev->fd_out);
138196514Sbrian      sets++;
139196514Sbrian    }
140196514Sbrian  }
141196514Sbrian
142196514Sbrian  return sets;
143196514Sbrian}
144196514Sbrian
145196514Sbrianstatic ssize_t
146196514Sbrianexec_Write(struct physical *p, const void *v, size_t n)
147196514Sbrian{
148196514Sbrian  struct execdevice *dev = device2exec(p->handler);
149196514Sbrian  int fd = dev->fd_out == -1 ? p->fd : dev->fd_out;
150196514Sbrian
151196514Sbrian  return write(fd, v, n);
152196514Sbrian}
153196514Sbrian
154196514Sbrianstatic struct device baseexecdevice = {
15547061Sbrian  EXEC_DEVICE,
15647061Sbrian  "exec",
15778410Sbrian  0,
15853733Sbrian  { CD_NOTREQUIRED, 0 },
15947061Sbrian  NULL,
160196514Sbrian  exec_RemoveFromSet,
16147061Sbrian  NULL,
16247061Sbrian  NULL,
16347061Sbrian  NULL,
16447061Sbrian  NULL,
16547061Sbrian  NULL,
166196514Sbrian  exec_Free,
16747061Sbrian  NULL,
168196514Sbrian  exec_Write,
169196514Sbrian  exec_device2iov,
17047061Sbrian  NULL,
17147061Sbrian  NULL,
17247061Sbrian  NULL
17347061Sbrian};
17447061Sbrian
17547061Sbrianstruct device *
17647061Sbrianexec_iov2device(int type, struct physical *p, struct iovec *iov,
177196514Sbrian                int *niov, int maxiov __unused, int *auxfd, int *nauxfd)
17846686Sbrian{
17947461Sbrian  if (type == EXEC_DEVICE) {
180196514Sbrian    struct execdevice *dev = (struct execdevice *)iov[(*niov)++].iov_base;
181196514Sbrian
182196514Sbrian    dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
183196514Sbrian    if (dev == NULL) {
184196514Sbrian      log_Printf(LogALERT, "Failed to allocate memory: %d\n",
185196514Sbrian                 (int)(sizeof *dev));
186196514Sbrian      AbortProgram(EX_OSERR);
187196514Sbrian    }
188196514Sbrian
189196514Sbrian    if (*nauxfd) {
190196514Sbrian      dev->fd_out = *auxfd;
191196514Sbrian      (*nauxfd)--;
192196514Sbrian    } else
193196514Sbrian      dev->fd_out = -1;
194196514Sbrian
195196514Sbrian    /* Refresh function pointers etc */
196196514Sbrian    memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
197196514Sbrian
198196514Sbrian    physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
199196514Sbrian    return &dev->dev;
20047461Sbrian  }
20147061Sbrian
20247061Sbrian  return NULL;
20347061Sbrian}
20447061Sbrian
205196514Sbrianstatic int
206196514Sbrianexec_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
20747061Sbrian{
208196514Sbrian  struct physical *p = descriptor2physical(d);
209196514Sbrian  struct execdevice *dev = device2exec(p->handler);
210196514Sbrian  int result = 0;
21146686Sbrian
212196514Sbrian  if (w && dev->fd_out >= 0) {
213196514Sbrian    FD_SET(dev->fd_out, w);
214196514Sbrian    log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, dev->fd_out);
215196514Sbrian    result++;
216196514Sbrian    w = NULL;
217196514Sbrian  }
21852942Sbrian
219196514Sbrian  if (e && dev->fd_out >= 0) {
220196514Sbrian    FD_SET(dev->fd_out, e);
221196514Sbrian    log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, dev->fd_out);
222196514Sbrian    result++;
223196514Sbrian  }
22446686Sbrian
225196514Sbrian  if (result && *n <= dev->fd_out)
226196514Sbrian    *n = dev->fd_out + 1;
22746686Sbrian
228196514Sbrian  return result + physical_doUpdateSet(d, r, w, e, n, 0);
229196514Sbrian}
23046686Sbrian
231196514Sbrianstatic int
232196514Sbrianexec_IsSet(struct fdescriptor *d, const fd_set *fdset)
233196514Sbrian{
234196514Sbrian  struct physical *p = descriptor2physical(d);
235196514Sbrian  struct execdevice *dev = device2exec(p->handler);
236196514Sbrian  int result = dev->fd_out >= 0 && FD_ISSET(dev->fd_out, fdset);
237196514Sbrian  result += physical_IsSet(d, fdset);
23846686Sbrian
239196514Sbrian  return result;
240196514Sbrian}
24158454Sbrian
242196514Sbrianstruct device *
243196514Sbrianexec_Create(struct physical *p)
244196514Sbrian{
245196514Sbrian  struct execdevice *dev;
24646686Sbrian
247196514Sbrian  dev = NULL;
248196514Sbrian  if (p->fd < 0) {
249196514Sbrian    if (*p->name.full == '!') {
250196514Sbrian      int fids[2], type;
251196514Sbrian
252196514Sbrian      if ((dev = malloc(sizeof *dev)) == NULL) {
253196514Sbrian        log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
254196514Sbrian                   p->link.name, strerror(errno));
255196514Sbrian        return NULL;
256196514Sbrian      }
257196514Sbrian      dev->fd_out = -1;
258196514Sbrian
259196514Sbrian      p->fd--;	/* We own the device but maybe can't use it - change fd */
260196514Sbrian      type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
261196514Sbrian
262196514Sbrian      if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) {
263196514Sbrian        log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
264196514Sbrian                   strerror(errno));
265196514Sbrian        free(dev);
266196514Sbrian        dev = NULL;
267196514Sbrian      } else {
268196514Sbrian        static int child_status;		/* This variable is abused ! */
269196514Sbrian        int stat, argc, i, ret, wret, pidpipe[2];
270196514Sbrian        pid_t pid, realpid;
271196514Sbrian        char *argv[MAXARGS];
272196514Sbrian
273196514Sbrian        stat = fcntl(fids[0], F_GETFL, 0);
274196514Sbrian        if (stat > 0) {
275196514Sbrian          stat |= O_NONBLOCK;
276196514Sbrian          fcntl(fids[0], F_SETFL, stat);
277196514Sbrian        }
278196514Sbrian        realpid = getpid();
279196514Sbrian        if (pipe(pidpipe) == -1) {
280196514Sbrian          log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
281196514Sbrian                     strerror(errno));
28246686Sbrian          close(fids[1]);
283196514Sbrian          close(fids[0]);
284196514Sbrian          free(dev);
285196514Sbrian          dev = NULL;
286196514Sbrian        } else switch ((pid = fork())) {
287196514Sbrian          case -1:
288196514Sbrian            log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
28958454Sbrian                       strerror(errno));
290196514Sbrian            close(pidpipe[0]);
291196514Sbrian            close(pidpipe[1]);
292196514Sbrian            close(fids[1]);
29358454Sbrian            close(fids[0]);
29458454Sbrian            break;
295196514Sbrian
296196514Sbrian          case 0:
297196514Sbrian            close(pidpipe[0]);
29858454Sbrian            close(fids[0]);
299196514Sbrian            timer_TermService();
300196514Sbrian  #ifndef NOSUID
301196514Sbrian            setuid(ID0realuid());
302196514Sbrian  #endif
303196514Sbrian
304196514Sbrian            child_status = 0;
305196514Sbrian            switch ((pid = vfork())) {
306196514Sbrian              case 0:
307196514Sbrian                close(pidpipe[1]);
308196514Sbrian                break;
309196514Sbrian
310196514Sbrian              case -1:
311196514Sbrian                ret = errno;
312196514Sbrian                log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
313196514Sbrian                           strerror(errno));
314196514Sbrian                close(pidpipe[1]);
315196514Sbrian                _exit(ret);
316196514Sbrian
317196514Sbrian              default:
318196514Sbrian                write(pidpipe[1], &pid, sizeof pid);
319196514Sbrian                close(pidpipe[1]);
320196514Sbrian                _exit(child_status);	/* The error from exec() ! */
321196514Sbrian            }
322196514Sbrian
323196514Sbrian            log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
324196514Sbrian
325196514Sbrian            if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
326196514Sbrian                                 PARSE_REDUCE|PARSE_NOHASH)) < 0) {
327196514Sbrian              log_Printf(LogWARN, "Syntax error in exec command\n");
328196514Sbrian              _exit(ESRCH);
329196514Sbrian            }
330196514Sbrian
331196514Sbrian            command_Expand(argv, argc, (char const *const *)argv,
332196514Sbrian                           p->dl->bundle, 0, realpid);
333196514Sbrian
334196514Sbrian            dup2(fids[1], STDIN_FILENO);
335196514Sbrian            dup2(fids[1], STDOUT_FILENO);
336196514Sbrian            dup2(fids[1], STDERR_FILENO);
337196514Sbrian            for (i = getdtablesize(); i > STDERR_FILENO; i--)
338196514Sbrian              fcntl(i, F_SETFD, 1);
339196514Sbrian
340196514Sbrian            execvp(*argv, argv);
341196514Sbrian            child_status = errno;		/* Only works for vfork() */
342196514Sbrian            printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
343196514Sbrian            _exit(child_status);
34458454Sbrian            break;
345196514Sbrian
346196514Sbrian          default:
347196514Sbrian            close(pidpipe[1]);
348196514Sbrian            close(fids[1]);
349196514Sbrian            if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
350196514Sbrian                sizeof p->session_owner)
351196514Sbrian              p->session_owner = (pid_t)-1;
352196514Sbrian            close(pidpipe[0]);
353196514Sbrian            while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
354196514Sbrian              ;
355196514Sbrian            if (wret == -1) {
356196514Sbrian              log_Printf(LogWARN, "Waiting for child process: %s\n",
357196514Sbrian                         strerror(errno));
358196514Sbrian              close(fids[0]);
359196514Sbrian              p->session_owner = (pid_t)-1;
360196514Sbrian              break;
361196514Sbrian            } else if (WIFSIGNALED(stat)) {
362196514Sbrian              log_Printf(LogWARN, "Child process received sig %d !\n",
363196514Sbrian                         WTERMSIG(stat));
364196514Sbrian              close(fids[0]);
365196514Sbrian              p->session_owner = (pid_t)-1;
366196514Sbrian              break;
367196514Sbrian            } else if (WIFSTOPPED(stat)) {
368196514Sbrian              log_Printf(LogWARN, "Child process received stop sig %d !\n",
369196514Sbrian                         WSTOPSIG(stat));
370196514Sbrian              /* I guess that's ok.... */
371196514Sbrian            } else if ((ret = WEXITSTATUS(stat))) {
372196514Sbrian              log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
373196514Sbrian                         strerror(ret));
374196514Sbrian              close(fids[0]);
375196514Sbrian              p->session_owner = (pid_t)-1;
376196514Sbrian              break;
377196514Sbrian            }
378196514Sbrian            p->fd = fids[0];
379196514Sbrian            log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
380196514Sbrian        }
38146686Sbrian      }
38246686Sbrian    }
383196514Sbrian  } else {
384196514Sbrian    struct stat st;
385196514Sbrian
386196514Sbrian    if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) {
387196514Sbrian      if ((dev = malloc(sizeof *dev)) == NULL)
388196514Sbrian        log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
389196514Sbrian                   p->link.name, strerror(errno));
390196514Sbrian      else if (p->fd == STDIN_FILENO) {
391196514Sbrian        log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with "
392196514Sbrian                   "parent (pipe mode)\n", p->link.name);
393196514Sbrian        dev->fd_out = dup(STDOUT_FILENO);
394196514Sbrian
395196514Sbrian        /* Hook things up so that we monitor dev->fd_out */
396196514Sbrian        p->desc.UpdateSet = exec_UpdateSet;
397196514Sbrian        p->desc.IsSet = exec_IsSet;
398196514Sbrian      } else
399196514Sbrian        dev->fd_out = -1;
400196514Sbrian    }
40146686Sbrian  }
40246686Sbrian
403196514Sbrian  if (dev) {
404196514Sbrian    memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
405196514Sbrian    physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
406196514Sbrian    if (p->cfg.cd.necessity != CD_DEFAULT)
407196514Sbrian      log_Printf(LogWARN, "Carrier settings ignored\n");
408196514Sbrian    return &dev->dev;
409196514Sbrian  }
410196514Sbrian
41147061Sbrian  return NULL;
41246686Sbrian}
413