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