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