1 /*- 2 * Copyright (c) 1983, 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rlogin.c 5.24 (Berkeley) 04/10/90"; 16 #endif /* not lint */ 17 18 /* 19 * $Source: mit/rlogin/RCS/rlogin.c,v $ 20 * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall Exp Locker: kfall $ 21 */ 22 23 /* 24 * rlogin - remote login 25 */ 26 #include <sys/param.h> 27 #include <sys/errno.h> 28 #include <sys/file.h> 29 #include <sys/socket.h> 30 #include <sys/signal.h> 31 #include <sys/time.h> 32 #include <sys/resource.h> 33 #include <sys/wait.h> 34 35 #include <netinet/in.h> 36 #include <netdb.h> 37 38 #include <sgtty.h> 39 #include <setjmp.h> 40 #include <errno.h> 41 #include <varargs.h> 42 #include <pwd.h> 43 #include <stdio.h> 44 #include <unistd.h> 45 #include <strings.h> 46 47 #ifdef KERBEROS 48 #include <kerberosIV/krb.h> 49 50 CREDENTIALS cred; 51 Key_schedule schedule; 52 int use_kerberos = 1, encrypt; 53 char dst_realm_buf[REALM_SZ], *dest_realm = NULL; 54 extern char *krb_realmofhost(); 55 #endif 56 57 #ifndef TIOCPKT_WINDOW 58 #define TIOCPKT_WINDOW 0x80 59 #endif 60 61 /* concession to Sun */ 62 #ifndef SIGUSR1 63 #define SIGUSR1 30 64 #endif 65 66 extern int errno; 67 int eight, litout, rem; 68 char cmdchar; 69 char *speeds[] = { 70 "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 71 "1800", "2400", "4800", "9600", "19200", "38400" 72 }; 73 74 #ifdef sun 75 struct winsize { 76 unsigned short ws_row, ws_col; 77 unsigned short ws_xpixel, ws_ypixel; 78 }; 79 #endif 80 struct winsize winsize; 81 82 #ifndef sun 83 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 84 #endif 85 86 void exit(); 87 88 main(argc, argv) 89 int argc; 90 char **argv; 91 { 92 extern char *optarg; 93 extern int optind; 94 struct passwd *pw; 95 struct servent *sp; 96 struct sgttyb ttyb; 97 long omask; 98 int argoff, ch, dflag, one, uid; 99 char *host, *p, *user, term[1024]; 100 void lostpeer(); 101 char *getenv(); 102 103 argoff = dflag = 0; 104 one = 1; 105 host = user = NULL; 106 cmdchar = '~'; 107 108 if (p = rindex(argv[0], '/')) 109 ++p; 110 else 111 p = argv[0]; 112 113 if (strcmp(p, "rlogin")) 114 host = p; 115 116 /* handle "rlogin host flags" */ 117 if (!host && argc > 2 && argv[1][0] != '-') { 118 host = argv[1]; 119 argoff = 1; 120 } 121 122 #ifdef KERBEROS 123 #define OPTIONS "8KLde:k:l:x" 124 #else 125 #define OPTIONS "8KLde:l:" 126 #endif 127 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) 128 switch(ch) { 129 case '8': 130 eight = 1; 131 break; 132 case 'K': 133 #ifdef KERBEROS 134 use_kerberos = 0; 135 #endif 136 break; 137 case 'L': 138 litout = 1; 139 break; 140 case 'd': 141 dflag = 1; 142 break; 143 case 'e': 144 cmdchar = optarg[0]; 145 break; 146 #ifdef KERBEROS 147 case 'k': 148 dest_realm = dst_realm_buf; 149 (void)strncpy(dest_realm, optarg, REALM_SZ); 150 break; 151 #endif 152 case 'l': 153 user = optarg; 154 break; 155 #ifdef KERBEROS 156 case 'x': 157 encrypt = 1; 158 des_set_key(cred.session, schedule); 159 break; 160 #endif 161 case '?': 162 default: 163 usage(); 164 } 165 optind += argoff; 166 argc -= optind; 167 argv += optind; 168 169 /* if haven't gotten a host yet, do so */ 170 if (!host && !(host = *argv++)) 171 usage(); 172 173 if (*argv) 174 usage(); 175 176 if (!(pw = getpwuid(uid = getuid()))) { 177 (void)fprintf(stderr, "rlogin: unknown user id.\n"); 178 exit(1); 179 } 180 if (!user) 181 user = pw->pw_name; 182 183 sp = NULL; 184 #ifdef KERBEROS 185 if (use_kerberos) { 186 sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp"); 187 if (sp == NULL) { 188 use_kerberos = 0; 189 warning("can't get entry for %s/tcp service", 190 encrypt ? "eklogin" : "klogin"); 191 } 192 } 193 #endif 194 if (sp == NULL) 195 sp = getservbyname("login", "tcp"); 196 if (sp == NULL) { 197 (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n"); 198 exit(1); 199 } 200 201 (void)strcpy(term, (p = getenv("TERM")) ? p : "network"); 202 if (ioctl(0, TIOCGETP, &ttyb) == 0) { 203 (void)strcat(term, "/"); 204 (void)strcat(term, speeds[ttyb.sg_ospeed]); 205 } 206 207 (void)get_window_size(0, &winsize); 208 209 (void)signal(SIGPIPE, lostpeer); 210 /* will use SIGUSR1 for window size hack, so hold it off */ 211 omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 212 213 #ifdef KERBEROS 214 try_connect: 215 if (use_kerberos) { 216 rem = KSUCCESS; 217 errno = 0; 218 if (dest_realm == NULL) 219 dest_realm = krb_realmofhost(host); 220 221 if (encrypt) 222 rem = krcmd_mutual(&host, sp->s_port, user, term, 0, 223 dest_realm, &cred, schedule); 224 else 225 rem = krcmd(&host, sp->s_port, user, term, 0, 226 dest_realm); 227 if (rem < 0) { 228 use_kerberos = 0; 229 sp = getservbyname("login", "tcp"); 230 if (sp == NULL) { 231 (void)fprintf(stderr, 232 "rlogin: unknown service login/tcp.\n"); 233 exit(1); 234 } 235 if (errno == ECONNREFUSED) 236 warning("remote host doesn't support Kerberos"); 237 if (errno == ENOENT) 238 warning("can't provide Kerberos auth data"); 239 goto try_connect; 240 } 241 } else { 242 if (encrypt) { 243 (void)fprintf(stderr, 244 "rlogin: the -x flag requires Kerberos authentication.\n"); 245 exit(1); 246 } 247 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 248 } 249 #else 250 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 251 #endif 252 253 if (rem < 0) 254 exit(1); 255 256 if (dflag && 257 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 258 (void)fprintf(stderr, "rlogin: setsockopt: %s.\n", 259 strerror(errno)); 260 261 (void)setuid(uid); 262 doit(omask); 263 /*NOTREACHED*/ 264 } 265 266 int child, defflags, deflflags, tabflag; 267 char deferase, defkill; 268 struct tchars deftc; 269 struct ltchars defltc; 270 struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 271 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 272 273 doit(omask) 274 long omask; 275 { 276 struct sgttyb sb; 277 void catch_child(), copytochild(), exit(), writeroob(); 278 279 (void)ioctl(0, TIOCGETP, (char *)&sb); 280 defflags = sb.sg_flags; 281 tabflag = defflags & TBDELAY; 282 defflags &= ECHO | CRMOD; 283 deferase = sb.sg_erase; 284 defkill = sb.sg_kill; 285 (void)ioctl(0, TIOCLGET, (char *)&deflflags); 286 (void)ioctl(0, TIOCGETC, (char *)&deftc); 287 notc.t_startc = deftc.t_startc; 288 notc.t_stopc = deftc.t_stopc; 289 (void)ioctl(0, TIOCGLTC, (char *)&defltc); 290 (void)signal(SIGINT, SIG_IGN); 291 setsignal(SIGHUP, exit); 292 setsignal(SIGQUIT, exit); 293 child = fork(); 294 if (child == -1) { 295 (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno)); 296 done(1); 297 } 298 if (child == 0) { 299 mode(1); 300 if (reader(omask) == 0) { 301 msg("connection closed."); 302 exit(0); 303 } 304 sleep(1); 305 msg("\007connection closed."); 306 exit(1); 307 } 308 309 /* 310 * We may still own the socket, and may have a pending SIGURG (or might 311 * receive one soon) that we really want to send to the reader. Set a 312 * trap that simply copies such signals to the child. 313 */ 314 (void)signal(SIGURG, copytochild); 315 (void)signal(SIGUSR1, writeroob); 316 (void)sigsetmask(omask); 317 (void)signal(SIGCHLD, catch_child); 318 writer(); 319 msg("closed connection."); 320 done(0); 321 } 322 323 /* trap a signal, unless it is being ignored. */ 324 setsignal(sig, act) 325 int sig; 326 void (*act)(); 327 { 328 int omask = sigblock(sigmask(sig)); 329 330 if (signal(sig, act) == SIG_IGN) 331 (void)signal(sig, SIG_IGN); 332 (void)sigsetmask(omask); 333 } 334 335 done(status) 336 int status; 337 { 338 int w; 339 340 mode(0); 341 if (child > 0) { 342 /* make sure catch_child does not snap it up */ 343 (void)signal(SIGCHLD, SIG_DFL); 344 if (kill(child, SIGKILL) >= 0) 345 while ((w = wait((union wait *)0)) > 0 && w != child); 346 } 347 exit(status); 348 } 349 350 int dosigwinch; 351 352 /* 353 * This is called when the reader process gets the out-of-band (urgent) 354 * request to turn on the window-changing protocol. 355 */ 356 void 357 writeroob() 358 { 359 void sigwinch(); 360 361 if (dosigwinch == 0) { 362 sendwindow(); 363 (void)signal(SIGWINCH, sigwinch); 364 } 365 dosigwinch = 1; 366 } 367 368 void 369 catch_child() 370 { 371 union wait status; 372 int pid; 373 374 for (;;) { 375 pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0); 376 if (pid == 0) 377 return; 378 /* if the child (reader) dies, just quit */ 379 if (pid < 0 || pid == child && !WIFSTOPPED(status)) 380 done((int)(status.w_termsig | status.w_retcode)); 381 } 382 /* NOTREACHED */ 383 } 384 385 /* 386 * writer: write to remote: 0 -> line. 387 * ~. terminate 388 * ~^Z suspend rlogin process. 389 * ~^Y suspend rlogin process, but leave reader alone. 390 */ 391 writer() 392 { 393 char c; 394 register int bol, local, n; 395 396 bol = 1; /* beginning of line */ 397 local = 0; 398 for (;;) { 399 n = read(STDIN_FILENO, &c, 1); 400 if (n <= 0) { 401 if (n < 0 && errno == EINTR) 402 continue; 403 break; 404 } 405 /* 406 * If we're at the beginning of the line and recognize a 407 * command character, then we echo locally. Otherwise, 408 * characters are echo'd remotely. If the command character 409 * is doubled, this acts as a force and local echo is 410 * suppressed. 411 */ 412 if (bol) { 413 bol = 0; 414 if (c == cmdchar) { 415 bol = 0; 416 local = 1; 417 continue; 418 } 419 } else if (local) { 420 local = 0; 421 if (c == '.' || c == deftc.t_eofc) { 422 echo(c); 423 break; 424 } 425 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 426 bol = 1; 427 echo(c); 428 stop(c); 429 continue; 430 } 431 if (c != cmdchar) { 432 #ifdef KERBEROS 433 if (encrypt) { 434 (void)des_write(rem, &cmdchar, 1); 435 } else 436 #endif 437 (void)write(rem, &cmdchar, 1); 438 } 439 } 440 441 #ifdef KERBEROS 442 if (encrypt) { 443 if (des_write(rem, &c, 1) == 0) { 444 msg("line gone"); 445 break; 446 } 447 } else 448 #endif 449 if (write(rem, &c, 1) == 0) { 450 msg("line gone"); 451 break; 452 } 453 bol = c == defkill || c == deftc.t_eofc || 454 c == deftc.t_intrc || c == defltc.t_suspc || 455 c == '\r' || c == '\n'; 456 } 457 } 458 459 echo(c) 460 register char c; 461 { 462 register char *p; 463 char buf[8]; 464 465 p = buf; 466 c &= 0177; 467 *p++ = cmdchar; 468 if (c < ' ') { 469 *p++ = '^'; 470 *p++ = c + '@'; 471 } else if (c == 0177) { 472 *p++ = '^'; 473 *p++ = '?'; 474 } else 475 *p++ = c; 476 *p++ = '\r'; 477 *p++ = '\n'; 478 (void)write(1, buf, p - buf); 479 } 480 481 stop(cmdc) 482 char cmdc; 483 { 484 mode(0); 485 (void)signal(SIGCHLD, SIG_IGN); 486 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 487 (void)signal(SIGCHLD, catch_child); 488 mode(1); 489 sigwinch(); /* check for size changes */ 490 } 491 492 void 493 sigwinch() 494 { 495 struct winsize ws; 496 497 if (dosigwinch && get_window_size(0, &ws) == 0 && 498 bcmp(&ws, &winsize, sizeof(ws))) { 499 winsize = ws; 500 sendwindow(); 501 } 502 } 503 504 /* 505 * Send the window size to the server via the magic escape 506 */ 507 sendwindow() 508 { 509 struct winsize *wp; 510 char obuf[4 + sizeof (struct winsize)]; 511 512 wp = (struct winsize *)(obuf+4); 513 obuf[0] = 0377; 514 obuf[1] = 0377; 515 obuf[2] = 's'; 516 obuf[3] = 's'; 517 wp->ws_row = htons(winsize.ws_row); 518 wp->ws_col = htons(winsize.ws_col); 519 wp->ws_xpixel = htons(winsize.ws_xpixel); 520 wp->ws_ypixel = htons(winsize.ws_ypixel); 521 522 #ifdef KERBEROS 523 if(encrypt) 524 (void)des_write(rem, obuf, sizeof(obuf)); 525 else 526 #endif 527 (void)write(rem, obuf, sizeof(obuf)); 528 } 529 530 /* 531 * reader: read from remote: line -> 1 532 */ 533 #define READING 1 534 #define WRITING 2 535 536 jmp_buf rcvtop; 537 int ppid, rcvcnt, rcvstate; 538 char rcvbuf[8 * 1024]; 539 540 void 541 oob() 542 { 543 struct sgttyb sb; 544 int atmark, n, out, rcvd; 545 char waste[BUFSIZ], mark; 546 547 out = FWRITE; 548 rcvd = 0; 549 while (recv(rem, &mark, 1, MSG_OOB) < 0) 550 switch (errno) { 551 case EWOULDBLOCK: 552 /* 553 * Urgent data not here yet. It may not be possible 554 * to send it yet if we are blocked for output and 555 * our input buffer is full. 556 */ 557 if (rcvcnt < sizeof(rcvbuf)) { 558 n = read(rem, rcvbuf + rcvcnt, 559 sizeof(rcvbuf) - rcvcnt); 560 if (n <= 0) 561 return; 562 rcvd += n; 563 } else { 564 n = read(rem, waste, sizeof(waste)); 565 if (n <= 0) 566 return; 567 } 568 continue; 569 default: 570 return; 571 } 572 if (mark & TIOCPKT_WINDOW) { 573 /* Let server know about window size changes */ 574 (void)kill(ppid, SIGUSR1); 575 } 576 if (!eight && (mark & TIOCPKT_NOSTOP)) { 577 (void)ioctl(0, TIOCGETP, (char *)&sb); 578 sb.sg_flags &= ~CBREAK; 579 sb.sg_flags |= RAW; 580 (void)ioctl(0, TIOCSETN, (char *)&sb); 581 notc.t_stopc = -1; 582 notc.t_startc = -1; 583 (void)ioctl(0, TIOCSETC, (char *)¬c); 584 } 585 if (!eight && (mark & TIOCPKT_DOSTOP)) { 586 (void)ioctl(0, TIOCGETP, (char *)&sb); 587 sb.sg_flags &= ~RAW; 588 sb.sg_flags |= CBREAK; 589 (void)ioctl(0, TIOCSETN, (char *)&sb); 590 notc.t_stopc = deftc.t_stopc; 591 notc.t_startc = deftc.t_startc; 592 (void)ioctl(0, TIOCSETC, (char *)¬c); 593 } 594 if (mark & TIOCPKT_FLUSHWRITE) { 595 (void)ioctl(1, TIOCFLUSH, (char *)&out); 596 for (;;) { 597 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 598 (void)fprintf(stderr, "rlogin: ioctl: %s.\n", 599 strerror(errno)); 600 break; 601 } 602 if (atmark) 603 break; 604 n = read(rem, waste, sizeof (waste)); 605 if (n <= 0) 606 break; 607 } 608 /* 609 * Don't want any pending data to be output, so clear the recv 610 * buffer. If we were hanging on a write when interrupted, 611 * don't want it to restart. If we were reading, restart 612 * anyway. 613 */ 614 rcvcnt = 0; 615 longjmp(rcvtop, 1); 616 } 617 618 /* oob does not do FLUSHREAD (alas!) */ 619 620 /* 621 * If we filled the receive buffer while a read was pending, longjmp 622 * to the top to restart appropriately. Don't abort a pending write, 623 * however, or we won't know how much was written. 624 */ 625 if (rcvd && rcvstate == READING) 626 longjmp(rcvtop, 1); 627 } 628 629 /* reader: read from remote: line -> 1 */ 630 reader(omask) 631 int omask; 632 { 633 void oob(); 634 635 #if !defined(BSD) || BSD < 43 636 int pid = -getpid(); 637 #else 638 int pid = getpid(); 639 #endif 640 int n, remaining; 641 char *bufp = rcvbuf; 642 643 (void)signal(SIGTTOU, SIG_IGN); 644 (void)signal(SIGURG, oob); 645 ppid = getppid(); 646 (void)fcntl(rem, F_SETOWN, pid); 647 (void)setjmp(rcvtop); 648 (void)sigsetmask(omask); 649 for (;;) { 650 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 651 rcvstate = WRITING; 652 n = write(1, bufp, remaining); 653 if (n < 0) { 654 if (errno != EINTR) 655 return(-1); 656 continue; 657 } 658 bufp += n; 659 } 660 bufp = rcvbuf; 661 rcvcnt = 0; 662 rcvstate = READING; 663 664 #ifdef KERBEROS 665 if (encrypt) 666 rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); 667 else 668 #endif 669 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 670 if (rcvcnt == 0) 671 return (0); 672 if (rcvcnt < 0) { 673 if (errno == EINTR) 674 continue; 675 (void)fprintf(stderr, "rlogin: read: %s.\n", 676 strerror(errno)); 677 return(-1); 678 } 679 } 680 } 681 682 mode(f) 683 { 684 struct ltchars *ltc; 685 struct sgttyb sb; 686 struct tchars *tc; 687 int lflags; 688 689 (void)ioctl(0, TIOCGETP, (char *)&sb); 690 (void)ioctl(0, TIOCLGET, (char *)&lflags); 691 switch(f) { 692 case 0: 693 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 694 sb.sg_flags |= defflags|tabflag; 695 tc = &deftc; 696 ltc = &defltc; 697 sb.sg_kill = defkill; 698 sb.sg_erase = deferase; 699 lflags = deflflags; 700 break; 701 case 1: 702 sb.sg_flags |= (eight ? RAW : CBREAK); 703 sb.sg_flags &= ~defflags; 704 /* preserve tab delays, but turn off XTABS */ 705 if ((sb.sg_flags & TBDELAY) == XTABS) 706 sb.sg_flags &= ~TBDELAY; 707 tc = ¬c; 708 ltc = &noltc; 709 sb.sg_kill = sb.sg_erase = -1; 710 if (litout) 711 lflags |= LLITOUT; 712 break; 713 default: 714 return; 715 } 716 (void)ioctl(0, TIOCSLTC, (char *)ltc); 717 (void)ioctl(0, TIOCSETC, (char *)tc); 718 (void)ioctl(0, TIOCSETN, (char *)&sb); 719 (void)ioctl(0, TIOCLSET, (char *)&lflags); 720 } 721 722 void 723 lostpeer() 724 { 725 (void)signal(SIGPIPE, SIG_IGN); 726 msg("\007connection closed."); 727 done(1); 728 } 729 730 /* copy SIGURGs to the child process. */ 731 void 732 copytochild() 733 { 734 (void)kill(child, SIGURG); 735 } 736 737 msg(str) 738 char *str; 739 { 740 (void)fprintf(stderr, "rlogin: %s\r\n", str); 741 } 742 743 #ifdef KERBEROS 744 /* VARARGS */ 745 warning(va_alist) 746 va_dcl 747 { 748 va_list ap; 749 char *fmt; 750 751 (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); 752 va_start(ap); 753 fmt = va_arg(ap, char *); 754 vfprintf(stderr, fmt, ap); 755 va_end(ap); 756 (void)fprintf(stderr, ".\n"); 757 } 758 #endif 759 760 usage() 761 { 762 (void)fprintf(stderr, 763 "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n", 764 #ifdef KERBEROS 765 "8Lx", " [-k realm] "); 766 #else 767 "8L", " "); 768 #endif 769 exit(1); 770 } 771 772 /* 773 * The following routine provides compatibility (such as it is) between 4.2BSD 774 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. 775 */ 776 #ifdef sun 777 int 778 get_window_size(fd, wp) 779 int fd; 780 struct winsize *wp; 781 { 782 struct ttysize ts; 783 int error; 784 785 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) 786 return(error); 787 wp->ws_row = ts.ts_lines; 788 wp->ws_col = ts.ts_cols; 789 wp->ws_xpixel = 0; 790 wp->ws_ypixel = 0; 791 return(0); 792 } 793 #endif 794