1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 1998 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 <netinet/in.h> 33 #include <netinet/in_systm.h> 34 #include <netinet/ip.h> 35 #include <sys/socket.h> 36 #include <sys/un.h> 37 38 #include <errno.h> 39 #include <stdarg.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sys/fcntl.h> 44 #include <termios.h> 45 #include <unistd.h> 46 47 #include "layer.h" 48 #include "defs.h" 49 #include "timer.h" 50 #include "command.h" 51 #include "log.h" 52 #include "descriptor.h" 53 #include "prompt.h" 54 #include "fsm.h" 55 #include "auth.h" 56 #include "iplist.h" 57 #include "throughput.h" 58 #include "slcompress.h" 59 #include "mbuf.h" 60 #include "lqr.h" 61 #include "hdlc.h" 62 #include "lcp.h" 63 #include "ncpaddr.h" 64 #include "ipcp.h" 65 #include "filter.h" 66 #include "async.h" 67 #include "ccp.h" 68 #include "link.h" 69 #include "physical.h" 70 #include "mp.h" 71 #ifndef NORADIUS 72 #include "radius.h" 73 #endif 74 #include "ipv6cp.h" 75 #include "ncp.h" 76 #include "bundle.h" 77 #include "chat.h" 78 #include "chap.h" 79 #include "cbcp.h" 80 #include "datalink.h" 81 #include "server.h" 82 #include "main.h" 83 84 static void 85 prompt_Display(struct prompt *p) 86 { 87 /* XXX: See Index2Nam() - should we only figure this out once ? */ 88 static char shostname[MAXHOSTNAMELEN]; 89 const char *pconnect, *pauth; 90 91 if (p->TermMode || !p->needprompt) 92 return; 93 94 p->needprompt = 0; 95 96 if (p->nonewline) 97 p->nonewline = 0; 98 else 99 fprintf(p->Term, "\n"); 100 101 if (p->auth == LOCAL_AUTH) 102 pauth = " ON "; 103 else 104 pauth = " on "; 105 106 if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED) 107 pconnect = "PPP"; 108 #ifndef NOINET6 109 else if (!Enabled(p->bundle, OPT_IPCP) && 110 p->bundle->ncp.ipv6cp.fsm.state == ST_OPENED) 111 pconnect = "PPP"; 112 #endif 113 else if (bundle_Phase(p->bundle) == PHASE_NETWORK) 114 pconnect = "PPp"; 115 else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE) 116 pconnect = "Ppp"; 117 else 118 pconnect = "ppp"; 119 120 if (*shostname == '\0') { 121 char *dot; 122 123 if (gethostname(shostname, sizeof shostname) || *shostname == '\0') 124 strcpy(shostname, "localhost"); 125 else if ((dot = strchr(shostname, '.'))) 126 *dot = '\0'; 127 } 128 129 fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname); 130 fflush(p->Term); 131 } 132 133 static int 134 prompt_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused, 135 fd_set *e, int *n) 136 { 137 struct prompt *p = descriptor2prompt(d); 138 int sets; 139 140 sets = 0; 141 142 if (!p->active) 143 return sets; 144 145 if (p->fd_in >= 0) { 146 if (r) { 147 FD_SET(p->fd_in, r); 148 log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 149 sets++; 150 } 151 if (e) { 152 FD_SET(p->fd_in, e); 153 log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 154 sets++; 155 } 156 if (sets && *n < p->fd_in + 1) 157 *n = p->fd_in + 1; 158 } 159 160 prompt_Display(p); 161 162 return sets; 163 } 164 165 static int 166 prompt_IsSet(struct fdescriptor *d, const fd_set *fdset) 167 { 168 struct prompt *p = descriptor2prompt(d); 169 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 170 } 171 172 173 static void 174 prompt_ShowHelp(struct prompt *p) 175 { 176 prompt_Printf(p, "The following commands are available:\n"); 177 prompt_Printf(p, " ~p\tEnter Packet mode\n"); 178 prompt_Printf(p, " ~t\tShow timers\n"); 179 prompt_Printf(p, " ~m\tShow memory map\n"); 180 prompt_Printf(p, " ~.\tTerminate program\n"); 181 prompt_Printf(p, " ~?\tThis help\n"); 182 } 183 184 static void 185 prompt_Read(struct fdescriptor *d, struct bundle *bundle, 186 const fd_set *fdset __unused) 187 { 188 struct prompt *p = descriptor2prompt(d); 189 struct prompt *op; 190 int n; 191 char ch; 192 char linebuff[LINE_LEN]; 193 194 if (p->TermMode == NULL) { 195 n = read(p->fd_in, linebuff, sizeof linebuff - 1); 196 if (n > 0) { 197 if (linebuff[n-1] == '\n') 198 linebuff[--n] = '\0'; 199 else 200 linebuff[n] = '\0'; 201 p->nonewline = 1; /* Maybe command_Decode does a prompt */ 202 prompt_Required(p); 203 if (n) { 204 if ((op = log_PromptContext) == NULL) 205 log_PromptContext = p; 206 if (!command_Decode(bundle, linebuff, n, p, p->src.from)) 207 prompt_Printf(p, "Syntax error\n"); 208 log_PromptContext = op; 209 } 210 } else if (n <= 0) { 211 log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from); 212 if (!p->owner) 213 Cleanup(); 214 prompt_Destroy(p, 0); 215 } 216 return; 217 } 218 219 switch (p->TermMode->state) { 220 case DATALINK_CLOSED: 221 prompt_Printf(p, "Link lost, terminal mode.\n"); 222 prompt_TtyCommandMode(p); 223 p->nonewline = 0; 224 prompt_Required(p); 225 return; 226 227 case DATALINK_READY: 228 break; 229 230 case DATALINK_OPEN: 231 prompt_Printf(p, "\nPacket mode detected.\n"); 232 prompt_TtyCommandMode(p); 233 p->nonewline = 0; 234 /* We'll get a prompt because of our status change */ 235 /* FALLTHROUGH */ 236 237 default: 238 /* Wait 'till we're in a state we care about */ 239 return; 240 } 241 242 /* 243 * We are in terminal mode, decode special sequences 244 */ 245 n = read(p->fd_in, &ch, 1); 246 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 247 248 if (n > 0) { 249 switch (p->readtilde) { 250 case 0: 251 if (ch == '~') 252 p->readtilde = 1; 253 else 254 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 255 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 256 prompt_TtyCommandMode(p); 257 } 258 break; 259 case 1: 260 switch (ch) { 261 case '?': 262 prompt_ShowHelp(p); 263 break; 264 case 'p': 265 datalink_Up(p->TermMode, 0, 1); 266 prompt_Printf(p, "\nPacket mode.\n"); 267 prompt_TtyCommandMode(p); 268 break; 269 case '.': 270 prompt_TtyCommandMode(p); 271 p->nonewline = 0; 272 prompt_Required(p); 273 break; 274 case 't': 275 timer_Show(0, p); 276 break; 277 case 'm': 278 { 279 struct cmdargs arg; 280 281 arg.cmdtab = NULL; 282 arg.cmd = NULL; 283 arg.argc = 0; 284 arg.argn = 0; 285 arg.argv = NULL; 286 arg.bundle = bundle; 287 arg.cx = p->TermMode; 288 arg.prompt = p; 289 290 mbuf_Show(&arg); 291 } 292 break; 293 default: 294 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 295 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 296 prompt_TtyCommandMode(p); 297 } 298 break; 299 } 300 p->readtilde = 0; 301 break; 302 } 303 } 304 } 305 306 static int 307 prompt_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused, 308 const fd_set *fdset __unused) 309 { 310 /* We never want to write here ! */ 311 log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n"); 312 return 0; 313 } 314 315 struct prompt * 316 prompt_Create(struct server *s, struct bundle *bundle, int fd) 317 { 318 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 319 320 if (p != NULL) { 321 p->desc.type = PROMPT_DESCRIPTOR; 322 p->desc.UpdateSet = prompt_UpdateSet; 323 p->desc.IsSet = prompt_IsSet; 324 p->desc.Read = prompt_Read; 325 p->desc.Write = prompt_Write; 326 327 if (fd == PROMPT_STD) { 328 char *tty = ttyname(STDIN_FILENO); 329 330 if (!tty) { 331 free(p); 332 return NULL; 333 } 334 p->fd_in = STDIN_FILENO; 335 p->fd_out = STDOUT_FILENO; 336 p->Term = stdout; 337 p->owner = NULL; 338 p->auth = LOCAL_AUTH; 339 p->src.type = "Controller"; 340 strncpy(p->src.from, tty, sizeof p->src.from - 1); 341 p->src.from[sizeof p->src.from - 1] = '\0'; 342 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 343 } else { 344 p->fd_in = p->fd_out = fd; 345 p->Term = fdopen(fd, "a+"); 346 p->owner = s; 347 p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 348 p->src.type = "unknown"; 349 *p->src.from = '\0'; 350 } 351 p->TermMode = NULL; 352 p->nonewline = 1; 353 p->needprompt = 1; 354 p->readtilde = 0; 355 p->bundle = bundle; 356 log_RegisterPrompt(p); 357 } 358 359 return p; 360 } 361 362 void 363 prompt_Destroy(struct prompt *p, int verbose) 364 { 365 if (p) { 366 if (p->Term != stdout) { 367 fclose(p->Term); 368 close(p->fd_in); 369 if (p->fd_out != p->fd_in) 370 close(p->fd_out); 371 if (verbose) 372 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 373 } else 374 prompt_TtyOldMode(p); 375 376 log_UnRegisterPrompt(p); 377 free(p); 378 } 379 } 380 381 void 382 prompt_Printf(struct prompt *p, const char *fmt,...) 383 { 384 if (p && p->active) { 385 va_list ap; 386 387 va_start(ap, fmt); 388 prompt_vPrintf(p, fmt, ap); 389 va_end(ap); 390 } 391 } 392 393 void 394 prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 395 { 396 if (p && p->active) { 397 char nfmt[LINE_LEN]; 398 const char *pfmt; 399 400 if (p->TermMode) { 401 /* Stuff '\r' in front of '\n' 'cos we're in raw mode */ 402 size_t len = strlen(fmt); 403 404 if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' && 405 (len == 1 || fmt[len-2] != '\r')) { 406 strcpy(nfmt, fmt); 407 strcpy(nfmt + len - 1, "\r\n"); 408 pfmt = nfmt; 409 } else 410 pfmt = fmt; 411 } else 412 pfmt = fmt; 413 vfprintf(p->Term, pfmt, ap); 414 fflush(p->Term); 415 p->nonewline = 1; 416 } 417 } 418 419 void 420 prompt_TtyInit(struct prompt *p) 421 { 422 int stat, fd = p ? p->fd_in : STDIN_FILENO; 423 struct termios newtio; 424 425 stat = fcntl(fd, F_GETFL, 0); 426 if (stat > 0) { 427 stat |= O_NONBLOCK; 428 fcntl(fd, F_SETFL, stat); 429 } 430 431 if (p) 432 newtio = p->oldtio; 433 else 434 tcgetattr(fd, &newtio); 435 436 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 437 newtio.c_iflag = 0; 438 newtio.c_oflag &= ~OPOST; 439 if (!p) 440 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 441 newtio.c_cc[VMIN] = 1; 442 newtio.c_cc[VTIME] = 0; 443 newtio.c_cflag |= CS8; 444 tcsetattr(fd, TCSANOW, &newtio); 445 if (p) 446 p->comtio = newtio; 447 } 448 449 /* 450 * Set tty into command mode. We allow canonical input and echo processing. 451 */ 452 void 453 prompt_TtyCommandMode(struct prompt *p) 454 { 455 struct termios newtio; 456 int stat; 457 458 tcgetattr(p->fd_in, &newtio); 459 newtio.c_lflag |= (ECHO | ISIG | ICANON); 460 newtio.c_iflag = p->oldtio.c_iflag; 461 newtio.c_oflag |= OPOST; 462 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 463 464 stat = fcntl(p->fd_in, F_GETFL, 0); 465 if (stat > 0) { 466 stat |= O_NONBLOCK; 467 fcntl(p->fd_in, F_SETFL, stat); 468 } 469 470 p->TermMode = NULL; 471 } 472 473 /* 474 * Set tty into terminal mode which is used while we invoke term command. 475 */ 476 void 477 prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 478 { 479 int stat; 480 481 if (p->Term == stdout) 482 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 483 484 stat = fcntl(p->fd_in, F_GETFL, 0); 485 if (stat > 0) { 486 stat &= ~O_NONBLOCK; 487 fcntl(p->fd_in, F_SETFL, stat); 488 } 489 p->TermMode = dl; 490 } 491 492 void 493 prompt_TtyOldMode(struct prompt *p) 494 { 495 int stat; 496 497 stat = fcntl(p->fd_in, F_GETFL, 0); 498 if (stat > 0) { 499 stat &= ~O_NONBLOCK; 500 fcntl(p->fd_in, F_SETFL, stat); 501 } 502 503 if (p->Term == stdout) 504 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 505 } 506 507 pid_t 508 prompt_pgrp(struct prompt *p) 509 { 510 return tcgetpgrp(p->fd_in); 511 } 512 513 int 514 PasswdCommand(struct cmdargs const *arg) 515 { 516 const char *pass; 517 518 if (!arg->prompt) { 519 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 520 return 0; 521 } 522 523 if (arg->prompt->owner == NULL) { 524 log_Printf(LogWARN, "passwd: Not required\n"); 525 return 0; 526 } 527 528 if (arg->argc == arg->argn) 529 pass = ""; 530 else if (arg->argc > arg->argn+1) 531 return -1; 532 else 533 pass = arg->argv[arg->argn]; 534 535 if (!strcmp(arg->prompt->owner->cfg.passwd, pass)) 536 arg->prompt->auth = LOCAL_AUTH; 537 else 538 arg->prompt->auth = LOCAL_NO_AUTH; 539 540 return 0; 541 } 542 543 static struct pppTimer bgtimer; 544 545 static void 546 prompt_TimedContinue(void *v) 547 { 548 prompt_Continue((struct prompt *)v); 549 } 550 551 void 552 prompt_Continue(struct prompt *p) 553 { 554 timer_Stop(&bgtimer); 555 if (getpgrp() == prompt_pgrp(p)) { 556 prompt_TtyCommandMode(p); 557 p->nonewline = 1; 558 prompt_Required(p); 559 log_ActivatePrompt(p); 560 } else if (!p->owner) { 561 bgtimer.func = prompt_TimedContinue; 562 bgtimer.name = "prompt bg"; 563 bgtimer.load = SECTICKS; 564 bgtimer.arg = p; 565 timer_Start(&bgtimer); 566 } 567 } 568 569 void 570 prompt_Suspend(struct prompt *p) 571 { 572 if (getpgrp() == prompt_pgrp(p)) { 573 prompt_TtyOldMode(p); 574 log_DeactivatePrompt(p); 575 } 576 } 577