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 2010 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 (void) profile_get_options_boolean(bsd_context->profile, 495 appdef, autologin_option); 496 } 497 } 498 499 if (krb5auth_flag) { 500 if (!bsd_context) { 501 status = krb5_init_context(&bsd_context); 502 if (status) { 503 com_err(rlogin, status, 504 gettext("while initializing krb5")); 505 return (EXIT_FAILURE); 506 } 507 } 508 /* 509 * Set up buffers for desread and deswrite. 510 */ 511 desinbuf.data = des_inbuf; 512 desoutbuf.data = des_outbuf; 513 desinbuf.length = sizeof (des_inbuf); 514 desoutbuf.length = sizeof (des_outbuf); 515 516 /* 517 * Get our local realm to look up local realm options. 518 */ 519 status = krb5_get_default_realm(bsd_context, &realmdef[1]); 520 if (status) { 521 com_err(rlogin, status, 522 gettext("while getting default realm")); 523 return (EXIT_FAILURE); 524 } 525 /* 526 * Check the realms section in krb5.conf for encryption, 527 * forward & forwardable info 528 */ 529 (void) profile_get_options_boolean(bsd_context->profile, 530 realmdef, option); 531 /* 532 * Check the appdefaults section 533 */ 534 (void) profile_get_options_boolean(bsd_context->profile, 535 appdef, option); 536 (void) profile_get_options_string(bsd_context->profile, 537 appdef, rcmdversion); 538 539 /* 540 * Set the *_flag variables, if the corresponding *_done are 541 * set to 1, because we dont want the config file values 542 * overriding the command line options. 543 */ 544 if (encrypt_done) 545 encrypt_flag = 1; 546 if (fwd_done) { 547 fflag = 1; 548 Fflag = 0; 549 } else if (fwdable_done) { 550 Fflag = 1; 551 fflag = 0; 552 } 553 if (!rcmdoption_done && (rcmdproto != NULL)) { 554 if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 555 kcmd_proto = KCMD_NEW_PROTOCOL; 556 } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 557 kcmd_proto = KCMD_OLD_PROTOCOL; 558 } else { 559 (void) fprintf(stderr, gettext("Unrecognized " 560 "KCMD protocol (%s)"), rcmdproto); 561 return (EXIT_FAILURE); 562 } 563 } 564 565 if (encrypt_flag && (!krb5_privacy_allowed())) { 566 (void) fprintf(stderr, gettext("rlogin: " 567 "Encryption not supported.\n")); 568 return (EXIT_FAILURE); 569 } 570 } 571 572 if (port_number == 0) { 573 if (krb5auth_flag) { 574 struct servent *sp; 575 576 /* 577 * If the krb5auth_flag is set (via -A, -f, -F, -k) & 578 * if there is an entry in /etc/services for Kerberos 579 * login, attempt to login with Kerberos. If we fail 580 * at any step, use the standard rlogin 581 */ 582 sp = getservbyname(encrypt_flag ? 583 "eklogin" : "klogin", "tcp"); 584 if (sp == NULL) { 585 port_number = encrypt_flag ? 586 htons(2105) : htons(543); 587 } else { 588 port_number = sp->s_port; 589 } 590 } else { 591 port_number = htons(IPPORT_LOGINSERVER); 592 } 593 } 594 595 cp = getenv("TERM"); 596 if (cp) { 597 (void) strncpy(term, cp, sizeof (term)); 598 term[sizeof (term) - 1] = '\0'; 599 } 600 if (getattr_ret == 0) { 601 speed = cfgetospeed(&savetty); 602 /* 603 * "Be conservative in what we send" -- Only send baud rates 604 * which at least all 4.x BSD derivatives are known to handle 605 * correctly. 606 * NOTE: This code assumes new termios speed values will 607 * be "higher" speeds. 608 */ 609 if (speed > B38400) 610 speed = B38400; 611 } 612 613 /* 614 * Only put the terminal speed info in if we have room 615 * so we don't overflow the buffer, and only if we have 616 * a speed we recognize. 617 */ 618 if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) && 619 strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 < 620 sizeof (term)) { 621 (void) strcat(term, "/"); 622 (void) strcat(term, speeds[speed]); 623 } 624 (void) sigset(SIGPIPE, (sigdisp_t)lostpeer); 625 /* will use SIGUSR1 for window size hack, so hold it off */ 626 oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 627 628 /* 629 * Determine if v4 literal address and if so store it to one 630 * side. This is to correct the undesired behaviour of rcmd_af 631 * which converts a passed in v4 literal address to a v4 mapped 632 * v6 literal address. If it was a v4 literal we then re-assign 633 * it to host. 634 */ 635 tmp = NULL; 636 if (inet_addr(host) != (in_addr_t)-1) 637 tmp = host; 638 639 if (krb5auth_flag) { 640 authopts = AP_OPTS_MUTUAL_REQUIRED; 641 642 /* Piggy-back forwarding flags on top of authopts; */ 643 /* they will be reset in kcmd */ 644 if (fflag || Fflag) 645 authopts |= OPTS_FORWARD_CREDS; 646 if (Fflag) 647 authopts |= OPTS_FORWARDABLE_CREDS; 648 649 status = kcmd(&sock, &host, port_number, 650 null_local_username ? "" : pwd->pw_name, 651 name, term, NULL, 652 "host", krb_realm, bsd_context, &auth_context, 653 &cred, 654 NULL, /* No need for sequence number */ 655 NULL, /* No need for server seq # */ 656 authopts, 657 0, /* Not any port # */ 658 &kcmd_proto); 659 660 if (status != 0) { 661 /* 662 * If new protocol requested, we dont fallback to 663 * less secure ones. 664 */ 665 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 666 (void) fprintf(stderr, gettext("rlogin: kcmdv2 " 667 "to host %s failed - %s\n" 668 "Fallback to normal rlogin denied."), 669 host, error_message(status)); 670 return (EXIT_FAILURE); 671 } 672 if (status != -1) { 673 (void) fprintf(stderr, gettext("rlogin: kcmd " 674 "to host %s failed - %s,\n" 675 "trying normal rlogin...\n\n"), 676 host, error_message(status)); 677 } else { 678 (void) fprintf(stderr, 679 gettext("trying normal rlogin...\n")); 680 } 681 /* 682 * kcmd() failed, so we have to 683 * fallback to normal rlogin 684 */ 685 port_number = htons(IPPORT_LOGINSERVER); 686 krb5auth_flag = 0; 687 fflag = Fflag = encrypt_flag = 0; 688 null_local_username = B_FALSE; 689 } else { 690 (void) fprintf(stderr, 691 gettext("connected with Kerberos V5\n")); 692 693 /* 694 * Setup eblock for desread and deswrite. 695 */ 696 session_key = &cred->keyblock; 697 698 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 699 status = krb5_auth_con_getlocalsubkey( 700 bsd_context, 701 auth_context, 702 &session_key); 703 if (status) { 704 com_err(rlogin, status, 705 "determining subkey for session"); 706 return (EXIT_FAILURE); 707 } 708 if (session_key == NULL) { 709 com_err(rlogin, 0, 710 "no subkey negotiated for " 711 "connection"); 712 return (EXIT_FAILURE); 713 } 714 } 715 716 eblock.crypto_entry = session_key->enctype; 717 eblock.key = (krb5_keyblock *)session_key; 718 719 init_encrypt(encrypt_flag, bsd_context, kcmd_proto, 720 &desinbuf, &desoutbuf, CLIENT, &eblock); 721 722 rem = sock; 723 if (rem < 0) 724 pop(EXIT_FAILURE); 725 } 726 } 727 728 /* 729 * Don't merge this with the "if" statement above because 730 * "krb5auth_flag" might be set to false inside it. 731 */ 732 if (!krb5auth_flag) { 733 rem = rcmd_af(&host, port_number, 734 null_local_username ? "" : pwd->pw_name, 735 name, term, NULL, AF_INET6); 736 if (rem < 0) 737 pop(EXIT_FAILURE); 738 } 739 740 /* Never need our privilege again */ 741 __priv_relinquish(); 742 743 if (tmp != NULL) 744 host = tmp; 745 746 if (options & SO_DEBUG && 747 setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on, 748 sizeof (on)) < 0) 749 perror("rlogin: setsockopt (SO_DEBUG)"); 750 751 { 752 int bufsize = 8192; 753 754 (void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, 755 sizeof (int)); 756 } 757 758 doit(oldmask); 759 return (0); 760 } 761 762 static void 763 doit(int oldmask) 764 { 765 struct sgttyb sb; 766 int atmark; 767 768 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 769 perror("ioctl TIOCGETP"); 770 defflags = sb.sg_flags; 771 tabflag = defflags & O_TBDELAY; 772 defflags &= ECHO | O_CRMOD; 773 deferase = sb.sg_erase; 774 defkill = sb.sg_kill; 775 if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&deflflags) == -1) 776 perror("ioctl TIOCLGET"); 777 if (ioctl(STDIN_FILENO, TIOCGETC, (char *)&deftc) == -1) 778 perror("ioctl TIOCGETC"); 779 notc.t_startc = deftc.t_startc; 780 notc.t_stopc = deftc.t_stopc; 781 if (ioctl(STDIN_FILENO, TIOCGLTC, (char *)&defltc) == -1) 782 perror("ioctl TIOCGLTC"); 783 (void) sigset(SIGINT, SIG_IGN); 784 if (sigdisp(SIGHUP) != SIG_IGN) 785 (void) sigset(SIGHUP, exit); 786 if (sigdisp(SIGQUIT) != SIG_IGN) 787 (void) sigset(SIGQUIT, exit); 788 child = fork(); 789 if (child == (pid_t)-1) { 790 perror("rlogin: fork"); 791 done(EXIT_FAILURE); 792 } 793 if (child == 0) { 794 mode(1); 795 if (reader(oldmask) == 0) { 796 prf(gettext("Connection to %.*s closed."), 797 MAXHOSTNAMELEN, host); 798 exit(EXIT_SUCCESS); 799 } 800 (void) sleep(1); 801 prf(gettext("\aConnection to %.*s closed."), 802 MAXHOSTNAMELEN, host); 803 exit(EXIT_FAILURE); 804 } 805 806 /* 807 * We may still own the socket, and may have a pending SIGURG (or might 808 * receive one soon) that we really want to send to the reader. Set a 809 * trap that simply copies such signals to the child. 810 */ 811 #ifdef F_SETOWN_BUG_FIXED 812 (void) sigset(SIGURG, copytochild); 813 #else 814 (void) sigset(SIGURG, SIG_IGN); 815 #endif /* F_SETOWN_BUG_FIXED */ 816 (void) sigset(SIGUSR1, writeroob); 817 /* 818 * Of course, if the urgent byte already arrived, allowing SIGURG 819 * won't get us notification. So, we check to see if we've got 820 * an urgent byte. If so, force a call to writeroob() to pretend 821 * we got SIGURG. 822 */ 823 if (ioctl(rem, SIOCATMARK, &atmark) >= 0) { 824 if (atmark) 825 writeroob(0); 826 } 827 sigsetmask(oldmask); 828 (void) sigset(SIGCHLD, catchild); 829 writer(); 830 prf(gettext("Closed connection to %.*s."), MAXHOSTNAMELEN, host); 831 done(EXIT_SUCCESS); 832 } 833 834 /* 835 * Get signal disposition (or signal handler) for a given signal 836 */ 837 static sigdisp_t 838 sigdisp(int sig) 839 { 840 struct sigaction act; 841 842 act.sa_handler = NULL; 843 act.sa_flags = 0; 844 (void) sigemptyset(&act.sa_mask); 845 (void) sigaction(sig, NULL, &act); 846 return (act.sa_handler); 847 } 848 849 static void 850 done(int status) 851 { 852 pid_t w; 853 854 mode(0); 855 if (child > 0) { 856 /* make sure catchild does not snap it up */ 857 (void) sigset(SIGCHLD, SIG_DFL); 858 if (kill(child, SIGKILL) >= 0) 859 while ((w = wait(0)) > (pid_t)0 && w != child) 860 /* void */; 861 } 862 pop(status); 863 } 864 865 /* 866 * Copy SIGURGs to the child process. 867 */ 868 869 /* ARGSUSED */ 870 static void 871 copytochild(int signum) 872 { 873 874 (void) kill(child, SIGURG); 875 } 876 877 /* 878 * This is called when the reader process gets the out-of-band (urgent) 879 * request to turn on the window-changing protocol. 880 */ 881 882 /* ARGSUSED */ 883 static void 884 writeroob(int signum) 885 { 886 int mask; 887 888 if (!dosigwinch) { 889 /* 890 * Start tracking window size. It doesn't matter which 891 * order the next two are in, because we'll be unconditionally 892 * sending a size notification in a moment. 893 */ 894 (void) sigset(SIGWINCH, sigwinch); 895 dosigwinch = B_TRUE; 896 897 /* 898 * It would be bad if a SIGWINCH came in between the ioctl 899 * and sending the data. It could result in the SIGWINCH 900 * handler sending a good message, and then us sending an 901 * outdated or inconsistent message. 902 * 903 * Instead, if the change is made before the 904 * ioctl, the sigwinch handler will send a size message 905 * and we'll send another, identical, one. If the change 906 * is made after the ioctl, we'll send a message with the 907 * old value, and then the sigwinch handler will send 908 * a revised, correct one. 909 */ 910 mask = sigblock(sigmask(SIGWINCH)); 911 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) == 0) 912 sendwindow(); 913 sigsetmask(mask); 914 } 915 } 916 917 /* ARGSUSED */ 918 static void 919 catchild(int signum) 920 { 921 int options; 922 siginfo_t info; 923 int error; 924 925 for (;;) { 926 options = WNOHANG | WEXITED; 927 error = waitid(P_ALL, 0, &info, options); 928 if (error != 0) 929 return; 930 if (info.si_pid == 0) 931 return; 932 if (info.si_code == CLD_TRAPPED) 933 continue; 934 if (info.si_code == CLD_STOPPED) 935 continue; 936 done(info.si_status); 937 } 938 } 939 940 /* 941 * writer: write to remote: 0 -> line. 942 * ~. terminate 943 * ~^Z suspend rlogin process. 944 * ~^Y suspend rlogin process, but leave reader alone. 945 */ 946 static void 947 writer(void) 948 { 949 char c; 950 int n; 951 boolean_t bol = B_TRUE; /* beginning of line */ 952 boolean_t local = B_FALSE; 953 954 for (;;) { 955 n = read(STDIN_FILENO, &c, 1); 956 if (n <= 0) { 957 if (n == 0) 958 break; 959 if (errno == EINTR) 960 continue; 961 else { 962 prf(gettext("Read error from terminal: %s"), 963 strerror(errno)); 964 break; 965 } 966 } 967 /* 968 * If we're at the beginning of the line 969 * and recognize a command character, then 970 * we echo locally. Otherwise, characters 971 * are echo'd remotely. If the command 972 * character is doubled, this acts as a 973 * force and local echo is suppressed. 974 */ 975 if (bol && !nocmdchar) { 976 bol = B_FALSE; 977 if (c == cmdchar) { 978 local = B_TRUE; 979 continue; 980 } 981 } else if (local) { 982 local = B_FALSE; 983 if (c == '.' || c == deftc.t_eofc) { 984 echo(c); 985 break; 986 } 987 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 988 bol = B_TRUE; 989 echo(c); 990 stop(c); 991 continue; 992 } 993 if (c != cmdchar) { 994 if (deswrite(rem, &cmdchar, 1, 0) < 0) { 995 prf(gettext( 996 "Write error to network: %s"), 997 strerror(errno)); 998 break; 999 } 1000 } 1001 } 1002 if ((n = deswrite(rem, &c, 1, 0)) <= 0) { 1003 if (n == 0) 1004 prf(gettext("line gone")); 1005 else 1006 prf(gettext("Write error to network: %s"), 1007 strerror(errno)); 1008 break; 1009 } 1010 bol = c == defkill || c == deftc.t_eofc || 1011 c == deftc.t_intrc || c == defltc.t_suspc || 1012 c == '\r' || c == '\n'; 1013 } 1014 } 1015 1016 static void 1017 echo(char c) 1018 { 1019 char buf[8]; 1020 char *p = buf; 1021 1022 c &= 0177; 1023 *p++ = cmdchar; 1024 if (c < ' ') { 1025 *p++ = '^'; 1026 *p++ = c + '@'; 1027 } else if (c == 0177) { 1028 *p++ = '^'; 1029 *p++ = '?'; 1030 } else 1031 *p++ = c; 1032 *p++ = '\r'; 1033 *p++ = '\n'; 1034 if (write(STDOUT_FILENO, buf, p - buf) < 0) 1035 prf(gettext("Write error to terminal: %s"), strerror(errno)); 1036 } 1037 1038 static void 1039 stop(char cmdc) 1040 { 1041 mode(0); 1042 (void) sigset(SIGCHLD, SIG_IGN); 1043 (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 1044 (void) sigset(SIGCHLD, catchild); 1045 mode(1); 1046 sigwinch(0); /* check for size changes */ 1047 } 1048 1049 /* ARGSUSED */ 1050 static void 1051 sigwinch(int signum) 1052 { 1053 struct winsize ws; 1054 1055 if (dosigwinch && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 && 1056 memcmp(&winsize, &ws, sizeof (ws)) != 0) { 1057 winsize = ws; 1058 sendwindow(); 1059 } 1060 } 1061 1062 /* 1063 * Send the window size to the server via the magic escape. 1064 * Note: SIGWINCH should be blocked when this is called, lest 1065 * winsize change underneath us and chaos result. 1066 */ 1067 static void 1068 sendwindow(void) 1069 { 1070 char obuf[4 + sizeof (struct winsize)]; 1071 struct winsize *wp = (struct winsize *)(void *)(obuf+4); 1072 1073 obuf[0] = -1; 1074 obuf[1] = -1; 1075 obuf[2] = 's'; 1076 obuf[3] = 's'; 1077 wp->ws_row = htons(winsize.ws_row); 1078 wp->ws_col = htons(winsize.ws_col); 1079 wp->ws_xpixel = htons(winsize.ws_xpixel); 1080 wp->ws_ypixel = htons(winsize.ws_ypixel); 1081 if (deswrite(rem, obuf, sizeof (obuf), 0) < 0) 1082 prf(gettext("Write error to network: %s"), strerror(errno)); 1083 } 1084 1085 1086 /* 1087 * reader: read from remote: remote -> stdout 1088 */ 1089 #define READING 1 1090 #define WRITING 2 1091 1092 static char rcvbuf[8 * 1024]; 1093 static int rcvcnt; 1094 static int rcvstate; 1095 static pid_t ppid; 1096 static jmp_buf rcvtop; 1097 1098 static void 1099 oob(void) 1100 { 1101 int out = FWRITE, atmark, n; 1102 int rcvd = 0; 1103 char waste[4*BUFSIZ], mark; 1104 struct sgttyb sb; 1105 fd_set exceptfds; 1106 struct timeval tv; 1107 int ret; 1108 1109 FD_ZERO(&exceptfds); 1110 FD_SET(rem, &exceptfds); 1111 timerclear(&tv); 1112 ret = select(rem+1, NULL, NULL, &exceptfds, &tv); 1113 /* 1114 * We may get an extra signal at start up time since we are trying 1115 * to take all precautions not to miss the urgent byte. This 1116 * means we may get here without any urgent data to process, in which 1117 * case we do nothing and just return. 1118 */ 1119 if (ret <= 0) 1120 return; 1121 1122 do { 1123 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 1124 break; 1125 } 1126 if (!atmark) { 1127 /* 1128 * Urgent data not here yet. 1129 * It may not be possible to send it yet 1130 * if we are blocked for output 1131 * and our input buffer is full. 1132 */ 1133 if (rcvcnt < sizeof (rcvbuf)) { 1134 n = desread(rem, rcvbuf + rcvcnt, 1135 sizeof (rcvbuf) - rcvcnt, 0); 1136 if (n <= 0) 1137 return; 1138 rcvd += n; 1139 rcvcnt += n; 1140 } else { 1141 /* 1142 * We still haven't gotten to the urgent mark 1143 * and we're out of buffer space. Since we 1144 * must clear our receive window to allow it 1145 * to arrive, we will have to throw away 1146 * these bytes. 1147 */ 1148 n = desread(rem, waste, sizeof (waste), 0); 1149 if (n <= 0) 1150 return; 1151 } 1152 } 1153 } while (atmark == 0); 1154 while (recv(rem, &mark, 1, MSG_OOB) < 0) { 1155 switch (errno) { 1156 1157 case EWOULDBLOCK: 1158 /* 1159 * We've reached the urgent mark, so the next 1160 * data to arrive will be the urgent, but it must 1161 * not have arrived yet. 1162 */ 1163 (void) sleep(1); 1164 continue; 1165 1166 default: 1167 return; 1168 } 1169 } 1170 if (mark & TIOCPKT_WINDOW) { 1171 /* 1172 * Let server know about window size changes 1173 */ 1174 (void) kill(ppid, SIGUSR1); 1175 } 1176 if (!eight && (mark & TIOCPKT_NOSTOP)) { 1177 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1178 perror("ioctl TIOCGETP"); 1179 sb.sg_flags &= ~O_CBREAK; 1180 sb.sg_flags |= O_RAW; 1181 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1182 perror("ioctl TIOCSETP 1"); 1183 notc.t_stopc = -1; 1184 notc.t_startc = -1; 1185 if (compat_ioctl(STDIN_FILENO, TIOCSETC, ¬c) == -1) 1186 perror("ioctl TIOCSETC"); 1187 } 1188 if (!eight && (mark & TIOCPKT_DOSTOP)) { 1189 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1190 perror("ioctl TIOCGETP"); 1191 sb.sg_flags &= ~O_RAW; 1192 sb.sg_flags |= O_CBREAK; 1193 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1194 perror("ioctl TIOCSETP 2"); 1195 notc.t_stopc = deftc.t_stopc; 1196 notc.t_startc = deftc.t_startc; 1197 if (compat_ioctl(STDIN_FILENO, TIOCSETC, ¬c) == -1) 1198 perror("ioctl TIOCSETC"); 1199 } 1200 if (mark & TIOCPKT_FLUSHWRITE) { 1201 if (ioctl(STDOUT_FILENO, TIOCFLUSH, (char *)&out) == -1) 1202 perror("ioctl TIOCFLUSH"); 1203 for (;;) { 1204 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 1205 perror("ioctl SIOCATMARK"); 1206 break; 1207 } 1208 if (atmark) 1209 break; 1210 n = desread(rem, waste, sizeof (waste), 0); 1211 if (n <= 0) { 1212 if (n < 0) 1213 prf(gettext( 1214 "Read error from network: %s"), 1215 strerror(errno)); 1216 break; 1217 } 1218 } 1219 /* 1220 * Don't want any pending data to be output, 1221 * so clear the recv buffer. 1222 * If we were hanging on a write when interrupted, 1223 * don't want it to restart. If we were reading, 1224 * restart anyway. 1225 */ 1226 rcvcnt = 0; 1227 longjmp(rcvtop, 1); 1228 } 1229 /* 1230 * If we filled the receive buffer while a read was pending, 1231 * longjmp to the top to restart appropriately. Don't abort 1232 * a pending write, however, or we won't know how much was written. 1233 */ 1234 if (rcvd && rcvstate == READING) 1235 longjmp(rcvtop, 1); 1236 } 1237 1238 /* 1239 * reader: read from remote: line -> 1 1240 */ 1241 static int 1242 reader(int oldmask) 1243 { 1244 /* 1245 * 4.3bsd or later and SunOS 4.0 or later use the posiitive 1246 * pid; otherwise use the negative. 1247 */ 1248 pid_t pid = getpid(); 1249 int n, remaining; 1250 char *bufp = rcvbuf; 1251 1252 (void) sigset(SIGTTOU, SIG_IGN); 1253 (void) sigset(SIGURG, (void (*)())oob); 1254 ppid = getppid(); 1255 if (fcntl(rem, F_SETOWN, pid) == -1) 1256 perror("fcntl F_SETOWN"); 1257 /* 1258 * A SIGURG may have been posted before we were completely forked, 1259 * which means we may not have received it. To insure we do not miss 1260 * any urgent data, we force the signal. The signal hander will be 1261 * able to determine if in fact there is urgent data or not. 1262 */ 1263 (void) kill(pid, SIGURG); 1264 (void) setjmp(rcvtop); 1265 sigsetmask(oldmask); 1266 for (;;) { 1267 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 1268 rcvstate = WRITING; 1269 n = write(STDOUT_FILENO, bufp, remaining); 1270 if (n < 0) { 1271 if (errno != EINTR) { 1272 prf(gettext( 1273 "Write error to terminal: %s"), 1274 strerror(errno)); 1275 return (-1); 1276 } 1277 continue; 1278 } 1279 bufp += n; 1280 } 1281 bufp = rcvbuf; 1282 rcvcnt = 0; 1283 rcvstate = READING; 1284 rcvcnt = desread(rem, rcvbuf, sizeof (rcvbuf), 0); 1285 if (rcvcnt == 0) 1286 return (0); 1287 if (rcvcnt < 0) { 1288 if (errno == EINTR) 1289 continue; 1290 prf(gettext("Read error from network: %s"), 1291 strerror(errno)); 1292 return (-1); 1293 } 1294 } 1295 } 1296 1297 static void 1298 mode(int f) 1299 { 1300 struct tchars *tc; 1301 struct ltchars *ltc; 1302 struct sgttyb sb; 1303 int lflags; 1304 1305 if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1) 1306 perror("ioctl TIOCGETP"); 1307 if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&lflags) == -1) 1308 perror("ioctl TIOCLGET"); 1309 switch (f) { 1310 1311 case 0: 1312 sb.sg_flags &= ~(O_CBREAK|O_RAW|O_TBDELAY); 1313 sb.sg_flags |= defflags|tabflag; 1314 tc = &deftc; 1315 ltc = &defltc; 1316 sb.sg_kill = defkill; 1317 sb.sg_erase = deferase; 1318 lflags = deflflags; 1319 break; 1320 1321 case 1: 1322 sb.sg_flags |= (eight ? O_RAW : O_CBREAK); 1323 sb.sg_flags &= ~defflags; 1324 /* preserve tab delays, but turn off XTABS */ 1325 if ((sb.sg_flags & O_TBDELAY) == O_XTABS) 1326 sb.sg_flags &= ~O_TBDELAY; 1327 tc = ¬c; 1328 ltc = &noltc; 1329 sb.sg_kill = sb.sg_erase = -1; 1330 if (litout) 1331 lflags |= LLITOUT; 1332 break; 1333 1334 default: 1335 /*NOTREACHED*/ 1336 return; 1337 } 1338 if (compat_ioctl(STDIN_FILENO, TIOCSLTC, ltc) == -1) 1339 perror("ioctl TIOCSLTC"); 1340 if (compat_ioctl(STDIN_FILENO, TIOCSETC, tc) == -1) 1341 perror("ioctl TIOCSETC"); 1342 if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1) 1343 perror("ioctl TIOCSETP 3"); 1344 if (compat_ioctl(STDIN_FILENO, TIOCLSET, &lflags) == -1) 1345 perror("ioctl TIOCLSET"); 1346 } 1347 1348 /* PRINTFLIKE(0) */ 1349 static void 1350 prf(const char *format, ...) 1351 { 1352 va_list ap; 1353 1354 va_start(ap, format); 1355 (void) vfprintf(stderr, format, ap); 1356 va_end(ap); 1357 (void) fputs(CRLF, stderr); 1358 } 1359 1360 static void 1361 lostpeer(void) 1362 { 1363 (void) sigset(SIGPIPE, SIG_IGN); 1364 prf(gettext("\aConnection to %.*s closed."), MAXHOSTNAMELEN, host); 1365 done(EXIT_FAILURE); 1366 } 1367 1368 static int 1369 compat_ioctl(int des, int request, void *arg) 1370 { 1371 struct termios tb; 1372 boolean_t flag = B_FALSE; 1373 1374 if (ioctl(des, request, arg) < 0) 1375 return (-1); 1376 1377 if (tcgetattr(des, &tb) < 0) 1378 return (-1); 1379 1380 if (cfgetispeed(&tb) != cfgetispeed(&savetty)) { 1381 (void) cfsetispeed(&tb, cfgetispeed(&savetty)); 1382 flag = B_TRUE; 1383 } 1384 if (cfgetospeed(&tb) != cfgetospeed(&savetty)) { 1385 (void) cfsetospeed(&tb, cfgetospeed(&savetty)); 1386 flag = B_TRUE; 1387 } 1388 1389 return (flag ? tcsetattr(des, TCSANOW, &tb) : 0); 1390 } 1391