1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 /* 40 * rlogin - remote login 41 */ 42 #include <sys/types.h> 43 #include <sys/param.h> 44 #include <sys/errno.h> 45 #include <sys/file.h> 46 #include <sys/socket.h> 47 #include <sys/wait.h> 48 #include <sys/stropts.h> 49 #include <sys/ttold.h> 50 #include <sys/sockio.h> 51 #include <sys/tty.h> 52 #include <sys/ptyvar.h> 53 #include <sys/resource.h> 54 #include <sys/select.h> 55 #include <sys/time.h> 56 57 #include <netinet/in.h> 58 #include <arpa/inet.h> 59 #include <priv_utils.h> 60 61 #include <stdio.h> 62 #include <errno.h> 63 #include <pwd.h> 64 #include <signal.h> 65 #include <setjmp.h> 66 #include <netdb.h> 67 #include <fcntl.h> 68 #include <locale.h> 69 #include <stdarg.h> 70 #include <stdlib.h> 71 #include <string.h> 72 #include <unistd.h> 73 74 #include <k5-int.h> 75 #include <profile/prof_int.h> 76 #include <com_err.h> 77 #include <kcmd.h> 78 #include <krb5.h> 79 80 /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */ 81 typedef void (*sigdisp_t)(int); 82 83 extern errcode_t profile_get_options_boolean(profile_t, char **, 84 profile_options_boolean *); 85 extern errcode_t profile_get_options_string(profile_t, char **, 86 profile_option_strings *); 87 88 #define RLOGIN_BUFSIZ (1024 * 50) 89 static char des_inbuf[2 * RLOGIN_BUFSIZ]; 90 /* needs to be > largest read size */ 91 static char des_outbuf[2 * RLOGIN_BUFSIZ]; 92 /* needs to be > largest write size */ 93 static krb5_data desinbuf, desoutbuf; 94 static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 95 static krb5_keyblock *session_key; 96 static krb5_creds *cred; 97 static krb5_context bsd_context = NULL; 98 static krb5_auth_context auth_context; 99 100 static char *krb_realm; 101 102 static int krb5auth_flag; /* Flag set, when KERBEROS is enabled */ 103 static profile_options_boolean autologin_option[] = { 104 { "autologin", &krb5auth_flag, 0 }, 105 { NULL, NULL, 0 } 106 }; 107 108 static int fflag, Fflag; /* Flag set, when option -f / -F used */ 109 static int encrypt_flag; /* Flag set, when the "-x" option is used */ 110 111 /* Flag set, if -PN / -PO is specified */ 112 static boolean_t rcmdoption_done; 113 114 /* Flags set, if corres. cmd line options are turned on */ 115 static boolean_t encrypt_done, fwd_done, fwdable_done; 116 117 static profile_options_boolean option[] = { 118 { "encrypt", &encrypt_flag, 0 }, 119 { "forward", &fflag, 0 }, 120 { "forwardable", &Fflag, 0 }, 121 { NULL, NULL, 0 } 122 }; 123 124 static char *rcmdproto; 125 static profile_option_strings rcmdversion[] = { 126 { "rcmd_protocol", &rcmdproto, 0 }, 127 { NULL, NULL, 0 } 128 }; 129 130 static char rlogin[] = "rlogin"; 131 132 static char *realmdef[] = { "realms", NULL, rlogin, NULL }; 133 static char *appdef[] = { "appdefaults", rlogin, NULL }; 134 135 #ifndef TIOCPKT_WINDOW 136 #define TIOCPKT_WINDOW 0x80 137 #endif /* TIOCPKT_WINDOW */ 138 139 #ifndef sigmask 140 #define sigmask(m) (1 << ((m)-1)) 141 #endif 142 143 #define set2mask(setp) ((setp)->__sigbits[0]) 144 #define mask2set(mask, setp) \ 145 ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask))) 146 147 #ifdef DEBUG 148 #define DEBUGOPTSTRING "D:" 149 #else 150 #define DEBUGOPTSTRING "" 151 #endif /* DEBUG */ 152 153 static boolean_t ttcompat; 154 static struct termios savetty; 155 156 static char *host; 157 static int port_number; 158 static int rem = -1; 159 static char cmdchar = '~'; 160 static boolean_t nocmdchar; 161 static boolean_t eight; 162 static boolean_t litout; 163 static boolean_t null_local_username; 164 /* 165 * Note that this list of speeds is shorter than the list of speeds 166 * supported by termios. This is because we can't be sure other rlogind's 167 * in the world will correctly cope with values other than what 4.2/4.3BSD 168 * supported. 169 */ 170 static char *speeds[] = 171 { "0", "50", "75", "110", "134", "150", "200", "300", 172 "600", "1200", "1800", "2400", "4800", "9600", "19200", 173 "38400" }; 174 static char term[256] = "network"; 175 static void lostpeer(void); 176 static boolean_t dosigwinch; 177 static struct winsize winsize; 178 static void sigwinch(int); 179 static void oob(void); 180 static void doit(int); 181 static sigdisp_t sigdisp(int); 182 183 #define CRLF "\r\n" 184 185 static pid_t child; 186 static void catchild(int); 187 /* LINTED */ 188 static void copytochild(int); 189 static void writeroob(int); 190 static void stop(char), echo(char); 191 192 static int defflags, tabflag; 193 static int deflflags; 194 static char deferase, defkill; 195 static struct tchars deftc; 196 static struct ltchars defltc; 197 static struct tchars notc = { (char)-1, (char)-1, (char)-1, 198 (char)-1, (char)-1, (char)-1 }; 199 static struct ltchars noltc = { (char)-1, (char)-1, (char)-1, 200 (char)-1, (char)-1, (char)-1 }; 201 202 static void done(int); 203 static void mode(int); 204 static int reader(int); 205 static void writer(void); 206 static void prf(const char *, ...); 207 static void sendwindow(void); 208 static int compat_ioctl(int, int, void *); 209 210 static void 211 sigsetmask(int mask) 212 { 213 sigset_t oset; 214 sigset_t nset; 215 216 (void) sigprocmask(0, NULL, &nset); 217 mask2set(mask, &nset); 218 (void) sigprocmask(SIG_SETMASK, &nset, &oset); 219 } 220 221 static int 222 sigblock(int mask) 223 { 224 sigset_t oset; 225 sigset_t nset; 226 227 (void) sigprocmask(0, NULL, &nset); 228 mask2set(mask, &nset); 229 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 230 return (set2mask(&oset)); 231 } 232 233 static void 234 pop(int status) { 235 if (ttcompat) { 236 /* 237 * Pop ttcompat module 238 */ 239 (void) ioctl(STDIN_FILENO, I_POP, 0); 240 } 241 (void) tcsetattr(STDIN_FILENO, TCSANOW, &savetty); 242 exit(status); 243 } 244 245 static void 246 usage(void) { 247 (void) fprintf(stderr, "%s\n%s\n", 248 gettext("usage: rlogin [-option] [-option...] " 249 "[-k realm] [-l username] host"), 250 gettext(" where option is e, 8, E, L, A, a, K, x, " 251 "PN / PO, f or F")); 252 pop(EXIT_FAILURE); 253 } 254 255 /* PRINTFLIKE(0) */ 256 static void 257 die(const char *format, ...) 258 { 259 va_list ap; 260 261 va_start(ap, format); 262 (void) vfprintf(stderr, format, ap); 263 va_end(ap); 264 usage(); 265 } 266 267 static void 268 usage_forward(void) 269 { 270 die(gettext("rlogin: Only one of -f and -F allowed.\n")); 271 } 272 273 int 274 main(int argc, char **argv) 275 { 276 int c; 277 char *cp, *cmd, *name = NULL; 278 struct passwd *pwd; 279 uid_t uid; 280 int options = 0, oldmask; 281 int on = 1; 282 speed_t speed = 0; 283 int getattr_ret; 284 char *tmp; 285 int sock; 286 krb5_flags authopts; 287 krb5_error_code status; 288 enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; 289 290 (void) setlocale(LC_ALL, ""); 291 292 #if !defined(TEXT_DOMAIN) 293 #define TEXT_DOMAIN "SYS_TEST" 294 #endif 295 (void) textdomain(TEXT_DOMAIN); 296 297 if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) { 298 (void) fprintf(stderr, 299 gettext("Insufficient privileges, " 300 "rlogin must be set-uid root\n")); 301 exit(1); 302 } 303 304 { 305 int it; 306 307 if ((getattr_ret = tcgetattr(STDIN_FILENO, &savetty)) < 0) 308 perror("tcgetattr"); 309 it = ioctl(STDIN_FILENO, I_FIND, "ttcompat"); 310 if (it < 0) { 311 perror("ioctl I_FIND ttcompat"); 312 return (EXIT_FAILURE); 313 } 314 if (it == 0) { 315 if (ioctl(STDIN_FILENO, I_PUSH, "ttcompat") < 0) { 316 perror("ioctl I_PUSH ttcompat"); 317 exit(EXIT_FAILURE); 318 } 319 ttcompat = B_TRUE; 320 } 321 } 322 323 /* 324 * Determine command name used to invoke to rlogin(1). Users can 325 * create links named by a host pointing to the binary and type 326 * "hostname" to log into that host afterwards. 327 */ 328 cmd = strrchr(argv[0], '/'); 329 cmd = (cmd != NULL) ? (cmd + 1) : argv[0]; 330 331 if (strcmp(cmd, rlogin) == 0) { 332 if (argc < 2) 333 usage(); 334 if (*argv[1] != '-') { 335 host = argv[1]; 336 argc--; 337 argv[1] = argv[0]; 338 argv++; 339 } 340 } else { 341 host = cmd; 342 } 343 344 while ((c = getopt(argc, argv, 345 DEBUGOPTSTRING "8AEFLP:aKde:fk:l:x")) != -1) { 346 switch (c) { 347 case '8': 348 eight = B_TRUE; 349 break; 350 case 'A': 351 krb5auth_flag++; 352 break; 353 #ifdef DEBUG 354 case 'D': 355 portnumber = htons(atoi(optarg)); 356 krb5auth_flag++; 357 break; 358 #endif /* DEBUG */ 359 case 'E': 360 nocmdchar = B_TRUE; 361 break; 362 case 'F': 363 if (fflag) 364 usage_forward(); 365 Fflag = 1; 366 krb5auth_flag++; 367 fwdable_done = B_TRUE; 368 break; 369 case 'f': 370 if (Fflag) 371 usage_forward(); 372 fflag = 1; 373 krb5auth_flag++; 374 fwd_done = B_TRUE; 375 break; 376 case 'L': 377 litout = B_TRUE; 378 break; 379 case 'P': 380 if (strcmp(optarg, "N") == 0) 381 kcmd_proto = KCMD_NEW_PROTOCOL; 382 else if (strcmp(optarg, "O") == 0) 383 kcmd_proto = KCMD_OLD_PROTOCOL; 384 else 385 die(gettext("rlogin: Only -PN or -PO " 386 "allowed.\n")); 387 if (rcmdoption_done) 388 die(gettext("rlogin: Only one of -PN and -PO " 389 "allowed.\n")); 390 rcmdoption_done = B_TRUE; 391 krb5auth_flag++; 392 break; 393 case 'a': 394 case 'K': 395 /* 396 * Force the remote host to prompt for a password by sending 397 * a NULL username. These options are mutually exclusive with 398 * the -A, -x, -f, -F, -k <realm> options. 399 */ 400 null_local_username = B_TRUE; 401 break; 402 case 'd': 403 options |= SO_DEBUG; 404 break; 405 case 'e': { 406 int c; 407 408 cp = optarg; 409 410 if ((c = *cp) != '\\') { 411 cmdchar = c; 412 } else { 413 c = cp[1]; 414 if (c == '\0' || c == '\\') { 415 cmdchar = '\\'; 416 } else if (c >= '0' && c <= '7') { 417 long lc; 418 419 lc = strtol(&cp[1], NULL, 8); 420 if (lc < 0 || lc > 255) 421 die(gettext("rlogin: octal " 422 "escape character %s too " 423 "large.\n"), cp); 424 cmdchar = (char)lc; 425 } else { 426 die(gettext("rlogin: unrecognized " 427 "escape character option %s.\n"), 428 cp); 429 } 430 } 431 break; 432 } 433 case 'k': 434 krb_realm = optarg; 435 krb5auth_flag++; 436 break; 437 case 'l': 438 name = optarg; 439 break; 440 case 'x': 441 encrypt_flag = 1; 442 krb5auth_flag++; 443 encrypt_done = B_TRUE; 444 break; 445 default: 446 usage(); 447 } 448 } 449 450 argc -= optind; 451 argv += optind; 452 453 if (host == NULL) { 454 if (argc == 0) 455 usage(); 456 argc--; 457 host = *argv++; 458 } 459 460 if (argc > 0) 461 usage(); 462 463 pwd = getpwuid(uid = getuid()); 464 if (pwd == NULL) { 465 (void) fprintf(stderr, gettext("getpwuid(): can not find " 466 "password entry for user id %d."), uid); 467 return (EXIT_FAILURE); 468 } 469 if (name == NULL) 470 name = pwd->pw_name; 471 472 /* 473 * If the `-a or -K' options are issued on the cmd line, we reset 474 * all flags associated with other KRB5 specific options, since 475 * these options are mutually exclusive with the rest. 476 */ 477 if (null_local_username) { 478 krb5auth_flag = 0; 479 fflag = Fflag = encrypt_flag = 0; 480 (void) fprintf(stderr, 481 gettext("Note: The -a (or -K) option nullifies " 482 "all other Kerberos-specific\noptions " 483 "you may have used.\n")); 484 } else if (!krb5auth_flag) { 485 /* is autologin set in krb5.conf? */ 486 status = krb5_init_context(&bsd_context); 487 /* don't sweat failure here */ 488 if (!status) { 489 /* 490 * note that the call to profile_get_options_boolean 491 * with autologin_option can affect value of 492 * krb5auth_flag 493 */ 494 profile_get_options_boolean(bsd_context->profile, 495 appdef, 496 autologin_option); 497 } 498 } 499 500 if (krb5auth_flag) { 501 if (!bsd_context) { 502 status = krb5_init_context(&bsd_context); 503 if (status) { 504 com_err(rlogin, status, 505 gettext("while initializing krb5")); 506 return (EXIT_FAILURE); 507 } 508 } 509 /* 510 * Set up buffers for desread and deswrite. 511 */ 512 desinbuf.data = des_inbuf; 513 desoutbuf.data = des_outbuf; 514 desinbuf.length = sizeof (des_inbuf); 515 desoutbuf.length = sizeof (des_outbuf); 516 517 /* 518 * Get our local realm to look up local realm options. 519 */ 520 status = krb5_get_default_realm(bsd_context, &realmdef[1]); 521 if (status) { 522 com_err(rlogin, status, 523 gettext("while getting default realm")); 524 return (EXIT_FAILURE); 525 } 526 /* 527 * Check the realms section in krb5.conf for encryption, 528 * forward & forwardable info 529 */ 530 profile_get_options_boolean(bsd_context->profile, realmdef, 531 option); 532 /* 533 * Check the appdefaults section 534 */ 535 profile_get_options_boolean(bsd_context->profile, appdef, 536 option); 537 profile_get_options_string(bsd_context->profile, appdef, 538 rcmdversion); 539 540 /* 541 * Set the *_flag variables, if the corresponding *_done are 542 * set to 1, because we dont want the config file values 543 * overriding the command line options. 544 */ 545 if (encrypt_done) 546 encrypt_flag = 1; 547 if (fwd_done) { 548 fflag = 1; 549 Fflag = 0; 550 } else if (fwdable_done) { 551 Fflag = 1; 552 fflag = 0; 553 } 554 if (!rcmdoption_done && (rcmdproto != NULL)) { 555 if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 556 kcmd_proto = KCMD_NEW_PROTOCOL; 557 } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 558 kcmd_proto = KCMD_OLD_PROTOCOL; 559 } else { 560 (void) fprintf(stderr, gettext("Unrecognized " 561 "KCMD protocol (%s)"), rcmdproto); 562 return (EXIT_FAILURE); 563 } 564 } 565 566 if (encrypt_flag && (!krb5_privacy_allowed())) { 567 (void) fprintf(stderr, gettext("rlogin: " 568 "Encryption not supported.\n")); 569 return (EXIT_FAILURE); 570 } 571 } 572 573 if (port_number == 0) { 574 if (krb5auth_flag) { 575 struct servent *sp; 576 577 /* 578 * If the krb5auth_flag is set (via -A, -f, -F, -k) & 579 * if there is an entry in /etc/services for Kerberos 580 * login, attempt to login with Kerberos. If we fail 581 * at any step, use the standard rlogin 582 */ 583 sp = getservbyname(encrypt_flag ? 584 "eklogin" : "klogin", "tcp"); 585 if (sp == NULL) { 586 port_number = encrypt_flag ? 587 htons(2105) : htons(543); 588 } else { 589 port_number = sp->s_port; 590 } 591 } else { 592 port_number = htons(IPPORT_LOGINSERVER); 593 } 594 } 595 596 cp = getenv("TERM"); 597 if (cp) { 598 (void) strncpy(term, cp, sizeof (term)); 599 term[sizeof (term) - 1] = '\0'; 600 } 601 if (getattr_ret == 0) { 602 speed = cfgetospeed(&savetty); 603 /* 604 * "Be conservative in what we send" -- Only send baud rates 605 * which at least all 4.x BSD derivatives are known to handle 606 * correctly. 607 * NOTE: This code assumes new termios speed values will 608 * be "higher" speeds. 609 */ 610 if (speed > B38400) 611 speed = B38400; 612 } 613 614 /* 615 * Only put the terminal speed info in if we have room 616 * so we don't overflow the buffer, and only if we have 617 * a speed we recognize. 618 */ 619 if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) && 620 strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 < 621 sizeof (term)) { 622 (void) strcat(term, "/"); 623 (void) strcat(term, speeds[speed]); 624 } 625 (void) sigset(SIGPIPE, (sigdisp_t)lostpeer); 626 /* will use SIGUSR1 for window size hack, so hold it off */ 627 oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 628 629 /* 630 * Determine if v4 literal address and if so store it to one 631 * side. This is to correct the undesired behaviour of rcmd_af 632 * which converts a passed in v4 literal address to a v4 mapped 633 * v6 literal address. If it was a v4 literal we then re-assign 634 * it to host. 635 */ 636 tmp = NULL; 637 if (inet_addr(host) != (in_addr_t)-1) 638 tmp = host; 639 640 if (krb5auth_flag) { 641 authopts = AP_OPTS_MUTUAL_REQUIRED; 642 643 /* Piggy-back forwarding flags on top of authopts; */ 644 /* they will be reset in kcmd */ 645 if (fflag || Fflag) 646 authopts |= OPTS_FORWARD_CREDS; 647 if (Fflag) 648 authopts |= OPTS_FORWARDABLE_CREDS; 649 650 status = kcmd(&sock, &host, port_number, 651 null_local_username ? "" : pwd->pw_name, 652 name, term, NULL, 653 "host", krb_realm, bsd_context, &auth_context, 654 &cred, 655 NULL, /* No need for sequence number */ 656 NULL, /* No need for server seq # */ 657 authopts, 658 0, /* Not any port # */ 659 &kcmd_proto); 660 661 if (status != 0) { 662 /* 663 * If new protocol requested, we dont fallback to 664 * less secure ones. 665 */ 666 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 667 (void) fprintf(stderr, gettext("rlogin: kcmdv2 " 668 "to host %s failed - %s\n" 669 "Fallback to normal rlogin denied."), 670 host, error_message(status)); 671 return (EXIT_FAILURE); 672 } 673 if (status != -1) { 674 (void) fprintf(stderr, gettext("rlogin: kcmd " 675 "to host %s failed - %s,\n" 676 "trying normal rlogin...\n\n"), 677 host, error_message(status)); 678 } else { 679 (void) fprintf(stderr, 680 gettext("trying normal rlogin...\n")); 681 } 682 /* 683 * kcmd() failed, so we have to 684 * fallback to normal rlogin 685 */ 686 port_number = htons(IPPORT_LOGINSERVER); 687 krb5auth_flag = 0; 688 fflag = Fflag = encrypt_flag = 0; 689 null_local_username = B_FALSE; 690 } else { 691 (void) fprintf(stderr, 692 gettext("connected with Kerberos V5\n")); 693 694 /* 695 * Setup eblock for desread and deswrite. 696 */ 697 session_key = &cred->keyblock; 698 699 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 700 status = krb5_auth_con_getlocalsubkey( 701 bsd_context, 702 auth_context, 703 &session_key); 704 if (status) { 705 com_err(rlogin, status, 706 "determining subkey for session"); 707 return (EXIT_FAILURE); 708 } 709 if (session_key == NULL) { 710 com_err(rlogin, 0, 711 "no subkey negotiated for " 712 "connection"); 713 return (EXIT_FAILURE); 714 } 715 } 716 717 eblock.crypto_entry = session_key->enctype; 718 eblock.key = (krb5_keyblock *)session_key; 719 720 init_encrypt(encrypt_flag, bsd_context, kcmd_proto, 721 &desinbuf, &desoutbuf, CLIENT, &eblock); 722 723 rem = sock; 724 if (rem < 0) 725 pop(EXIT_FAILURE); 726 } 727 } 728 729 /* 730 * Don't merge this with the "if" statement above because 731 * "krb5auth_flag" might be set to false inside it. 732 */ 733 if (!krb5auth_flag) { 734 rem = rcmd_af(&host, port_number, 735 null_local_username ? "" : pwd->pw_name, 736 name, term, NULL, AF_INET6); 737 if (rem < 0) 738 pop(EXIT_FAILURE); 739 } 740 741 /* Never need our privilege again */ 742 __priv_relinquish(); 743 744 if (tmp != NULL) 745 host = tmp; 746 747 if (options & SO_DEBUG && 748 setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on, 749 sizeof (on)) < 0) 750 perror("rlogin: setsockopt (SO_DEBUG)"); 751 752 { 753 int bufsize = 8192; 754 755 (void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, 756 sizeof (int)); 757 } 758 759 doit(oldmask); 760 return (0); 761 } 762 763 static void 764 doit(int oldmask) 765 { 766 struct sgttyb sb; 767 int atmark; 768 769 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 770 perror("ioctl TIOCGETP"); 771 defflags = sb.sg_flags; 772 tabflag = defflags & O_TBDELAY; 773 defflags &= ECHO | O_CRMOD; 774 deferase = sb.sg_erase; 775 defkill = sb.sg_kill; 776 if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&deflflags) == -1) 777 perror("ioctl TIOCLGET"); 778 if (ioctl(STDIN_FILENO, TIOCGETC, (char *)&deftc) == -1) 779 perror("ioctl TIOCGETC"); 780 notc.t_startc = deftc.t_startc; 781 notc.t_stopc = deftc.t_stopc; 782 if (ioctl(STDIN_FILENO, TIOCGLTC, (char *)&defltc) == -1) 783 perror("ioctl TIOCGLTC"); 784 (void) sigset(SIGINT, SIG_IGN); 785 if (sigdisp(SIGHUP) != SIG_IGN) 786 (void) sigset(SIGHUP, exit); 787 if (sigdisp(SIGQUIT) != SIG_IGN) 788 (void) sigset(SIGQUIT, exit); 789 child = fork(); 790 if (child == (pid_t)-1) { 791 perror("rlogin: fork"); 792 done(EXIT_FAILURE); 793 } 794 if (child == 0) { 795 mode(1); 796 if (reader(oldmask) == 0) { 797 prf(gettext("Connection to %.*s closed."), 798 MAXHOSTNAMELEN, host); 799 exit(EXIT_SUCCESS); 800 } 801 (void) sleep(1); 802 prf(gettext("\aConnection to %.*s closed."), 803 MAXHOSTNAMELEN, host); 804 exit(EXIT_FAILURE); 805 } 806 807 /* 808 * We may still own the socket, and may have a pending SIGURG (or might 809 * receive one soon) that we really want to send to the reader. Set a 810 * trap that simply copies such signals to the child. 811 */ 812 #ifdef F_SETOWN_BUG_FIXED 813 (void) sigset(SIGURG, copytochild); 814 #else 815 (void) sigset(SIGURG, SIG_IGN); 816 #endif /* F_SETOWN_BUG_FIXED */ 817 (void) sigset(SIGUSR1, writeroob); 818 /* 819 * Of course, if the urgent byte already arrived, allowing SIGURG 820 * won't get us notification. So, we check to see if we've got 821 * an urgent byte. If so, force a call to writeroob() to pretend 822 * we got SIGURG. 823 */ 824 if (ioctl(rem, SIOCATMARK, &atmark) >= 0) { 825 if (atmark) 826 writeroob(0); 827 } 828 sigsetmask(oldmask); 829 (void) sigset(SIGCHLD, catchild); 830 writer(); 831 prf(gettext("Closed connection to %.*s."), MAXHOSTNAMELEN, host); 832 done(EXIT_SUCCESS); 833 } 834 835 /* 836 * Get signal disposition (or signal handler) for a given signal 837 */ 838 static sigdisp_t 839 sigdisp(int sig) 840 { 841 struct sigaction act; 842 843 act.sa_handler = NULL; 844 act.sa_flags = 0; 845 (void) sigemptyset(&act.sa_mask); 846 (void) sigaction(sig, NULL, &act); 847 return (act.sa_handler); 848 } 849 850 static void 851 done(int status) 852 { 853 pid_t w; 854 855 mode(0); 856 if (child > 0) { 857 /* make sure catchild does not snap it up */ 858 (void) sigset(SIGCHLD, SIG_DFL); 859 if (kill(child, SIGKILL) >= 0) 860 while ((w = wait(0)) > (pid_t)0 && w != child) 861 /* void */; 862 } 863 pop(status); 864 } 865 866 /* 867 * Copy SIGURGs to the child process. 868 */ 869 870 /* ARGSUSED */ 871 static void 872 copytochild(int signum) 873 { 874 875 (void) kill(child, SIGURG); 876 } 877 878 /* 879 * This is called when the reader process gets the out-of-band (urgent) 880 * request to turn on the window-changing protocol. 881 */ 882 883 /* ARGSUSED */ 884 static void 885 writeroob(int signum) 886 { 887 int mask; 888 889 if (!dosigwinch) { 890 /* 891 * Start tracking window size. It doesn't matter which 892 * order the next two are in, because we'll be unconditionally 893 * sending a size notification in a moment. 894 */ 895 (void) sigset(SIGWINCH, sigwinch); 896 dosigwinch = B_TRUE; 897 898 /* 899 * It would be bad if a SIGWINCH came in between the ioctl 900 * and sending the data. It could result in the SIGWINCH 901 * handler sending a good message, and then us sending an 902 * outdated or inconsistent message. 903 * 904 * Instead, if the change is made before the 905 * ioctl, the sigwinch handler will send a size message 906 * and we'll send another, identical, one. If the change 907 * is made after the ioctl, we'll send a message with the 908 * old value, and then the sigwinch handler will send 909 * a revised, correct one. 910 */ 911 mask = sigblock(sigmask(SIGWINCH)); 912 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) == 0) 913 sendwindow(); 914 sigsetmask(mask); 915 } 916 } 917 918 /* ARGSUSED */ 919 static void 920 catchild(int signum) 921 { 922 int options; 923 siginfo_t info; 924 int error; 925 926 for (;;) { 927 options = WNOHANG | WEXITED; 928 error = waitid(P_ALL, 0, &info, options); 929 if (error != 0) 930 return; 931 if (info.si_pid == 0) 932 return; 933 if (info.si_code == CLD_TRAPPED) 934 continue; 935 if (info.si_code == CLD_STOPPED) 936 continue; 937 done(info.si_status); 938 } 939 } 940 941 /* 942 * writer: write to remote: 0 -> line. 943 * ~. terminate 944 * ~^Z suspend rlogin process. 945 * ~^Y suspend rlogin process, but leave reader alone. 946 */ 947 static void 948 writer(void) 949 { 950 char c; 951 int n; 952 boolean_t bol = B_TRUE; /* beginning of line */ 953 boolean_t local = B_FALSE; 954 955 for (;;) { 956 n = read(STDIN_FILENO, &c, 1); 957 if (n <= 0) { 958 if (n == 0) 959 break; 960 if (errno == EINTR) 961 continue; 962 else { 963 prf(gettext("Read error from terminal: %s"), 964 strerror(errno)); 965 break; 966 } 967 } 968 /* 969 * If we're at the beginning of the line 970 * and recognize a command character, then 971 * we echo locally. Otherwise, characters 972 * are echo'd remotely. If the command 973 * character is doubled, this acts as a 974 * force and local echo is suppressed. 975 */ 976 if (bol && !nocmdchar) { 977 bol = B_FALSE; 978 if (c == cmdchar) { 979 local = B_TRUE; 980 continue; 981 } 982 } else if (local) { 983 local = B_FALSE; 984 if (c == '.' || c == deftc.t_eofc) { 985 echo(c); 986 break; 987 } 988 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 989 bol = B_TRUE; 990 echo(c); 991 stop(c); 992 continue; 993 } 994 if (c != cmdchar) { 995 if (deswrite(rem, &cmdchar, 1, 0) < 0) { 996 prf(gettext( 997 "Write error to network: %s"), 998 strerror(errno)); 999 break; 1000 } 1001 } 1002 } 1003 if ((n = deswrite(rem, &c, 1, 0)) <= 0) { 1004 if (n == 0) 1005 prf(gettext("line gone")); 1006 else 1007 prf(gettext("Write error to network: %s"), 1008 strerror(errno)); 1009 break; 1010 } 1011 bol = c == defkill || c == deftc.t_eofc || 1012 c == deftc.t_intrc || c == defltc.t_suspc || 1013 c == '\r' || c == '\n'; 1014 } 1015 } 1016 1017 static void 1018 echo(char c) 1019 { 1020 char buf[8]; 1021 char *p = buf; 1022 1023 c &= 0177; 1024 *p++ = cmdchar; 1025 if (c < ' ') { 1026 *p++ = '^'; 1027 *p++ = c + '@'; 1028 } else if (c == 0177) { 1029 *p++ = '^'; 1030 *p++ = '?'; 1031 } else 1032 *p++ = c; 1033 *p++ = '\r'; 1034 *p++ = '\n'; 1035 if (write(STDOUT_FILENO, buf, p - buf) < 0) 1036 prf(gettext("Write error to terminal: %s"), strerror(errno)); 1037 } 1038 1039 static void 1040 stop(char cmdc) 1041 { 1042 mode(0); 1043 (void) sigset(SIGCHLD, SIG_IGN); 1044 (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 1045 (void) sigset(SIGCHLD, catchild); 1046 mode(1); 1047 sigwinch(0); /* check for size changes */ 1048 } 1049 1050 /* ARGSUSED */ 1051 static void 1052 sigwinch(int signum) 1053 { 1054 struct winsize ws; 1055 1056 if (dosigwinch && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 && 1057 memcmp(&winsize, &ws, sizeof (ws)) != 0) { 1058 winsize = ws; 1059 sendwindow(); 1060 } 1061 } 1062 1063 /* 1064 * Send the window size to the server via the magic escape. 1065 * Note: SIGWINCH should be blocked when this is called, lest 1066 * winsize change underneath us and chaos result. 1067 */ 1068 static void 1069 sendwindow(void) 1070 { 1071 char obuf[4 + sizeof (struct winsize)]; 1072 struct winsize *wp = (struct winsize *)(void *)(obuf+4); 1073 1074 obuf[0] = -1; 1075 obuf[1] = -1; 1076 obuf[2] = 's'; 1077 obuf[3] = 's'; 1078 wp->ws_row = htons(winsize.ws_row); 1079 wp->ws_col = htons(winsize.ws_col); 1080 wp->ws_xpixel = htons(winsize.ws_xpixel); 1081 wp->ws_ypixel = htons(winsize.ws_ypixel); 1082 if (deswrite(rem, obuf, sizeof (obuf), 0) < 0) 1083 prf(gettext("Write error to network: %s"), strerror(errno)); 1084 } 1085 1086 1087 /* 1088 * reader: read from remote: remote -> stdout 1089 */ 1090 #define READING 1 1091 #define WRITING 2 1092 1093 static char rcvbuf[8 * 1024]; 1094 static int rcvcnt; 1095 static int rcvstate; 1096 static pid_t ppid; 1097 static jmp_buf rcvtop; 1098 1099 static void 1100 oob(void) 1101 { 1102 int out = FWRITE, atmark, n; 1103 int rcvd = 0; 1104 char waste[4*BUFSIZ], mark; 1105 struct sgttyb sb; 1106 fd_set exceptfds; 1107 struct timeval tv; 1108 int ret; 1109 1110 FD_ZERO(&exceptfds); 1111 FD_SET(rem, &exceptfds); 1112 timerclear(&tv); 1113 ret = select(rem+1, NULL, NULL, &exceptfds, &tv); 1114 /* 1115 * We may get an extra signal at start up time since we are trying 1116 * to take all precautions not to miss the urgent byte. This 1117 * means we may get here without any urgent data to process, in which 1118 * case we do nothing and just return. 1119 */ 1120 if (ret <= 0) 1121 return; 1122 1123 do { 1124 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 1125 break; 1126 } 1127 if (!atmark) { 1128 /* 1129 * Urgent data not here yet. 1130 * It may not be possible to send it yet 1131 * if we are blocked for output 1132 * and our input buffer is full. 1133 */ 1134 if (rcvcnt < sizeof (rcvbuf)) { 1135 n = desread(rem, rcvbuf + rcvcnt, 1136 sizeof (rcvbuf) - rcvcnt, 0); 1137 if (n <= 0) 1138 return; 1139 rcvd += n; 1140 rcvcnt += n; 1141 } else { 1142 /* 1143 * We still haven't gotten to the urgent mark 1144 * and we're out of buffer space. Since we 1145 * must clear our receive window to allow it 1146 * to arrive, we will have to throw away 1147 * these bytes. 1148 */ 1149 n = desread(rem, waste, sizeof (waste), 0); 1150 if (n <= 0) 1151 return; 1152 } 1153 } 1154 } while (atmark == 0); 1155 while (recv(rem, &mark, 1, MSG_OOB) < 0) { 1156 switch (errno) { 1157 1158 case EWOULDBLOCK: 1159 /* 1160 * We've reached the urgent mark, so the next 1161 * data to arrive will be the urgent, but it must 1162 * not have arrived yet. 1163 */ 1164 (void) sleep(1); 1165 continue; 1166 1167 default: 1168 return; 1169 } 1170 } 1171 if (mark & TIOCPKT_WINDOW) { 1172 /* 1173 * Let server know about window size changes 1174 */ 1175 (void) kill(ppid, SIGUSR1); 1176 } 1177 if (!eight && (mark & TIOCPKT_NOSTOP)) { 1178 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1179 perror("ioctl TIOCGETP"); 1180 sb.sg_flags &= ~O_CBREAK; 1181 sb.sg_flags |= O_RAW; 1182 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1183 perror("ioctl TIOCSETP 1"); 1184 notc.t_stopc = -1; 1185 notc.t_startc = -1; 1186 if (compat_ioctl(STDIN_FILENO, TIOCSETC, ¬c) == -1) 1187 perror("ioctl TIOCSETC"); 1188 } 1189 if (!eight && (mark & TIOCPKT_DOSTOP)) { 1190 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1191 perror("ioctl TIOCGETP"); 1192 sb.sg_flags &= ~O_RAW; 1193 sb.sg_flags |= O_CBREAK; 1194 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1195 perror("ioctl TIOCSETP 2"); 1196 notc.t_stopc = deftc.t_stopc; 1197 notc.t_startc = deftc.t_startc; 1198 if (compat_ioctl(STDIN_FILENO, TIOCSETC, ¬c) == -1) 1199 perror("ioctl TIOCSETC"); 1200 } 1201 if (mark & TIOCPKT_FLUSHWRITE) { 1202 if (ioctl(STDOUT_FILENO, TIOCFLUSH, (char *)&out) == -1) 1203 perror("ioctl TIOCFLUSH"); 1204 for (;;) { 1205 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 1206 perror("ioctl SIOCATMARK"); 1207 break; 1208 } 1209 if (atmark) 1210 break; 1211 n = desread(rem, waste, sizeof (waste), 0); 1212 if (n <= 0) { 1213 if (n < 0) 1214 prf(gettext( 1215 "Read error from network: %s"), 1216 strerror(errno)); 1217 break; 1218 } 1219 } 1220 /* 1221 * Don't want any pending data to be output, 1222 * so clear the recv buffer. 1223 * If we were hanging on a write when interrupted, 1224 * don't want it to restart. If we were reading, 1225 * restart anyway. 1226 */ 1227 rcvcnt = 0; 1228 longjmp(rcvtop, 1); 1229 } 1230 /* 1231 * If we filled the receive buffer while a read was pending, 1232 * longjmp to the top to restart appropriately. Don't abort 1233 * a pending write, however, or we won't know how much was written. 1234 */ 1235 if (rcvd && rcvstate == READING) 1236 longjmp(rcvtop, 1); 1237 } 1238 1239 /* 1240 * reader: read from remote: line -> 1 1241 */ 1242 static int 1243 reader(int oldmask) 1244 { 1245 /* 1246 * 4.3bsd or later and SunOS 4.0 or later use the posiitive 1247 * pid; otherwise use the negative. 1248 */ 1249 pid_t pid = getpid(); 1250 int n, remaining; 1251 char *bufp = rcvbuf; 1252 1253 (void) sigset(SIGTTOU, SIG_IGN); 1254 (void) sigset(SIGURG, (void (*)())oob); 1255 ppid = getppid(); 1256 if (fcntl(rem, F_SETOWN, pid) == -1) 1257 perror("fcntl F_SETOWN"); 1258 /* 1259 * A SIGURG may have been posted before we were completely forked, 1260 * which means we may not have received it. To insure we do not miss 1261 * any urgent data, we force the signal. The signal hander will be 1262 * able to determine if in fact there is urgent data or not. 1263 */ 1264 (void) kill(pid, SIGURG); 1265 (void) setjmp(rcvtop); 1266 sigsetmask(oldmask); 1267 for (;;) { 1268 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 1269 rcvstate = WRITING; 1270 n = write(STDOUT_FILENO, bufp, remaining); 1271 if (n < 0) { 1272 if (errno != EINTR) { 1273 prf(gettext( 1274 "Write error to terminal: %s"), 1275 strerror(errno)); 1276 return (-1); 1277 } 1278 continue; 1279 } 1280 bufp += n; 1281 } 1282 bufp = rcvbuf; 1283 rcvcnt = 0; 1284 rcvstate = READING; 1285 rcvcnt = desread(rem, rcvbuf, sizeof (rcvbuf), 0); 1286 if (rcvcnt == 0) 1287 return (0); 1288 if (rcvcnt < 0) { 1289 if (errno == EINTR) 1290 continue; 1291 prf(gettext("Read error from network: %s"), 1292 strerror(errno)); 1293 return (-1); 1294 } 1295 } 1296 } 1297 1298 static void 1299 mode(int f) 1300 { 1301 struct tchars *tc; 1302 struct ltchars *ltc; 1303 struct sgttyb sb; 1304 int lflags; 1305 1306 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1307 perror("ioctl TIOCGETP"); 1308 if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&lflags) == -1) 1309 perror("ioctl TIOCLGET"); 1310 switch (f) { 1311 1312 case 0: 1313 sb.sg_flags &= ~(O_CBREAK|O_RAW|O_TBDELAY); 1314 sb.sg_flags |= defflags|tabflag; 1315 tc = &deftc; 1316 ltc = &defltc; 1317 sb.sg_kill = defkill; 1318 sb.sg_erase = deferase; 1319 lflags = deflflags; 1320 break; 1321 1322 case 1: 1323 sb.sg_flags |= (eight ? O_RAW : O_CBREAK); 1324 sb.sg_flags &= ~defflags; 1325 /* preserve tab delays, but turn off XTABS */ 1326 if ((sb.sg_flags & O_TBDELAY) == O_XTABS) 1327 sb.sg_flags &= ~O_TBDELAY; 1328 tc = ¬c; 1329 ltc = &noltc; 1330 sb.sg_kill = sb.sg_erase = -1; 1331 if (litout) 1332 lflags |= LLITOUT; 1333 break; 1334 1335 default: 1336 /*NOTREACHED*/ 1337 return; 1338 } 1339 if (compat_ioctl(STDIN_FILENO, TIOCSLTC, ltc) == -1) 1340 perror("ioctl TIOCSLTC"); 1341 if (compat_ioctl(STDIN_FILENO, TIOCSETC, tc) == -1) 1342 perror("ioctl TIOCSETC"); 1343 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1344 perror("ioctl TIOCSETP 3"); 1345 if (compat_ioctl(STDIN_FILENO, TIOCLSET, &lflags) == -1) 1346 perror("ioctl TIOCLSET"); 1347 } 1348 1349 /* PRINTFLIKE(0) */ 1350 static void 1351 prf(const char *format, ...) 1352 { 1353 va_list ap; 1354 1355 va_start(ap, format); 1356 (void) vfprintf(stderr, format, ap); 1357 va_end(ap); 1358 (void) fputs(CRLF, stderr); 1359 } 1360 1361 static void 1362 lostpeer(void) 1363 { 1364 (void) sigset(SIGPIPE, SIG_IGN); 1365 prf(gettext("\aConnection to %.*s closed."), MAXHOSTNAMELEN, host); 1366 done(EXIT_FAILURE); 1367 } 1368 1369 static int 1370 compat_ioctl(int des, int request, void *arg) 1371 { 1372 struct termios tb; 1373 boolean_t flag = B_FALSE; 1374 1375 if (ioctl(des, request, arg) < 0) 1376 return (-1); 1377 1378 if (tcgetattr(des, &tb) < 0) 1379 return (-1); 1380 1381 if (cfgetispeed(&tb) != cfgetispeed(&savetty)) { 1382 (void) cfsetispeed(&tb, cfgetispeed(&savetty)); 1383 flag = B_TRUE; 1384 } 1385 if (cfgetospeed(&tb) != cfgetospeed(&savetty)) { 1386 (void) cfsetospeed(&tb, cfgetospeed(&savetty)); 1387 flag = B_TRUE; 1388 } 1389 1390 return (flag ? tcsetattr(des, TCSANOW, &tb) : 0); 1391 } 1392