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