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