1 0 stevel /* 2 8175 Peter * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 0 stevel * Use is subject to license terms. 4 0 stevel */ 5 0 stevel 6 0 stevel /* 7 0 stevel * Copyright (c) 1983 Regents of the University of California. 8 0 stevel * All rights reserved. The Berkeley software License Agreement 9 0 stevel * specifies the terms and conditions for redistribution. 10 0 stevel * 11 0 stevel */ 12 0 stevel 13 0 stevel #define _FILE_OFFSET_BITS 64 14 0 stevel 15 0 stevel #include <sys/time.h> 16 0 stevel #include <sys/types.h> 17 0 stevel #include <sys/socket.h> 18 0 stevel #include <sys/ioctl.h> 19 0 stevel /* just for FIONBIO ... */ 20 0 stevel #include <sys/filio.h> 21 0 stevel #include <sys/stat.h> 22 0 stevel #include <sys/select.h> 23 0 stevel 24 0 stevel #include <netinet/in.h> 25 0 stevel 26 0 stevel #include <assert.h> 27 0 stevel #include <fcntl.h> 28 0 stevel #include <stdlib.h> 29 0 stevel #include <string.h> 30 0 stevel #include <unistd.h> 31 0 stevel #include <stdio.h> 32 0 stevel #include <errno.h> 33 0 stevel #include <signal.h> 34 0 stevel #include <pwd.h> 35 0 stevel #include <netdb.h> 36 0 stevel #include <locale.h> 37 0 stevel #include <priv_utils.h> 38 0 stevel 39 0 stevel #include <k5-int.h> 40 0 stevel #include <profile/prof_int.h> 41 0 stevel #include <com_err.h> 42 0 stevel #include <kcmd.h> 43 0 stevel #include <krb5.h> 44 0 stevel 45 0 stevel /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */ 46 0 stevel typedef void (*sigdisp_t)(int); 47 0 stevel 48 0 stevel extern errcode_t profile_get_options_boolean(profile_t, char **, 49 0 stevel profile_options_boolean *); 50 0 stevel extern errcode_t profile_get_options_string(profile_t, char **, 51 0 stevel profile_option_strings *); 52 0 stevel 53 0 stevel #define RSH_BUFSIZ (1024 * 50) 54 0 stevel 55 0 stevel static char des_inbuf[2 * RSH_BUFSIZ]; /* needs to be > largest read size */ 56 0 stevel static char des_outbuf[2 * RSH_BUFSIZ]; /* needs to be > largest write size */ 57 0 stevel static krb5_data desinbuf, desoutbuf; 58 0 stevel static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 59 8175 Peter static krb5_context bsd_context = NULL; 60 0 stevel static krb5_auth_context auth_context; 61 0 stevel static krb5_creds *cred; 62 0 stevel static krb5_keyblock *session_key; 63 0 stevel 64 0 stevel static int encrypt_flag; /* Flag set, when encryption is used */ 65 8175 Peter static int krb5auth_flag; /* Flag set, when KERBEROS is enabled */ 66 8175 Peter static profile_options_boolean autologin_option[] = { 67 8175 Peter { "autologin", &krb5auth_flag, 0 }, 68 8175 Peter { NULL, NULL, 0 } 69 8175 Peter }; 70 8175 Peter 71 8175 Peter static int no_krb5auth_flag = 0; 72 0 stevel static int fflag; /* Flag set, if creds to be fwd'ed via -f */ 73 0 stevel static int Fflag; /* Flag set, if fwd'able creds to be fwd'ed via -F */ 74 0 stevel 75 0 stevel /* Flag set, if -PN / -PO is specified */ 76 0 stevel static boolean_t rcmdoption_done; 77 0 stevel 78 0 stevel /* Flags set, if corres. cmd line options are turned on */ 79 0 stevel static boolean_t encrypt_done, fwd_done, fwdable_done; 80 0 stevel 81 0 stevel static profile_options_boolean option[] = { 82 0 stevel { "encrypt", &encrypt_flag, 0 }, 83 0 stevel { "forward", &fflag, 0 }, 84 0 stevel { "forwardable", &Fflag, 0 }, 85 0 stevel { NULL, NULL, 0 } 86 0 stevel }; 87 0 stevel 88 0 stevel static char *rcmdproto; 89 0 stevel static profile_option_strings rcmdversion[] = { 90 0 stevel { "rcmd_protocol", &rcmdproto, 0 }, 91 0 stevel { NULL, NULL, 0 } 92 0 stevel }; 93 0 stevel 94 0 stevel static char *realmdef[] = { "realms", NULL, "rsh", NULL }; 95 0 stevel static char *appdef[] = { "appdefaults", "rsh", NULL }; 96 0 stevel 97 0 stevel static void sendsig(int); 98 0 stevel static sigdisp_t sigdisp(int); 99 0 stevel static boolean_t init_service(boolean_t); 100 0 stevel static int desrshread(int, char *, int); 101 0 stevel static int desrshwrite(int, char *, int); 102 0 stevel 103 0 stevel static int options; 104 0 stevel static int rfd2; 105 0 stevel static int portnumber; 106 0 stevel 107 0 stevel static const char rlogin_path[] = "/usr/bin/rlogin"; 108 0 stevel static const char dash_x[] = "-x "; /* Note the blank after -x */ 109 0 stevel 110 0 stevel static boolean_t readiv, writeiv; 111 0 stevel 112 0 stevel #define set2mask(setp) ((setp)->__sigbits[0]) 113 0 stevel #define mask2set(mask, setp) \ 114 0 stevel ((mask) == -1 ? sigfillset(setp) : (set2mask(setp) = (mask))) 115 0 stevel 116 0 stevel #ifdef DEBUG 117 0 stevel #define DEBUGOPTSTRING "D:" 118 0 stevel #else 119 0 stevel #define DEBUGOPTSTRING "" 120 0 stevel #endif /* DEBUG */ 121 0 stevel 122 0 stevel static void 123 0 stevel sigsetmask(int mask) 124 0 stevel { 125 0 stevel sigset_t nset; 126 0 stevel 127 0 stevel (void) sigprocmask(0, NULL, &nset); 128 0 stevel mask2set(mask, &nset); 129 0 stevel (void) sigprocmask(SIG_SETMASK, &nset, NULL); 130 0 stevel } 131 0 stevel 132 0 stevel static int 133 0 stevel sigblock(int mask) 134 0 stevel { 135 0 stevel sigset_t oset; 136 0 stevel sigset_t nset; 137 0 stevel 138 0 stevel (void) sigprocmask(0, NULL, &nset); 139 0 stevel mask2set(mask, &nset); 140 0 stevel (void) sigprocmask(SIG_BLOCK, &nset, &oset); 141 0 stevel return (set2mask(&oset)); 142 0 stevel } 143 0 stevel 144 0 stevel /* 145 0 stevel * Get signal disposition (or signal handler) for a given signal 146 0 stevel */ 147 0 stevel static sigdisp_t 148 0 stevel sigdisp(int sig) 149 0 stevel { 150 0 stevel struct sigaction act; 151 0 stevel 152 0 stevel act.sa_handler = NULL; 153 0 stevel act.sa_flags = 0; 154 0 stevel (void) sigemptyset(&act.sa_mask); 155 0 stevel (void) sigaction(sig, NULL, &act); 156 0 stevel return (act.sa_handler); 157 0 stevel } 158 0 stevel 159 0 stevel static pid_t child_pid = -1; 160 0 stevel 161 0 stevel /* 162 0 stevel * If you do a command like "rsh host output | wc" 163 0 stevel * and wc terminates, then the parent will receive SIGPIPE 164 0 stevel * and the child needs to be terminated. 165 0 stevel */ 166 0 stevel /* ARGSUSED */ 167 0 stevel static void 168 0 stevel sigpipehandler(int signal) 169 0 stevel { 170 0 stevel if (child_pid != -1) 171 0 stevel (void) kill(child_pid, SIGKILL); 172 0 stevel exit(EXIT_SUCCESS); 173 0 stevel } 174 0 stevel 175 0 stevel #define mask(s) (1 << ((s) - 1)) 176 0 stevel 177 0 stevel static void 178 0 stevel usage(void) { 179 0 stevel (void) fprintf(stderr, "%s\n%s\n", 180 0 stevel gettext("usage: rsh [ -PN / -PO ] [ -l login ] [ -n ] " 181 0 stevel "[ -k realm ] [ -a ] [ -x ] [ -f / -F ] host command"), 182 0 stevel gettext(" rsh [ -PN / -PO ] [ -l login ] [ -k realm ] " 183 0 stevel "[ -a ] [ -x ] [ -f / -F ] host")); 184 0 stevel exit(EXIT_FAILURE); 185 0 stevel } 186 0 stevel 187 0 stevel static void 188 0 stevel die(const char *message) 189 0 stevel { 190 0 stevel (void) fputs(message, stderr); 191 0 stevel usage(); 192 0 stevel } 193 0 stevel 194 0 stevel static void 195 0 stevel usage_forward(void) 196 0 stevel { 197 0 stevel die(gettext("rsh: Only one of -f and -F allowed.\n")); 198 0 stevel } 199 0 stevel 200 0 stevel /* 201 0 stevel * rsh - remote shell 202 0 stevel */ 203 0 stevel /* VARARGS */ 204 0 stevel int 205 0 stevel main(int argc, char **argv) 206 0 stevel { 207 0 stevel int c, rem; 208 0 stevel char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x; 209 0 stevel char *host = NULL, *user = NULL; 210 0 stevel int cc; 211 0 stevel boolean_t asrsh = B_FALSE; 212 0 stevel struct passwd *pwd; 213 0 stevel boolean_t readfrom_rem; 214 0 stevel boolean_t readfrom_rfd2; 215 0 stevel int one = 1; 216 0 stevel int omask; 217 0 stevel boolean_t nflag = B_FALSE; 218 0 stevel char *krb_realm = NULL; 219 0 stevel krb5_flags authopts; 220 0 stevel krb5_error_code status; 221 0 stevel enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; 222 0 stevel uid_t uid = getuid(); 223 0 stevel 224 0 stevel c = (argc + 1) * sizeof (char *); 225 0 stevel if ((argv0 = malloc(c)) == NULL) { 226 0 stevel perror("malloc"); 227 0 stevel return (EXIT_FAILURE); 228 0 stevel } 229 0 stevel (void) memcpy(argv0, argv, c); 230 0 stevel 231 0 stevel (void) setlocale(LC_ALL, ""); 232 0 stevel 233 0 stevel (void) textdomain(TEXT_DOMAIN); 234 0 stevel 235 0 stevel /* 236 0 stevel * Determine command name used to invoke to rlogin(1). Users can 237 0 stevel * create links named by a host pointing to the binary and type 238 0 stevel * "hostname" to log into that host afterwards. 239 0 stevel */ 240 0 stevel cmd = strrchr(argv[0], '/'); 241 0 stevel cmd = (cmd != NULL) ? (cmd + 1) : argv[0]; 242 0 stevel 243 0 stevel /* 244 0 stevel * Add "remsh" as an alias for "rsh" (System III, V networking 245 0 stevel * add-ons often used this name for the remote shell since rsh 246 0 stevel * was already taken for the restricted shell). Note that this 247 0 stevel * usurps the ability to use "remsh" as the name of a host (by 248 0 stevel * symlinking it to rsh), so we go one step farther: if the 249 0 stevel * file "/usr/bin/remsh" does not exist, we behave as if "remsh" 250 0 stevel * is a host name. If it does exist, we accept "remsh" as an 251 0 stevel * "rsh" alias. 252 0 stevel */ 253 0 stevel if (strcmp(cmd, "remsh") == 0) { 254 0 stevel struct stat sb; 255 0 stevel 256 0 stevel if (stat("/usr/bin/remsh", &sb) < 0) 257 0 stevel host = cmd; 258 0 stevel } else if (strcmp(cmd, "rsh") != 0) { 259 0 stevel host = cmd; 260 0 stevel } 261 0 stevel 262 0 stevel /* Handle legacy synopsis "rsh hostname options [command]". */ 263 0 stevel if (host == NULL) { 264 0 stevel if (argc < 2) 265 0 stevel usage(); 266 0 stevel if (*argv[1] != '-') { 267 0 stevel host = argv[1]; 268 0 stevel argc--; 269 0 stevel argv[1] = argv[0]; 270 0 stevel argv++; 271 0 stevel asrsh = B_TRUE; 272 0 stevel } 273 0 stevel } 274 0 stevel 275 0 stevel while ((c = getopt(argc, argv, 276 8175 Peter DEBUGOPTSTRING "8AFKLP:ade:fk:l:nwx")) != -1) { 277 0 stevel switch (c) { 278 0 stevel #ifdef DEBUG 279 0 stevel case 'D': 280 0 stevel portnumber = htons(atoi(optarg)); 281 8175 Peter krb5auth_flag++; 282 0 stevel break; 283 0 stevel #endif /* DEBUG */ 284 0 stevel case 'F': 285 0 stevel if (fflag) 286 0 stevel usage_forward(); 287 0 stevel Fflag = 1; 288 8175 Peter krb5auth_flag++; 289 0 stevel fwdable_done = B_TRUE; 290 0 stevel break; 291 0 stevel case 'f': 292 0 stevel if (Fflag) 293 0 stevel usage_forward(); 294 0 stevel fflag = 1; 295 8175 Peter krb5auth_flag++; 296 0 stevel fwd_done = B_TRUE; 297 0 stevel break; 298 0 stevel case 'P': 299 0 stevel if (strcmp(optarg, "N") == 0) 300 0 stevel kcmd_proto = KCMD_NEW_PROTOCOL; 301 0 stevel else if (strcmp(optarg, "O") == 0) 302 0 stevel kcmd_proto = KCMD_OLD_PROTOCOL; 303 0 stevel else 304 0 stevel die(gettext("rsh: Only -PN or -PO " 305 0 stevel "allowed.\n")); 306 0 stevel if (rcmdoption_done) 307 0 stevel die(gettext("rsh: Only one of -PN and -PO " 308 0 stevel "allowed.\n")); 309 0 stevel rcmdoption_done = B_TRUE; 310 8175 Peter krb5auth_flag++; 311 0 stevel break; 312 0 stevel case 'a': 313 8175 Peter krb5auth_flag++; 314 8175 Peter break; 315 8175 Peter case 'K': 316 8175 Peter no_krb5auth_flag++; 317 0 stevel break; 318 0 stevel case 'd': 319 0 stevel options |= SO_DEBUG; 320 0 stevel break; 321 0 stevel case 'k': 322 0 stevel krb_realm = optarg; 323 8175 Peter krb5auth_flag++; 324 0 stevel break; 325 0 stevel case 'l': 326 0 stevel user = optarg; 327 0 stevel break; 328 0 stevel case 'n': 329 0 stevel if (!nflag) { 330 0 stevel if (close(STDIN_FILENO) < 0) { 331 0 stevel perror("close"); 332 0 stevel return (EXIT_FAILURE); 333 0 stevel } 334 0 stevel /* 335 0 stevel * "STDION_FILENO" defined to 0 by POSIX 336 0 stevel * and hence the lowest file descriptor. 337 0 stevel * So the open(2) below is guaranteed to 338 0 stevel * reopen it because we closed it above. 339 0 stevel */ 340 0 stevel if (open("/dev/null", O_RDONLY) < 0) { 341 0 stevel perror("open"); 342 0 stevel return (EXIT_FAILURE); 343 0 stevel } 344 0 stevel nflag = B_TRUE; 345 0 stevel } 346 0 stevel break; 347 0 stevel case 'x': 348 0 stevel encrypt_flag = 1; 349 8175 Peter krb5auth_flag++; 350 0 stevel encrypt_done = B_TRUE; 351 0 stevel break; 352 0 stevel /* 353 0 stevel * Ignore the -L, -w, -e and -8 flags to allow aliases with 354 0 stevel * rlogin to work. Actually rlogin(1) doesn't understand 355 0 stevel * -w either but because "rsh -w hostname command" used 356 0 stevel * to work we still accept it. 357 0 stevel */ 358 0 stevel case '8': 359 0 stevel case 'L': 360 0 stevel case 'e': 361 0 stevel case 'w': 362 0 stevel /* 363 0 stevel * On the lines of the -L, -w, -e and -8 options above, we 364 0 stevel * ignore the -A option too, in order to allow aliases with 365 0 stevel * rlogin to work. 366 0 stevel * 367 0 stevel * Mind you !, the -a option to trigger Kerberos authentication 368 0 stevel * in rsh, has a totally different usage in rlogin, its the 369 0 stevel * -A option (in rlogin) which needs to be used to talk 370 0 stevel * Kerberos. 371 0 stevel */ 372 0 stevel case 'A': 373 0 stevel break; 374 0 stevel default: 375 0 stevel usage(); 376 0 stevel } 377 0 stevel } 378 0 stevel 379 0 stevel argc -= optind; 380 0 stevel argv += optind; 381 0 stevel 382 0 stevel if (host == NULL) { 383 0 stevel if (argc == 0) 384 0 stevel usage(); 385 0 stevel argc--; 386 0 stevel host = *argv++; 387 0 stevel asrsh = B_TRUE; 388 0 stevel } 389 0 stevel 390 0 stevel if (argc == 0) { 391 0 stevel (void) setreuid(uid, uid); 392 0 stevel if (nflag) 393 0 stevel usage(); 394 0 stevel if (asrsh) 395 0 stevel *argv0 = "rlogin"; 396 0 stevel (void) execv(rlogin_path, argv0); 397 0 stevel perror(rlogin_path); 398 0 stevel 399 0 stevel (void) fprintf(stderr, gettext("No local rlogin " 400 0 stevel "program found.\n")); 401 0 stevel return (EXIT_FAILURE); 402 0 stevel } 403 0 stevel 404 0 stevel if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) { 405 0 stevel (void) fprintf(stderr, 406 0 stevel gettext("Insufficient privileges, " 407 0 stevel "rsh must be set-uid root\n")); 408 0 stevel return (EXIT_FAILURE); 409 0 stevel } 410 0 stevel 411 0 stevel pwd = getpwuid(uid); 412 0 stevel if (pwd == NULL) { 413 0 stevel (void) fprintf(stderr, gettext("who are you?\n")); 414 0 stevel return (EXIT_FAILURE); 415 0 stevel } 416 0 stevel if (user == NULL) 417 0 stevel user = pwd->pw_name; 418 0 stevel 419 8175 Peter /* 420 8175 Peter * if the user disables krb5 on the cmdline (-K), then skip 421 8175 Peter * all krb5 setup. 422 8175 Peter * 423 8175 Peter * if the user does not disable krb5 or enable krb5 on the 424 8175 Peter * cmdline, check krb5.conf to see if it should be enabled. 425 8175 Peter */ 426 8175 Peter 427 8175 Peter if (no_krb5auth_flag) { 428 8175 Peter krb5auth_flag = 0; 429 8175 Peter Fflag = fflag = encrypt_flag = 0; 430 8175 Peter } else if (!krb5auth_flag) { 431 8175 Peter /* is autologin set in krb5.conf? */ 432 8175 Peter status = krb5_init_context(&bsd_context); 433 8175 Peter /* don't sweat failure here */ 434 8175 Peter if (!status) { 435 8175 Peter /* 436 8175 Peter * note that the call to profile_get_options_boolean 437 8175 Peter * with autologin_option can affect value of 438 8175 Peter * krb5auth_flag 439 8175 Peter */ 440 8175 Peter (void) profile_get_options_boolean(bsd_context->profile, 441 8175 Peter appdef, 442 8175 Peter autologin_option); 443 8175 Peter } 444 8175 Peter } 445 8175 Peter 446 0 stevel if (krb5auth_flag) { 447 8175 Peter if (!bsd_context) { 448 8175 Peter status = krb5_init_context(&bsd_context); 449 8175 Peter if (status) { 450 8175 Peter com_err("rsh", status, 451 8175 Peter "while initializing krb5"); 452 8175 Peter return (EXIT_FAILURE); 453 8175 Peter 454 8175 Peter } 455 0 stevel } 456 0 stevel 457 0 stevel /* 458 0 stevel * Get our local realm to look up local realm options. 459 0 stevel */ 460 0 stevel status = krb5_get_default_realm(bsd_context, &realmdef[1]); 461 0 stevel if (status) { 462 0 stevel com_err("rsh", status, 463 0 stevel gettext("while getting default realm")); 464 0 stevel return (EXIT_FAILURE); 465 0 stevel } 466 0 stevel /* 467 0 stevel * Check the realms section in krb5.conf for encryption, 468 0 stevel * forward & forwardable info 469 0 stevel */ 470 0 stevel profile_get_options_boolean(bsd_context->profile, realmdef, 471 0 stevel option); 472 0 stevel /* 473 0 stevel * Check the appdefaults section 474 0 stevel */ 475 0 stevel profile_get_options_boolean(bsd_context->profile, appdef, 476 0 stevel option); 477 0 stevel profile_get_options_string(bsd_context->profile, appdef, 478 0 stevel rcmdversion); 479 0 stevel /* 480 0 stevel * Set the *_flag variables, if the corresponding *_done are 481 0 stevel * set to 1, because we dont want the config file values 482 0 stevel * overriding the command line options. 483 0 stevel */ 484 0 stevel if (encrypt_done) 485 0 stevel encrypt_flag = 1; 486 0 stevel if (fwd_done) { 487 0 stevel fflag = 1; 488 0 stevel Fflag = 0; 489 0 stevel } else if (fwdable_done) { 490 0 stevel Fflag = 1; 491 0 stevel fflag = 0; 492 0 stevel } 493 0 stevel if (!rcmdoption_done && (rcmdproto != NULL)) { 494 0 stevel if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 495 0 stevel kcmd_proto = KCMD_NEW_PROTOCOL; 496 0 stevel } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 497 0 stevel kcmd_proto = KCMD_OLD_PROTOCOL; 498 0 stevel } else { 499 0 stevel (void) fprintf(stderr, gettext("Unrecognized " 500 0 stevel "KCMD protocol (%s)"), rcmdproto); 501 0 stevel return (EXIT_FAILURE); 502 0 stevel } 503 0 stevel } 504 0 stevel 505 0 stevel 506 0 stevel if (encrypt_flag && (!krb5_privacy_allowed())) { 507 0 stevel (void) fprintf(stderr, gettext("rsh: Encryption not " 508 0 stevel "supported.\n")); 509 0 stevel return (EXIT_FAILURE); 510 0 stevel } 511 0 stevel } 512 0 stevel 513 0 stevel /* 514 0 stevel * Connect with the service (shell/kshell) on the daemon side 515 0 stevel */ 516 0 stevel if (portnumber == 0) { 517 0 stevel while (!init_service(krb5auth_flag)) { 518 0 stevel /* 519 0 stevel * Connecting to the 'kshell' service failed, 520 0 stevel * fallback to normal rsh; Reset all KRB5 flags 521 0 stevel * and connect to 'shell' service on the server 522 0 stevel */ 523 8175 Peter krb5auth_flag = 0; 524 0 stevel encrypt_flag = fflag = Fflag = 0; 525 0 stevel } 526 0 stevel } 527 0 stevel 528 0 stevel cc = encrypt_flag ? strlen(dash_x) : 0; 529 0 stevel for (ap = argv; *ap != NULL; ap++) 530 0 stevel cc += strlen(*ap) + 1; 531 0 stevel cp = args = malloc(cc); 532 0 stevel if (cp == NULL) 533 0 stevel perror("malloc"); 534 0 stevel if (encrypt_flag) { 535 0 stevel int length; 536 0 stevel 537 0 stevel length = strlcpy(args, dash_x, cc); 538 0 stevel cp += length; 539 0 stevel cc -= length; 540 0 stevel } 541 0 stevel args_no_x = args; 542 0 stevel 543 0 stevel for (ap = argv; *ap != NULL; ap++) { 544 0 stevel int length; 545 0 stevel 546 0 stevel length = strlcpy(cp, *ap, cc); 547 0 stevel assert(length < cc); 548 0 stevel cp += length; 549 0 stevel cc -= length; 550 0 stevel if (ap[1] != NULL) { 551 0 stevel *cp++ = ' '; 552 0 stevel cc--; 553 0 stevel } 554 0 stevel } 555 0 stevel 556 0 stevel if (krb5auth_flag) { 557 0 stevel authopts = AP_OPTS_MUTUAL_REQUIRED; 558 0 stevel /* 559 0 stevel * Piggy-back forwarding flags on top of authopts; 560 0 stevel * they will be reset in kcmd 561 0 stevel */ 562 0 stevel if (fflag || Fflag) 563 0 stevel authopts |= OPTS_FORWARD_CREDS; 564 0 stevel if (Fflag) 565 0 stevel authopts |= OPTS_FORWARDABLE_CREDS; 566 0 stevel 567 0 stevel status = kcmd(&rem, &host, portnumber, 568 0 stevel pwd->pw_name, user, 569 0 stevel args, &rfd2, "host", krb_realm, 570 0 stevel bsd_context, &auth_context, &cred, 571 0 stevel NULL, /* No need for sequence number */ 572 0 stevel NULL, /* No need for server seq # */ 573 0 stevel authopts, 574 0 stevel 1, /* Always set anyport */ 575 0 stevel &kcmd_proto); 576 0 stevel if (status != 0) { 577 0 stevel /* 578 0 stevel * If new protocol requested, we dont fallback to 579 0 stevel * less secure ones. 580 0 stevel */ 581 0 stevel if (kcmd_proto == KCMD_NEW_PROTOCOL) { 582 0 stevel (void) fprintf(stderr, gettext("rsh: kcmdv2 " 583 0 stevel "to host %s failed - %s\n" 584 0 stevel "Fallback to normal rsh denied."), 585 0 stevel host, error_message(status)); 586 0 stevel return (EXIT_FAILURE); 587 0 stevel } 588 0 stevel /* check NO_TKT_FILE or equivalent... */ 589 0 stevel if (status != -1) { 590 0 stevel (void) fprintf(stderr, 591 0 stevel gettext("rsh: kcmd to host %s failed - %s\n" 592 0 stevel "trying normal rsh...\n\n"), 593 0 stevel host, error_message(status)); 594 0 stevel } else { 595 0 stevel (void) fprintf(stderr, 596 0 stevel gettext("trying normal rsh...\n")); 597 0 stevel } 598 0 stevel /* 599 0 stevel * kcmd() failed, so we now fallback to normal rsh, 600 0 stevel * after resetting the KRB5 flags and the 'args' array 601 0 stevel */ 602 8175 Peter krb5auth_flag = 0; 603 0 stevel encrypt_flag = fflag = Fflag = 0; 604 0 stevel args = args_no_x; 605 0 stevel (void) init_service(B_FALSE); 606 0 stevel } else { 607 0 stevel /* 608 0 stevel * Set up buffers for desread and deswrite. 609 0 stevel */ 610 0 stevel desinbuf.data = des_inbuf; 611 0 stevel desoutbuf.data = des_outbuf; 612 0 stevel desinbuf.length = sizeof (des_inbuf); 613 0 stevel desoutbuf.length = sizeof (des_outbuf); 614 0 stevel 615 0 stevel session_key = &cred->keyblock; 616 0 stevel 617 0 stevel if (kcmd_proto == KCMD_NEW_PROTOCOL) { 618 0 stevel status = krb5_auth_con_getlocalsubkey( 619 0 stevel bsd_context, 620 0 stevel auth_context, 621 0 stevel &session_key); 622 0 stevel if (status) { 623 0 stevel com_err("rsh", status, 624 0 stevel "determining subkey for session"); 625 0 stevel return (EXIT_FAILURE); 626 0 stevel } 627 0 stevel if (session_key == NULL) { 628 0 stevel com_err("rsh", 0, "no subkey " 629 0 stevel "negotiated for connection"); 630 0 stevel return (EXIT_FAILURE); 631 0 stevel } 632 0 stevel } 633 0 stevel 634 0 stevel eblock.crypto_entry = session_key->enctype; 635 0 stevel eblock.key = (krb5_keyblock *)session_key; 636 0 stevel 637 0 stevel init_encrypt(encrypt_flag, bsd_context, kcmd_proto, 638 0 stevel &desinbuf, &desoutbuf, CLIENT, &eblock); 639 0 stevel if (encrypt_flag) { 640 0 stevel char *s = gettext("This rsh session is using " 641 0 stevel "encryption for all data transmissions."); 642 0 stevel (void) write(STDERR_FILENO, s, strlen(s)); 643 0 stevel (void) write(STDERR_FILENO, "\r\n", 2); 644 0 stevel } 645 0 stevel } 646 0 stevel } 647 0 stevel 648 0 stevel /* 649 0 stevel * Don't merge this with the "if" statement above because 650 0 stevel * "krb5auth_flag" might be set to false inside it. 651 0 stevel */ 652 0 stevel if (!krb5auth_flag) { 653 0 stevel rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args, 654 0 stevel &rfd2, AF_INET6); 655 0 stevel if (rem < 0) 656 0 stevel return (EXIT_FAILURE); 657 0 stevel } 658 0 stevel __priv_relinquish(); 659 0 stevel 660 0 stevel if (rfd2 < 0) { 661 0 stevel (void) fprintf(stderr, gettext("rsh: can't establish " 662 0 stevel "stderr\n")); 663 0 stevel return (EXIT_FAILURE); 664 0 stevel } 665 0 stevel if (options & SO_DEBUG) { 666 0 stevel if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one, 667 0 stevel sizeof (one)) < 0) 668 0 stevel perror("rsh: setsockopt (stdin)"); 669 0 stevel if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one, 670 0 stevel sizeof (one)) < 0) 671 0 stevel perror("rsh: setsockopt (stderr)"); 672 0 stevel } 673 0 stevel omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 674 0 stevel 675 0 stevel if (sigdisp(SIGINT) != SIG_IGN) 676 0 stevel (void) sigset(SIGINT, sendsig); 677 0 stevel if (sigdisp(SIGQUIT) != SIG_IGN) 678 0 stevel (void) sigset(SIGQUIT, sendsig); 679 0 stevel if (sigdisp(SIGTERM) != SIG_IGN) 680 0 stevel (void) sigset(SIGTERM, sendsig); 681 0 stevel 682 0 stevel if (nflag) { 683 0 stevel (void) shutdown(rem, SHUT_WR); 684 0 stevel } else { 685 0 stevel child_pid = fork(); 686 0 stevel if (child_pid < 0) { 687 0 stevel perror("rsh: fork"); 688 0 stevel return (EXIT_FAILURE); 689 0 stevel } 690 0 stevel 691 0 stevel if (!encrypt_flag) { 692 0 stevel (void) ioctl(rfd2, FIONBIO, &one); 693 0 stevel (void) ioctl(rem, FIONBIO, &one); 694 0 stevel } 695 0 stevel 696 0 stevel if (child_pid == 0) { 697 0 stevel /* Child */ 698 0 stevel fd_set remset; 699 0 stevel char *bp; 700 0 stevel int wc; 701 0 stevel (void) close(rfd2); 702 0 stevel reread: 703 0 stevel errno = 0; 704 0 stevel cc = read(0, buf, sizeof (buf)); 705 0 stevel if (cc <= 0) 706 0 stevel goto done; 707 0 stevel bp = buf; 708 0 stevel rewrite: 709 0 stevel FD_ZERO(&remset); 710 0 stevel FD_SET(rem, &remset); 711 0 stevel if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) { 712 0 stevel if (errno != EINTR) { 713 0 stevel perror("rsh: select"); 714 0 stevel return (EXIT_FAILURE); 715 0 stevel } 716 0 stevel goto rewrite; 717 0 stevel } 718 0 stevel if (!FD_ISSET(rem, &remset)) 719 0 stevel goto rewrite; 720 0 stevel writeiv = B_FALSE; 721 0 stevel wc = desrshwrite(rem, bp, cc); 722 0 stevel if (wc < 0) { 723 0 stevel if (errno == EWOULDBLOCK) 724 0 stevel goto rewrite; 725 0 stevel goto done; 726 0 stevel } 727 0 stevel cc -= wc; bp += wc; 728 0 stevel if (cc == 0) 729 0 stevel goto reread; 730 0 stevel goto rewrite; 731 0 stevel done: 732 0 stevel (void) shutdown(rem, SHUT_WR); 733 0 stevel return (EXIT_SUCCESS); 734 0 stevel } 735 0 stevel } 736 0 stevel 737 0 stevel #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 738 0 stevel 739 0 stevel sigsetmask(omask); 740 0 stevel readfrom_rem = B_TRUE; 741 0 stevel readfrom_rfd2 = B_TRUE; 742 0 stevel (void) sigset(SIGPIPE, sigpipehandler); 743 0 stevel do { 744 0 stevel fd_set readyset; 745 0 stevel 746 0 stevel FD_ZERO(&readyset); 747 0 stevel if (readfrom_rem) 748 0 stevel FD_SET(rem, &readyset); 749 0 stevel if (readfrom_rfd2) 750 0 stevel FD_SET(rfd2, &readyset); 751 0 stevel if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL, 752 0 stevel NULL) < 0) { 753 0 stevel if (errno != EINTR) { 754 0 stevel perror("rsh: select"); 755 0 stevel return (EXIT_FAILURE); 756 0 stevel } 757 0 stevel continue; 758 0 stevel } 759 0 stevel if (FD_ISSET(rfd2, &readyset)) { 760 0 stevel errno = 0; 761 0 stevel readiv = B_TRUE; 762 0 stevel cc = desrshread(rfd2, buf, sizeof (buf)); 763 0 stevel if (cc <= 0) { 764 0 stevel if (errno != EWOULDBLOCK) 765 0 stevel readfrom_rfd2 = B_FALSE; 766 0 stevel } else { 767 0 stevel (void) write(STDERR_FILENO, buf, cc); 768 0 stevel } 769 0 stevel } 770 0 stevel if (FD_ISSET(rem, &readyset)) { 771 0 stevel errno = 0; 772 0 stevel readiv = B_FALSE; 773 0 stevel cc = desrshread(rem, buf, sizeof (buf)); 774 0 stevel if (cc <= 0) { 775 0 stevel if (errno != EWOULDBLOCK) 776 0 stevel readfrom_rem = B_FALSE; 777 0 stevel } else 778 0 stevel (void) write(STDOUT_FILENO, buf, cc); 779 0 stevel } 780 0 stevel } while (readfrom_rem || readfrom_rfd2); 781 0 stevel 782 0 stevel if (!nflag) 783 0 stevel (void) kill(child_pid, SIGKILL); 784 0 stevel return (EXIT_SUCCESS); 785 0 stevel } 786 0 stevel 787 0 stevel static void 788 0 stevel sendsig(int signum) 789 0 stevel { 790 0 stevel char buffer; 791 0 stevel 792 0 stevel writeiv = B_TRUE; 793 0 stevel buffer = (char)signum; 794 0 stevel (void) desrshwrite(rfd2, &buffer, 1); 795 0 stevel } 796 0 stevel 797 0 stevel static boolean_t 798 0 stevel init_service(boolean_t krb5flag) 799 0 stevel { 800 0 stevel struct servent *sp; 801 0 stevel 802 0 stevel if (krb5flag) { 803 0 stevel sp = getservbyname("kshell", "tcp"); 804 0 stevel if (sp == NULL) { 805 0 stevel (void) fprintf(stderr, 806 0 stevel gettext("rsh: kshell/tcp: unknown service.\n" 807 0 stevel "trying normal shell/tcp service\n")); 808 0 stevel return (B_FALSE); 809 0 stevel } 810 0 stevel } else { 811 0 stevel sp = getservbyname("shell", "tcp"); 812 0 stevel if (sp == NULL) { 813 0 stevel portnumber = htons(IPPORT_CMDSERVER); 814 0 stevel return (B_TRUE); 815 0 stevel } 816 0 stevel } 817 0 stevel 818 0 stevel portnumber = sp->s_port; 819 0 stevel return (B_TRUE); 820 0 stevel } 821 0 stevel 822 0 stevel static int 823 0 stevel desrshread(int fd, char *buf, int len) 824 0 stevel { 825 0 stevel return (desread(fd, buf, len, readiv ? 1 : 0)); 826 0 stevel } 827 0 stevel 828 0 stevel static int 829 0 stevel desrshwrite(int fd, char *buf, int len) 830 0 stevel { 831 0 stevel return (deswrite(fd, buf, len, writeiv ? 1 : 0)); 832 0 stevel } 833