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