1 /* 2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Copyright (c) 1983 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms are permitted 11 * provided that the above copyright notice and this paragraph are 12 * duplicated in all such forms and that any documentation, 13 * advertising materials, and other materials related to such 14 * distribution and use acknowledge that the software was developed 15 * by the University of California, Berkeley. The name of the 16 * University may not be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 */ 20 21 #define _FILE_OFFSET_BITS 64 22 23 /* 24 * rcp 25 */ 26 #include <sys/param.h> 27 #include <sys/file.h> 28 #include <sys/stat.h> 29 #include <sys/time.h> 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 #include <sys/acl.h> 33 #include <dirent.h> 34 #include <signal.h> 35 #include <sys/socket.h> 36 #include <netinet/in.h> 37 #include <pwd.h> 38 #include <netdb.h> 39 #include <wchar.h> 40 #include <stdlib.h> 41 #include <errno.h> 42 #include <locale.h> 43 #include <strings.h> 44 #include <stdio.h> 45 #include <ctype.h> 46 #include <fcntl.h> 47 #include <unistd.h> 48 #include <limits.h> 49 #include <priv_utils.h> 50 #include <sys/sendfile.h> 51 #include <sys/sysmacros.h> 52 #include <sys/wait.h> 53 #include <aclutils.h> 54 #include <sys/varargs.h> 55 56 /* 57 * It seems like Berkeley got these from pathnames.h? 58 */ 59 #define _PATH_RSH "/usr/bin/rsh" 60 #define _PATH_CP "/usr/bin/cp" 61 62 #define ACL_FAIL 1 63 #define ACL_OK 0 64 #define RCP_BUFSIZE (64 * 1024) 65 66 #define RCP_ACL "/usr/lib/sunw,rcp" 67 /* see PSARC/1993/004/opinion */ 68 69 typedef struct _buf { 70 int cnt; 71 char *buf; 72 } BUF; 73 74 static char *cmd_sunw; 75 static struct passwd *pwd; 76 static int errs; 77 static int pflag; 78 static uid_t userid; 79 static int rem; 80 static int zflag; 81 static int iamremote; 82 static int iamrecursive; 83 static int targetshouldbedirectory; 84 static int aclflag; 85 static int acl_aclflag; 86 static int retval = 0; 87 static int portnumber = 0; 88 89 static void lostconn(void); 90 static char *search_char(unsigned char *, unsigned char); 91 static char *removebrackets(char *); 92 static char *colon(char *); 93 static int response(void); 94 static void usage(void); 95 static void source(int, char **); 96 static void sink(int, char **); 97 static void toremote(char *, int, char **); 98 static void tolocal(int, char **); 99 static void verifydir(char *); 100 static int okname(char *); 101 static int susystem(char *, char **); 102 static void rsource(char *, struct stat *); 103 static int sendacl(int); 104 static int recvacl(int, int, int); 105 static int zwrite(int, char *, int); 106 static void zopen(int, int); 107 static int zclose(int); 108 static int notzero(char *, int); 109 static BUF *allocbuf(BUF *, int, int); 110 static void error(char *fmt, ...); 111 static void addargs(char **, ...); 112 113 /* 114 * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF 115 * bytes of data. We would like the size to be aligned to the nearest 116 * MAXBOFFSET (8192) boundary for optimal performance. 117 */ 118 #define SENDFILE_SIZE 0x7FFFE000 119 120 #include <k5-int.h> 121 #include <profile/prof_int.h> 122 #include <com_err.h> 123 #include <kcmd.h> 124 125 #define NULLBUF (BUF *) 0 126 #define MAXARGS 10 /* Number of arguments passed to execv() */ 127 128 static int sock; 129 static char *cmd, *cmd_orig, *cmd_sunw_orig; 130 static char *krb_realm = NULL; 131 static char *krb_cache = NULL; 132 static char *krb_config = NULL; 133 static char des_inbuf[2 * RCP_BUFSIZE]; 134 /* needs to be > largest read size */ 135 static char des_outbuf[2 * RCP_BUFSIZE]; 136 /* needs to be > largest write size */ 137 138 static krb5_data desinbuf, desoutbuf; 139 static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 140 static krb5_keyblock *session_key; /* static key for session */ 141 static krb5_context bsd_context = NULL; 142 static krb5_auth_context auth_context; 143 static krb5_flags authopts; 144 static krb5_error_code status; 145 146 static void try_normal_rcp(int, char **); 147 static int init_service(int); 148 static char **save_argv(int, char **); 149 static void answer_auth(char *, char *); 150 static int desrcpwrite(int, char *, int); 151 static int desrcpread(int, char *, int); 152 153 /* 154 * Not sure why these two don't have their own header file declarations, but 155 * lint complains about absent declarations so place some here. Sigh. 156 */ 157 extern errcode_t profile_get_options_boolean(profile_t, char **, 158 profile_options_boolean *); 159 extern errcode_t profile_get_options_string(profile_t, char **, 160 profile_option_strings *); 161 162 static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */ 163 static profile_options_boolean autologin_option[] = { 164 { "autologin", &krb5auth_flag, 0 }, 165 { NULL, NULL, 0 } 166 }; 167 static int no_krb5auth_flag = 0; 168 169 static int encrypt_flag = 0; /* Flag set, when encryption is enabled */ 170 static int encrypt_done = 0; /* Flag set, if "-x" is specified */ 171 static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; 172 173 /* Flag set, if -PN / -PO is specified */ 174 static boolean_t rcmdoption_done = B_FALSE; 175 176 static profile_options_boolean option[] = { 177 { "encrypt", &encrypt_flag, 0 }, 178 { NULL, NULL, 0 } 179 }; 180 181 static char *rcmdproto = NULL; 182 static profile_option_strings rcmdversion[] = { 183 { "rcmd_protocol", &rcmdproto, 0 }, 184 { NULL, NULL, 0 } 185 }; 186 187 static char *realmdef[] = { "realms", NULL, "rcp", NULL }; 188 static char *appdef[] = { "appdefaults", "rcp", NULL }; 189 static char **prev_argv; 190 static int prev_argc; 191 192 int 193 main(int argc, char *argv[]) 194 { 195 int ch, fflag, tflag; 196 char *targ; 197 size_t cmdsiz; 198 199 (void) setlocale(LC_ALL, ""); 200 201 if (strcmp(argv[0], RCP_ACL) == 0) 202 aclflag = 1; 203 204 if (!(pwd = getpwuid(userid = getuid()))) { 205 (void) fprintf(stderr, "rcp: unknown user %d.\n", 206 (uint_t)userid); 207 return (1); 208 } 209 210 fflag = tflag = 0; 211 while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:ZK")) != EOF) { 212 switch (ch) { 213 case 'd': 214 targetshouldbedirectory = 1; 215 break; 216 case 'f': /* "from" */ 217 fflag = 1; 218 if (aclflag | acl_aclflag) 219 /* ok response */ 220 (void) desrcpwrite(rem, "", 1); 221 break; 222 case 'p': /* preserve access/mod times */ 223 ++pflag; 224 break; 225 case 'r': 226 ++iamrecursive; 227 break; 228 case 't': /* "to" */ 229 tflag = 1; 230 break; 231 case 'Z': 232 acl_aclflag++; 233 break; 234 case 'K': 235 no_krb5auth_flag++; 236 break; 237 case 'x': 238 if (!krb5_privacy_allowed()) { 239 (void) fprintf(stderr, gettext("rcp: " 240 "Encryption not supported.\n")); 241 return (1); 242 } 243 encrypt_flag++; 244 krb5auth_flag++; 245 encrypt_done++; 246 break; 247 case 'k': 248 if ((krb_realm = (char *)strdup(optarg)) == NULL) { 249 (void) fprintf(stderr, gettext("rcp:" 250 " Cannot malloc.\n")); 251 return (1); 252 } 253 krb5auth_flag++; 254 break; 255 case 'P': 256 if (strncmp(optarg, "O", 1) == 0) { 257 if (rcmdoption_done == B_TRUE) { 258 (void) fprintf(stderr, gettext("rcp: " 259 "Only one of -PN and -PO " 260 "allowed.\n")); 261 usage(); 262 } 263 kcmd_proto = KCMD_OLD_PROTOCOL; 264 rcmdoption_done = B_TRUE; 265 } else if (strncmp(optarg, "N", 1) == 0) { 266 if (rcmdoption_done == B_TRUE) { 267 (void) fprintf(stderr, gettext("rcp: " 268 "Only one of -PN and -PO " 269 "allowed.\n")); 270 usage(); 271 } 272 kcmd_proto = KCMD_NEW_PROTOCOL; 273 rcmdoption_done = B_TRUE; 274 } else { 275 usage(); 276 } 277 krb5auth_flag++; 278 break; 279 case 'a': 280 krb5auth_flag++; 281 break; 282 #ifdef DEBUG 283 case 'D': 284 portnumber = htons(atoi(optarg)); 285 krb5auth_flag++; 286 break; 287 #endif /* DEBUG */ 288 case '?': 289 default: 290 usage(); 291 } 292 } 293 argc -= optind; 294 argv += optind; 295 296 /* 297 * if the user disables krb5 on the cmdline (-K), then skip 298 * all krb5 setup. 299 * 300 * if the user does not disable krb5 or enable krb5 on the 301 * cmdline, check krb5.conf to see if it should be enabled. 302 */ 303 304 if (no_krb5auth_flag) { 305 krb5auth_flag = 0; 306 fflag = encrypt_flag = 0; 307 } else if (!krb5auth_flag) { 308 /* is autologin set in krb5.conf? */ 309 status = krb5_init_context(&bsd_context); 310 /* don't sweat failure here */ 311 if (!status) { 312 /* 313 * note that the call to profile_get_options_boolean 314 * with autologin_option can affect value of 315 * krb5auth_flag 316 */ 317 (void) profile_get_options_boolean(bsd_context->profile, 318 appdef, 319 autologin_option); 320 } 321 } 322 323 if (krb5auth_flag > 0) { 324 if (!bsd_context) { 325 status = krb5_init_context(&bsd_context); 326 if (status) { 327 com_err("rcp", status, 328 gettext("while initializing krb5")); 329 return (1); 330 } 331 } 332 333 /* 334 * Set up buffers for desread and deswrite. 335 */ 336 desinbuf.data = des_inbuf; 337 desoutbuf.data = des_outbuf; 338 desinbuf.length = sizeof (des_inbuf); 339 desoutbuf.length = sizeof (des_outbuf); 340 } 341 342 if (fflag || tflag) 343 if (encrypt_flag > 0) 344 (void) answer_auth(krb_config, krb_cache); 345 346 if (fflag) { 347 iamremote = 1; 348 (void) response(); 349 (void) setuid(userid); 350 source(argc, argv); 351 return (errs); 352 } 353 354 if (tflag) { 355 iamremote = 1; 356 (void) setuid(userid); 357 sink(argc, argv); 358 return (errs); 359 } 360 361 if (argc < 2) 362 usage(); 363 364 /* This will make "rcmd_af()" magically get the proper privilege */ 365 if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) { 366 (void) fprintf(stderr, "rcp: must be set-uid root\n"); 367 exit(1); 368 } 369 370 if (krb5auth_flag > 0) { 371 /* 372 * Get our local realm to look up local realm options. 373 */ 374 status = krb5_get_default_realm(bsd_context, &realmdef[1]); 375 if (status) { 376 com_err("rcp", status, 377 gettext("while getting default realm")); 378 return (1); 379 } 380 /* 381 * See if encryption should be done for this realm 382 */ 383 (void) profile_get_options_boolean(bsd_context->profile, 384 realmdef, option); 385 /* 386 * Check the appdefaults section 387 */ 388 (void) profile_get_options_boolean(bsd_context->profile, 389 appdef, option); 390 (void) profile_get_options_string(bsd_context->profile, 391 appdef, rcmdversion); 392 if ((encrypt_done > 0) || (encrypt_flag > 0)) { 393 if (krb5_privacy_allowed() == TRUE) { 394 encrypt_flag++; 395 } else { 396 (void) fprintf(stderr, gettext("rcp: Encryption" 397 " not supported.\n")); 398 return (1); 399 } 400 } 401 402 if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) { 403 if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 404 kcmd_proto = KCMD_NEW_PROTOCOL; 405 } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 406 kcmd_proto = KCMD_OLD_PROTOCOL; 407 } else { 408 (void) fprintf(stderr, gettext("Unrecognized " 409 "KCMD protocol (%s)"), rcmdproto); 410 return (1); 411 } 412 } 413 } 414 415 if (argc > 2) 416 targetshouldbedirectory = 1; 417 418 rem = -1; 419 420 if (portnumber == 0) { 421 if (krb5auth_flag > 0) { 422 retval = init_service(krb5auth_flag); 423 if (!retval) { 424 /* 425 * Connecting to the kshell service failed, 426 * fallback to normal rcp & reset KRB5 flags. 427 */ 428 krb5auth_flag = encrypt_flag = 0; 429 encrypt_done = 0; 430 (void) init_service(krb5auth_flag); 431 } 432 } 433 else 434 (void) init_service(krb5auth_flag); 435 } 436 437 #ifdef DEBUG 438 if (retval || krb5auth_flag) { 439 (void) fprintf(stderr, gettext("Kerberized rcp session, " 440 "port %d in use "), portnumber); 441 if (kcmd_proto == KCMD_OLD_PROTOCOL) 442 (void) fprintf(stderr, gettext("[kcmd ver.1]\n")); 443 else 444 (void) fprintf(stderr, gettext("[kcmd ver.2]\n")); 445 } else { 446 (void) fprintf(stderr, gettext("Normal rcp session, port %d " 447 "in use.\n"), portnumber); 448 } 449 #endif /* DEBUG */ 450 451 if (krb5auth_flag > 0) { 452 /* 453 * We calculate here a buffer size that can be used in the 454 * allocation of the three buffers cmd, cmd_orig and 455 * cmd_sunw_orig that are used to hold different incantations 456 * of rcp. 457 */ 458 cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") + 459 strlen(krb_realm != NULL ? krb_realm : ""), 460 sizeof (RCP_ACL " -r -p -z -d")); 461 462 if (((cmd = (char *)malloc(cmdsiz)) == NULL) || 463 ((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) || 464 ((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) { 465 (void) fprintf(stderr, gettext("rcp: Cannot " 466 "malloc.\n")); 467 return (1); 468 } 469 470 (void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s", 471 encrypt_flag ? "-x " : "", 472 iamrecursive ? " -r" : "", pflag ? " -p" : "", 473 targetshouldbedirectory ? " -d" : "", 474 krb_realm != NULL ? " -k " : "", 475 krb_realm != NULL ? krb_realm : ""); 476 477 /* 478 * We would use cmd-orig as the 'cmd-buffer' if kerberized 479 * rcp fails, in which case we fallback to normal rcp. We also 480 * save argc & argv for the same purpose 481 */ 482 (void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s", 483 iamrecursive ? " -r" : "", 484 pflag ? " -p" : "", 485 zflag ? " -z" : "", 486 targetshouldbedirectory ? " -d" : ""); 487 488 (void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL, 489 iamrecursive ? " -r" : "", 490 pflag ? " -p" : "", 491 zflag ? " -z" : "", 492 targetshouldbedirectory ? " -d" : ""); 493 494 prev_argc = argc; 495 prev_argv = save_argv(argc, argv); 496 497 } else { 498 cmdsiz = sizeof ("rcp -r -p -z -d"); 499 if (((cmd = (char *)malloc(cmdsiz)) == NULL)) { 500 (void) fprintf(stderr, gettext("rcp: Cannot " 501 "malloc.\n")); 502 return (1); 503 } 504 505 (void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s", 506 iamrecursive ? " -r" : "", 507 pflag ? " -p" : "", 508 zflag ? " -z" : "", 509 targetshouldbedirectory ? " -d" : ""); 510 } 511 512 cmdsiz = sizeof (RCP_ACL " -r -p -z -d"); 513 if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) { 514 (void) fprintf(stderr, gettext("rcp: Cannot malloc.\n")); 515 return (1); 516 } 517 518 (void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL, 519 iamrecursive ? " -r" : "", 520 pflag ? " -p" : "", 521 zflag ? " -z" : "", 522 targetshouldbedirectory ? " -d" : ""); 523 524 (void) signal(SIGPIPE, (void (*)(int))lostconn); 525 526 if (targ = colon(argv[argc - 1])) 527 toremote(targ, argc, argv); 528 else { 529 tolocal(argc, argv); 530 if (targetshouldbedirectory) 531 verifydir(argv[argc - 1]); 532 } 533 534 return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS); 535 } 536 537 538 static void 539 toremote(char *targ, int argc, char *argv[]) 540 { 541 int i; 542 char *host, *src, *suser, *thost, *tuser; 543 char resp; 544 size_t buffersize; 545 char bp[RCP_BUFSIZE]; 546 krb5_creds *cred; 547 char *arglist[MAXARGS+1]; 548 buffersize = RCP_BUFSIZE; 549 550 *targ++ = 0; 551 if (*targ == 0) 552 targ = "."; 553 554 if (thost = search_char((unsigned char *)argv[argc - 1], '@')) { 555 *thost++ = 0; 556 tuser = argv[argc - 1]; 557 if (*tuser == '\0') 558 tuser = NULL; 559 else if (!okname(tuser)) 560 exit(1); 561 } else { 562 thost = argv[argc - 1]; 563 tuser = NULL; 564 } 565 thost = removebrackets(thost); 566 567 for (i = 0; i < argc - 1; i++) { 568 src = colon(argv[i]); 569 if (src) { /* remote to remote */ 570 *src++ = 0; 571 if (*src == 0) 572 src = "."; 573 host = search_char((unsigned char *)argv[i], '@'); 574 if (host) { 575 *host++ = 0; 576 host = removebrackets(host); 577 suser = argv[i]; 578 if (*suser == '\0') { 579 suser = pwd->pw_name; 580 } else if (!okname(suser)) { 581 errs++; 582 continue; 583 } 584 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 585 tuser ? tuser : "", tuser ? "@" : "", 586 thost, targ); 587 (void) addargs(arglist, "rsh", host, "-l", 588 suser, "-n", cmd, src, bp, (char *)NULL); 589 } else { 590 host = removebrackets(argv[i]); 591 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 592 tuser ? tuser : "", tuser ? "@" : "", 593 thost, targ); 594 (void) addargs(arglist, "rsh", host, "-n", cmd, 595 src, bp, (char *)NULL); 596 } 597 if (susystem(_PATH_RSH, arglist) == -1) 598 errs++; 599 } else { /* local to remote */ 600 if (rem == -1) { 601 host = thost; 602 if (krb5auth_flag > 0) { 603 604 (void) snprintf(bp, buffersize, 605 "%s -t %s", cmd, targ); 606 authopts = AP_OPTS_MUTUAL_REQUIRED; 607 status = kcmd(&sock, &host, 608 portnumber, 609 pwd->pw_name, 610 tuser ? tuser : 611 pwd->pw_name, 612 bp, 613 0, 614 "host", 615 krb_realm, 616 bsd_context, 617 &auth_context, 618 &cred, 619 0, /* No seq # */ 620 0, /* No server seq # */ 621 authopts, 622 0, /* Not any port # */ 623 &kcmd_proto); 624 if (status) { 625 /* 626 * If new protocol requested, we dont 627 * fallback to less secure ones. 628 */ 629 630 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 631 (void) fprintf(stderr, 632 gettext("rcp: kcmdv2 " 633 "to host %s failed - %s" 634 "\nFallback to normal " 635 "rcp denied."), host, 636 error_message(status)); 637 exit(1); 638 } 639 if (status != -1) { 640 (void) fprintf(stderr, 641 gettext("rcp: kcmd to host " 642 "%s failed - %s,\n" 643 "trying normal rcp...\n\n"), 644 host, 645 error_message(status)); 646 } else { 647 (void) fprintf(stderr, 648 gettext("trying normal" 649 " rcp...\n")); 650 } 651 /* 652 * kcmd() failed, so we have to 653 * fallback to normal rcp 654 */ 655 try_normal_rcp(prev_argc, prev_argv); 656 } else { 657 rem = sock; 658 session_key = &cred->keyblock; 659 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 660 /* CSTYLED */ 661 status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key); 662 if (status) { 663 com_err("rcp", status, 664 "determining " 665 "subkey for " 666 "session"); 667 exit(1); 668 } 669 if (!session_key) { 670 com_err("rcp", 0, 671 "no subkey " 672 "negotiated for" 673 " connection"); 674 exit(1); 675 } 676 } 677 eblock.crypto_entry = 678 session_key->enctype; 679 eblock.key = 680 (krb5_keyblock *)session_key; 681 682 init_encrypt(encrypt_flag, 683 bsd_context, kcmd_proto, 684 &desinbuf, &desoutbuf, CLIENT, 685 &eblock); 686 if (encrypt_flag > 0) { 687 char *s = gettext("This rcp " 688 "session is using " 689 "encryption for all " 690 "data transmissions." 691 "\r\n"); 692 693 (void) write(2, s, strlen(s)); 694 } 695 } 696 if (response() < 0) 697 exit(1); 698 699 } else { 700 701 /* 702 * ACL support: try to find out if the 703 * remote site is running acl cognizant 704 * version of rcp. A special binary 705 * name is used for this purpose. 706 */ 707 aclflag = 1; 708 acl_aclflag = 1; 709 710 /* 711 * First see if the remote side will 712 * support both aclent_t and ace_t 713 * acl's? 714 */ 715 (void) snprintf(bp, buffersize, 716 "%s -tZ %s", 717 cmd_sunw, targ); 718 rem = rcmd_af(&host, portnumber, 719 pwd->pw_name, 720 tuser ? tuser : pwd->pw_name, 721 bp, 0, AF_INET6); 722 if (rem < 0) 723 exit(1); 724 725 /* 726 * This is similar to routine 727 * response(). If response is not ok, 728 * treat the other side as non-acl rcp. 729 */ 730 if (read(rem, &resp, sizeof (resp)) 731 != sizeof (resp)) 732 lostconn(); 733 if (resp != 0) { 734 acl_aclflag = 0; 735 (void) snprintf(bp, buffersize, 736 "%s -t %s", cmd_sunw, targ); 737 738 (void) close(rem); 739 host = thost; 740 rem = rcmd_af(&host, portnumber, 741 pwd->pw_name, 742 tuser ? tuser : 743 pwd->pw_name, 744 bp, 0, AF_INET6); 745 if (rem < 0) 746 exit(1); 747 748 if (read(rem, &resp, 749 sizeof (resp)) 750 != sizeof (resp)) 751 lostconn(); 752 if (resp != 0) { 753 /* 754 * Not OK: 755 * The other side is 756 * running non-acl rcp. 757 * Try again with 758 * normal stuff. 759 */ 760 aclflag = 0; 761 (void) snprintf(bp, 762 buffersize, 763 "%s -t %s", cmd, 764 targ); 765 (void) close(rem); 766 host = thost; 767 rem = rcmd_af(&host, 768 portnumber, 769 pwd->pw_name, 770 tuser ? tuser : 771 pwd->pw_name, bp, 0, 772 AF_INET6); 773 if (rem < 0) 774 exit(1); 775 if (response() < 0) 776 exit(1); 777 } 778 } 779 /* everything should be fine now */ 780 (void) setuid(userid); 781 782 } 783 } 784 source(1, argv + i); 785 } 786 } 787 } 788 789 static void 790 tolocal(int argc, char *argv[]) 791 { 792 int i; 793 char *host, *src, *suser, *lhost; 794 char resp; 795 size_t buffersize; 796 char bp[RCP_BUFSIZE]; 797 krb5_creds *cred; 798 char *arglist[MAXARGS+1]; 799 buffersize = RCP_BUFSIZE; 800 801 for (i = 0; i < argc - 1; i++) { 802 if (!(src = colon(argv[i]))) { /* local to local */ 803 (void) addargs(arglist, "cp", 804 iamrecursive ? "-r" : "", pflag ? "-p" : "", 805 zflag ? "-z" : "", argv[i], argv[argc - 1], 806 (char *)NULL); 807 if (susystem(_PATH_CP, arglist) == -1) 808 errs++; 809 continue; 810 } 811 *src++ = 0; 812 if (*src == 0) 813 src = "."; 814 host = search_char((unsigned char *)argv[i], '@'); 815 if (host) { 816 *host++ = 0; 817 suser = argv[i]; 818 if (*suser == '\0') { 819 suser = pwd->pw_name; 820 } else if (!okname(suser)) { 821 errs++; 822 continue; 823 } 824 } else { 825 host = argv[i]; 826 suser = pwd->pw_name; 827 } 828 host = removebrackets(host); 829 lhost = host; 830 if (krb5auth_flag > 0) { 831 832 (void) snprintf(bp, buffersize, "%s -f %s", cmd, src); 833 authopts = AP_OPTS_MUTUAL_REQUIRED; 834 status = kcmd(&sock, &host, 835 portnumber, 836 pwd->pw_name, suser, 837 bp, 838 0, /* &rfd2 */ 839 "host", 840 krb_realm, 841 bsd_context, 842 &auth_context, 843 &cred, 844 0, /* No seq # */ 845 0, /* No server seq # */ 846 authopts, 847 1, /* Not any port # */ 848 &kcmd_proto); 849 if (status) { 850 /* 851 * If new protocol requested, we dont 852 * fallback to less secure ones. 853 */ 854 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 855 (void) fprintf(stderr, 856 gettext("rcp: kcmdv2 " 857 "to host %s failed - %s\n" 858 "Fallback to normal rcp denied."), 859 host, error_message(status)); 860 exit(1); 861 } 862 if (status != -1) { 863 (void) fprintf(stderr, 864 gettext("rcp: kcmd " 865 "to host %s failed - %s,\n" 866 "trying normal rcp...\n\n"), 867 host, error_message(status)); 868 } else { 869 (void) fprintf(stderr, 870 gettext("trying normal rcp...\n")); 871 } 872 /* 873 * kcmd() failed, so we have to 874 * fallback to normal rcp 875 */ 876 try_normal_rcp(prev_argc, prev_argv); 877 } else { 878 rem = sock; 879 session_key = &cred->keyblock; 880 if (kcmd_proto == KCMD_NEW_PROTOCOL) { 881 status = krb5_auth_con_getlocalsubkey( 882 bsd_context, auth_context, 883 &session_key); 884 if (status) { 885 com_err("rcp", status, 886 "determining " 887 "subkey for session"); 888 exit(1); 889 } 890 if (!session_key) { 891 com_err("rcp", 0, 892 "no subkey negotiated" 893 " for connection"); 894 exit(1); 895 } 896 } 897 eblock.crypto_entry = session_key->enctype; 898 eblock.key = (krb5_keyblock *)session_key; 899 900 init_encrypt(encrypt_flag, bsd_context, 901 kcmd_proto, 902 &desinbuf, &desoutbuf, CLIENT, 903 &eblock); 904 if (encrypt_flag > 0) { 905 char *s = gettext("This rcp " 906 "session is using DES " 907 "encryption for all " 908 "data transmissions." 909 "\r\n"); 910 911 (void) write(2, s, strlen(s)); 912 } 913 } 914 915 } 916 else 917 { 918 919 /* 920 * ACL support: try to find out if the remote site is 921 * running acl cognizant version of rcp. 922 */ 923 aclflag = 1; 924 acl_aclflag = 1; 925 926 (void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, 927 src); 928 rem = rcmd_af(&host, portnumber, pwd->pw_name, suser, 929 bp, 0, AF_INET6); 930 931 if (rem < 0) { 932 ++errs; 933 continue; 934 } 935 936 /* 937 * The remote system is supposed to send an ok response. 938 * If there are any data other than "ok", it must be 939 * error messages from the remote system. We can assume 940 * the remote system is running non-acl version rcp. 941 */ 942 if (read(rem, &resp, sizeof (resp)) != sizeof (resp)) 943 lostconn(); 944 945 if (resp != 0) { 946 947 /* 948 * Try again without ace_acl support 949 */ 950 acl_aclflag = 0; 951 (void) snprintf(bp, buffersize, "%s -f %s", 952 cmd_sunw, src); 953 (void) close(rem); 954 rem = rcmd_af(&host, portnumber, pwd->pw_name, 955 suser, bp, 0, AF_INET6); 956 957 if (rem < 0) { 958 ++errs; 959 continue; 960 } 961 962 if (read(rem, &resp, 963 sizeof (resp)) != sizeof (resp)) 964 lostconn(); 965 966 if (resp != 0) { 967 /* 968 * NOT ok: 969 * The other side is running non-acl 970 * rcp. Try again with normal stuff. 971 */ 972 aclflag = 0; 973 (void) snprintf(bp, buffersize, 974 "%s -f %s", cmd, src); 975 (void) close(rem); 976 host = lhost; 977 rem = rcmd_af(&host, portnumber, 978 pwd->pw_name, suser, bp, 0, 979 AF_INET6); 980 if (rem < 0) { 981 ++errs; 982 continue; 983 } 984 } 985 } 986 } 987 988 sink(1, argv + argc - 1); 989 990 (void) close(rem); 991 rem = -1; 992 } 993 } 994 995 996 static void 997 verifydir(char *cp) 998 { 999 struct stat stb; 1000 1001 if (stat(cp, &stb) >= 0) { 1002 if ((stb.st_mode & S_IFMT) == S_IFDIR) 1003 return; 1004 errno = ENOTDIR; 1005 } 1006 error("rcp: %s: %s.\n", cp, strerror(errno)); 1007 exit(1); 1008 } 1009 1010 static char * 1011 colon(char *cp) 1012 { 1013 boolean_t is_bracket_open = B_FALSE; 1014 1015 for (; *cp; ++cp) { 1016 if (*cp == '[') 1017 is_bracket_open = B_TRUE; 1018 else if (*cp == ']') 1019 is_bracket_open = B_FALSE; 1020 else if (*cp == ':' && !is_bracket_open) 1021 return (cp); 1022 else if (*cp == '/') 1023 return (0); 1024 } 1025 return (0); 1026 } 1027 1028 static int 1029 okname(char *cp0) 1030 { 1031 register char *cp = cp0; 1032 register int c; 1033 1034 do { 1035 c = *cp; 1036 if (c & 0200) 1037 goto bad; 1038 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 1039 goto bad; 1040 } while (*++cp); 1041 return (1); 1042 bad: 1043 (void) fprintf(stderr, "rcp: invalid user name %s\n", cp0); 1044 return (0); 1045 } 1046 1047 1048 static char * 1049 removebrackets(char *str) 1050 { 1051 char *newstr = str; 1052 1053 if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) { 1054 newstr = str + 1; 1055 str[strlen(str) - 1] = '\0'; 1056 } 1057 return (newstr); 1058 } 1059 1060 static int 1061 susystem(char *path, char **arglist) 1062 { 1063 int status, pid, w; 1064 register void (*istat)(), (*qstat)(); 1065 int pfds[2]; 1066 char buf[BUFSIZ]; 1067 int cnt; 1068 boolean_t seen_stderr_traffic; 1069 1070 /* 1071 * Due to the fact that rcp uses rsh to copy between 2 remote 1072 * machines, rsh doesn't return the exit status of the remote 1073 * command, and we can't modify the rcmd protocol used by rsh 1074 * (for interoperability reasons) we use the hack of using any 1075 * output on stderr as indication that an error occurred and 1076 * that we should return a non-zero error code. 1077 */ 1078 1079 if (pipe(pfds) == -1) { 1080 (void) fprintf(stderr, "Couldn't create pipe: %s\n", 1081 strerror(errno)); 1082 return (-1); 1083 } 1084 1085 if ((pid = vfork()) < 0) { 1086 (void) close(pfds[0]); 1087 (void) close(pfds[1]); 1088 (void) fprintf(stderr, "Couldn't fork child process: %s\n", 1089 strerror(errno)); 1090 return (-1); 1091 } else if (pid == 0) { 1092 /* 1093 * Child. 1094 */ 1095 (void) close(pfds[0]); 1096 /* 1097 * Send stderr messages down the pipe so that we can detect 1098 * them in the parent process. 1099 */ 1100 if (pfds[1] != STDERR_FILENO) { 1101 (void) dup2(pfds[1], STDERR_FILENO); 1102 (void) close(pfds[1]); 1103 } 1104 /* 1105 * This shell does not inherit the additional privilege 1106 * we have in our Permitted set. 1107 */ 1108 (void) execv(path, arglist); 1109 _exit(127); 1110 } 1111 /* 1112 * Parent. 1113 */ 1114 istat = signal(SIGINT, SIG_IGN); 1115 qstat = signal(SIGQUIT, SIG_IGN); 1116 1117 (void) close(pfds[1]); 1118 seen_stderr_traffic = B_FALSE; 1119 while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) { 1120 /* 1121 * If any data is read from the pipe the child process 1122 * has output something on stderr so we set the boolean 1123 * 'seen_stderr_traffic' to true, which will cause the 1124 * function to return -1. 1125 */ 1126 (void) write(STDERR_FILENO, buf, cnt); 1127 seen_stderr_traffic = B_TRUE; 1128 } 1129 (void) close(pfds[0]); 1130 while ((w = wait(&status)) != pid && w != -1) 1131 ; 1132 if (w == -1) 1133 status = -1; 1134 1135 (void) signal(SIGINT, istat); 1136 (void) signal(SIGQUIT, qstat); 1137 1138 return (seen_stderr_traffic ? -1 : status); 1139 } 1140 1141 static void 1142 source(int argc, char *argv[]) 1143 { 1144 struct stat stb; 1145 static BUF buffer; 1146 BUF *bp; 1147 int x, readerr, f, amt; 1148 char *last, *name, buf[RCP_BUFSIZE]; 1149 off_t off, size, i; 1150 ssize_t cnt; 1151 struct linger lingerbuf; 1152 1153 for (x = 0; x < argc; x++) { 1154 name = argv[x]; 1155 if ((f = open(name, O_RDONLY, 0)) < 0) { 1156 error("rcp: %s: %s\n", name, strerror(errno)); 1157 continue; 1158 } 1159 if (fstat(f, &stb) < 0) 1160 goto notreg; 1161 switch (stb.st_mode&S_IFMT) { 1162 1163 case S_IFREG: 1164 break; 1165 1166 case S_IFDIR: 1167 if (iamrecursive) { 1168 (void) close(f); 1169 rsource(name, &stb); 1170 continue; 1171 } 1172 /* FALLTHROUGH */ 1173 default: 1174 notreg: 1175 (void) close(f); 1176 error("rcp: %s: not a plain file\n", name); 1177 continue; 1178 } 1179 last = rindex(name, '/'); 1180 if (last == 0) 1181 last = name; 1182 else 1183 last++; 1184 if (pflag) { 1185 time_t mtime, atime; 1186 time_t now; 1187 1188 /* 1189 * Make it compatible with possible future 1190 * versions expecting microseconds. 1191 */ 1192 mtime = stb.st_mtime; 1193 atime = stb.st_atime; 1194 1195 if ((mtime < 0) || (atime < 0)) { 1196 now = time(NULL); 1197 1198 if (mtime < 0) { 1199 mtime = now; 1200 error("negative modification time on " 1201 "%s; not preserving\n", name); 1202 } 1203 if (atime < 0) { 1204 atime = now; 1205 error("negative access time on " 1206 "%s; not preserving\n", name); 1207 } 1208 } 1209 (void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n", 1210 mtime, atime); 1211 (void) desrcpwrite(rem, buf, strlen(buf)); 1212 if (response() < 0) { 1213 (void) close(f); 1214 continue; 1215 } 1216 } 1217 (void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n", 1218 (uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size, 1219 last); 1220 (void) desrcpwrite(rem, buf, strlen(buf)); 1221 if (response() < 0) { 1222 (void) close(f); 1223 continue; 1224 } 1225 1226 /* ACL support: send */ 1227 if (aclflag | acl_aclflag) { 1228 /* get acl from f and send it over */ 1229 if (sendacl(f) == ACL_FAIL) { 1230 (void) close(f); 1231 continue; 1232 } 1233 } 1234 if ((krb5auth_flag > 0) || (iamremote == 1)) { 1235 bp = allocbuf(&buffer, f, RCP_BUFSIZE); 1236 if (bp == NULLBUF) { 1237 (void) close(f); 1238 continue; 1239 } 1240 readerr = 0; 1241 for (i = 0; i < stb.st_size; i += bp->cnt) { 1242 amt = bp->cnt; 1243 if (i + amt > stb.st_size) 1244 amt = stb.st_size - i; 1245 if (readerr == 0 && 1246 read(f, bp->buf, amt) != amt) 1247 readerr = errno; 1248 (void) desrcpwrite(rem, bp->buf, amt); 1249 } 1250 (void) close(f); 1251 if (readerr == 0) 1252 (void) desrcpwrite(rem, "", 1); 1253 else 1254 error("rcp: %s: %s\n", name, 1255 error_message(readerr)); 1256 } else { 1257 cnt = off = 0; 1258 size = stb.st_size; 1259 while (size != 0) { 1260 amt = MIN(size, SENDFILE_SIZE); 1261 cnt = sendfile(rem, f, &off, amt); 1262 if (cnt == -1) { 1263 if (errno == EINTR) { 1264 continue; 1265 } else { 1266 break; 1267 } 1268 } 1269 if (cnt == 0) 1270 break; 1271 size -= cnt; 1272 } 1273 if (cnt < 0) { 1274 error("rcp: %s: %s\n", name, strerror(errno)); 1275 } else if (cnt == 0 && size != 0) { 1276 error("rcp: %s: unexpected end of file\n", 1277 name); 1278 lingerbuf.l_onoff = 1; 1279 lingerbuf.l_linger = 0; 1280 (void) setsockopt(rem, SOL_SOCKET, SO_LINGER, 1281 &lingerbuf, sizeof (lingerbuf)); 1282 /* 1283 * When response() (see below) is invoked it 1284 * tries to read data from closed handle which 1285 * triggers error and lostconn() function. 1286 * lostconn() terminates the program with 1287 * appropriate message. 1288 */ 1289 (void) close(rem); 1290 rem = -1; 1291 } else { 1292 (void) write(rem, "", 1); 1293 } 1294 (void) close(f); 1295 } 1296 (void) response(); 1297 } 1298 } 1299 1300 1301 static void 1302 rsource(char *name, struct stat *statp) 1303 { 1304 DIR *d; 1305 struct dirent *dp; 1306 char *last, *vect[1]; 1307 char path[MAXPATHLEN]; 1308 1309 if (!(d = opendir(name))) { 1310 error("rcp: %s: %s\n", name, strerror(errno)); 1311 return; 1312 } 1313 last = rindex(name, '/'); 1314 if (last == 0) 1315 last = name; 1316 else 1317 last++; 1318 if (pflag) { 1319 (void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n", 1320 statp->st_mtime, statp->st_atime); 1321 (void) desrcpwrite(rem, path, strlen(path)); 1322 if (response() < 0) { 1323 (void) closedir(d); 1324 return; 1325 } 1326 } 1327 (void) snprintf(path, sizeof (path), "D%04o %d %s\n", 1328 (uint_t)(statp->st_mode & 07777), 0, last); 1329 (void) desrcpwrite(rem, path, strlen(path)); 1330 1331 /* acl support for directory */ 1332 if (aclflag) { 1333 /* get acl from f and send it over */ 1334 if (sendacl(d->dd_fd) == ACL_FAIL) { 1335 (void) closedir(d); 1336 return; 1337 } 1338 } 1339 1340 if (response() < 0) { 1341 (void) closedir(d); 1342 return; 1343 } 1344 1345 while (dp = readdir(d)) { 1346 if (dp->d_ino == 0) 1347 continue; 1348 if ((strcmp(dp->d_name, ".") == 0) || 1349 (strcmp(dp->d_name, "..") == 0)) 1350 continue; 1351 if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >= 1352 MAXPATHLEN - 1) { 1353 error("%s/%s: name too long.\n", name, dp->d_name); 1354 continue; 1355 } 1356 (void) snprintf(path, sizeof (path), "%s/%s", 1357 name, dp->d_name); 1358 vect[0] = path; 1359 source(1, vect); 1360 } 1361 (void) closedir(d); 1362 (void) desrcpwrite(rem, "E\n", 2); 1363 (void) response(); 1364 } 1365 1366 static int 1367 response(void) 1368 { 1369 register char *cp; 1370 char ch, resp, rbuf[RCP_BUFSIZE]; 1371 1372 if (desrcpread(rem, &resp, 1) != 1) 1373 lostconn(); 1374 cp = rbuf; 1375 switch (resp) { 1376 case 0: /* ok */ 1377 return (0); 1378 default: 1379 *cp++ = resp; 1380 /* FALLTHROUGH */ 1381 case 1: /* error, followed by err msg */ 1382 case 2: /* fatal error, "" */ 1383 do { 1384 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 1385 lostconn(); 1386 *cp++ = ch; 1387 } while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n'); 1388 1389 if (!iamremote) 1390 (void) write(STDERR_FILENO, rbuf, cp - rbuf); 1391 ++errs; 1392 if (resp == 1) 1393 return (-1); 1394 exit(1); 1395 } 1396 /*NOTREACHED*/ 1397 } 1398 1399 static void 1400 lostconn(void) 1401 { 1402 if (!iamremote) 1403 (void) fprintf(stderr, "rcp: lost connection\n"); 1404 exit(1); 1405 } 1406 1407 1408 static void 1409 sink(int argc, char *argv[]) 1410 { 1411 char *cp; 1412 static BUF buffer; 1413 struct stat stb; 1414 struct timeval tv[2]; 1415 BUF *bp; 1416 off_t i, j; 1417 char ch, *targ, *why; 1418 int amt, count, exists, first, mask, mode; 1419 off_t size; 1420 int ofd, setimes, targisdir, wrerr; 1421 char *np, *vect[1], buf[RCP_BUFSIZE]; 1422 char *namebuf = NULL; 1423 size_t namebuf_sz = 0; 1424 size_t need; 1425 1426 #define atime tv[0] 1427 #define mtime tv[1] 1428 #define SCREWUP(str) { why = str; goto screwup; } 1429 1430 setimes = targisdir = 0; 1431 mask = umask(0); 1432 if (!pflag) 1433 (void) umask(mask); 1434 if (argc != 1) { 1435 error("rcp: ambiguous target\n"); 1436 exit(1); 1437 } 1438 targ = *argv; 1439 if (targetshouldbedirectory) 1440 verifydir(targ); 1441 (void) desrcpwrite(rem, "", 1); 1442 1443 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 1444 targisdir = 1; 1445 for (first = 1; ; first = 0) { 1446 cp = buf; 1447 if (desrcpread(rem, cp, 1) <= 0) { 1448 if (namebuf != NULL) 1449 free(namebuf); 1450 return; 1451 } 1452 1453 if (*cp++ == '\n') 1454 SCREWUP("unexpected <newline>"); 1455 do { 1456 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 1457 SCREWUP("lost connection"); 1458 *cp++ = ch; 1459 } while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n'); 1460 *cp = 0; 1461 1462 if (buf[0] == '\01' || buf[0] == '\02') { 1463 if (iamremote == 0) 1464 (void) write(STDERR_FILENO, buf + 1, 1465 strlen(buf + 1)); 1466 if (buf[0] == '\02') 1467 exit(1); 1468 errs++; 1469 continue; 1470 } 1471 if (buf[0] == 'E') { 1472 (void) desrcpwrite(rem, "", 1); 1473 if (namebuf != NULL) 1474 free(namebuf); 1475 return; 1476 } 1477 1478 if (ch == '\n') 1479 *--cp = 0; 1480 cp = buf; 1481 if (*cp == 'T') { 1482 setimes++; 1483 cp++; 1484 mtime.tv_sec = strtol(cp, &cp, 0); 1485 if (*cp++ != ' ') 1486 SCREWUP("mtime.sec not delimited"); 1487 mtime.tv_usec = strtol(cp, &cp, 0); 1488 if (*cp++ != ' ') 1489 SCREWUP("mtime.usec not delimited"); 1490 atime.tv_sec = strtol(cp, &cp, 0); 1491 if (*cp++ != ' ') 1492 SCREWUP("atime.sec not delimited"); 1493 atime.tv_usec = strtol(cp, &cp, 0); 1494 if (*cp++ != '\0') 1495 SCREWUP("atime.usec not delimited"); 1496 (void) desrcpwrite(rem, "", 1); 1497 continue; 1498 } 1499 if (*cp != 'C' && *cp != 'D') { 1500 /* 1501 * Check for the case "rcp remote:foo\* local:bar". 1502 * In this case, the line "No match." can be returned 1503 * by the shell before the rcp command on the remote is 1504 * executed so the ^Aerror_message convention isn't 1505 * followed. 1506 */ 1507 if (first) { 1508 error("%s\n", cp); 1509 exit(1); 1510 } 1511 SCREWUP("expected control record"); 1512 } 1513 mode = 0; 1514 for (++cp; cp < buf + 5; cp++) { 1515 if (*cp < '0' || *cp > '7') 1516 SCREWUP("bad mode"); 1517 mode = (mode << 3) | (*cp - '0'); 1518 } 1519 if (*cp++ != ' ') 1520 SCREWUP("mode not delimited"); 1521 size = 0; 1522 while (isdigit(*cp)) 1523 size = size * 10 + (*cp++ - '0'); 1524 if (*cp++ != ' ') 1525 SCREWUP("size not delimited"); 1526 if (targisdir) { 1527 need = strlen(targ) + sizeof ("/") + strlen(cp); 1528 if (need > namebuf_sz) { 1529 if ((namebuf = realloc(namebuf, need)) == 1530 NULL) { 1531 error("rcp: out of memory\n"); 1532 exit(1); 1533 } 1534 namebuf_sz = need; 1535 } 1536 (void) snprintf(namebuf, need, "%s%s%s", targ, 1537 *targ ? "/" : "", cp); 1538 np = namebuf; 1539 } else { 1540 np = targ; 1541 } 1542 1543 exists = stat(np, &stb) == 0; 1544 if (buf[0] == 'D') { 1545 if (exists) { 1546 if ((stb.st_mode&S_IFMT) != S_IFDIR) { 1547 if (aclflag | acl_aclflag) { 1548 /* 1549 * consume acl in the pipe 1550 * fd = -1 to indicate the 1551 * special case 1552 */ 1553 if (recvacl(-1, exists, pflag) 1554 == ACL_FAIL) { 1555 goto bad; 1556 } 1557 } 1558 errno = ENOTDIR; 1559 goto bad; 1560 } 1561 if (pflag) 1562 (void) chmod(np, mode); 1563 } else if (mkdir(np, mode) < 0) { 1564 if (aclflag) { 1565 /* consume acl in the pipe */ 1566 (void) recvacl(-1, exists, pflag); 1567 } 1568 goto bad; 1569 } 1570 1571 /* acl support for directories */ 1572 if (aclflag | acl_aclflag) { 1573 int dfd; 1574 1575 if ((dfd = open(np, O_RDONLY)) == -1) 1576 goto bad; 1577 1578 /* get acl and set it to ofd */ 1579 if (recvacl(dfd, exists, pflag) == ACL_FAIL) { 1580 (void) close(dfd); 1581 if (!exists) 1582 (void) rmdir(np); 1583 goto bad; 1584 } 1585 (void) close(dfd); 1586 } 1587 1588 vect[0] = np; 1589 sink(1, vect); 1590 if (setimes) { 1591 setimes = 0; 1592 if (utimes(np, tv) < 0) 1593 error("rcp: can't set " 1594 "times on %s: %s\n", 1595 np, strerror(errno)); 1596 } 1597 continue; 1598 } 1599 1600 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 1601 bad: 1602 error("rcp: %s: %s\n", np, strerror(errno)); 1603 continue; 1604 } 1605 1606 /* 1607 * If the output file exists we have to force zflag off 1608 * to avoid erroneously seeking past old data. 1609 */ 1610 zopen(ofd, zflag && !exists); 1611 1612 if (exists && pflag) 1613 (void) fchmod(ofd, mode); 1614 1615 (void) desrcpwrite(rem, "", 1); 1616 1617 /* 1618 * ACL support: receiving 1619 */ 1620 if (aclflag | acl_aclflag) { 1621 /* get acl and set it to ofd */ 1622 if (recvacl(ofd, exists, pflag) == ACL_FAIL) { 1623 (void) close(ofd); 1624 if (!exists) 1625 (void) unlink(np); 1626 continue; 1627 } 1628 } 1629 1630 if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) { 1631 (void) close(ofd); 1632 continue; 1633 } 1634 cp = bp->buf; 1635 count = 0; 1636 wrerr = 0; 1637 for (i = 0; i < size; i += RCP_BUFSIZE) { 1638 amt = RCP_BUFSIZE; 1639 if (i + amt > size) 1640 amt = size - i; 1641 count += amt; 1642 do { 1643 j = desrcpread(rem, cp, amt); 1644 if (j <= 0) { 1645 int sverrno = errno; 1646 1647 /* 1648 * Connection to supplier lost. 1649 * Truncate file to correspond 1650 * to amount already transferred. 1651 * 1652 * Note that we must call ftruncate() 1653 * before any call to error() (which 1654 * might result in a SIGPIPE and 1655 * sudden death before we have a chance 1656 * to correct the file's size). 1657 */ 1658 size = lseek(ofd, 0, SEEK_CUR); 1659 if ((ftruncate(ofd, size) == -1) && 1660 (errno != EINVAL) && 1661 (errno != EACCES)) 1662 #define TRUNCERR "rcp: can't truncate %s: %s\n" 1663 error(TRUNCERR, np, 1664 strerror(errno)); 1665 error("rcp: %s\n", 1666 j ? strerror(sverrno) : 1667 "dropped connection"); 1668 (void) close(ofd); 1669 exit(1); 1670 } 1671 amt -= j; 1672 cp += j; 1673 } while (amt > 0); 1674 if (count == bp->cnt) { 1675 cp = bp->buf; 1676 if (wrerr == 0 && 1677 zwrite(ofd, cp, count) < 0) 1678 wrerr++; 1679 count = 0; 1680 } 1681 } 1682 if (count != 0 && wrerr == 0 && 1683 zwrite(ofd, bp->buf, count) < 0) 1684 wrerr++; 1685 if (zclose(ofd) < 0) 1686 wrerr++; 1687 1688 if ((ftruncate(ofd, size) == -1) && (errno != EINVAL) && 1689 (errno != EACCES)) { 1690 error(TRUNCERR, np, strerror(errno)); 1691 } 1692 (void) close(ofd); 1693 (void) response(); 1694 if (setimes) { 1695 setimes = 0; 1696 if (utimes(np, tv) < 0) 1697 error("rcp: can't set times on %s: %s\n", 1698 np, strerror(errno)); 1699 } 1700 if (wrerr) 1701 error("rcp: %s: %s\n", np, strerror(errno)); 1702 else 1703 (void) desrcpwrite(rem, "", 1); 1704 } 1705 screwup: 1706 error("rcp: protocol screwup: %s\n", why); 1707 exit(1); 1708 } 1709 1710 #ifndef roundup 1711 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 1712 #endif /* !roundup */ 1713 1714 static BUF * 1715 allocbuf(BUF *bp, int fd, int blksize) 1716 { 1717 struct stat stb; 1718 int size; 1719 1720 if (fstat(fd, &stb) < 0) { 1721 error("rcp: fstat: %s\n", strerror(errno)); 1722 return (0); 1723 } 1724 size = roundup(stb.st_blksize, blksize); 1725 if (size == 0) 1726 size = blksize; 1727 if (bp->cnt < size) { 1728 if (bp->buf != 0) 1729 free(bp->buf); 1730 bp->buf = (char *)malloc((uint_t)size); 1731 if (!bp->buf) { 1732 error("rcp: malloc: out of memory\n"); 1733 return (0); 1734 } 1735 } 1736 bp->cnt = size; 1737 return (bp); 1738 } 1739 1740 static void 1741 usage(void) 1742 { 1743 (void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"), 1744 gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] " 1745 #ifdef DEBUG 1746 "[-D port] " 1747 #endif /* DEBUG */ 1748 "f1 f2; or:\n"), 1749 gettext("\trcp [-r] [-p] [-a] [-x] " 1750 #ifdef DEBUG 1751 "[-D port] " 1752 #endif /* DEBUG */ 1753 "[-k realm] [-PN / -PO] f1...fn d2\n")); 1754 exit(1); 1755 } 1756 1757 1758 /* 1759 * sparse file support 1760 */ 1761 1762 static off_t zbsize; 1763 static off_t zlastseek; 1764 1765 /* is it ok to try to create holes? */ 1766 static void 1767 zopen(int fd, int flag) 1768 { 1769 struct stat st; 1770 1771 zbsize = 0; 1772 zlastseek = 0; 1773 1774 if (flag && 1775 fstat(fd, &st) == 0 && 1776 (st.st_mode & S_IFMT) == S_IFREG) 1777 zbsize = st.st_blksize; 1778 } 1779 1780 /* write and/or seek */ 1781 static int 1782 zwrite(int fd, char *buf, int nbytes) 1783 { 1784 off_t block = zbsize ? zbsize : nbytes; 1785 1786 do { 1787 if (block > nbytes) 1788 block = nbytes; 1789 nbytes -= block; 1790 1791 if (!zbsize || notzero(buf, block)) { 1792 register int n, count = block; 1793 1794 do { 1795 if ((n = write(fd, buf, count)) < 0) 1796 return (-1); 1797 buf += n; 1798 } while ((count -= n) > 0); 1799 zlastseek = 0; 1800 } else { 1801 if (lseek(fd, (off_t)block, SEEK_CUR) < 0) 1802 return (-1); 1803 buf += block; 1804 zlastseek = 1; 1805 } 1806 } while (nbytes > 0); 1807 1808 return (0); 1809 } 1810 1811 /* write last byte of file if necessary */ 1812 static int 1813 zclose(int fd) 1814 { 1815 zbsize = 0; 1816 1817 if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 || 1818 zwrite(fd, "", 1) < 0)) 1819 return (-1); 1820 else 1821 return (0); 1822 } 1823 1824 /* return true if buffer is not all zeros */ 1825 static int 1826 notzero(char *p, int n) 1827 { 1828 register int result = 0; 1829 1830 while ((int)p & 3 && --n >= 0) 1831 result |= *p++; 1832 1833 while ((n -= 4 * sizeof (int)) >= 0) { 1834 /* LINTED */ 1835 result |= ((int *)p)[0]; 1836 /* LINTED */ 1837 result |= ((int *)p)[1]; 1838 /* LINTED */ 1839 result |= ((int *)p)[2]; 1840 /* LINTED */ 1841 result |= ((int *)p)[3]; 1842 if (result) 1843 return (result); 1844 p += 4 * sizeof (int); 1845 } 1846 n += 4 * sizeof (int); 1847 1848 while (--n >= 0) 1849 result |= *p++; 1850 1851 return (result); 1852 } 1853 1854 /* 1855 * New functions to support ACLs 1856 */ 1857 1858 /* 1859 * Get acl from f and send it over. 1860 * ACL record includes acl entry count, acl text length, and acl text. 1861 */ 1862 static int 1863 sendacl(int f) 1864 { 1865 int aclcnt; 1866 char *acltext; 1867 char buf[BUFSIZ]; 1868 acl_t *aclp; 1869 char acltype; 1870 int aclerror; 1871 int trivial; 1872 1873 1874 aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp); 1875 if (aclerror != 0) { 1876 error("can't retrieve ACL: %s \n", acl_strerror(aclerror)); 1877 return (ACL_FAIL); 1878 } 1879 1880 /* 1881 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0 1882 * then don't do the malloc and facl(fd, getcntcmd,...); 1883 * since the remote side doesn't support alternate style ACL's. 1884 */ 1885 1886 if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) { 1887 aclcnt = MIN_ACL_ENTRIES; 1888 acltype = 'A'; 1889 trivial = ACL_IS_TRIVIAL; 1890 } else { 1891 1892 aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0; 1893 1894 if (aclp) { 1895 acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A'; 1896 aclcnt = acl_cnt(aclp); 1897 trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL); 1898 } else { 1899 acltype = 'A'; 1900 aclcnt = MIN_ACL_ENTRIES; 1901 trivial = ACL_IS_TRIVIAL; 1902 } 1903 1904 } 1905 1906 /* send the acl count over */ 1907 (void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt); 1908 (void) desrcpwrite(rem, buf, strlen(buf)); 1909 1910 /* 1911 * only send acl when we have an aclp, which would 1912 * imply its not trivial. 1913 */ 1914 if (aclp && (trivial != ACL_IS_TRIVIAL)) { 1915 acltext = acl_totext(aclp, 0); 1916 if (acltext == NULL) { 1917 error("rcp: failed to convert to text\n"); 1918 acl_free(aclp); 1919 return (ACL_FAIL); 1920 } 1921 1922 /* send ACLs over: send the length first */ 1923 (void) snprintf(buf, sizeof (buf), "%c%d\n", 1924 acltype, strlen(acltext)); 1925 1926 (void) desrcpwrite(rem, buf, strlen(buf)); 1927 (void) desrcpwrite(rem, acltext, strlen(acltext)); 1928 free(acltext); 1929 if (response() < 0) { 1930 acl_free(aclp); 1931 return (ACL_FAIL); 1932 } 1933 1934 } 1935 1936 if (aclp) 1937 acl_free(aclp); 1938 return (ACL_OK); 1939 } 1940 1941 /* 1942 * Use this routine to get acl entry count and acl text size (in bytes) 1943 */ 1944 static int 1945 getaclinfo(int *cnt, int *acltype) 1946 { 1947 char buf[BUFSIZ]; 1948 char *cp; 1949 char ch; 1950 1951 /* get acl count */ 1952 cp = buf; 1953 if (desrcpread(rem, cp, 1) <= 0) 1954 return (ACL_FAIL); 1955 1956 switch (*cp++) { 1957 case 'A': 1958 *acltype = 0; 1959 break; 1960 case 'Z': 1961 *acltype = 1; 1962 break; 1963 default: 1964 error("rcp: expect an ACL record, but got %c\n", *cp); 1965 return (ACL_FAIL); 1966 } 1967 do { 1968 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) { 1969 error("rcp: lost connection ..\n"); 1970 return (ACL_FAIL); 1971 } 1972 *cp++ = ch; 1973 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 1974 if (ch != '\n') { 1975 error("rcp: ACL record corrupted \n"); 1976 return (ACL_FAIL); 1977 } 1978 cp = &buf[1]; 1979 *cnt = strtol(cp, &cp, 0); 1980 if (*cp != '\n') { 1981 error("rcp: ACL record corrupted \n"); 1982 return (ACL_FAIL); 1983 } 1984 return (ACL_OK); 1985 } 1986 1987 1988 /* 1989 * Receive acl from the pipe and set it to f 1990 */ 1991 static int 1992 recvacl(int f, int exists, int preserve) 1993 { 1994 int aclcnt; /* acl entry count */ 1995 int aclsize; /* acl text length */ 1996 int j; 1997 char *tp; 1998 char *acltext; /* external format */ 1999 acl_t *aclp; 2000 int acltype; 2001 int min_entries; 2002 int aclerror; 2003 2004 /* get acl count */ 2005 if (getaclinfo(&aclcnt, &acltype) != ACL_OK) 2006 return (ACL_FAIL); 2007 2008 if (acltype == 0) { 2009 min_entries = MIN_ACL_ENTRIES; 2010 } else { 2011 min_entries = 1; 2012 } 2013 2014 if (aclcnt > min_entries) { 2015 /* get acl text size */ 2016 if (getaclinfo(&aclsize, &acltype) != ACL_OK) 2017 return (ACL_FAIL); 2018 if ((acltext = malloc(aclsize + 1)) == NULL) { 2019 error("rcp: cant allocate memory: %d\n", aclsize); 2020 return (ACL_FAIL); 2021 } 2022 2023 tp = acltext; 2024 do { 2025 j = desrcpread(rem, tp, aclsize); 2026 if (j <= 0) { 2027 error("rcp: %s\n", j ? strerror(errno) : 2028 "dropped connection"); 2029 exit(1); 2030 } 2031 aclsize -= j; 2032 tp += j; 2033 } while (aclsize > 0); 2034 *tp = '\0'; 2035 2036 if (preserve || !exists) { 2037 aclerror = acl_fromtext(acltext, &aclp); 2038 if (aclerror != 0) { 2039 error("rcp: failed to parse acl : %s\n", 2040 acl_strerror(aclerror)); 2041 free(acltext); 2042 return (ACL_FAIL); 2043 } 2044 2045 if (f != -1) { 2046 if (facl_set(f, aclp) < 0) { 2047 error("rcp: failed to set acl\n"); 2048 acl_free(aclp); 2049 free(acltext); 2050 return (ACL_FAIL); 2051 } 2052 } 2053 /* -1 means that just consume the data in the pipe */ 2054 acl_free(aclp); 2055 } 2056 free(acltext); 2057 (void) desrcpwrite(rem, "", 1); 2058 } 2059 return (ACL_OK); 2060 } 2061 2062 2063 static char * 2064 search_char(unsigned char *cp, unsigned char chr) 2065 { 2066 int len; 2067 2068 while (*cp) { 2069 if (*cp == chr) 2070 return ((char *)cp); 2071 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 2072 len = 1; 2073 cp += len; 2074 } 2075 return (0); 2076 } 2077 2078 2079 static int 2080 desrcpread(int fd, char *buf, int len) 2081 { 2082 return ((int)desread(fd, buf, len, 0)); 2083 } 2084 2085 static int 2086 desrcpwrite(int fd, char *buf, int len) 2087 { 2088 /* 2089 * Note that rcp depends on the same file descriptor being both 2090 * input and output to the remote side. This is bogus, especially 2091 * when rcp is being run by a rsh that pipes. Fix it here because 2092 * it would require significantly more work in other places. 2093 * --hartmans 1/96 2094 */ 2095 2096 if (fd == 0) 2097 fd = 1; 2098 return ((int)deswrite(fd, buf, len, 0)); 2099 } 2100 2101 static char ** 2102 save_argv(int argc, char **argv) 2103 { 2104 int i; 2105 2106 char **local_argv = (char **)calloc((unsigned)argc + 1, 2107 (unsigned)sizeof (char *)); 2108 2109 /* 2110 * allocate an extra pointer, so that it is initialized to NULL and 2111 * execv() will work 2112 */ 2113 for (i = 0; i < argc; i++) { 2114 local_argv[i] = strsave(argv[i]); 2115 } 2116 2117 return (local_argv); 2118 } 2119 2120 #define SIZEOF_INADDR sizeof (struct in_addr) 2121 2122 static void 2123 answer_auth(char *config_file, char *ccache_file) 2124 { 2125 krb5_data pname_data, msg; 2126 krb5_creds creds, *new_creds; 2127 krb5_ccache cc; 2128 krb5_auth_context auth_context = NULL; 2129 2130 if (config_file) { 2131 const char *filenames[2]; 2132 2133 filenames[1] = NULL; 2134 filenames[0] = config_file; 2135 if (krb5_set_config_files(bsd_context, filenames)) 2136 exit(1); 2137 } 2138 (void) memset((char *)&creds, 0, sizeof (creds)); 2139 2140 if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data)) 2141 exit(1); 2142 2143 if (krb5_read_message(bsd_context, (krb5_pointer) &rem, 2144 &creds.second_ticket)) 2145 exit(1); 2146 2147 if (ccache_file == NULL) { 2148 if (krb5_cc_default(bsd_context, &cc)) 2149 exit(1); 2150 } else { 2151 if (krb5_cc_resolve(bsd_context, ccache_file, &cc)) 2152 exit(1); 2153 } 2154 2155 if (krb5_cc_get_principal(bsd_context, cc, &creds.client)) 2156 exit(1); 2157 2158 if (krb5_parse_name(bsd_context, pname_data.data, &creds.server)) 2159 exit(1); 2160 2161 krb5_xfree(pname_data.data); 2162 if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds, 2163 &new_creds)) 2164 exit(1); 2165 2166 if (krb5_mk_req_extended(bsd_context, &auth_context, 2167 AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg)) 2168 exit(1); 2169 2170 if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) { 2171 krb5_xfree(msg.data); 2172 exit(1); 2173 } 2174 /* setup eblock for des_read and write */ 2175 (void) krb5_copy_keyblock(bsd_context, 2176 &new_creds->keyblock, &session_key); 2177 2178 /* OK process key */ 2179 eblock.crypto_entry = session_key->enctype; 2180 eblock.key = (krb5_keyblock *)session_key; 2181 2182 init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL, 2183 &desinbuf, &desoutbuf, CLIENT, &eblock); 2184 /* cleanup */ 2185 krb5_free_cred_contents(bsd_context, &creds); 2186 krb5_free_creds(bsd_context, new_creds); 2187 krb5_xfree(msg.data); 2188 } 2189 2190 2191 static void 2192 try_normal_rcp(int cur_argc, char **cur_argv) 2193 { 2194 char *target; 2195 2196 /* 2197 * Reset all KRB5 relevant flags and set the 2198 * cmd-buffer so that normal rcp works 2199 */ 2200 krb5auth_flag = encrypt_flag = encrypt_done = 0; 2201 cmd = cmd_orig; 2202 cmd_sunw = cmd_sunw_orig; 2203 2204 if (cur_argc < 2) 2205 usage(); 2206 2207 if (cur_argc > 2) 2208 targetshouldbedirectory = 1; 2209 2210 rem = -1; 2211 2212 prev_argc = cur_argc; 2213 prev_argv = save_argv(cur_argc, cur_argv); 2214 2215 (void) init_service(krb5auth_flag); 2216 2217 if (target = colon(cur_argv[cur_argc - 1])) { 2218 toremote(target, cur_argc, cur_argv); 2219 } else { 2220 tolocal(cur_argc, cur_argv); 2221 if (targetshouldbedirectory) 2222 verifydir(cur_argv[cur_argc - 1]); 2223 } 2224 exit(errs); 2225 /* NOTREACHED */ 2226 } 2227 2228 2229 static int 2230 init_service(int krb5flag) 2231 { 2232 struct servent *sp; 2233 boolean_t success = B_FALSE; 2234 2235 if (krb5flag > 0) { 2236 sp = getservbyname("kshell", "tcp"); 2237 if (sp == NULL) { 2238 (void) fprintf(stderr, 2239 gettext("rcp: kshell/tcp: unknown service.\n" 2240 "trying normal shell/tcp service\n")); 2241 } else { 2242 portnumber = sp->s_port; 2243 success = B_TRUE; 2244 } 2245 } else { 2246 portnumber = htons(IPPORT_CMDSERVER); 2247 success = B_TRUE; 2248 } 2249 return (success); 2250 } 2251 2252 /*PRINTFLIKE1*/ 2253 static void 2254 error(char *fmt, ...) 2255 { 2256 va_list ap; 2257 char buf[RCP_BUFSIZE]; 2258 char *cp = buf; 2259 2260 va_start(ap, fmt); 2261 errs++; 2262 *cp++ = 1; 2263 (void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap); 2264 va_end(ap); 2265 2266 (void) desrcpwrite(rem, buf, strlen(buf)); 2267 if (iamremote == 0) 2268 (void) write(2, buf + 1, strlen(buf + 1)); 2269 } 2270 2271 static void 2272 addargs(char **arglist, ...) 2273 { 2274 va_list ap; 2275 int i = 0; 2276 char *pm; 2277 2278 va_start(ap, arglist); 2279 while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL) 2280 if (strcmp(pm, "")) 2281 arglist[i++] = pm; 2282 arglist[i] = NULL; 2283 va_end(ap); 2284 } 2285