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