1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * Copyright (c) 1983 The Regents of the University of California. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms are permitted 13 * provided that the above copyright notice and this paragraph are 14 * duplicated in all such forms and that any documentation, 15 * advertising materials, and other materials related to such 16 * distribution and use acknowledge that the software was developed 17 * by the University of California, Berkeley. The name of the 18 * University may not be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 */ 22 23 #define _FILE_OFFSET_BITS 64 24 25 /* 26 * rcp 27 */ 28 #include <sys/param.h> 29 #include <sys/file.h> 30 #include <sys/stat.h> 31 #include <sys/time.h> 32 #include <sys/types.h> 33 #include <sys/ioctl.h> 34 #include <sys/acl.h> 35 #include <dirent.h> 36 #include <signal.h> 37 #include <sys/socket.h> 38 #include <netinet/in.h> 39 #include <pwd.h> 40 #include <netdb.h> 41 #include <wchar.h> 42 #include <stdlib.h> 43 #include <errno.h> 44 #include <locale.h> 45 #include <strings.h> 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <fcntl.h> 49 #include <unistd.h> 50 #include <limits.h> 51 #include <priv_utils.h> 52 #include <sys/sendfile.h> 53 #include <sys/sysmacros.h> 54 #include <sys/wait.h> 55 #include <aclutils.h> 56 #include <sys/varargs.h> 57 58 /* 59 * It seems like Berkeley got these from pathnames.h? 60 */ 61 #define _PATH_RSH "/usr/bin/rsh" 62 #define _PATH_CP "/usr/bin/cp" 63 64 #define ACL_FAIL 1 65 #define ACL_OK 0 66 #define RCP_BUFSIZE (64 * 1024) 67 68 #define RCP_ACL "/usr/lib/sunw,rcp" 69 /* see PSARC/1993/004/opinion */ 70 71 typedef struct _buf { 72 int cnt; 73 char *buf; 74 } BUF; 75 76 static char *cmd_sunw; 77 static struct passwd *pwd; 78 static int errs; 79 static int pflag; 80 static uid_t userid; 81 static int rem; 82 static int zflag; 83 static int iamremote; 84 static int iamrecursive; 85 static int targetshouldbedirectory; 86 static int aclflag; 87 static int acl_aclflag; 88 static int retval = 0; 89 static int portnumber = 0; 90 91 static void lostconn(void); 92 static char *search_char(unsigned char *, unsigned char); 93 static char *removebrackets(char *); 94 static char *colon(char *); 95 static int response(void); 96 static void usage(void); 97 static void source(int, char **); 98 static void sink(int, char **); 99 static void toremote(char *, int, char **); 100 static void tolocal(int, char **); 101 static void verifydir(char *); 102 static int okname(char *); 103 static int susystem(char *, char **); 104 static void rsource(char *, struct stat *); 105 static int sendacl(int); 106 static int recvacl(int, int, int); 107 static int zwrite(int, char *, int); 108 static void zopen(int, int); 109 static int zclose(int); 110 static int notzero(char *, int); 111 static BUF *allocbuf(BUF *, int, int); 112 static void error(char *fmt, ...); 113 static void addargs(char **, ...); 114 115 /* 116 * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF 117 * bytes of data. We would like the size to be aligned to the nearest 118 * MAXBOFFSET (8192) boundary for optimal performance. 119 */ 120 #define SENDFILE_SIZE 0x7FFFE000 121 122 #include <k5-int.h> 123 #include <profile/prof_int.h> 124 #include <com_err.h> 125 #include <kcmd.h> 126 127 #define NULLBUF (BUF *) 0 128 #define MAXARGS 10 /* Number of arguments passed to execv() */ 129 130 static int sock; 131 static char *cmd, *cmd_orig, *cmd_sunw_orig; 132 static char *krb_realm = NULL; 133 static char *krb_cache = NULL; 134 static char *krb_config = NULL; 135 static char des_inbuf[2 * RCP_BUFSIZE]; 136 /* needs to be > largest read size */ 137 static char des_outbuf[2 * RCP_BUFSIZE]; 138 /* needs to be > largest write size */ 139 140 static krb5_data desinbuf, desoutbuf; 141 static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 142 static krb5_keyblock *session_key; /* static key for session */ 143 static krb5_context bsd_context; 144 static krb5_auth_context auth_context; 145 static krb5_flags authopts; 146 static krb5_error_code status; 147 148 static void try_normal_rcp(int, char **); 149 static int init_service(int); 150 static char **save_argv(int, char **); 151 static void answer_auth(char *, char *); 152 static int desrcpwrite(int, char *, int); 153 static int desrcpread(int, char *, int); 154 155 /* 156 * Not sure why these two don't have their own header file declarations, but 157 * lint complains about absent declarations so place some here. Sigh. 158 */ 159 extern errcode_t profile_get_options_boolean(profile_t, char **, 160 profile_options_boolean *); 161 extern errcode_t profile_get_options_string(profile_t, char **, 162 profile_option_strings *); 163 164 static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */ 165 static int encrypt_flag = 0; /* Flag set, when encryption is enabled */ 166 static int encrypt_done = 0; /* Flag set, if "-x" is specified */ 167 static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; 168 169 /* Flag set, if -PN / -PO is specified */ 170 static boolean_t rcmdoption_done = B_FALSE; 171 172 static profile_options_boolean option[] = { 173 { "encrypt", &encrypt_flag, 0 }, 174 { NULL, NULL, 0 } 175 }; 176 177 static char *rcmdproto = NULL; 178 static profile_option_strings rcmdversion[] = { 179 { "rcmd_protocol", &rcmdproto, 0 }, 180 { NULL, NULL, 0 } 181 }; 182 183 static char *realmdef[] = { "realms", NULL, "rcp", NULL }; 184 static char *appdef[] = { "appdefaults", "rcp", NULL }; 185 static char **prev_argv; 186 static int prev_argc; 187 188 int 189 main(int argc, char *argv[]) 190 { 191 int ch, fflag, tflag; 192 char *targ; 193 size_t cmdsiz; 194 195 (void) setlocale(LC_ALL, ""); 196 197 if (strcmp(argv[0], RCP_ACL) == 0) 198 aclflag = 1; 199 200 if (!(pwd = getpwuid(userid = getuid()))) { 201 (void) fprintf(stderr, "rcp: unknown user %d.\n", 202 (uint_t)userid); 203 return (1); 204 } 205 206 fflag = tflag = 0; 207 while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:Z")) != EOF) { 208 switch (ch) { 209 case 'd': 210 targetshouldbedirectory = 1; 211 break; 212 case 'f': /* "from" */ 213 fflag = 1; 214 if (aclflag | acl_aclflag) 215 /* ok response */ 216 (void) desrcpwrite(rem, "", 1); 217 break; 218 case 'p': /* preserve access/mod times */ 219 ++pflag; 220 break; 221 case 'r': 222 ++iamrecursive; 223 break; 224 case 't': /* "to" */ 225 tflag = 1; 226 break; 227 case 'Z': 228 acl_aclflag++; 229 break; 230 case 'x': 231 if (!krb5_privacy_allowed()) { 232 (void) fprintf(stderr, gettext("rcp: " 233 "Encryption not supported.\n")); 234 return (1); 235 } 236 encrypt_flag++; 237 krb5auth_flag++; 238 encrypt_done++; 239 break; 240 case 'k': 241 if ((krb_realm = (char *)strdup(optarg)) == NULL) { 242 (void) fprintf(stderr, gettext("rcp:" 243 " Cannot malloc.\n")); 244 return (1); 245 } 246 krb5auth_flag++; 247 break; 248 case 'P': 249 if (strncmp(optarg, "O", 1) == 0) { 250 if (rcmdoption_done == B_TRUE) { 251 (void) fprintf(stderr, gettext("rcp: " 252 "Only one of -PN and -PO " 253 "allowed.\n")); 254 usage(); 255 } 256 kcmd_proto = KCMD_OLD_PROTOCOL; 257 rcmdoption_done = B_TRUE; 258 } else if (strncmp(optarg, "N", 1) == 0) { 259 if (rcmdoption_done == B_TRUE) { 260 (void) fprintf(stderr, gettext("rcp: " 261 "Only one of -PN and -PO " 262 "allowed.\n")); 263 usage(); 264 } 265 kcmd_proto = KCMD_NEW_PROTOCOL; 266 rcmdoption_done = B_TRUE; 267 } else { 268 usage(); 269 } 270 krb5auth_flag++; 271 break; 272 case 'a': 273 krb5auth_flag++; 274 break; 275 #ifdef DEBUG 276 case 'D': 277 portnumber = htons(atoi(optarg)); 278 krb5auth_flag++; 279 break; 280 #endif /* DEBUG */ 281 case '?': 282 default: 283 usage(); 284 } 285 } 286 argc -= optind; 287 argv += optind; 288 289 if (krb5auth_flag > 0) { 290 status = krb5_init_context(&bsd_context); 291 if (status) { 292 com_err("rcp", status, 293 gettext("while initializing krb5")); 294 return (1); 295 } 296 297 /* 298 * Set up buffers for desread and deswrite. 299 */ 300 desinbuf.data = des_inbuf; 301 desoutbuf.data = des_outbuf; 302 desinbuf.length = sizeof (des_inbuf); 303 desoutbuf.length = sizeof (des_outbuf); 304 } 305 306 if (fflag || tflag) 307 if (encrypt_flag > 0) 308 (void) answer_auth(krb_config, krb_cache); 309 310 if (fflag) { 311 iamremote = 1; 312 (void) response(); 313 (void) setuid(userid); 314 source(argc, argv); 315 return (errs); 316 } 317 318 if (tflag) { 319 iamremote = 1; 320 (void) setuid(userid); 321 sink(argc, argv); 322 return (errs); 323 } 324 325 if (argc < 2) 326 usage(); 327 328 /* This will make "rcmd_af()" magically get the proper privilege */ 329 if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) { 330 (void) fprintf(stderr, "rcp: must be set-uid root\n"); 331 exit(1); 332 } 333 334 if (krb5auth_flag > 0) { 335 /* 336 * Get our local realm to look up local realm options. 337 */ 338 status = krb5_get_default_realm(bsd_context, &realmdef[1]); 339 if (status) { 340 com_err("rcp", status, 341 gettext("while getting default realm")); 342 return (1); 343 } 344 /* 345 * See if encryption should be done for this realm 346 */ 347 profile_get_options_boolean(bsd_context->profile, realmdef, 348 option); 349 /* 350 * Check the appdefaults section 351 */ 352 profile_get_options_boolean(bsd_context->profile, appdef, 353 option); 354 profile_get_options_string(bsd_context->profile, appdef, 355 rcmdversion); 356 if ((encrypt_done > 0) || (encrypt_flag > 0)) { 357 if (krb5_privacy_allowed() == TRUE) { 358 encrypt_flag++; 359 } else { 360 (void) fprintf(stderr, gettext("rcp: Encryption" 361 " not supported.\n")); 362 return (1); 363 } 364 } 365 366 if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) { 367 if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 368 kcmd_proto = KCMD_NEW_PROTOCOL; 369 } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 370 kcmd_proto = KCMD_OLD_PROTOCOL; 371 } else { 372 (void) fprintf(stderr, gettext("Unrecognized " 373 "KCMD protocol (%s)"), rcmdproto); 374 return (1); 375 } 376 } 377 } 378 379 if (argc > 2) 380 targetshouldbedirectory = 1; 381 382 rem = -1; 383 384 if (portnumber == 0) { 385 if (krb5auth_flag > 0) { 386 retval = init_service(krb5auth_flag); 387 if (!retval) { 388 /* 389 * Connecting to the kshell service failed, 390 * fallback to normal rcp & reset KRB5 flags. 391 */ 392 krb5auth_flag = encrypt_flag = 0; 393 encrypt_done = 0; 394 (void) init_service(krb5auth_flag); 395 } 396 } 397 else 398 (void) init_service(krb5auth_flag); 399 } 400 401 #ifdef DEBUG 402 if (retval || krb5auth_flag) { 403 (void) fprintf(stderr, gettext("Kerberized rcp session, " 404 "port %d in use "), portnumber); 405 if (kcmd_proto == KCMD_OLD_PROTOCOL) 406 (void) fprintf(stderr, gettext("[kcmd ver.1]\n")); 407 else 408 (void) fprintf(stderr, gettext("[kcmd ver.2]\n")); 409 } else { 410 (void) fprintf(stderr, gettext("Normal rcp session, port %d " 411 "in use.\n"), portnumber); 412 } 413 #endif /* DEBUG */ 414 415 if (krb5auth_flag > 0) { 416 /* 417 * We calculate here a buffer size that can be used in the 418 * allocation of the three buffers cmd, cmd_orig and 419 * cmd_sunw_orig that are used to hold different incantations 420 * of rcp. 421 */ 422 cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") + 423 strlen(krb_realm != NULL ? krb_realm : ""), 424 sizeof (RCP_ACL " -r -p -z -d")); 425 426 if (((cmd = (char *)malloc(cmdsiz)) == NULL) || 427 ((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) || 428 ((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) { 429 (void) fprintf(stderr, gettext("rcp: Cannot " 430 "malloc.\n")); 431 return (1); 432 } 433 434 (void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s", 435 encrypt_flag ? "-x " : "", 436 iamrecursive ? " -r" : "", pflag ? " -p" : "", 437 targetshouldbedirectory ? " -d" : "", 438 krb_realm != NULL ? " -k " : "", 439 krb_realm != NULL ? krb_realm : ""); 440 441 /* 442 * We would use cmd-orig as the 'cmd-buffer' if kerberized 443 * rcp fails, in which case we fallback to normal rcp. We also 444 * save argc & argv for the same purpose 445 */ 446 (void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s", 447 iamrecursive ? " -r" : "", 448 pflag ? " -p" : "", 449 zflag ? " -z" : "", 450 targetshouldbedirectory ? " -d" : ""); 451 452 (void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL, 453 iamrecursive ? " -r" : "", 454 pflag ? " -p" : "", 455 zflag ? " -z" : "", 456 targetshouldbedirectory ? " -d" : ""); 457 458 prev_argc = argc; 459 prev_argv = save_argv(argc, argv); 460 461 } else { 462 cmdsiz = sizeof ("rcp -r -p -z -d"); 463 if (((cmd = (char *)malloc(cmdsiz)) == NULL)) { 464 (void) fprintf(stderr, gettext("rcp: Cannot " 465 "malloc.\n")); 466 return (1); 467 } 468 469 (void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s", 470 iamrecursive ? " -r" : "", 471 pflag ? " -p" : "", 472 zflag ? " -z" : "", 473 targetshouldbedirectory ? " -d" : ""); 474 } 475 476 cmdsiz = sizeof (RCP_ACL " -r -p -z -d"); 477 if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) { 478 (void) fprintf(stderr, gettext("rcp: Cannot malloc.\n")); 479 return (1); 480 } 481 482 (void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL, 483 iamrecursive ? " -r" : "", 484 pflag ? " -p" : "", 485 zflag ? " -z" : "", 486 targetshouldbedirectory ? " -d" : ""); 487 488 (void) signal(SIGPIPE, (void (*)(int))lostconn); 489 490 if (targ = colon(argv[argc - 1])) 491 toremote(targ, argc, argv); 492 else { 493 tolocal(argc, argv); 494 if (targetshouldbedirectory) 495 verifydir(argv[argc - 1]); 496 } 497 498 return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS); 499 } 500 501 502 static void 503 toremote(char *targ, int argc, char *argv[]) 504 { 505 int i; 506 char *host, *src, *suser, *thost, *tuser; 507 char resp; 508 size_t buffersize; 509 char bp[RCP_BUFSIZE]; 510 krb5_creds *cred; 511 char *arglist[MAXARGS+1]; 512 buffersize = RCP_BUFSIZE; 513 514 *targ++ = 0; 515 if (*targ == 0) 516 targ = "."; 517 518 if (thost = search_char((unsigned char *)argv[argc - 1], '@')) { 519 *thost++ = 0; 520 tuser = argv[argc - 1]; 521 if (*tuser == '\0') 522 tuser = NULL; 523 else if (!okname(tuser)) 524 exit(1); 525 } else { 526 thost = argv[argc - 1]; 527 tuser = NULL; 528 } 529 thost = removebrackets(thost); 530 531 for (i = 0; i < argc - 1; i++) { 532 src = colon(argv[i]); 533 if (src) { /* remote to remote */ 534 *src++ = 0; 535 if (*src == 0) 536 src = "."; 537 host = search_char((unsigned char *)argv[i], '@'); 538 if (host) { 539 *host++ = 0; 540 host = removebrackets(host); 541 suser = argv[i]; 542 if (*suser == '\0') { 543 suser = pwd->pw_name; 544 } else if (!okname(suser)) { 545 errs++; 546 continue; 547 } 548 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 549 tuser ? tuser : "", tuser ? "@" : "", 550 thost, targ); 551 (void) addargs(arglist, "rsh", host, "-l", 552 suser, "-n", cmd, src, bp, (char *)NULL); 553 } else { 554 host = removebrackets(argv[i]); 555 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 556 tuser ? tuser : "", tuser ? "@" : "", 557 thost, targ); 558 (void) addargs(arglist, "rsh", host, "-n", cmd, 559 src, bp, (char *)NULL); 560 } 561 if (susystem(_PATH_RSH, arglist) == -1) 562 errs++; 563 } else { /* local to remote */ 564 if (rem == -1) { 565 host = thost; 566 if (krb5auth_flag > 0) { 567 568 (void) snprintf(bp, buffersize, 569 "%s -t %s", cmd, targ); 570 authopts = AP_OPTS_MUTUAL_REQUIRED; 571 status = kcmd(&sock, &host, 572 portnumber, 573 pwd->pw_name, 574 tuser ? tuser : 575 pwd->pw_name, 576 bp, 577 0, 578 "host", 579 krb_realm, 580 bsd_context, 581 &auth_context, 582 &cred, 583 0, /* No seq # */ 584 0, /* No server seq # */ 585 authopts, 586 0, /* Not any port # */ 587 &kcmd_proto); 588 if (status) { 589 /* 590 * If new protocol requested, we dont 591 * fallback to less secure ones. 592 */ 593 594 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 595 (void) fprintf(stderr, 596 gettext("rcp: kcmdv2 " 597 "to host %s failed - %s" 598 "\nFallback to normal " 599 "rcp denied."), host, 600 error_message(status)); 601 exit(1); 602 } 603 if (status != -1) { 604 (void) fprintf(stderr, 605 gettext("rcp: kcmd to host " 606 "%s failed - %s,\n" 607 "trying normal rcp...\n\n"), 608 host, error_message(status)); 609 } else { 610 (void) fprintf(stderr, 611 gettext("trying normal" 612 " rcp...\n")); 613 } 614 /* 615 * kcmd() failed, so we have to 616 * fallback to normal rcp 617 */ 618 try_normal_rcp(prev_argc, prev_argv); 619 } else { 620 rem = sock; 621 session_key = &cred->keyblock; 622 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 623 /* CSTYLED */ 624 status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key); 625 if (status) { 626 com_err("rcp", status, 627 "determining " 628 "subkey for " 629 "session"); 630 exit(1); 631 } 632 if (!session_key) { 633 com_err("rcp", 0, 634 "no subkey " 635 "negotiated for" 636 " connection"); 637 exit(1); 638 } 639 } 640 eblock.crypto_entry = 641 session_key->enctype; 642 eblock.key = 643 (krb5_keyblock *)session_key; 644 645 init_encrypt(encrypt_flag, 646 bsd_context, kcmd_proto, 647 &desinbuf, &desoutbuf, CLIENT, 648 &eblock); 649 if (encrypt_flag > 0) { 650 char *s = gettext("This rcp " 651 "session is using " 652 "encryption for all " 653 "data transmissions." 654 "\r\n"); 655 656 (void) write(2, s, strlen(s)); 657 } 658 } 659 if (response() < 0) 660 exit(1); 661 662 } else { 663 664 /* 665 * ACL support: try to find out if the remote 666 * site is running acl cognizant version of 667 * rcp. A special binary name is used for this 668 * purpose. 669 */ 670 aclflag = 1; 671 acl_aclflag = 1; 672 673 /* 674 * First see if the remote side will support 675 * both aclent_t and ace_t acl's? 676 */ 677 (void) snprintf(bp, buffersize, "%s -tZ %s", 678 cmd_sunw, targ); 679 rem = rcmd_af(&host, portnumber, pwd->pw_name, 680 tuser ? tuser : pwd->pw_name, 681 bp, 0, AF_INET6); 682 if (rem < 0) 683 exit(1); 684 685 /* 686 * This is similar to routine response(). 687 * If response is not ok, treat the other 688 * side as non-acl rcp. 689 */ 690 if (read(rem, &resp, sizeof (resp)) 691 != sizeof (resp)) 692 lostconn(); 693 if (resp != 0) { 694 acl_aclflag = 0; 695 (void) snprintf(bp, buffersize, 696 "%s -t %s", cmd_sunw, targ); 697 698 (void) close(rem); 699 host = thost; 700 rem = rcmd_af(&host, portnumber, 701 pwd->pw_name, 702 tuser ? tuser : pwd->pw_name, 703 bp, 0, AF_INET6); 704 if (rem < 0) 705 exit(1); 706 707 if (read(rem, &resp, sizeof (resp)) 708 != sizeof (resp)) 709 lostconn(); 710 if (resp != 0) { 711 /* 712 * Not OK: 713 * The other side is running 714 * non-acl rcp. Try again with 715 * normal stuff 716 */ 717 aclflag = 0; 718 (void) snprintf(bp, buffersize, 719 "%s -t %s", cmd, targ); 720 (void) close(rem); 721 host = thost; 722 rem = rcmd_af(&host, portnumber, 723 pwd->pw_name, 724 tuser ? tuser : 725 pwd->pw_name, bp, 0, 726 AF_INET6); 727 if (rem < 0) 728 exit(1); 729 if (response() < 0) 730 exit(1); 731 } 732 } 733 /* everything should be fine now */ 734 (void) setuid(userid); 735 736 } 737 } 738 source(1, argv + i); 739 } 740 } 741 } 742 743 static void 744 tolocal(int argc, char *argv[]) 745 { 746 int i; 747 char *host, *src, *suser, *lhost; 748 char resp; 749 size_t buffersize; 750 char bp[RCP_BUFSIZE]; 751 krb5_creds *cred; 752 char *arglist[MAXARGS+1]; 753 buffersize = RCP_BUFSIZE; 754 755 for (i = 0; i < argc - 1; i++) { 756 if (!(src = colon(argv[i]))) { /* local to local */ 757 (void) addargs(arglist, "cp", 758 iamrecursive ? "-r" : "", pflag ? "-p" : "", 759 zflag ? "-z" : "", argv[i], argv[argc - 1], 760 (char *)NULL); 761 if (susystem(_PATH_CP, arglist) == -1) 762 errs++; 763 continue; 764 } 765 *src++ = 0; 766 if (*src == 0) 767 src = "."; 768 host = search_char((unsigned char *)argv[i], '@'); 769 if (host) { 770 *host++ = 0; 771 suser = argv[i]; 772 if (*suser == '\0') { 773 suser = pwd->pw_name; 774 } else if (!okname(suser)) { 775 errs++; 776 continue; 777 } 778 } else { 779 host = argv[i]; 780 suser = pwd->pw_name; 781 } 782 host = removebrackets(host); 783 lhost = host; 784 if (krb5auth_flag > 0) { 785 786 (void) snprintf(bp, buffersize, "%s -f %s", cmd, src); 787 authopts = AP_OPTS_MUTUAL_REQUIRED; 788 status = kcmd(&sock, &host, 789 portnumber, 790 pwd->pw_name, suser, 791 bp, 792 0, /* &rfd2 */ 793 "host", 794 krb_realm, 795 bsd_context, 796 &auth_context, 797 &cred, 798 0, /* No seq # */ 799 0, /* No server seq # */ 800 authopts, 801 1, /* Not any port # */ 802 &kcmd_proto); 803 if (status) { 804 /* 805 * If new protocol requested, we dont 806 * fallback to less secure ones. 807 */ 808 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 809 (void) fprintf(stderr, gettext("rcp: kcmdv2 " 810 "to host %s failed - %s\n" 811 "Fallback to normal rcp denied."), 812 host, error_message(status)); 813 exit(1); 814 } 815 if (status != -1) { 816 (void) fprintf(stderr, gettext("rcp: kcmd " 817 "to host %s failed - %s,\n" 818 "trying normal rcp...\n\n"), 819 host, error_message(status)); 820 } else { 821 (void) fprintf(stderr, 822 gettext("trying normal rcp...\n")); 823 } 824 /* 825 * kcmd() failed, so we have to 826 * fallback to normal rcp 827 */ 828 try_normal_rcp(prev_argc, prev_argv); 829 } else { 830 rem = sock; 831 session_key = &cred->keyblock; 832 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 833 status = krb5_auth_con_getlocalsubkey( 834 bsd_context, auth_context, 835 &session_key); 836 if (status) { 837 com_err("rcp", status, "determining " 838 "subkey for session"); 839 exit(1); 840 } 841 if (!session_key) { 842 com_err("rcp", 0, "no subkey negotiated" 843 " for connection"); 844 exit(1); 845 } 846 } 847 eblock.crypto_entry = session_key->enctype; 848 eblock.key = (krb5_keyblock *)session_key; 849 850 init_encrypt(encrypt_flag, bsd_context, kcmd_proto, 851 &desinbuf, &desoutbuf, CLIENT, 852 &eblock); 853 if (encrypt_flag > 0) { 854 char *s = gettext("This rcp " 855 "session is using DES " 856 "encryption for all " 857 "data transmissions." 858 "\r\n"); 859 860 (void) write(2, s, strlen(s)); 861 } 862 } 863 864 } 865 else 866 { 867 868 /* 869 * ACL support: try to find out if the remote site is 870 * running acl cognizant version of rcp. 871 */ 872 aclflag = 1; 873 acl_aclflag = 1; 874 875 (void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, src); 876 rem = rcmd_af(&host, portnumber, pwd->pw_name, suser, 877 bp, 0, AF_INET6); 878 879 if (rem < 0) { 880 ++errs; 881 continue; 882 } 883 884 /* 885 * The remote system is supposed to send an ok response. 886 * If there are any data other than "ok", it must be error 887 * messages from the remote system. We can assume the 888 * remote system is running non-acl version rcp. 889 */ 890 if (read(rem, &resp, sizeof (resp)) != sizeof (resp)) 891 lostconn(); 892 if (resp != 0) { 893 894 /* 895 * Try again without ace_acl support 896 */ 897 acl_aclflag = 0; 898 (void) snprintf(bp, buffersize, "%s -f %s", 899 cmd_sunw, src); 900 rem = rcmd_af(&host, portnumber, pwd->pw_name, suser, 901 bp, 0, AF_INET6); 902 903 if (rem < 0) { 904 ++errs; 905 continue; 906 } 907 908 if (read(rem, &resp, sizeof (resp)) != sizeof (resp)) 909 lostconn(); 910 911 /* 912 * NOT ok: 913 * The other side is running non-acl rcp. 914 * Try again with normal stuff 915 */ 916 aclflag = 0; 917 (void) snprintf(bp, buffersize, "%s -f %s", cmd, src); 918 (void) close(rem); 919 host = lhost; 920 rem = rcmd_af(&host, portnumber, pwd->pw_name, 921 suser, bp, 0, AF_INET6); 922 if (rem < 0) { 923 ++errs; 924 continue; 925 } 926 } 927 } 928 929 sink(1, argv + argc - 1); 930 931 (void) close(rem); 932 rem = -1; 933 } 934 } 935 936 937 static void 938 verifydir(char *cp) 939 { 940 struct stat stb; 941 942 if (stat(cp, &stb) >= 0) { 943 if ((stb.st_mode & S_IFMT) == S_IFDIR) 944 return; 945 errno = ENOTDIR; 946 } 947 error("rcp: %s: %s.\n", cp, strerror(errno)); 948 exit(1); 949 } 950 951 static char * 952 colon(char *cp) 953 { 954 boolean_t is_bracket_open = B_FALSE; 955 956 for (; *cp; ++cp) { 957 if (*cp == '[') 958 is_bracket_open = B_TRUE; 959 else if (*cp == ']') 960 is_bracket_open = B_FALSE; 961 else if (*cp == ':' && !is_bracket_open) 962 return (cp); 963 else if (*cp == '/') 964 return (0); 965 } 966 return (0); 967 } 968 969 static int 970 okname(char *cp0) 971 { 972 register char *cp = cp0; 973 register int c; 974 975 do { 976 c = *cp; 977 if (c & 0200) 978 goto bad; 979 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 980 goto bad; 981 } while (*++cp); 982 return (1); 983 bad: 984 (void) fprintf(stderr, "rcp: invalid user name %s\n", cp0); 985 return (0); 986 } 987 988 989 static char * 990 removebrackets(char *str) 991 { 992 char *newstr = str; 993 994 if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) { 995 newstr = str + 1; 996 str[strlen(str) - 1] = '\0'; 997 } 998 return (newstr); 999 } 1000 1001 static int 1002 susystem(char *path, char **arglist) 1003 { 1004 int status, pid, w; 1005 register void (*istat)(), (*qstat)(); 1006 int pfds[2]; 1007 char buf[BUFSIZ]; 1008 int cnt; 1009 boolean_t seen_stderr_traffic; 1010 1011 /* 1012 * Due to the fact that rcp uses rsh to copy between 2 remote 1013 * machines, rsh doesn't return the exit status of the remote 1014 * command, and we can't modify the rcmd protocol used by rsh 1015 * (for interoperability reasons) we use the hack of using any 1016 * output on stderr as indication that an error occurred and 1017 * that we should return a non-zero error code. 1018 */ 1019 1020 if (pipe(pfds) == -1) { 1021 (void) fprintf(stderr, "Couldn't create pipe: %s\n", 1022 strerror(errno)); 1023 return (-1); 1024 } 1025 1026 if ((pid = vfork()) < 0) { 1027 (void) close(pfds[0]); 1028 (void) close(pfds[1]); 1029 (void) fprintf(stderr, "Couldn't fork child process: %s\n", 1030 strerror(errno)); 1031 return (-1); 1032 } else if (pid == 0) { 1033 /* 1034 * Child. 1035 */ 1036 (void) close(pfds[0]); 1037 /* 1038 * Send stderr messages down the pipe so that we can detect 1039 * them in the parent process. 1040 */ 1041 if (pfds[1] != STDERR_FILENO) { 1042 (void) dup2(pfds[1], STDERR_FILENO); 1043 (void) close(pfds[1]); 1044 } 1045 /* 1046 * This shell does not inherit the additional privilege 1047 * we have in our Permitted set. 1048 */ 1049 (void) execv(path, arglist); 1050 _exit(127); 1051 } 1052 /* 1053 * Parent. 1054 */ 1055 istat = signal(SIGINT, SIG_IGN); 1056 qstat = signal(SIGQUIT, SIG_IGN); 1057 1058 (void) close(pfds[1]); 1059 seen_stderr_traffic = B_FALSE; 1060 while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) { 1061 /* 1062 * If any data is read from the pipe the child process 1063 * has output something on stderr so we set the boolean 1064 * 'seen_stderr_traffic' to true, which will cause the 1065 * function to return -1. 1066 */ 1067 (void) write(STDERR_FILENO, buf, cnt); 1068 seen_stderr_traffic = B_TRUE; 1069 } 1070 (void) close(pfds[0]); 1071 while ((w = wait(&status)) != pid && w != -1) 1072 ; 1073 if (w == -1) 1074 status = -1; 1075 1076 (void) signal(SIGINT, istat); 1077 (void) signal(SIGQUIT, qstat); 1078 1079 return (seen_stderr_traffic ? -1 : status); 1080 } 1081 1082 static void 1083 source(int argc, char *argv[]) 1084 { 1085 struct stat stb; 1086 static BUF buffer; 1087 BUF *bp; 1088 int x, readerr, f, amt; 1089 char *last, *name, buf[RCP_BUFSIZE]; 1090 off_t off, size, i; 1091 ssize_t cnt; 1092 struct linger lingerbuf; 1093 1094 for (x = 0; x < argc; x++) { 1095 name = argv[x]; 1096 if ((f = open(name, O_RDONLY, 0)) < 0) { 1097 error("rcp: %s: %s\n", name, strerror(errno)); 1098 continue; 1099 } 1100 if (fstat(f, &stb) < 0) 1101 goto notreg; 1102 switch (stb.st_mode&S_IFMT) { 1103 1104 case S_IFREG: 1105 break; 1106 1107 case S_IFDIR: 1108 if (iamrecursive) { 1109 (void) close(f); 1110 rsource(name, &stb); 1111 continue; 1112 } 1113 /* FALLTHROUGH */ 1114 default: