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