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: 1115 notreg: 1116 (void) close(f); 1117 error("rcp: %s: not a plain file\n", name); 1118 continue; 1119 } 1120 last = rindex(name, '/'); 1121 if (last == 0) 1122 last = name; 1123 else 1124 last++; 1125 if (pflag) { 1126 time_t mtime, atime; 1127 time_t now; 1128 1129 /* 1130 * Make it compatible with possible future 1131 * versions expecting microseconds. 1132 */ 1133 mtime = stb.st_mtime; 1134 atime = stb.st_atime; 1135 1136 if ((mtime < 0) || (atime < 0)) { 1137 now = time(NULL); 1138 1139 if (mtime < 0) { 1140 mtime = now; 1141 error("negative modification time on " 1142 "%s; not preserving\n", name); 1143 } 1144 if (atime < 0) { 1145 atime = now; 1146 error("negative access time on " 1147 "%s; not preserving\n", name); 1148 } 1149 } 1150 (void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n", 1151 mtime, atime); 1152 (void) desrcpwrite(rem, buf, strlen(buf)); 1153 if (response() < 0) { 1154 (void) close(f); 1155 continue; 1156 } 1157 } 1158 (void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n", 1159 (uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size, 1160 last); 1161 (void) desrcpwrite(rem, buf, strlen(buf)); 1162 if (response() < 0) { 1163 (void) close(f); 1164 continue; 1165 } 1166 1167 /* ACL support: send */ 1168 if (aclflag | acl_aclflag) { 1169 /* get acl from f and send it over */ 1170 if (sendacl(f) == ACL_FAIL) { 1171 (void) close(f); 1172 continue; 1173 } 1174 } 1175 if ((krb5auth_flag > 0) || (iamremote == 1)) { 1176 bp = allocbuf(&buffer, f, RCP_BUFSIZE); 1177 if (bp == NULLBUF) { 1178 (void) close(f); 1179 continue; 1180 } 1181 readerr = 0; 1182 for (i = 0; i < stb.st_size; i += bp->cnt) { 1183 amt = bp->cnt; 1184 if (i + amt > stb.st_size) 1185 amt = stb.st_size - i; 1186 if (readerr == 0 && 1187 read(f, bp->buf, amt) != amt) 1188 readerr = errno; 1189 (void) desrcpwrite(rem, bp->buf, amt); 1190 } 1191 (void) close(f); 1192 if (readerr == 0) 1193 (void) desrcpwrite(rem, "", 1); 1194 else 1195 error("rcp: %s: %s\n", name, 1196 error_message(readerr)); 1197 } else { 1198 cnt = off = 0; 1199 size = stb.st_size; 1200 while (size != 0) { 1201 amt = MIN(size, SENDFILE_SIZE); 1202 cnt = sendfile(rem, f, &off, amt); 1203 if (cnt == -1) { 1204 if (errno == EINTR) { 1205 continue; 1206 } else { 1207 break; 1208 } 1209 } 1210 if (cnt == 0) 1211 break; 1212 size -= cnt; 1213 } 1214 if (cnt < 0) { 1215 error("rcp: %s: %s\n", name, strerror(errno)); 1216 } else if (cnt == 0 && size != 0) { 1217 error("rcp: %s: unexpected end of file\n", 1218 name); 1219 lingerbuf.l_onoff = 1; 1220 lingerbuf.l_linger = 0; 1221 (void) setsockopt(rem, SOL_SOCKET, SO_LINGER, 1222 &lingerbuf, sizeof (lingerbuf)); 1223 /* 1224 * When response() (see below) is invoked it 1225 * tries to read data from closed handle which 1226 * triggers error and lostconn() function. 1227 * lostconn() terminates the program with 1228 * appropriate message. 1229 */ 1230 (void) close(rem); 1231 rem = -1; 1232 } else { 1233 (void) write(rem, "", 1); 1234 } 1235 (void) close(f); 1236 } 1237 (void) response(); 1238 } 1239 } 1240 1241 1242 static void 1243 rsource(char *name, struct stat *statp) 1244 { 1245 DIR *d; 1246 struct dirent *dp; 1247 char *last, *vect[1]; 1248 char path[MAXPATHLEN]; 1249 1250 if (!(d = opendir(name))) { 1251 error("rcp: %s: %s\n", name, strerror(errno)); 1252 return; 1253 } 1254 last = rindex(name, '/'); 1255 if (last == 0) 1256 last = name; 1257 else 1258 last++; 1259 if (pflag) { 1260 (void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n", 1261 statp->st_mtime, statp->st_atime); 1262 (void) desrcpwrite(rem, path, strlen(path)); 1263 if (response() < 0) { 1264 (void) closedir(d); 1265 return; 1266 } 1267 } 1268 (void) snprintf(path, sizeof (path), "D%04o %d %s\n", 1269 (uint_t)(statp->st_mode & 07777), 0, last); 1270 (void) desrcpwrite(rem, path, strlen(path)); 1271 1272 /* acl support for directory */ 1273 if (aclflag) { 1274 /* get acl from f and send it over */ 1275 if (sendacl(d->dd_fd) == ACL_FAIL) { 1276 (void) closedir(d); 1277 return; 1278 } 1279 } 1280 1281 if (response() < 0) { 1282 (void) closedir(d); 1283 return; 1284 } 1285 1286 while (dp = readdir(d)) { 1287 if (dp->d_ino == 0) 1288 continue; 1289 if ((strcmp(dp->d_name, ".") == 0) || 1290 (strcmp(dp->d_name, "..") == 0)) 1291 continue; 1292 if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >= 1293 MAXPATHLEN - 1) { 1294 error("%s/%s: name too long.\n", name, dp->d_name); 1295 continue; 1296 } 1297 (void) snprintf(path, sizeof (path), "%s/%s", 1298 name, dp->d_name); 1299 vect[0] = path; 1300 source(1, vect); 1301 } 1302 (void) closedir(d); 1303 (void) desrcpwrite(rem, "E\n", 2); 1304 (void) response(); 1305 } 1306 1307 static int 1308 response(void) 1309 { 1310 register char *cp; 1311 char ch, resp, rbuf[RCP_BUFSIZE]; 1312 1313 if (desrcpread(rem, &resp, 1) != 1) 1314 lostconn(); 1315 cp = rbuf; 1316 switch (resp) { 1317 case 0: /* ok */ 1318 return (0); 1319 default: 1320 *cp++ = resp; 1321 /* FALLTHROUGH */ 1322 case 1: /* error, followed by err msg */ 1323 case 2: /* fatal error, "" */ 1324 do { 1325 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 1326 lostconn(); 1327 *cp++ = ch; 1328 } while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n'); 1329 1330 if (!iamremote) 1331 (void) write(STDERR_FILENO, rbuf, cp - rbuf); 1332 ++errs; 1333 if (resp == 1) 1334 return (-1); 1335 exit(1); 1336 } 1337 /*NOTREACHED*/ 1338 } 1339 1340 static void 1341 lostconn(void) 1342 { 1343 if (!iamremote) 1344 (void) fprintf(stderr, "rcp: lost connection\n"); 1345 exit(1); 1346 } 1347 1348 1349 static void 1350 sink(int argc, char *argv[]) 1351 { 1352 char *cp; 1353 static BUF buffer; 1354 struct stat stb; 1355 struct timeval tv[2]; 1356 BUF *bp; 1357 off_t i, j; 1358 char ch, *targ, *why; 1359 int amt, count, exists, first, mask, mode; 1360 off_t size; 1361 int ofd, setimes, targisdir, wrerr; 1362 char *np, *vect[1], buf[RCP_BUFSIZE]; 1363 char *namebuf = NULL; 1364 size_t namebuf_sz = 0; 1365 size_t need; 1366 1367 #define atime tv[0] 1368 #define mtime tv[1] 1369 #define SCREWUP(str) { why = str; goto screwup; } 1370 1371 setimes = targisdir = 0; 1372 mask = umask(0); 1373 if (!pflag) 1374 (void) umask(mask); 1375 if (argc != 1) { 1376 error("rcp: ambiguous target\n"); 1377 exit(1); 1378 } 1379 targ = *argv; 1380 if (targetshouldbedirectory) 1381 verifydir(targ); 1382 (void) desrcpwrite(rem, "", 1); 1383 1384 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 1385 targisdir = 1; 1386 for (first = 1; ; first = 0) { 1387 cp = buf; 1388 if (desrcpread(rem, cp, 1) <= 0) { 1389 if (namebuf != NULL) 1390 free(namebuf); 1391 return; 1392 } 1393 1394 if (*cp++ == '\n') 1395 SCREWUP("unexpected <newline>"); 1396 do { 1397 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 1398 SCREWUP("lost connection"); 1399 *cp++ = ch; 1400 } while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n'); 1401 *cp = 0; 1402 1403 if (buf[0] == '\01' || buf[0] == '\02') { 1404 if (iamremote == 0) 1405 (void) write(STDERR_FILENO, buf + 1, 1406 strlen(buf + 1)); 1407 if (buf[0] == '\02') 1408 exit(1); 1409 errs++; 1410 continue; 1411 } 1412 if (buf[0] == 'E') { 1413 (void) desrcpwrite(rem, "", 1); 1414 if (namebuf != NULL) 1415 free(namebuf); 1416 return; 1417 } 1418 1419 if (ch == '\n') 1420 *--cp = 0; 1421 cp = buf; 1422 if (*cp == 'T') { 1423 setimes++; 1424 cp++; 1425 mtime.tv_sec = strtol(cp, &cp, 0); 1426 if (*cp++ != ' ') 1427 SCREWUP("mtime.sec not delimited"); 1428 mtime.tv_usec = strtol(cp, &cp, 0); 1429 if (*cp++ != ' ') 1430 SCREWUP("mtime.usec not delimited"); 1431 atime.tv_sec = strtol(cp, &cp, 0); 1432 if (*cp++ != ' ') 1433 SCREWUP("atime.sec not delimited"); 1434 atime.tv_usec = strtol(cp, &cp, 0); 1435 if (*cp++ != '\0') 1436 SCREWUP("atime.usec not delimited"); 1437 (void) desrcpwrite(rem, "", 1); 1438 continue; 1439 } 1440 if (*cp != 'C' && *cp != 'D') { 1441 /* 1442 * Check for the case "rcp remote:foo\* local:bar". 1443 * In this case, the line "No match." can be returned 1444 * by the shell before the rcp command on the remote is 1445 * executed so the ^Aerror_message convention isn't 1446 * followed. 1447 */ 1448 if (first) { 1449 error("%s\n", cp); 1450 exit(1); 1451 } 1452 SCREWUP("expected control record"); 1453 } 1454 mode = 0; 1455 for (++cp; cp < buf + 5; cp++) { 1456 if (*cp < '0' || *cp > '7') 1457 SCREWUP("bad mode"); 1458 mode = (mode << 3) | (*cp - '0'); 1459 } 1460 if (*cp++ != ' ') 1461 SCREWUP("mode not delimited"); 1462 size = 0; 1463 while (isdigit(*cp)) 1464 size = size * 10 + (*cp++ - '0'); 1465 if (*cp++ != ' ') 1466 SCREWUP("size not delimited"); 1467 if (targisdir) { 1468 need = strlen(targ) + sizeof ("/") + strlen(cp); 1469 if (need > namebuf_sz) { 1470 if ((namebuf = realloc(namebuf, need)) == NULL) { 1471 error("rcp: out of memory\n"); 1472 exit(1); 1473 } 1474 namebuf_sz = need; 1475 } 1476 (void) snprintf(namebuf, need, "%s%s%s", targ, 1477 *targ ? "/" : "", cp); 1478 np = namebuf; 1479 } else { 1480 np = targ; 1481 } 1482 1483 exists = stat(np, &stb) == 0; 1484 if (buf[0] == 'D') { 1485 if (exists) { 1486 if ((stb.st_mode&S_IFMT) != S_IFDIR) { 1487 if (aclflag | acl_aclflag) { 1488 /* 1489 * consume acl in the pipe 1490 * fd = -1 to indicate the 1491 * special case 1492 */ 1493 if (recvacl(-1, exists, pflag) 1494 == ACL_FAIL) { 1495 goto bad; 1496 } 1497 } 1498 errno = ENOTDIR; 1499 goto bad; 1500 } 1501 if (pflag) 1502 (void) chmod(np, mode); 1503 } else if (mkdir(np, mode) < 0) { 1504 if (aclflag) { 1505 /* consume acl in the pipe */ 1506 (void) recvacl(-1, exists, pflag); 1507 } 1508 goto bad; 1509 } 1510 1511 /* acl support for directories */ 1512 if (aclflag | acl_aclflag) { 1513 int dfd; 1514 1515 if ((dfd = open(np, O_RDONLY)) == -1) 1516 goto bad; 1517 1518 /* get acl and set it to ofd */ 1519 if (recvacl(dfd, exists, pflag) == ACL_FAIL) { 1520 (void) close(dfd); 1521 if (!exists) 1522 (void) rmdir(np); 1523 goto bad; 1524 } 1525 (void) close(dfd); 1526 } 1527 1528 vect[0] = np; 1529 sink(1, vect); 1530 if (setimes) { 1531 setimes = 0; 1532 if (utimes(np, tv) < 0) 1533 error("rcp: can't set times on %s: %s\n", 1534 np, strerror(errno)); 1535 } 1536 continue; 1537 } 1538 1539 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 1540 bad: 1541 error("rcp: %s: %s\n", np, strerror(errno)); 1542 continue; 1543 } 1544 1545 /* 1546 * If the output file exists we have to force zflag off 1547 * to avoid erroneously seeking past old data. 1548 */ 1549 zopen(ofd, zflag && !exists); 1550 1551 if (exists && pflag) 1552 (void) fchmod(ofd, mode); 1553 1554 (void) desrcpwrite(rem, "", 1); 1555 1556 /* 1557 * ACL support: receiving 1558 */ 1559 if (aclflag | acl_aclflag) { 1560 /* get acl and set it to ofd */ 1561 if (recvacl(ofd, exists, pflag) == ACL_FAIL) { 1562 (void) close(ofd); 1563 if (!exists) 1564 (void) unlink(np); 1565 continue; 1566 } 1567 } 1568 1569 if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) { 1570 (void) close(ofd); 1571 continue; 1572 } 1573 cp = bp->buf; 1574 count = 0; 1575 wrerr = 0; 1576 for (i = 0; i < size; i += RCP_BUFSIZE) { 1577 amt = RCP_BUFSIZE; 1578 if (i + amt > size) 1579 amt = size - i; 1580 count += amt; 1581 do { 1582 j = desrcpread(rem, cp, amt); 1583 if (j <= 0) { 1584 int sverrno = errno; 1585 1586 /* 1587 * Connection to supplier lost. 1588 * Truncate file to correspond 1589 * to amount already transferred. 1590 * 1591 * Note that we must call ftruncate() 1592 * before any call to error() (which 1593 * might result in a SIGPIPE and 1594 * sudden death before we have a chance 1595 * to correct the file's size). 1596 */ 1597 size = lseek(ofd, 0, SEEK_CUR); 1598 if ((ftruncate(ofd, size) == -1) && 1599 (errno != EINVAL) && 1600 (errno != EACCES)) 1601 #define TRUNCERR "rcp: can't truncate %s: %s\n" 1602 error(TRUNCERR, np, 1603 strerror(errno)); 1604 error("rcp: %s\n", 1605 j ? strerror(sverrno) : 1606 "dropped connection"); 1607 (void) close(ofd); 1608 exit(1); 1609 } 1610 amt -= j; 1611 cp += j; 1612 } while (amt > 0); 1613 if (count == bp->cnt) { 1614 cp = bp->buf; 1615 if (wrerr == 0 && 1616 zwrite(ofd, cp, count) < 0) 1617 wrerr++; 1618 count = 0; 1619 } 1620 } 1621 if (count != 0 && wrerr == 0 && 1622 zwrite(ofd, bp->buf, count) < 0) 1623 wrerr++; 1624 if (zclose(ofd) < 0) 1625 wrerr++; 1626 1627 1628 if ((ftruncate(ofd, size) == -1) && (errno != EINVAL) && 1629 (errno != EACCES)) { 1630 error(TRUNCERR, np, strerror(errno)); 1631 } 1632 (void) close(ofd); 1633 (void) response(); 1634 if (setimes) { 1635 setimes = 0; 1636 if (utimes(np, tv) < 0) 1637 error("rcp: can't set times on %s: %s\n", 1638 np, strerror(errno)); 1639 } 1640 if (wrerr) 1641 error("rcp: %s: %s\n", np, strerror(errno)); 1642 else 1643 (void) desrcpwrite(rem, "", 1); 1644 } 1645 screwup: 1646 error("rcp: protocol screwup: %s\n", why); 1647 exit(1); 1648 } 1649 1650 #ifndef roundup 1651 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 1652 #endif /* !roundup */ 1653 1654 static BUF * 1655 allocbuf(BUF *bp, int fd, int blksize) 1656 { 1657 struct stat stb; 1658 int size; 1659 1660 if (fstat(fd, &stb) < 0) { 1661 error("rcp: fstat: %s\n", strerror(errno)); 1662 return (0); 1663 } 1664 size = roundup(stb.st_blksize, blksize); 1665 if (size == 0) 1666 size = blksize; 1667 if (bp->cnt < size) { 1668 if (bp->buf != 0) 1669 free(bp->buf); 1670 bp->buf = (char *)malloc((uint_t)size); 1671 if (!bp->buf) { 1672 error("rcp: malloc: out of memory\n"); 1673 return (0); 1674 } 1675 } 1676 bp->cnt = size; 1677 return (bp); 1678 } 1679 1680 static void 1681 usage(void) 1682 { 1683 (void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"), 1684 gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] " 1685 #ifdef DEBUG 1686 "[-D port] " 1687 #endif /* DEBUG */ 1688 "f1 f2; or:\n"), 1689 gettext("\trcp [-r] [-p] [-a] [-x] " 1690 #ifdef DEBUG 1691 "[-D port] " 1692 #endif /* DEBUG */ 1693 "[-k realm] [-PN / -PO] f1...fn d2\n")); 1694 exit(1); 1695 } 1696 1697 1698 /* 1699 * sparse file support 1700 */ 1701 1702 static off_t zbsize; 1703 static off_t zlastseek; 1704 1705 /* is it ok to try to create holes? */ 1706 static void 1707 zopen(int fd, int flag) 1708 { 1709 struct stat st; 1710 1711 zbsize = 0; 1712 zlastseek = 0; 1713 1714 if (flag && 1715 fstat(fd, &st) == 0 && 1716 (st.st_mode & S_IFMT) == S_IFREG) 1717 zbsize = st.st_blksize; 1718 } 1719 1720 /* write and/or seek */ 1721 static int 1722 zwrite(int fd, char *buf, int nbytes) 1723 { 1724 off_t block = zbsize ? zbsize : nbytes; 1725 1726 do { 1727 if (block > nbytes) 1728 block = nbytes; 1729 nbytes -= block; 1730 1731 if (!zbsize || notzero(buf, block)) { 1732 register int n, count = block; 1733 1734 do { 1735 if ((n = write(fd, buf, count)) < 0) 1736 return (-1); 1737 buf += n; 1738 } while ((count -= n) > 0); 1739 zlastseek = 0; 1740 } else { 1741 if (lseek(fd, (off_t)block, SEEK_CUR) < 0) 1742 return (-1); 1743 buf += block; 1744 zlastseek = 1; 1745 } 1746 } while (nbytes > 0); 1747 1748 return (0); 1749 } 1750 1751 /* write last byte of file if necessary */ 1752 static int 1753 zclose(int fd) 1754 { 1755 zbsize = 0; 1756 1757 if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 || 1758 zwrite(fd, "", 1) < 0)) 1759 return (-1); 1760 else 1761 return (0); 1762 } 1763 1764 /* return true if buffer is not all zeros */ 1765 static int 1766 notzero(char *p, int n) 1767 { 1768 register int result = 0; 1769 1770 while ((int)p & 3 && --n >= 0) 1771 result |= *p++; 1772 1773 while ((n -= 4 * sizeof (int)) >= 0) { 1774 /* LINTED */ 1775 result |= ((int *)p)[0]; 1776 /* LINTED */ 1777 result |= ((int *)p)[1]; 1778 /* LINTED */ 1779 result |= ((int *)p)[2]; 1780 /* LINTED */ 1781 result |= ((int *)p)[3]; 1782 if (result) 1783 return (result); 1784 p += 4 * sizeof (int); 1785 } 1786 n += 4 * sizeof (int); 1787 1788 while (--n >= 0) 1789 result |= *p++; 1790 1791 return (result); 1792 } 1793 1794 /* 1795 * New functions to support ACLs 1796 */ 1797 1798 /* 1799 * Get acl from f and send it over. 1800 * ACL record includes acl entry count, acl text length, and acl text. 1801 */ 1802 static int 1803 sendacl(int f) 1804 { 1805 int aclcnt; 1806 char *acltext; 1807 char buf[BUFSIZ]; 1808 acl_t *aclp; 1809 char acltype; 1810 int aclerror; 1811 int trivial; 1812 1813 1814 aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp); 1815 if (aclerror != 0) { 1816 error("can't retrieve ACL: %s \n", acl_strerror(aclerror)); 1817 return (ACL_FAIL); 1818 } 1819 1820 /* 1821 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0 1822 * then don't do the malloc and facl(fd, getcntcmd,...); 1823 * since the remote side doesn't support alternate style ACL's. 1824 */ 1825 1826 if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) { 1827 aclcnt = MIN_ACL_ENTRIES; 1828 acltype = 'A'; 1829 trivial = ACL_IS_TRIVIAL; 1830 } else { 1831 1832 aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0; 1833 1834 if (aclp) { 1835 acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A'; 1836 aclcnt = acl_cnt(aclp); 1837 trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL); 1838 } else { 1839 acltype = 'A'; 1840 aclcnt = MIN_ACL_ENTRIES; 1841 trivial = ACL_IS_TRIVIAL; 1842 } 1843 1844 } 1845 1846 /* send the acl count over */ 1847 (void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt); 1848 (void) desrcpwrite(rem, buf, strlen(buf)); 1849 1850 /* 1851 * only send acl when we have an aclp, which would 1852 * imply its not trivial. 1853 */ 1854 if (aclp && (trivial != ACL_IS_TRIVIAL)) { 1855 acltext = acl_totext(aclp, 0); 1856 if (acltext == NULL) { 1857 error("rcp: failed to convert to text\n"); 1858 acl_free(aclp); 1859 return (ACL_FAIL); 1860 } 1861 1862 /* send ACLs over: send the length first */ 1863 (void) snprintf(buf, sizeof (buf), "%c%d\n", 1864 acltype, strlen(acltext)); 1865 1866 (void) desrcpwrite(rem, buf, strlen(buf)); 1867 (void) desrcpwrite(rem, acltext, strlen(acltext)); 1868 free(acltext); 1869 if (response() < 0) { 1870 acl_free(aclp); 1871 return (ACL_FAIL); 1872 } 1873 1874 } 1875 1876 if (aclp) 1877 acl_free(aclp); 1878 return (ACL_OK); 1879 } 1880 1881 /* 1882 * Use this routine to get acl entry count and acl text size (in bytes) 1883 */ 1884 static int 1885 getaclinfo(int *cnt, int *acltype) 1886 { 1887 char buf[BUFSIZ]; 1888 char *cp; 1889 char ch; 1890 1891 /* get acl count */ 1892 cp = buf; 1893 if (desrcpread(rem, cp, 1) <= 0) 1894 return (ACL_FAIL); 1895 1896 switch (*cp++) { 1897 case 'A': 1898 *acltype = 0; 1899 break; 1900 case 'Z': 1901 *acltype = 1; 1902 break; 1903 default: 1904 error("rcp: expect an ACL record, but got %c\n", *cp); 1905 return (ACL_FAIL); 1906 } 1907 do { 1908 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) { 1909 error("rcp: lost connection ..\n"); 1910 return (ACL_FAIL); 1911 } 1912 *cp++ = ch; 1913 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 1914 if (ch != '\n') { 1915 error("rcp: ACL record corrupted \n"); 1916 return (ACL_FAIL); 1917 } 1918 cp = &buf[1]; 1919 *cnt = strtol(cp, &cp, 0); 1920 if (*cp != '\n') { 1921 error("rcp: ACL record corrupted \n"); 1922 return (ACL_FAIL); 1923 } 1924 return (ACL_OK); 1925 } 1926 1927 1928 /* 1929 * Receive acl from the pipe and set it to f 1930 */ 1931 static int 1932 recvacl(int f, int exists, int preserve) 1933 { 1934 int aclcnt; /* acl entry count */ 1935 int aclsize; /* acl text length */ 1936 int j; 1937 char *tp; 1938 char *acltext; /* external format */ 1939 acl_t *aclp; 1940 int acltype; 1941 int min_entries; 1942 int aclerror; 1943 1944 /* get acl count */ 1945 if (getaclinfo(&aclcnt, &acltype) != ACL_OK) 1946 return (ACL_FAIL); 1947 1948 if (acltype == 0) { 1949 min_entries = MIN_ACL_ENTRIES; 1950 } else { 1951 min_entries = 1; 1952 } 1953 1954 if (aclcnt > min_entries) { 1955 /* get acl text size */ 1956 if (getaclinfo(&aclsize, &acltype) != ACL_OK) 1957 return (ACL_FAIL); 1958 if ((acltext = malloc(aclsize + 1)) == NULL) { 1959 error("rcp: cant allocate memory: %d\n", aclsize); 1960 return (ACL_FAIL); 1961 } 1962 1963 tp = acltext; 1964 do { 1965 j = desrcpread(rem, tp, aclsize); 1966 if (j <= 0) { 1967 error("rcp: %s\n", j ? strerror(errno) : 1968 "dropped connection"); 1969 exit(1); 1970 } 1971 aclsize -= j; 1972 tp += j; 1973 } while (aclsize > 0); 1974 *tp = '\0'; 1975 1976 if (preserve || !exists) { 1977 aclerror = acl_fromtext(acltext, &aclp); 1978 if (aclerror != 0) { 1979 error("rcp: failed to parse acl : %s\n", 1980 acl_strerror(aclerror)); 1981 free(acltext); 1982 return (ACL_FAIL); 1983 } 1984 1985 if (f != -1) { 1986 if (facl_set(f, aclp) < 0) { 1987 error("rcp: failed to set acl\n"); 1988 acl_free(aclp); 1989 free(acltext); 1990 return (ACL_FAIL); 1991 } 1992 } 1993 /* -1 means that just consume the data in the pipe */ 1994 acl_free(aclp); 1995 } 1996 free(acltext); 1997 (void) desrcpwrite(rem, "", 1); 1998 } 1999 return (ACL_OK); 2000 } 2001 2002 2003 static char * 2004 search_char(unsigned char *cp, unsigned char chr) 2005 { 2006 int len; 2007 2008 while (*cp) { 2009 if (*cp == chr) 2010 return ((char *)cp); 2011 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 2012 len = 1; 2013 cp += len; 2014 } 2015 return (0); 2016 } 2017 2018 2019 static int 2020 desrcpread(int fd, char *buf, int len) 2021 { 2022 return ((int)desread(fd, buf, len, 0)); 2023 } 2024 2025 static int 2026 desrcpwrite(int fd, char *buf, int len) 2027 { 2028 /* 2029 * Note that rcp depends on the same file descriptor being both 2030 * input and output to the remote side. This is bogus, especially 2031 * when rcp is being run by a rsh that pipes. Fix it here because 2032 * it would require significantly more work in other places. 2033 * --hartmans 1/96 2034 */ 2035 2036 if (fd == 0) 2037 fd = 1; 2038 return ((int)deswrite(fd, buf, len, 0)); 2039 } 2040 2041 static char ** 2042 save_argv(int argc, char **argv) 2043 { 2044 int i; 2045 2046 char **local_argv = (char **)calloc((unsigned)argc + 1, 2047 (unsigned)sizeof (char *)); 2048 2049 /* 2050 * allocate an extra pointer, so that it is initialized to NULL and 2051 * execv() will work 2052 */ 2053 for (i = 0; i < argc; i++) { 2054 local_argv[i] = strsave(argv[i]); 2055 } 2056 2057 return (local_argv); 2058 } 2059 2060 #define SIZEOF_INADDR sizeof (struct in_addr) 2061 2062 static void 2063 answer_auth(char *config_file, char *ccache_file) 2064 { 2065 krb5_data pname_data, msg; 2066 krb5_creds creds, *new_creds; 2067 krb5_ccache cc; 2068 krb5_auth_context auth_context = NULL; 2069 2070 if (config_file) { 2071 const char *filenames[2]; 2072 2073 filenames[1] = NULL; 2074 filenames[0] = config_file; 2075 if (krb5_set_config_files(bsd_context, filenames)) 2076 exit(1); 2077 } 2078 (void) memset((char *)&creds, 0, sizeof (creds)); 2079 2080 if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data)) 2081 exit(1); 2082 2083 if (krb5_read_message(bsd_context, (krb5_pointer) &rem, 2084 &creds.second_ticket)) 2085 exit(1); 2086 2087 if (ccache_file == NULL) { 2088 if (krb5_cc_default(bsd_context, &cc)) 2089 exit(1); 2090 } else { 2091 if (krb5_cc_resolve(bsd_context, ccache_file, &cc)) 2092 exit(1); 2093 } 2094 2095 if (krb5_cc_get_principal(bsd_context, cc, &creds.client)) 2096 exit(1); 2097 2098 if (krb5_parse_name(bsd_context, pname_data.data, &creds.server)) 2099 exit(1); 2100 2101 krb5_xfree(pname_data.data); 2102 if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds, 2103 &new_creds)) 2104 exit(1); 2105 2106 if (krb5_mk_req_extended(bsd_context, &auth_context, 2107 AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg)) 2108 exit(1); 2109 2110 if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) { 2111 krb5_xfree(msg.data); 2112 exit(1); 2113 } 2114 /* setup eblock for des_read and write */ 2115 krb5_copy_keyblock(bsd_context, &new_creds->keyblock, &session_key); 2116 2117 /* OK process key */ 2118 eblock.crypto_entry = session_key->enctype; 2119 eblock.key = (krb5_keyblock *)session_key; 2120 2121 init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL, 2122 &desinbuf, &desoutbuf, CLIENT, &eblock); 2123 /* cleanup */ 2124 krb5_free_cred_contents(bsd_context, &creds); 2125 krb5_free_creds(bsd_context, new_creds); 2126 krb5_xfree(msg.data); 2127 } 2128 2129 2130 static void 2131 try_normal_rcp(int cur_argc, char **cur_argv) 2132 { 2133 char *target; 2134 2135 /* 2136 * Reset all KRB5 relevant flags and set the 2137 * cmd-buffer so that normal rcp works 2138 */ 2139 krb5auth_flag = encrypt_flag = encrypt_done = 0; 2140 cmd = cmd_orig; 2141 cmd_sunw = cmd_sunw_orig; 2142 2143 if (cur_argc < 2) 2144 usage(); 2145 2146 if (cur_argc > 2) 2147 targetshouldbedirectory = 1; 2148 2149 rem = -1; 2150 2151 prev_argc = cur_argc; 2152 prev_argv = save_argv(cur_argc, cur_argv); 2153 2154 (void) init_service(krb5auth_flag); 2155 2156 if (target = colon(cur_argv[cur_argc - 1])) { 2157 toremote(target, cur_argc, cur_argv); 2158 } else { 2159 tolocal(cur_argc, cur_argv); 2160 if (targetshouldbedirectory) 2161 verifydir(cur_argv[cur_argc - 1]); 2162 } 2163 exit(errs); 2164 /* NOTREACHED */ 2165 } 2166 2167 2168 static int 2169 init_service(int krb5flag) 2170 { 2171 struct servent *sp; 2172 boolean_t success = B_FALSE; 2173 2174 if (krb5flag > 0) { 2175 sp = getservbyname("kshell", "tcp"); 2176 if (sp == NULL) { 2177 (void) fprintf(stderr, 2178 gettext("rcp: kshell/tcp: unknown service.\n" 2179 "trying normal shell/tcp service\n")); 2180 } else { 2181 portnumber = sp->s_port; 2182 success = B_TRUE; 2183 } 2184 } else { 2185 portnumber = htons(IPPORT_CMDSERVER); 2186 success = B_TRUE; 2187 } 2188 return (success); 2189 } 2190 2191 /*PRINTFLIKE1*/ 2192 static void 2193 error(char *fmt, ...) 2194 { 2195 va_list ap; 2196 char buf[RCP_BUFSIZE]; 2197 char *cp = buf; 2198 2199 va_start(ap, fmt); 2200 errs++; 2201 *cp++ = 1; 2202 (void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap); 2203 va_end(ap); 2204 2205 (void) desrcpwrite(rem, buf, strlen(buf)); 2206 if (iamremote == 0) 2207 (void) write(2, buf + 1, strlen(buf + 1)); 2208 } 2209 2210 static void 2211 addargs(char **arglist, ...) 2212 { 2213 va_list ap; 2214 int i = 0; 2215 char *pm; 2216 2217 va_start(ap, arglist); 2218 while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL) 2219 if (strcmp(pm, "")) 2220 arglist[i++] = pm; 2221 arglist[i] = NULL; 2222 va_end(ap); 2223 } 2224