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