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