10Sstevel@tonic-gate /* 2*11415SSurya.Prakki@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 30Sstevel@tonic-gate * Use is subject to license terms. 40Sstevel@tonic-gate */ 50Sstevel@tonic-gate 60Sstevel@tonic-gate /* 70Sstevel@tonic-gate * Copyright (c) 1983 The Regents of the University of California. 80Sstevel@tonic-gate * All rights reserved. 90Sstevel@tonic-gate * 100Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 110Sstevel@tonic-gate * provided that the above copyright notice and this paragraph are 120Sstevel@tonic-gate * duplicated in all such forms and that any documentation, 130Sstevel@tonic-gate * advertising materials, and other materials related to such 140Sstevel@tonic-gate * distribution and use acknowledge that the software was developed 150Sstevel@tonic-gate * by the University of California, Berkeley. The name of the 160Sstevel@tonic-gate * University may not be used to endorse or promote products derived 170Sstevel@tonic-gate * from this software without specific prior written permission. 180Sstevel@tonic-gate * 190Sstevel@tonic-gate */ 200Sstevel@tonic-gate 210Sstevel@tonic-gate #define _FILE_OFFSET_BITS 64 220Sstevel@tonic-gate 230Sstevel@tonic-gate /* 240Sstevel@tonic-gate * rcp 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate #include <sys/param.h> 270Sstevel@tonic-gate #include <sys/file.h> 280Sstevel@tonic-gate #include <sys/stat.h> 290Sstevel@tonic-gate #include <sys/time.h> 300Sstevel@tonic-gate #include <sys/types.h> 310Sstevel@tonic-gate #include <sys/ioctl.h> 320Sstevel@tonic-gate #include <sys/acl.h> 330Sstevel@tonic-gate #include <dirent.h> 340Sstevel@tonic-gate #include <signal.h> 350Sstevel@tonic-gate #include <sys/socket.h> 360Sstevel@tonic-gate #include <netinet/in.h> 370Sstevel@tonic-gate #include <pwd.h> 380Sstevel@tonic-gate #include <netdb.h> 390Sstevel@tonic-gate #include <wchar.h> 400Sstevel@tonic-gate #include <stdlib.h> 410Sstevel@tonic-gate #include <errno.h> 420Sstevel@tonic-gate #include <locale.h> 430Sstevel@tonic-gate #include <strings.h> 440Sstevel@tonic-gate #include <stdio.h> 450Sstevel@tonic-gate #include <ctype.h> 460Sstevel@tonic-gate #include <fcntl.h> 470Sstevel@tonic-gate #include <unistd.h> 480Sstevel@tonic-gate #include <limits.h> 490Sstevel@tonic-gate #include <priv_utils.h> 500Sstevel@tonic-gate #include <sys/sendfile.h> 510Sstevel@tonic-gate #include <sys/sysmacros.h> 520Sstevel@tonic-gate #include <sys/wait.h> 53789Sahrens #include <aclutils.h> 544619Ssn199410 #include <sys/varargs.h> 550Sstevel@tonic-gate 560Sstevel@tonic-gate /* 570Sstevel@tonic-gate * It seems like Berkeley got these from pathnames.h? 580Sstevel@tonic-gate */ 590Sstevel@tonic-gate #define _PATH_RSH "/usr/bin/rsh" 600Sstevel@tonic-gate #define _PATH_CP "/usr/bin/cp" 610Sstevel@tonic-gate 620Sstevel@tonic-gate #define ACL_FAIL 1 630Sstevel@tonic-gate #define ACL_OK 0 640Sstevel@tonic-gate #define RCP_BUFSIZE (64 * 1024) 650Sstevel@tonic-gate 660Sstevel@tonic-gate #define RCP_ACL "/usr/lib/sunw,rcp" 670Sstevel@tonic-gate /* see PSARC/1993/004/opinion */ 680Sstevel@tonic-gate 690Sstevel@tonic-gate typedef struct _buf { 700Sstevel@tonic-gate int cnt; 710Sstevel@tonic-gate char *buf; 720Sstevel@tonic-gate } BUF; 730Sstevel@tonic-gate 740Sstevel@tonic-gate static char *cmd_sunw; 750Sstevel@tonic-gate static struct passwd *pwd; 760Sstevel@tonic-gate static int errs; 770Sstevel@tonic-gate static int pflag; 780Sstevel@tonic-gate static uid_t userid; 790Sstevel@tonic-gate static int rem; 800Sstevel@tonic-gate static int zflag; 810Sstevel@tonic-gate static int iamremote; 820Sstevel@tonic-gate static int iamrecursive; 830Sstevel@tonic-gate static int targetshouldbedirectory; 840Sstevel@tonic-gate static int aclflag; 85789Sahrens static int acl_aclflag; 860Sstevel@tonic-gate static int retval = 0; 870Sstevel@tonic-gate static int portnumber = 0; 880Sstevel@tonic-gate 890Sstevel@tonic-gate static void lostconn(void); 900Sstevel@tonic-gate static char *search_char(unsigned char *, unsigned char); 910Sstevel@tonic-gate static char *removebrackets(char *); 920Sstevel@tonic-gate static char *colon(char *); 930Sstevel@tonic-gate static int response(void); 940Sstevel@tonic-gate static void usage(void); 950Sstevel@tonic-gate static void source(int, char **); 960Sstevel@tonic-gate static void sink(int, char **); 970Sstevel@tonic-gate static void toremote(char *, int, char **); 980Sstevel@tonic-gate static void tolocal(int, char **); 990Sstevel@tonic-gate static void verifydir(char *); 1000Sstevel@tonic-gate static int okname(char *); 1014619Ssn199410 static int susystem(char *, char **); 1020Sstevel@tonic-gate static void rsource(char *, struct stat *); 1030Sstevel@tonic-gate static int sendacl(int); 1040Sstevel@tonic-gate static int recvacl(int, int, int); 1050Sstevel@tonic-gate static int zwrite(int, char *, int); 1060Sstevel@tonic-gate static void zopen(int, int); 1070Sstevel@tonic-gate static int zclose(int); 1080Sstevel@tonic-gate static int notzero(char *, int); 1090Sstevel@tonic-gate static BUF *allocbuf(BUF *, int, int); 1100Sstevel@tonic-gate static void error(char *fmt, ...); 1114619Ssn199410 static void addargs(char **, ...); 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate /* 1140Sstevel@tonic-gate * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF 1150Sstevel@tonic-gate * bytes of data. We would like the size to be aligned to the nearest 1160Sstevel@tonic-gate * MAXBOFFSET (8192) boundary for optimal performance. 1170Sstevel@tonic-gate */ 1180Sstevel@tonic-gate #define SENDFILE_SIZE 0x7FFFE000 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate #include <k5-int.h> 1210Sstevel@tonic-gate #include <profile/prof_int.h> 1220Sstevel@tonic-gate #include <com_err.h> 1230Sstevel@tonic-gate #include <kcmd.h> 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate #define NULLBUF (BUF *) 0 1264619Ssn199410 #define MAXARGS 10 /* Number of arguments passed to execv() */ 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate static int sock; 1290Sstevel@tonic-gate static char *cmd, *cmd_orig, *cmd_sunw_orig; 1300Sstevel@tonic-gate static char *krb_realm = NULL; 1310Sstevel@tonic-gate static char *krb_cache = NULL; 1320Sstevel@tonic-gate static char *krb_config = NULL; 1330Sstevel@tonic-gate static char des_inbuf[2 * RCP_BUFSIZE]; 1340Sstevel@tonic-gate /* needs to be > largest read size */ 1350Sstevel@tonic-gate static char des_outbuf[2 * RCP_BUFSIZE]; 1360Sstevel@tonic-gate /* needs to be > largest write size */ 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate static krb5_data desinbuf, desoutbuf; 1390Sstevel@tonic-gate static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 1400Sstevel@tonic-gate static krb5_keyblock *session_key; /* static key for session */ 1418175SPeter.Shoults@Sun.COM static krb5_context bsd_context = NULL; 1420Sstevel@tonic-gate static krb5_auth_context auth_context; 1430Sstevel@tonic-gate static krb5_flags authopts; 1440Sstevel@tonic-gate static krb5_error_code status; 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate static void try_normal_rcp(int, char **); 1470Sstevel@tonic-gate static int init_service(int); 1480Sstevel@tonic-gate static char **save_argv(int, char **); 1490Sstevel@tonic-gate static void answer_auth(char *, char *); 1500Sstevel@tonic-gate static int desrcpwrite(int, char *, int); 1510Sstevel@tonic-gate static int desrcpread(int, char *, int); 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate /* 1540Sstevel@tonic-gate * Not sure why these two don't have their own header file declarations, but 1550Sstevel@tonic-gate * lint complains about absent declarations so place some here. Sigh. 1560Sstevel@tonic-gate */ 1570Sstevel@tonic-gate extern errcode_t profile_get_options_boolean(profile_t, char **, 1580Sstevel@tonic-gate profile_options_boolean *); 1590Sstevel@tonic-gate extern errcode_t profile_get_options_string(profile_t, char **, 1600Sstevel@tonic-gate profile_option_strings *); 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */ 1638175SPeter.Shoults@Sun.COM static profile_options_boolean autologin_option[] = { 1648175SPeter.Shoults@Sun.COM { "autologin", &krb5auth_flag, 0 }, 1658175SPeter.Shoults@Sun.COM { NULL, NULL, 0 } 1668175SPeter.Shoults@Sun.COM }; 1678175SPeter.Shoults@Sun.COM static int no_krb5auth_flag = 0; 1688175SPeter.Shoults@Sun.COM 1690Sstevel@tonic-gate static int encrypt_flag = 0; /* Flag set, when encryption is enabled */ 1700Sstevel@tonic-gate static int encrypt_done = 0; /* Flag set, if "-x" is specified */ 1710Sstevel@tonic-gate static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* Flag set, if -PN / -PO is specified */ 1740Sstevel@tonic-gate static boolean_t rcmdoption_done = B_FALSE; 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate static profile_options_boolean option[] = { 1770Sstevel@tonic-gate { "encrypt", &encrypt_flag, 0 }, 1780Sstevel@tonic-gate { NULL, NULL, 0 } 1790Sstevel@tonic-gate }; 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate static char *rcmdproto = NULL; 1820Sstevel@tonic-gate static profile_option_strings rcmdversion[] = { 1830Sstevel@tonic-gate { "rcmd_protocol", &rcmdproto, 0 }, 1840Sstevel@tonic-gate { NULL, NULL, 0 } 1850Sstevel@tonic-gate }; 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate static char *realmdef[] = { "realms", NULL, "rcp", NULL }; 1880Sstevel@tonic-gate static char *appdef[] = { "appdefaults", "rcp", NULL }; 1890Sstevel@tonic-gate static char **prev_argv; 1900Sstevel@tonic-gate static int prev_argc; 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate int 1930Sstevel@tonic-gate main(int argc, char *argv[]) 1940Sstevel@tonic-gate { 1950Sstevel@tonic-gate int ch, fflag, tflag; 1960Sstevel@tonic-gate char *targ; 1970Sstevel@tonic-gate size_t cmdsiz; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate if (strcmp(argv[0], RCP_ACL) == 0) 2020Sstevel@tonic-gate aclflag = 1; 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate if (!(pwd = getpwuid(userid = getuid()))) { 2050Sstevel@tonic-gate (void) fprintf(stderr, "rcp: unknown user %d.\n", 2060Sstevel@tonic-gate (uint_t)userid); 2070Sstevel@tonic-gate return (1); 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate fflag = tflag = 0; 2118175SPeter.Shoults@Sun.COM while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:ZK")) != EOF) { 2120Sstevel@tonic-gate switch (ch) { 2130Sstevel@tonic-gate case 'd': 2140Sstevel@tonic-gate targetshouldbedirectory = 1; 2150Sstevel@tonic-gate break; 2160Sstevel@tonic-gate case 'f': /* "from" */ 2170Sstevel@tonic-gate fflag = 1; 218789Sahrens if (aclflag | acl_aclflag) 2190Sstevel@tonic-gate /* ok response */ 2200Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 2210Sstevel@tonic-gate break; 2220Sstevel@tonic-gate case 'p': /* preserve access/mod times */ 2230Sstevel@tonic-gate ++pflag; 2240Sstevel@tonic-gate break; 2250Sstevel@tonic-gate case 'r': 2260Sstevel@tonic-gate ++iamrecursive; 2270Sstevel@tonic-gate break; 2280Sstevel@tonic-gate case 't': /* "to" */ 2290Sstevel@tonic-gate tflag = 1; 2300Sstevel@tonic-gate break; 231789Sahrens case 'Z': 232789Sahrens acl_aclflag++; 233789Sahrens break; 2348175SPeter.Shoults@Sun.COM case 'K': 2358175SPeter.Shoults@Sun.COM no_krb5auth_flag++; 2368175SPeter.Shoults@Sun.COM break; 2370Sstevel@tonic-gate case 'x': 2380Sstevel@tonic-gate if (!krb5_privacy_allowed()) { 2390Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: " 2400Sstevel@tonic-gate "Encryption not supported.\n")); 2410Sstevel@tonic-gate return (1); 2420Sstevel@tonic-gate } 2430Sstevel@tonic-gate encrypt_flag++; 2440Sstevel@tonic-gate krb5auth_flag++; 2450Sstevel@tonic-gate encrypt_done++; 2460Sstevel@tonic-gate break; 2470Sstevel@tonic-gate case 'k': 2480Sstevel@tonic-gate if ((krb_realm = (char *)strdup(optarg)) == NULL) { 2490Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp:" 2500Sstevel@tonic-gate " Cannot malloc.\n")); 2510Sstevel@tonic-gate return (1); 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate krb5auth_flag++; 2540Sstevel@tonic-gate break; 2550Sstevel@tonic-gate case 'P': 2560Sstevel@tonic-gate if (strncmp(optarg, "O", 1) == 0) { 2570Sstevel@tonic-gate if (rcmdoption_done == B_TRUE) { 2580Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: " 2590Sstevel@tonic-gate "Only one of -PN and -PO " 2600Sstevel@tonic-gate "allowed.\n")); 2610Sstevel@tonic-gate usage(); 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate kcmd_proto = KCMD_OLD_PROTOCOL; 2640Sstevel@tonic-gate rcmdoption_done = B_TRUE; 2650Sstevel@tonic-gate } else if (strncmp(optarg, "N", 1) == 0) { 2660Sstevel@tonic-gate if (rcmdoption_done == B_TRUE) { 2670Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: " 2680Sstevel@tonic-gate "Only one of -PN and -PO " 2690Sstevel@tonic-gate "allowed.\n")); 2700Sstevel@tonic-gate usage(); 2710Sstevel@tonic-gate } 2720Sstevel@tonic-gate kcmd_proto = KCMD_NEW_PROTOCOL; 2730Sstevel@tonic-gate rcmdoption_done = B_TRUE; 2740Sstevel@tonic-gate } else { 2750Sstevel@tonic-gate usage(); 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate krb5auth_flag++; 2780Sstevel@tonic-gate break; 2790Sstevel@tonic-gate case 'a': 2800Sstevel@tonic-gate krb5auth_flag++; 2810Sstevel@tonic-gate break; 2820Sstevel@tonic-gate #ifdef DEBUG 2830Sstevel@tonic-gate case 'D': 2840Sstevel@tonic-gate portnumber = htons(atoi(optarg)); 2850Sstevel@tonic-gate krb5auth_flag++; 2860Sstevel@tonic-gate break; 2870Sstevel@tonic-gate #endif /* DEBUG */ 2880Sstevel@tonic-gate case '?': 2890Sstevel@tonic-gate default: 2900Sstevel@tonic-gate usage(); 2910Sstevel@tonic-gate } 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate argc -= optind; 2940Sstevel@tonic-gate argv += optind; 2950Sstevel@tonic-gate 2968175SPeter.Shoults@Sun.COM /* 2978175SPeter.Shoults@Sun.COM * if the user disables krb5 on the cmdline (-K), then skip 2988175SPeter.Shoults@Sun.COM * all krb5 setup. 2998175SPeter.Shoults@Sun.COM * 3008175SPeter.Shoults@Sun.COM * if the user does not disable krb5 or enable krb5 on the 3018175SPeter.Shoults@Sun.COM * cmdline, check krb5.conf to see if it should be enabled. 3028175SPeter.Shoults@Sun.COM */ 3038175SPeter.Shoults@Sun.COM 3048175SPeter.Shoults@Sun.COM if (no_krb5auth_flag) { 3058175SPeter.Shoults@Sun.COM krb5auth_flag = 0; 3068175SPeter.Shoults@Sun.COM fflag = encrypt_flag = 0; 3078175SPeter.Shoults@Sun.COM } else if (!krb5auth_flag) { 3088175SPeter.Shoults@Sun.COM /* is autologin set in krb5.conf? */ 3090Sstevel@tonic-gate status = krb5_init_context(&bsd_context); 3108175SPeter.Shoults@Sun.COM /* don't sweat failure here */ 3118175SPeter.Shoults@Sun.COM if (!status) { 3128175SPeter.Shoults@Sun.COM /* 3138175SPeter.Shoults@Sun.COM * note that the call to profile_get_options_boolean 3148175SPeter.Shoults@Sun.COM * with autologin_option can affect value of 3158175SPeter.Shoults@Sun.COM * krb5auth_flag 3168175SPeter.Shoults@Sun.COM */ 3178175SPeter.Shoults@Sun.COM (void) profile_get_options_boolean(bsd_context->profile, 3188175SPeter.Shoults@Sun.COM appdef, 3198175SPeter.Shoults@Sun.COM autologin_option); 3208175SPeter.Shoults@Sun.COM } 3218175SPeter.Shoults@Sun.COM } 3228175SPeter.Shoults@Sun.COM 3238175SPeter.Shoults@Sun.COM if (krb5auth_flag > 0) { 3248175SPeter.Shoults@Sun.COM if (!bsd_context) { 3258175SPeter.Shoults@Sun.COM status = krb5_init_context(&bsd_context); 3268175SPeter.Shoults@Sun.COM if (status) { 3278175SPeter.Shoults@Sun.COM com_err("rcp", status, 3288175SPeter.Shoults@Sun.COM gettext("while initializing krb5")); 3298175SPeter.Shoults@Sun.COM return (1); 3308175SPeter.Shoults@Sun.COM } 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate /* 3340Sstevel@tonic-gate * Set up buffers for desread and deswrite. 3350Sstevel@tonic-gate */ 3360Sstevel@tonic-gate desinbuf.data = des_inbuf; 3370Sstevel@tonic-gate desoutbuf.data = des_outbuf; 3380Sstevel@tonic-gate desinbuf.length = sizeof (des_inbuf); 3390Sstevel@tonic-gate desoutbuf.length = sizeof (des_outbuf); 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate if (fflag || tflag) 3430Sstevel@tonic-gate if (encrypt_flag > 0) 3440Sstevel@tonic-gate (void) answer_auth(krb_config, krb_cache); 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate if (fflag) { 3470Sstevel@tonic-gate iamremote = 1; 3480Sstevel@tonic-gate (void) response(); 3490Sstevel@tonic-gate (void) setuid(userid); 3500Sstevel@tonic-gate source(argc, argv); 3510Sstevel@tonic-gate return (errs); 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate if (tflag) { 3550Sstevel@tonic-gate iamremote = 1; 3560Sstevel@tonic-gate (void) setuid(userid); 3570Sstevel@tonic-gate sink(argc, argv); 3580Sstevel@tonic-gate return (errs); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate if (argc < 2) 3620Sstevel@tonic-gate usage(); 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* This will make "rcmd_af()" magically get the proper privilege */ 3650Sstevel@tonic-gate if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) { 3660Sstevel@tonic-gate (void) fprintf(stderr, "rcp: must be set-uid root\n"); 3670Sstevel@tonic-gate exit(1); 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (krb5auth_flag > 0) { 3710Sstevel@tonic-gate /* 3720Sstevel@tonic-gate * Get our local realm to look up local realm options. 3730Sstevel@tonic-gate */ 3740Sstevel@tonic-gate status = krb5_get_default_realm(bsd_context, &realmdef[1]); 3750Sstevel@tonic-gate if (status) { 3760Sstevel@tonic-gate com_err("rcp", status, 3770Sstevel@tonic-gate gettext("while getting default realm")); 3780Sstevel@tonic-gate return (1); 3790Sstevel@tonic-gate } 3800Sstevel@tonic-gate /* 3810Sstevel@tonic-gate * See if encryption should be done for this realm 3820Sstevel@tonic-gate */ 383*11415SSurya.Prakki@Sun.COM (void) profile_get_options_boolean(bsd_context->profile, 384*11415SSurya.Prakki@Sun.COM realmdef, option); 3850Sstevel@tonic-gate /* 3860Sstevel@tonic-gate * Check the appdefaults section 3870Sstevel@tonic-gate */ 388*11415SSurya.Prakki@Sun.COM (void) profile_get_options_boolean(bsd_context->profile, 389*11415SSurya.Prakki@Sun.COM appdef, option); 390*11415SSurya.Prakki@Sun.COM (void) profile_get_options_string(bsd_context->profile, 391*11415SSurya.Prakki@Sun.COM appdef, rcmdversion); 3920Sstevel@tonic-gate if ((encrypt_done > 0) || (encrypt_flag > 0)) { 3930Sstevel@tonic-gate if (krb5_privacy_allowed() == TRUE) { 3940Sstevel@tonic-gate encrypt_flag++; 3950Sstevel@tonic-gate } else { 3960Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: Encryption" 3970Sstevel@tonic-gate " not supported.\n")); 3980Sstevel@tonic-gate return (1); 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) { 4030Sstevel@tonic-gate if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { 4040Sstevel@tonic-gate kcmd_proto = KCMD_NEW_PROTOCOL; 4050Sstevel@tonic-gate } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { 4060Sstevel@tonic-gate kcmd_proto = KCMD_OLD_PROTOCOL; 4070Sstevel@tonic-gate } else { 4080Sstevel@tonic-gate (void) fprintf(stderr, gettext("Unrecognized " 4090Sstevel@tonic-gate "KCMD protocol (%s)"), rcmdproto); 4100Sstevel@tonic-gate return (1); 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate if (argc > 2) 4160Sstevel@tonic-gate targetshouldbedirectory = 1; 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate rem = -1; 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate if (portnumber == 0) { 4210Sstevel@tonic-gate if (krb5auth_flag > 0) { 4220Sstevel@tonic-gate retval = init_service(krb5auth_flag); 4230Sstevel@tonic-gate if (!retval) { 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * Connecting to the kshell service failed, 4260Sstevel@tonic-gate * fallback to normal rcp & reset KRB5 flags. 4270Sstevel@tonic-gate */ 4280Sstevel@tonic-gate krb5auth_flag = encrypt_flag = 0; 4290Sstevel@tonic-gate encrypt_done = 0; 4300Sstevel@tonic-gate (void) init_service(krb5auth_flag); 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate } 4330Sstevel@tonic-gate else 4340Sstevel@tonic-gate (void) init_service(krb5auth_flag); 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate #ifdef DEBUG 4380Sstevel@tonic-gate if (retval || krb5auth_flag) { 4390Sstevel@tonic-gate (void) fprintf(stderr, gettext("Kerberized rcp session, " 4400Sstevel@tonic-gate "port %d in use "), portnumber); 4410Sstevel@tonic-gate if (kcmd_proto == KCMD_OLD_PROTOCOL) 4420Sstevel@tonic-gate (void) fprintf(stderr, gettext("[kcmd ver.1]\n")); 4430Sstevel@tonic-gate else 4440Sstevel@tonic-gate (void) fprintf(stderr, gettext("[kcmd ver.2]\n")); 4450Sstevel@tonic-gate } else { 4460Sstevel@tonic-gate (void) fprintf(stderr, gettext("Normal rcp session, port %d " 4470Sstevel@tonic-gate "in use.\n"), portnumber); 4480Sstevel@tonic-gate } 4490Sstevel@tonic-gate #endif /* DEBUG */ 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate if (krb5auth_flag > 0) { 4520Sstevel@tonic-gate /* 4530Sstevel@tonic-gate * We calculate here a buffer size that can be used in the 4540Sstevel@tonic-gate * allocation of the three buffers cmd, cmd_orig and 4550Sstevel@tonic-gate * cmd_sunw_orig that are used to hold different incantations 4560Sstevel@tonic-gate * of rcp. 4570Sstevel@tonic-gate */ 4584619Ssn199410 cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") + 4590Sstevel@tonic-gate strlen(krb_realm != NULL ? krb_realm : ""), 4600Sstevel@tonic-gate sizeof (RCP_ACL " -r -p -z -d")); 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate if (((cmd = (char *)malloc(cmdsiz)) == NULL) || 4630Sstevel@tonic-gate ((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) || 4640Sstevel@tonic-gate ((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) { 4650Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: Cannot " 4660Sstevel@tonic-gate "malloc.\n")); 4670Sstevel@tonic-gate return (1); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate (void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s", 4710Sstevel@tonic-gate encrypt_flag ? "-x " : "", 4720Sstevel@tonic-gate iamrecursive ? " -r" : "", pflag ? " -p" : "", 4730Sstevel@tonic-gate targetshouldbedirectory ? " -d" : "", 4740Sstevel@tonic-gate krb_realm != NULL ? " -k " : "", 4750Sstevel@tonic-gate krb_realm != NULL ? krb_realm : ""); 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate /* 4780Sstevel@tonic-gate * We would use cmd-orig as the 'cmd-buffer' if kerberized 4790Sstevel@tonic-gate * rcp fails, in which case we fallback to normal rcp. We also 4800Sstevel@tonic-gate * save argc & argv for the same purpose 4810Sstevel@tonic-gate */ 4820Sstevel@tonic-gate (void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s", 4830Sstevel@tonic-gate iamrecursive ? " -r" : "", 4840Sstevel@tonic-gate pflag ? " -p" : "", 4850Sstevel@tonic-gate zflag ? " -z" : "", 4860Sstevel@tonic-gate targetshouldbedirectory ? " -d" : ""); 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate (void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL, 4890Sstevel@tonic-gate iamrecursive ? " -r" : "", 4900Sstevel@tonic-gate pflag ? " -p" : "", 4910Sstevel@tonic-gate zflag ? " -z" : "", 4920Sstevel@tonic-gate targetshouldbedirectory ? " -d" : ""); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate prev_argc = argc; 4950Sstevel@tonic-gate prev_argv = save_argv(argc, argv); 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate } else { 4980Sstevel@tonic-gate cmdsiz = sizeof ("rcp -r -p -z -d"); 4990Sstevel@tonic-gate if (((cmd = (char *)malloc(cmdsiz)) == NULL)) { 5000Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: Cannot " 5010Sstevel@tonic-gate "malloc.\n")); 5020Sstevel@tonic-gate return (1); 5030Sstevel@tonic-gate } 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate (void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s", 5060Sstevel@tonic-gate iamrecursive ? " -r" : "", 5070Sstevel@tonic-gate pflag ? " -p" : "", 5080Sstevel@tonic-gate zflag ? " -z" : "", 5090Sstevel@tonic-gate targetshouldbedirectory ? " -d" : ""); 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate cmdsiz = sizeof (RCP_ACL " -r -p -z -d"); 5130Sstevel@tonic-gate if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) { 5140Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: Cannot malloc.\n")); 5150Sstevel@tonic-gate return (1); 5160Sstevel@tonic-gate } 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate (void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL, 5190Sstevel@tonic-gate iamrecursive ? " -r" : "", 5200Sstevel@tonic-gate pflag ? " -p" : "", 5210Sstevel@tonic-gate zflag ? " -z" : "", 5220Sstevel@tonic-gate targetshouldbedirectory ? " -d" : ""); 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate (void) signal(SIGPIPE, (void (*)(int))lostconn); 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate if (targ = colon(argv[argc - 1])) 5270Sstevel@tonic-gate toremote(targ, argc, argv); 5280Sstevel@tonic-gate else { 5290Sstevel@tonic-gate tolocal(argc, argv); 5300Sstevel@tonic-gate if (targetshouldbedirectory) 5310Sstevel@tonic-gate verifydir(argv[argc - 1]); 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS); 5350Sstevel@tonic-gate } 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate static void 5390Sstevel@tonic-gate toremote(char *targ, int argc, char *argv[]) 5400Sstevel@tonic-gate { 5410Sstevel@tonic-gate int i; 5420Sstevel@tonic-gate char *host, *src, *suser, *thost, *tuser; 5430Sstevel@tonic-gate char resp; 5440Sstevel@tonic-gate size_t buffersize; 5450Sstevel@tonic-gate char bp[RCP_BUFSIZE]; 5460Sstevel@tonic-gate krb5_creds *cred; 5474619Ssn199410 char *arglist[MAXARGS+1]; 5480Sstevel@tonic-gate buffersize = RCP_BUFSIZE; 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate *targ++ = 0; 5510Sstevel@tonic-gate if (*targ == 0) 5520Sstevel@tonic-gate targ = "."; 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate if (thost = search_char((unsigned char *)argv[argc - 1], '@')) { 5550Sstevel@tonic-gate *thost++ = 0; 5560Sstevel@tonic-gate tuser = argv[argc - 1]; 5570Sstevel@tonic-gate if (*tuser == '\0') 5580Sstevel@tonic-gate tuser = NULL; 5590Sstevel@tonic-gate else if (!okname(tuser)) 5600Sstevel@tonic-gate exit(1); 5610Sstevel@tonic-gate } else { 5620Sstevel@tonic-gate thost = argv[argc - 1]; 5630Sstevel@tonic-gate tuser = NULL; 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate thost = removebrackets(thost); 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate for (i = 0; i < argc - 1; i++) { 5680Sstevel@tonic-gate src = colon(argv[i]); 5690Sstevel@tonic-gate if (src) { /* remote to remote */ 5700Sstevel@tonic-gate *src++ = 0; 5710Sstevel@tonic-gate if (*src == 0) 5720Sstevel@tonic-gate src = "."; 5730Sstevel@tonic-gate host = search_char((unsigned char *)argv[i], '@'); 5740Sstevel@tonic-gate if (host) { 5750Sstevel@tonic-gate *host++ = 0; 5760Sstevel@tonic-gate host = removebrackets(host); 5770Sstevel@tonic-gate suser = argv[i]; 5780Sstevel@tonic-gate if (*suser == '\0') { 5790Sstevel@tonic-gate suser = pwd->pw_name; 5800Sstevel@tonic-gate } else if (!okname(suser)) { 5810Sstevel@tonic-gate errs++; 5820Sstevel@tonic-gate continue; 5830Sstevel@tonic-gate } 5844619Ssn199410 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 5850Sstevel@tonic-gate tuser ? tuser : "", tuser ? "@" : "", 5860Sstevel@tonic-gate thost, targ); 5874619Ssn199410 (void) addargs(arglist, "rsh", host, "-l", 5884619Ssn199410 suser, "-n", cmd, src, bp, (char *)NULL); 5890Sstevel@tonic-gate } else { 5900Sstevel@tonic-gate host = removebrackets(argv[i]); 5914619Ssn199410 (void) snprintf(bp, buffersize, "'%s%s%s:%s'", 5924619Ssn199410 tuser ? tuser : "", tuser ? "@" : "", 5934619Ssn199410 thost, targ); 5944619Ssn199410 (void) addargs(arglist, "rsh", host, "-n", cmd, 5954619Ssn199410 src, bp, (char *)NULL); 5960Sstevel@tonic-gate } 5974619Ssn199410 if (susystem(_PATH_RSH, arglist) == -1) 5980Sstevel@tonic-gate errs++; 5990Sstevel@tonic-gate } else { /* local to remote */ 6000Sstevel@tonic-gate if (rem == -1) { 6010Sstevel@tonic-gate host = thost; 6020Sstevel@tonic-gate if (krb5auth_flag > 0) { 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate (void) snprintf(bp, buffersize, 6050Sstevel@tonic-gate "%s -t %s", cmd, targ); 6060Sstevel@tonic-gate authopts = AP_OPTS_MUTUAL_REQUIRED; 6070Sstevel@tonic-gate status = kcmd(&sock, &host, 6080Sstevel@tonic-gate portnumber, 6090Sstevel@tonic-gate pwd->pw_name, 6100Sstevel@tonic-gate tuser ? tuser : 6110Sstevel@tonic-gate pwd->pw_name, 6120Sstevel@tonic-gate bp, 6130Sstevel@tonic-gate 0, 6140Sstevel@tonic-gate "host", 6150Sstevel@tonic-gate krb_realm, 6160Sstevel@tonic-gate bsd_context, 6170Sstevel@tonic-gate &auth_context, 6180Sstevel@tonic-gate &cred, 6190Sstevel@tonic-gate 0, /* No seq # */ 6200Sstevel@tonic-gate 0, /* No server seq # */ 6210Sstevel@tonic-gate authopts, 6220Sstevel@tonic-gate 0, /* Not any port # */ 6230Sstevel@tonic-gate &kcmd_proto); 6240Sstevel@tonic-gate if (status) { 6250Sstevel@tonic-gate /* 6260Sstevel@tonic-gate * If new protocol requested, we dont 6270Sstevel@tonic-gate * fallback to less secure ones. 6280Sstevel@tonic-gate */ 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate if (kcmd_proto == KCMD_NEW_PROTOCOL) { 6310Sstevel@tonic-gate (void) fprintf(stderr, 6320Sstevel@tonic-gate gettext("rcp: kcmdv2 " 6330Sstevel@tonic-gate "to host %s failed - %s" 6340Sstevel@tonic-gate "\nFallback to normal " 6350Sstevel@tonic-gate "rcp denied."), host, 6360Sstevel@tonic-gate error_message(status)); 6370Sstevel@tonic-gate exit(1); 6380Sstevel@tonic-gate } 6390Sstevel@tonic-gate if (status != -1) { 6400Sstevel@tonic-gate (void) fprintf(stderr, 6410Sstevel@tonic-gate gettext("rcp: kcmd to host " 6420Sstevel@tonic-gate "%s failed - %s,\n" 6430Sstevel@tonic-gate "trying normal rcp...\n\n"), 6440Sstevel@tonic-gate host, error_message(status)); 6450Sstevel@tonic-gate } else { 6460Sstevel@tonic-gate (void) fprintf(stderr, 6470Sstevel@tonic-gate gettext("trying normal" 6480Sstevel@tonic-gate " rcp...\n")); 6490Sstevel@tonic-gate } 6500Sstevel@tonic-gate /* 6510Sstevel@tonic-gate * kcmd() failed, so we have to 6520Sstevel@tonic-gate * fallback to normal rcp 6530Sstevel@tonic-gate */ 6540Sstevel@tonic-gate try_normal_rcp(prev_argc, prev_argv); 6550Sstevel@tonic-gate } else { 6560Sstevel@tonic-gate rem = sock; 6570Sstevel@tonic-gate session_key = &cred->keyblock; 6580Sstevel@tonic-gate if (kcmd_proto == KCMD_NEW_PROTOCOL) { 6590Sstevel@tonic-gate /* CSTYLED */ 6600Sstevel@tonic-gate status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key); 6610Sstevel@tonic-gate if (status) { 6620Sstevel@tonic-gate com_err("rcp", status, 6630Sstevel@tonic-gate "determining " 6640Sstevel@tonic-gate "subkey for " 6650Sstevel@tonic-gate "session"); 6660Sstevel@tonic-gate exit(1); 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate if (!session_key) { 6690Sstevel@tonic-gate com_err("rcp", 0, 6700Sstevel@tonic-gate "no subkey " 6710Sstevel@tonic-gate "negotiated for" 6720Sstevel@tonic-gate " connection"); 6730Sstevel@tonic-gate exit(1); 6740Sstevel@tonic-gate } 6750Sstevel@tonic-gate } 6760Sstevel@tonic-gate eblock.crypto_entry = 6770Sstevel@tonic-gate session_key->enctype; 6780Sstevel@tonic-gate eblock.key = 6790Sstevel@tonic-gate (krb5_keyblock *)session_key; 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate init_encrypt(encrypt_flag, 6820Sstevel@tonic-gate bsd_context, kcmd_proto, 6830Sstevel@tonic-gate &desinbuf, &desoutbuf, CLIENT, 6840Sstevel@tonic-gate &eblock); 6850Sstevel@tonic-gate if (encrypt_flag > 0) { 6860Sstevel@tonic-gate char *s = gettext("This rcp " 6870Sstevel@tonic-gate "session is using " 6880Sstevel@tonic-gate "encryption for all " 6890Sstevel@tonic-gate "data transmissions." 6900Sstevel@tonic-gate "\r\n"); 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate (void) write(2, s, strlen(s)); 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate } 6950Sstevel@tonic-gate if (response() < 0) 6960Sstevel@tonic-gate exit(1); 6970Sstevel@tonic-gate 698789Sahrens } else { 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate /* 7010Sstevel@tonic-gate * ACL support: try to find out if the remote 7020Sstevel@tonic-gate * site is running acl cognizant version of 7030Sstevel@tonic-gate * rcp. A special binary name is used for this 7040Sstevel@tonic-gate * purpose. 7050Sstevel@tonic-gate */ 7060Sstevel@tonic-gate aclflag = 1; 707789Sahrens acl_aclflag = 1; 7080Sstevel@tonic-gate 709789Sahrens /* 710789Sahrens * First see if the remote side will support 711789Sahrens * both aclent_t and ace_t acl's? 712789Sahrens */ 713789Sahrens (void) snprintf(bp, buffersize, "%s -tZ %s", 7140Sstevel@tonic-gate cmd_sunw, targ); 7150Sstevel@tonic-gate rem = rcmd_af(&host, portnumber, pwd->pw_name, 7160Sstevel@tonic-gate tuser ? tuser : pwd->pw_name, 7170Sstevel@tonic-gate bp, 0, AF_INET6); 7180Sstevel@tonic-gate if (rem < 0) 7190Sstevel@tonic-gate exit(1); 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate /* 7220Sstevel@tonic-gate * This is similar to routine response(). 7230Sstevel@tonic-gate * If response is not ok, treat the other 7240Sstevel@tonic-gate * side as non-acl rcp. 7250Sstevel@tonic-gate */ 7260Sstevel@tonic-gate if (read(rem, &resp, sizeof (resp)) 7270Sstevel@tonic-gate != sizeof (resp)) 7280Sstevel@tonic-gate lostconn(); 7290Sstevel@tonic-gate if (resp != 0) { 730789Sahrens acl_aclflag = 0; 7310Sstevel@tonic-gate (void) snprintf(bp, buffersize, 732789Sahrens "%s -t %s", cmd_sunw, targ); 733789Sahrens 7340Sstevel@tonic-gate (void) close(rem); 7350Sstevel@tonic-gate host = thost; 7360Sstevel@tonic-gate rem = rcmd_af(&host, portnumber, 737789Sahrens pwd->pw_name, 738789Sahrens tuser ? tuser : pwd->pw_name, 739789Sahrens bp, 0, AF_INET6); 7400Sstevel@tonic-gate if (rem < 0) 7410Sstevel@tonic-gate exit(1); 742789Sahrens 743789Sahrens if (read(rem, &resp, sizeof (resp)) 744789Sahrens != sizeof (resp)) 745789Sahrens lostconn(); 746789Sahrens if (resp != 0) { 747789Sahrens /* 748789Sahrens * Not OK: 749789Sahrens * The other side is running 750789Sahrens * non-acl rcp. Try again with 751789Sahrens * normal stuff 752789Sahrens */ 753789Sahrens aclflag = 0; 754789Sahrens (void) snprintf(bp, buffersize, 755789Sahrens "%s -t %s", cmd, targ); 756789Sahrens (void) close(rem); 757789Sahrens host = thost; 758789Sahrens rem = rcmd_af(&host, portnumber, 759789Sahrens pwd->pw_name, 760789Sahrens tuser ? tuser : 761789Sahrens pwd->pw_name, bp, 0, 762789Sahrens AF_INET6); 763789Sahrens if (rem < 0) 764789Sahrens exit(1); 765789Sahrens if (response() < 0) 766789Sahrens exit(1); 767789Sahrens } 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate /* everything should be fine now */ 7700Sstevel@tonic-gate (void) setuid(userid); 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate } 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate source(1, argv + i); 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate } 7770Sstevel@tonic-gate } 7780Sstevel@tonic-gate 7790Sstevel@tonic-gate static void 7800Sstevel@tonic-gate tolocal(int argc, char *argv[]) 7810Sstevel@tonic-gate { 7820Sstevel@tonic-gate int i; 7830Sstevel@tonic-gate char *host, *src, *suser, *lhost; 7840Sstevel@tonic-gate char resp; 7850Sstevel@tonic-gate size_t buffersize; 7860Sstevel@tonic-gate char bp[RCP_BUFSIZE]; 7870Sstevel@tonic-gate krb5_creds *cred; 7884619Ssn199410 char *arglist[MAXARGS+1]; 7890Sstevel@tonic-gate buffersize = RCP_BUFSIZE; 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate for (i = 0; i < argc - 1; i++) { 7920Sstevel@tonic-gate if (!(src = colon(argv[i]))) { /* local to local */ 7934619Ssn199410 (void) addargs(arglist, "cp", 7944619Ssn199410 iamrecursive ? "-r" : "", pflag ? "-p" : "", 7954619Ssn199410 zflag ? "-z" : "", argv[i], argv[argc - 1], 7964619Ssn199410 (char *)NULL); 7974619Ssn199410 if (susystem(_PATH_CP, arglist) == -1) 7980Sstevel@tonic-gate errs++; 7990Sstevel@tonic-gate continue; 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate *src++ = 0; 8020Sstevel@tonic-gate if (*src == 0) 8030Sstevel@tonic-gate src = "."; 8040Sstevel@tonic-gate host = search_char((unsigned char *)argv[i], '@'); 8050Sstevel@tonic-gate if (host) { 8060Sstevel@tonic-gate *host++ = 0; 8070Sstevel@tonic-gate suser = argv[i]; 8080Sstevel@tonic-gate if (*suser == '\0') { 8090Sstevel@tonic-gate suser = pwd->pw_name; 8100Sstevel@tonic-gate } else if (!okname(suser)) { 8110Sstevel@tonic-gate errs++; 8120Sstevel@tonic-gate continue; 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate } else { 8150Sstevel@tonic-gate host = argv[i]; 8160Sstevel@tonic-gate suser = pwd->pw_name; 8170Sstevel@tonic-gate } 8180Sstevel@tonic-gate host = removebrackets(host); 8190Sstevel@tonic-gate lhost = host; 8200Sstevel@tonic-gate if (krb5auth_flag > 0) { 8210Sstevel@tonic-gate 8220Sstevel@tonic-gate (void) snprintf(bp, buffersize, "%s -f %s", cmd, src); 8230Sstevel@tonic-gate authopts = AP_OPTS_MUTUAL_REQUIRED; 8240Sstevel@tonic-gate status = kcmd(&sock, &host, 8250Sstevel@tonic-gate portnumber, 8260Sstevel@tonic-gate pwd->pw_name, suser, 8270Sstevel@tonic-gate bp, 8280Sstevel@tonic-gate 0, /* &rfd2 */ 8290Sstevel@tonic-gate "host", 8300Sstevel@tonic-gate krb_realm, 8310Sstevel@tonic-gate bsd_context, 8320Sstevel@tonic-gate &auth_context, 8330Sstevel@tonic-gate &cred, 8340Sstevel@tonic-gate 0, /* No seq # */ 8350Sstevel@tonic-gate 0, /* No server seq # */ 8360Sstevel@tonic-gate authopts, 8370Sstevel@tonic-gate 1, /* Not any port # */ 8380Sstevel@tonic-gate &kcmd_proto); 8390Sstevel@tonic-gate if (status) { 8400Sstevel@tonic-gate /* 8410Sstevel@tonic-gate * If new protocol requested, we dont 8420Sstevel@tonic-gate * fallback to less secure ones. 8430Sstevel@tonic-gate */ 8440Sstevel@tonic-gate if (kcmd_proto == KCMD_NEW_PROTOCOL) { 8450Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: kcmdv2 " 8460Sstevel@tonic-gate "to host %s failed - %s\n" 8470Sstevel@tonic-gate "Fallback to normal rcp denied."), 8480Sstevel@tonic-gate host, error_message(status)); 8490Sstevel@tonic-gate exit(1); 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate if (status != -1) { 8520Sstevel@tonic-gate (void) fprintf(stderr, gettext("rcp: kcmd " 8530Sstevel@tonic-gate "to host %s failed - %s,\n" 8540Sstevel@tonic-gate "trying normal rcp...\n\n"), 8550Sstevel@tonic-gate host, error_message(status)); 8560Sstevel@tonic-gate } else { 8570Sstevel@tonic-gate (void) fprintf(stderr, 8580Sstevel@tonic-gate gettext("trying normal rcp...\n")); 8590Sstevel@tonic-gate } 8600Sstevel@tonic-gate /* 8610Sstevel@tonic-gate * kcmd() failed, so we have to 8620Sstevel@tonic-gate * fallback to normal rcp 8630Sstevel@tonic-gate */ 8640Sstevel@tonic-gate try_normal_rcp(prev_argc, prev_argv); 8650Sstevel@tonic-gate } else { 8660Sstevel@tonic-gate rem = sock; 8670Sstevel@tonic-gate session_key = &cred->keyblock; 8680Sstevel@tonic-gate if (kcmd_proto == KCMD_NEW_PROTOCOL) { 8690Sstevel@tonic-gate status = krb5_auth_con_getlocalsubkey( 8700Sstevel@tonic-gate bsd_context, auth_context, 8710Sstevel@tonic-gate &session_key); 8720Sstevel@tonic-gate if (status) { 8730Sstevel@tonic-gate com_err("rcp", status, "determining " 8740Sstevel@tonic-gate "subkey for session"); 8750Sstevel@tonic-gate exit(1); 8760Sstevel@tonic-gate } 8770Sstevel@tonic-gate if (!session_key) { 8780Sstevel@tonic-gate com_err("rcp", 0, "no subkey negotiated" 8790Sstevel@tonic-gate " for connection"); 8800Sstevel@tonic-gate exit(1); 8810Sstevel@tonic-gate } 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate eblock.crypto_entry = session_key->enctype; 8840Sstevel@tonic-gate eblock.key = (krb5_keyblock *)session_key; 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate init_encrypt(encrypt_flag, bsd_context, kcmd_proto, 8870Sstevel@tonic-gate &desinbuf, &desoutbuf, CLIENT, 8880Sstevel@tonic-gate &eblock); 8890Sstevel@tonic-gate if (encrypt_flag > 0) { 8900Sstevel@tonic-gate char *s = gettext("This rcp " 8910Sstevel@tonic-gate "session is using DES " 8920Sstevel@tonic-gate "encryption for all " 8930Sstevel@tonic-gate "data transmissions." 8940Sstevel@tonic-gate "\r\n"); 8950Sstevel@tonic-gate 8960Sstevel@tonic-gate (void) write(2, s, strlen(s)); 8970Sstevel@tonic-gate } 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate } 9010Sstevel@tonic-gate else 9020Sstevel@tonic-gate { 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate /* 9050Sstevel@tonic-gate * ACL support: try to find out if the remote site is 9060Sstevel@tonic-gate * running acl cognizant version of rcp. 9070Sstevel@tonic-gate */ 9080Sstevel@tonic-gate aclflag = 1; 909789Sahrens acl_aclflag = 1; 9100Sstevel@tonic-gate 911789Sahrens (void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, src); 9120Sstevel@tonic-gate rem = rcmd_af(&host, portnumber, pwd->pw_name, suser, 9130Sstevel@tonic-gate bp, 0, AF_INET6); 9140Sstevel@tonic-gate 9150Sstevel@tonic-gate if (rem < 0) { 9160Sstevel@tonic-gate ++errs; 9170Sstevel@tonic-gate continue; 9180Sstevel@tonic-gate } 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate /* 9210Sstevel@tonic-gate * The remote system is supposed to send an ok response. 9220Sstevel@tonic-gate * If there are any data other than "ok", it must be error 9230Sstevel@tonic-gate * messages from the remote system. We can assume the 9240Sstevel@tonic-gate * remote system is running non-acl version rcp. 9250Sstevel@tonic-gate */ 9260Sstevel@tonic-gate if (read(rem, &resp, sizeof (resp)) != sizeof (resp)) 9270Sstevel@tonic-gate lostconn(); 9280Sstevel@tonic-gate if (resp != 0) { 929789Sahrens 930789Sahrens /* 931789Sahrens * Try again without ace_acl support 932789Sahrens */ 933789Sahrens acl_aclflag = 0; 934789Sahrens (void) snprintf(bp, buffersize, "%s -f %s", 935789Sahrens cmd_sunw, src); 936789Sahrens rem = rcmd_af(&host, portnumber, pwd->pw_name, suser, 937789Sahrens bp, 0, AF_INET6); 938789Sahrens 939789Sahrens if (rem < 0) { 940789Sahrens ++errs; 941789Sahrens continue; 942789Sahrens } 943789Sahrens 944789Sahrens if (read(rem, &resp, sizeof (resp)) != sizeof (resp)) 945789Sahrens lostconn(); 946789Sahrens 9470Sstevel@tonic-gate /* 9480Sstevel@tonic-gate * NOT ok: 9490Sstevel@tonic-gate * The other side is running non-acl rcp. 9500Sstevel@tonic-gate * Try again with normal stuff 9510Sstevel@tonic-gate */ 9520Sstevel@tonic-gate aclflag = 0; 9530Sstevel@tonic-gate (void) snprintf(bp, buffersize, "%s -f %s", cmd, src); 9540Sstevel@tonic-gate (void) close(rem); 9550Sstevel@tonic-gate host = lhost; 9560Sstevel@tonic-gate rem = rcmd_af(&host, portnumber, pwd->pw_name, 9570Sstevel@tonic-gate suser, bp, 0, AF_INET6); 9580Sstevel@tonic-gate if (rem < 0) { 9590Sstevel@tonic-gate ++errs; 9600Sstevel@tonic-gate continue; 9610Sstevel@tonic-gate } 9620Sstevel@tonic-gate } 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate sink(1, argv + argc - 1); 9660Sstevel@tonic-gate 9670Sstevel@tonic-gate (void) close(rem); 9680Sstevel@tonic-gate rem = -1; 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate static void 9740Sstevel@tonic-gate verifydir(char *cp) 9750Sstevel@tonic-gate { 9760Sstevel@tonic-gate struct stat stb; 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate if (stat(cp, &stb) >= 0) { 9790Sstevel@tonic-gate if ((stb.st_mode & S_IFMT) == S_IFDIR) 9800Sstevel@tonic-gate return; 9810Sstevel@tonic-gate errno = ENOTDIR; 9820Sstevel@tonic-gate } 9830Sstevel@tonic-gate error("rcp: %s: %s.\n", cp, strerror(errno)); 9840Sstevel@tonic-gate exit(1); 9850Sstevel@tonic-gate } 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate static char * 9880Sstevel@tonic-gate colon(char *cp) 9890Sstevel@tonic-gate { 9900Sstevel@tonic-gate boolean_t is_bracket_open = B_FALSE; 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate for (; *cp; ++cp) { 9930Sstevel@tonic-gate if (*cp == '[') 9940Sstevel@tonic-gate is_bracket_open = B_TRUE; 9950Sstevel@tonic-gate else if (*cp == ']') 9960Sstevel@tonic-gate is_bracket_open = B_FALSE; 9970Sstevel@tonic-gate else if (*cp == ':' && !is_bracket_open) 9980Sstevel@tonic-gate return (cp); 9990Sstevel@tonic-gate else if (*cp == '/') 10000Sstevel@tonic-gate return (0); 10010Sstevel@tonic-gate } 10020Sstevel@tonic-gate return (0); 10030Sstevel@tonic-gate } 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate static int 10060Sstevel@tonic-gate okname(char *cp0) 10070Sstevel@tonic-gate { 10080Sstevel@tonic-gate register char *cp = cp0; 10090Sstevel@tonic-gate register int c; 10100Sstevel@tonic-gate 10110Sstevel@tonic-gate do { 10120Sstevel@tonic-gate c = *cp; 10130Sstevel@tonic-gate if (c & 0200) 10140Sstevel@tonic-gate goto bad; 10150Sstevel@tonic-gate if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 10160Sstevel@tonic-gate goto bad; 10170Sstevel@tonic-gate } while (*++cp); 10180Sstevel@tonic-gate return (1); 10190Sstevel@tonic-gate bad: 10200Sstevel@tonic-gate (void) fprintf(stderr, "rcp: invalid user name %s\n", cp0); 10210Sstevel@tonic-gate return (0); 10220Sstevel@tonic-gate } 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate static char * 10260Sstevel@tonic-gate removebrackets(char *str) 10270Sstevel@tonic-gate { 10280Sstevel@tonic-gate char *newstr = str; 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) { 10310Sstevel@tonic-gate newstr = str + 1; 10320Sstevel@tonic-gate str[strlen(str) - 1] = '\0'; 10330Sstevel@tonic-gate } 10340Sstevel@tonic-gate return (newstr); 10350Sstevel@tonic-gate } 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate static int 10384619Ssn199410 susystem(char *path, char **arglist) 10390Sstevel@tonic-gate { 10400Sstevel@tonic-gate int status, pid, w; 10410Sstevel@tonic-gate register void (*istat)(), (*qstat)(); 10420Sstevel@tonic-gate int pfds[2]; 10430Sstevel@tonic-gate char buf[BUFSIZ]; 10440Sstevel@tonic-gate int cnt; 10450Sstevel@tonic-gate boolean_t seen_stderr_traffic; 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate /* 10480Sstevel@tonic-gate * Due to the fact that rcp uses rsh to copy between 2 remote 10490Sstevel@tonic-gate * machines, rsh doesn't return the exit status of the remote 10500Sstevel@tonic-gate * command, and we can't modify the rcmd protocol used by rsh 10510Sstevel@tonic-gate * (for interoperability reasons) we use the hack of using any 10520Sstevel@tonic-gate * output on stderr as indication that an error occurred and 10530Sstevel@tonic-gate * that we should return a non-zero error code. 10540Sstevel@tonic-gate */ 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate if (pipe(pfds) == -1) { 10570Sstevel@tonic-gate (void) fprintf(stderr, "Couldn't create pipe: %s\n", 10580Sstevel@tonic-gate strerror(errno)); 10590Sstevel@tonic-gate return (-1); 10600Sstevel@tonic-gate } 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate if ((pid = vfork()) < 0) { 10630Sstevel@tonic-gate (void) close(pfds[0]); 10640Sstevel@tonic-gate (void) close(pfds[1]); 10650Sstevel@tonic-gate (void) fprintf(stderr, "Couldn't fork child process: %s\n", 10660Sstevel@tonic-gate strerror(errno)); 10670Sstevel@tonic-gate return (-1); 10680Sstevel@tonic-gate } else if (pid == 0) { 10690Sstevel@tonic-gate /* 10700Sstevel@tonic-gate * Child. 10710Sstevel@tonic-gate */ 10720Sstevel@tonic-gate (void) close(pfds[0]); 10730Sstevel@tonic-gate /* 10740Sstevel@tonic-gate * Send stderr messages down the pipe so that we can detect 10750Sstevel@tonic-gate * them in the parent process. 10760Sstevel@tonic-gate */ 10770Sstevel@tonic-gate if (pfds[1] != STDERR_FILENO) { 10780Sstevel@tonic-gate (void) dup2(pfds[1], STDERR_FILENO); 10790Sstevel@tonic-gate (void) close(pfds[1]); 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate /* 10820Sstevel@tonic-gate * This shell does not inherit the additional privilege 10830Sstevel@tonic-gate * we have in our Permitted set. 10840Sstevel@tonic-gate */ 10854619Ssn199410 (void) execv(path, arglist); 10860Sstevel@tonic-gate _exit(127); 10870Sstevel@tonic-gate } 10880Sstevel@tonic-gate /* 10890Sstevel@tonic-gate * Parent. 10900Sstevel@tonic-gate */ 10910Sstevel@tonic-gate istat = signal(SIGINT, SIG_IGN); 10920Sstevel@tonic-gate qstat = signal(SIGQUIT, SIG_IGN); 10930Sstevel@tonic-gate 10940Sstevel@tonic-gate (void) close(pfds[1]); 10950Sstevel@tonic-gate seen_stderr_traffic = B_FALSE; 10960Sstevel@tonic-gate while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) { 10970Sstevel@tonic-gate /* 10980Sstevel@tonic-gate * If any data is read from the pipe the child process 10990Sstevel@tonic-gate * has output something on stderr so we set the boolean 11000Sstevel@tonic-gate * 'seen_stderr_traffic' to true, which will cause the 11010Sstevel@tonic-gate * function to return -1. 11020Sstevel@tonic-gate */ 11030Sstevel@tonic-gate (void) write(STDERR_FILENO, buf, cnt); 11040Sstevel@tonic-gate seen_stderr_traffic = B_TRUE; 11050Sstevel@tonic-gate } 11060Sstevel@tonic-gate (void) close(pfds[0]); 11070Sstevel@tonic-gate while ((w = wait(&status)) != pid && w != -1) 11080Sstevel@tonic-gate ; 11090Sstevel@tonic-gate if (w == -1) 11100Sstevel@tonic-gate status = -1; 11110Sstevel@tonic-gate 11120Sstevel@tonic-gate (void) signal(SIGINT, istat); 11130Sstevel@tonic-gate (void) signal(SIGQUIT, qstat); 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate return (seen_stderr_traffic ? -1 : status); 11160Sstevel@tonic-gate } 11170Sstevel@tonic-gate 11180Sstevel@tonic-gate static void 11190Sstevel@tonic-gate source(int argc, char *argv[]) 11200Sstevel@tonic-gate { 11210Sstevel@tonic-gate struct stat stb; 11220Sstevel@tonic-gate static BUF buffer; 11230Sstevel@tonic-gate BUF *bp; 11240Sstevel@tonic-gate int x, readerr, f, amt; 11250Sstevel@tonic-gate char *last, *name, buf[RCP_BUFSIZE]; 11260Sstevel@tonic-gate off_t off, size, i; 11270Sstevel@tonic-gate ssize_t cnt; 11283370Sjs198686 struct linger lingerbuf; 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate for (x = 0; x < argc; x++) { 11310Sstevel@tonic-gate name = argv[x]; 11320Sstevel@tonic-gate if ((f = open(name, O_RDONLY, 0)) < 0) { 11330Sstevel@tonic-gate error("rcp: %s: %s\n", name, strerror(errno)); 11340Sstevel@tonic-gate continue; 11350Sstevel@tonic-gate } 11360Sstevel@tonic-gate if (fstat(f, &stb) < 0) 11370Sstevel@tonic-gate goto notreg; 11380Sstevel@tonic-gate switch (stb.st_mode&S_IFMT) { 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate case S_IFREG: 11410Sstevel@tonic-gate break; 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate case S_IFDIR: 11440Sstevel@tonic-gate if (iamrecursive) { 11450Sstevel@tonic-gate (void) close(f); 11460Sstevel@tonic-gate rsource(name, &stb); 11470Sstevel@tonic-gate continue; 11480Sstevel@tonic-gate } 11490Sstevel@tonic-gate /* FALLTHROUGH */ 11500Sstevel@tonic-gate default: 11510Sstevel@tonic-gate notreg: 11520Sstevel@tonic-gate (void) close(f); 11530Sstevel@tonic-gate error("rcp: %s: not a plain file\n", name); 11540Sstevel@tonic-gate continue; 11550Sstevel@tonic-gate } 11560Sstevel@tonic-gate last = rindex(name, '/'); 11570Sstevel@tonic-gate if (last == 0) 11580Sstevel@tonic-gate last = name; 11590Sstevel@tonic-gate else 11600Sstevel@tonic-gate last++; 11610Sstevel@tonic-gate if (pflag) { 11620Sstevel@tonic-gate time_t mtime, atime; 11630Sstevel@tonic-gate time_t now; 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate /* 11660Sstevel@tonic-gate * Make it compatible with possible future 11670Sstevel@tonic-gate * versions expecting microseconds. 11680Sstevel@tonic-gate */ 11690Sstevel@tonic-gate mtime = stb.st_mtime; 11700Sstevel@tonic-gate atime = stb.st_atime; 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate if ((mtime < 0) || (atime < 0)) { 11730Sstevel@tonic-gate now = time(NULL); 11740Sstevel@tonic-gate 11750Sstevel@tonic-gate if (mtime < 0) { 11760Sstevel@tonic-gate mtime = now; 11770Sstevel@tonic-gate error("negative modification time on " 11780Sstevel@tonic-gate "%s; not preserving\n", name); 11790Sstevel@tonic-gate } 11800Sstevel@tonic-gate if (atime < 0) { 11810Sstevel@tonic-gate atime = now; 11820Sstevel@tonic-gate error("negative access time on " 11830Sstevel@tonic-gate "%s; not preserving\n", name); 11840Sstevel@tonic-gate } 11850Sstevel@tonic-gate } 11860Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n", 11870Sstevel@tonic-gate mtime, atime); 11880Sstevel@tonic-gate (void) desrcpwrite(rem, buf, strlen(buf)); 11890Sstevel@tonic-gate if (response() < 0) { 11900Sstevel@tonic-gate (void) close(f); 11910Sstevel@tonic-gate continue; 11920Sstevel@tonic-gate } 11930Sstevel@tonic-gate } 11940Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n", 11950Sstevel@tonic-gate (uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size, 11960Sstevel@tonic-gate last); 11970Sstevel@tonic-gate (void) desrcpwrite(rem, buf, strlen(buf)); 11980Sstevel@tonic-gate if (response() < 0) { 11990Sstevel@tonic-gate (void) close(f); 12000Sstevel@tonic-gate continue; 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate /* ACL support: send */ 1204789Sahrens if (aclflag | acl_aclflag) { 12050Sstevel@tonic-gate /* get acl from f and send it over */ 12060Sstevel@tonic-gate if (sendacl(f) == ACL_FAIL) { 12070Sstevel@tonic-gate (void) close(f); 12080Sstevel@tonic-gate continue; 12090Sstevel@tonic-gate } 12100Sstevel@tonic-gate } 12110Sstevel@tonic-gate if ((krb5auth_flag > 0) || (iamremote == 1)) { 12120Sstevel@tonic-gate bp = allocbuf(&buffer, f, RCP_BUFSIZE); 12130Sstevel@tonic-gate if (bp == NULLBUF) { 12140Sstevel@tonic-gate (void) close(f); 12150Sstevel@tonic-gate continue; 12160Sstevel@tonic-gate } 12170Sstevel@tonic-gate readerr = 0; 12180Sstevel@tonic-gate for (i = 0; i < stb.st_size; i += bp->cnt) { 12190Sstevel@tonic-gate amt = bp->cnt; 12200Sstevel@tonic-gate if (i + amt > stb.st_size) 12210Sstevel@tonic-gate amt = stb.st_size - i; 12220Sstevel@tonic-gate if (readerr == 0 && 12230Sstevel@tonic-gate read(f, bp->buf, amt) != amt) 12240Sstevel@tonic-gate readerr = errno; 12250Sstevel@tonic-gate (void) desrcpwrite(rem, bp->buf, amt); 12260Sstevel@tonic-gate } 12270Sstevel@tonic-gate (void) close(f); 12280Sstevel@tonic-gate if (readerr == 0) 12290Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 12300Sstevel@tonic-gate else 12310Sstevel@tonic-gate error("rcp: %s: %s\n", name, 12320Sstevel@tonic-gate error_message(readerr)); 12330Sstevel@tonic-gate } else { 12340Sstevel@tonic-gate cnt = off = 0; 12350Sstevel@tonic-gate size = stb.st_size; 12360Sstevel@tonic-gate while (size != 0) { 12370Sstevel@tonic-gate amt = MIN(size, SENDFILE_SIZE); 12380Sstevel@tonic-gate cnt = sendfile(rem, f, &off, amt); 12392039Sblu if (cnt == -1) { 12402039Sblu if (errno == EINTR) { 12412039Sblu continue; 12422039Sblu } else { 12432039Sblu break; 12442039Sblu } 12452039Sblu } 12463370Sjs198686 if (cnt == 0) 12473370Sjs198686 break; 12480Sstevel@tonic-gate size -= cnt; 12490Sstevel@tonic-gate } 12503370Sjs198686 if (cnt < 0) { 12510Sstevel@tonic-gate error("rcp: %s: %s\n", name, strerror(errno)); 12523370Sjs198686 } else if (cnt == 0 && size != 0) { 12533370Sjs198686 error("rcp: %s: unexpected end of file\n", 12543370Sjs198686 name); 12553370Sjs198686 lingerbuf.l_onoff = 1; 12563370Sjs198686 lingerbuf.l_linger = 0; 12573370Sjs198686 (void) setsockopt(rem, SOL_SOCKET, SO_LINGER, 12583370Sjs198686 &lingerbuf, sizeof (lingerbuf)); 12593370Sjs198686 /* 12603370Sjs198686 * When response() (see below) is invoked it 12613370Sjs198686 * tries to read data from closed handle which 12623370Sjs198686 * triggers error and lostconn() function. 12633370Sjs198686 * lostconn() terminates the program with 12643370Sjs198686 * appropriate message. 12653370Sjs198686 */ 12663370Sjs198686 (void) close(rem); 12673370Sjs198686 rem = -1; 12680Sstevel@tonic-gate } else { 12690Sstevel@tonic-gate (void) write(rem, "", 1); 12700Sstevel@tonic-gate } 12710Sstevel@tonic-gate (void) close(f); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate (void) response(); 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate } 12760Sstevel@tonic-gate 12770Sstevel@tonic-gate 12780Sstevel@tonic-gate static void 12790Sstevel@tonic-gate rsource(char *name, struct stat *statp) 12800Sstevel@tonic-gate { 12810Sstevel@tonic-gate DIR *d; 12820Sstevel@tonic-gate struct dirent *dp; 12830Sstevel@tonic-gate char *last, *vect[1]; 12840Sstevel@tonic-gate char path[MAXPATHLEN]; 12850Sstevel@tonic-gate 12860Sstevel@tonic-gate if (!(d = opendir(name))) { 12870Sstevel@tonic-gate error("rcp: %s: %s\n", name, strerror(errno)); 12880Sstevel@tonic-gate return; 12890Sstevel@tonic-gate } 12900Sstevel@tonic-gate last = rindex(name, '/'); 12910Sstevel@tonic-gate if (last == 0) 12920Sstevel@tonic-gate last = name; 12930Sstevel@tonic-gate else 12940Sstevel@tonic-gate last++; 12950Sstevel@tonic-gate if (pflag) { 12960Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n", 12970Sstevel@tonic-gate statp->st_mtime, statp->st_atime); 12980Sstevel@tonic-gate (void) desrcpwrite(rem, path, strlen(path)); 12990Sstevel@tonic-gate if (response() < 0) { 13000Sstevel@tonic-gate (void) closedir(d); 13010Sstevel@tonic-gate return; 13020Sstevel@tonic-gate } 13030Sstevel@tonic-gate } 13040Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "D%04o %d %s\n", 13050Sstevel@tonic-gate (uint_t)(statp->st_mode & 07777), 0, last); 13060Sstevel@tonic-gate (void) desrcpwrite(rem, path, strlen(path)); 13070Sstevel@tonic-gate 13080Sstevel@tonic-gate /* acl support for directory */ 13090Sstevel@tonic-gate if (aclflag) { 13100Sstevel@tonic-gate /* get acl from f and send it over */ 13110Sstevel@tonic-gate if (sendacl(d->dd_fd) == ACL_FAIL) { 13120Sstevel@tonic-gate (void) closedir(d); 13130Sstevel@tonic-gate return; 13140Sstevel@tonic-gate } 13150Sstevel@tonic-gate } 13160Sstevel@tonic-gate 13170Sstevel@tonic-gate if (response() < 0) { 13180Sstevel@tonic-gate (void) closedir(d); 13190Sstevel@tonic-gate return; 13200Sstevel@tonic-gate } 13210Sstevel@tonic-gate 13220Sstevel@tonic-gate while (dp = readdir(d)) { 13230Sstevel@tonic-gate if (dp->d_ino == 0) 13240Sstevel@tonic-gate continue; 13250Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) || 13260Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0)) 13270Sstevel@tonic-gate continue; 13280Sstevel@tonic-gate if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >= 13290Sstevel@tonic-gate MAXPATHLEN - 1) { 13300Sstevel@tonic-gate error("%s/%s: name too long.\n", name, dp->d_name); 13310Sstevel@tonic-gate continue; 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", 13340Sstevel@tonic-gate name, dp->d_name); 13350Sstevel@tonic-gate vect[0] = path; 13360Sstevel@tonic-gate source(1, vect); 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate (void) closedir(d); 13390Sstevel@tonic-gate (void) desrcpwrite(rem, "E\n", 2); 13400Sstevel@tonic-gate (void) response(); 13410Sstevel@tonic-gate } 13420Sstevel@tonic-gate 13430Sstevel@tonic-gate static int 13440Sstevel@tonic-gate response(void) 13450Sstevel@tonic-gate { 13460Sstevel@tonic-gate register char *cp; 13470Sstevel@tonic-gate char ch, resp, rbuf[RCP_BUFSIZE]; 13480Sstevel@tonic-gate 13490Sstevel@tonic-gate if (desrcpread(rem, &resp, 1) != 1) 13500Sstevel@tonic-gate lostconn(); 13510Sstevel@tonic-gate cp = rbuf; 13520Sstevel@tonic-gate switch (resp) { 13530Sstevel@tonic-gate case 0: /* ok */ 13540Sstevel@tonic-gate return (0); 13550Sstevel@tonic-gate default: 13560Sstevel@tonic-gate *cp++ = resp; 13570Sstevel@tonic-gate /* FALLTHROUGH */ 13580Sstevel@tonic-gate case 1: /* error, followed by err msg */ 13590Sstevel@tonic-gate case 2: /* fatal error, "" */ 13600Sstevel@tonic-gate do { 13610Sstevel@tonic-gate if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 13620Sstevel@tonic-gate lostconn(); 13630Sstevel@tonic-gate *cp++ = ch; 13640Sstevel@tonic-gate } while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n'); 13650Sstevel@tonic-gate 13660Sstevel@tonic-gate if (!iamremote) 13670Sstevel@tonic-gate (void) write(STDERR_FILENO, rbuf, cp - rbuf); 13680Sstevel@tonic-gate ++errs; 13690Sstevel@tonic-gate if (resp == 1) 13700Sstevel@tonic-gate return (-1); 13710Sstevel@tonic-gate exit(1); 13720Sstevel@tonic-gate } 13730Sstevel@tonic-gate /*NOTREACHED*/ 13740Sstevel@tonic-gate } 13750Sstevel@tonic-gate 13760Sstevel@tonic-gate static void 13770Sstevel@tonic-gate lostconn(void) 13780Sstevel@tonic-gate { 13790Sstevel@tonic-gate if (!iamremote) 13800Sstevel@tonic-gate (void) fprintf(stderr, "rcp: lost connection\n"); 13810Sstevel@tonic-gate exit(1); 13820Sstevel@tonic-gate } 13830Sstevel@tonic-gate 13840Sstevel@tonic-gate 13850Sstevel@tonic-gate static void 13860Sstevel@tonic-gate sink(int argc, char *argv[]) 13870Sstevel@tonic-gate { 13880Sstevel@tonic-gate char *cp; 13890Sstevel@tonic-gate static BUF buffer; 13900Sstevel@tonic-gate struct stat stb; 13910Sstevel@tonic-gate struct timeval tv[2]; 13920Sstevel@tonic-gate BUF *bp; 13930Sstevel@tonic-gate off_t i, j; 13940Sstevel@tonic-gate char ch, *targ, *why; 13950Sstevel@tonic-gate int amt, count, exists, first, mask, mode; 13960Sstevel@tonic-gate off_t size; 13970Sstevel@tonic-gate int ofd, setimes, targisdir, wrerr; 13980Sstevel@tonic-gate char *np, *vect[1], buf[RCP_BUFSIZE]; 13990Sstevel@tonic-gate char *namebuf = NULL; 14000Sstevel@tonic-gate size_t namebuf_sz = 0; 14010Sstevel@tonic-gate size_t need; 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate #define atime tv[0] 14040Sstevel@tonic-gate #define mtime tv[1] 14050Sstevel@tonic-gate #define SCREWUP(str) { why = str; goto screwup; } 14060Sstevel@tonic-gate 14070Sstevel@tonic-gate setimes = targisdir = 0; 14080Sstevel@tonic-gate mask = umask(0); 14090Sstevel@tonic-gate if (!pflag) 14100Sstevel@tonic-gate (void) umask(mask); 14110Sstevel@tonic-gate if (argc != 1) { 14120Sstevel@tonic-gate error("rcp: ambiguous target\n"); 14130Sstevel@tonic-gate exit(1); 14140Sstevel@tonic-gate } 14150Sstevel@tonic-gate targ = *argv; 14160Sstevel@tonic-gate if (targetshouldbedirectory) 14170Sstevel@tonic-gate verifydir(targ); 14180Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 14190Sstevel@tonic-gate 14200Sstevel@tonic-gate if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) 14210Sstevel@tonic-gate targisdir = 1; 14220Sstevel@tonic-gate for (first = 1; ; first = 0) { 14230Sstevel@tonic-gate cp = buf; 14240Sstevel@tonic-gate if (desrcpread(rem, cp, 1) <= 0) { 14250Sstevel@tonic-gate if (namebuf != NULL) 14260Sstevel@tonic-gate free(namebuf); 14270Sstevel@tonic-gate return; 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate 14300Sstevel@tonic-gate if (*cp++ == '\n') 14310Sstevel@tonic-gate SCREWUP("unexpected <newline>"); 14320Sstevel@tonic-gate do { 14330Sstevel@tonic-gate if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) 14340Sstevel@tonic-gate SCREWUP("lost connection"); 14350Sstevel@tonic-gate *cp++ = ch; 14360Sstevel@tonic-gate } while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n'); 14370Sstevel@tonic-gate *cp = 0; 14380Sstevel@tonic-gate 14390Sstevel@tonic-gate if (buf[0] == '\01' || buf[0] == '\02') { 14400Sstevel@tonic-gate if (iamremote == 0) 14410Sstevel@tonic-gate (void) write(STDERR_FILENO, buf + 1, 14420Sstevel@tonic-gate strlen(buf + 1)); 14430Sstevel@tonic-gate if (buf[0] == '\02') 14440Sstevel@tonic-gate exit(1); 14450Sstevel@tonic-gate errs++; 14460Sstevel@tonic-gate continue; 14470Sstevel@tonic-gate } 14480Sstevel@tonic-gate if (buf[0] == 'E') { 14490Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 14500Sstevel@tonic-gate if (namebuf != NULL) 14510Sstevel@tonic-gate free(namebuf); 14520Sstevel@tonic-gate return; 14530Sstevel@tonic-gate } 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate if (ch == '\n') 14560Sstevel@tonic-gate *--cp = 0; 14570Sstevel@tonic-gate cp = buf; 14580Sstevel@tonic-gate if (*cp == 'T') { 14590Sstevel@tonic-gate setimes++; 14600Sstevel@tonic-gate cp++; 14610Sstevel@tonic-gate mtime.tv_sec = strtol(cp, &cp, 0); 14620Sstevel@tonic-gate if (*cp++ != ' ') 14630Sstevel@tonic-gate SCREWUP("mtime.sec not delimited"); 14640Sstevel@tonic-gate mtime.tv_usec = strtol(cp, &cp, 0); 14650Sstevel@tonic-gate if (*cp++ != ' ') 14660Sstevel@tonic-gate SCREWUP("mtime.usec not delimited"); 14670Sstevel@tonic-gate atime.tv_sec = strtol(cp, &cp, 0); 14680Sstevel@tonic-gate if (*cp++ != ' ') 14690Sstevel@tonic-gate SCREWUP("atime.sec not delimited"); 14700Sstevel@tonic-gate atime.tv_usec = strtol(cp, &cp, 0); 14710Sstevel@tonic-gate if (*cp++ != '\0') 14720Sstevel@tonic-gate SCREWUP("atime.usec not delimited"); 14730Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 14740Sstevel@tonic-gate continue; 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate if (*cp != 'C' && *cp != 'D') { 14770Sstevel@tonic-gate /* 14780Sstevel@tonic-gate * Check for the case "rcp remote:foo\* local:bar". 14790Sstevel@tonic-gate * In this case, the line "No match." can be returned 14800Sstevel@tonic-gate * by the shell before the rcp command on the remote is 14810Sstevel@tonic-gate * executed so the ^Aerror_message convention isn't 14820Sstevel@tonic-gate * followed. 14830Sstevel@tonic-gate */ 14840Sstevel@tonic-gate if (first) { 14850Sstevel@tonic-gate error("%s\n", cp); 14860Sstevel@tonic-gate exit(1); 14870Sstevel@tonic-gate } 14880Sstevel@tonic-gate SCREWUP("expected control record"); 14890Sstevel@tonic-gate } 14900Sstevel@tonic-gate mode = 0; 14910Sstevel@tonic-gate for (++cp; cp < buf + 5; cp++) { 14920Sstevel@tonic-gate if (*cp < '0' || *cp > '7') 14930Sstevel@tonic-gate SCREWUP("bad mode"); 14940Sstevel@tonic-gate mode = (mode << 3) | (*cp - '0'); 14950Sstevel@tonic-gate } 14960Sstevel@tonic-gate if (*cp++ != ' ') 14970Sstevel@tonic-gate SCREWUP("mode not delimited"); 14980Sstevel@tonic-gate size = 0; 14990Sstevel@tonic-gate while (isdigit(*cp)) 15000Sstevel@tonic-gate size = size * 10 + (*cp++ - '0'); 15010Sstevel@tonic-gate if (*cp++ != ' ') 15020Sstevel@tonic-gate SCREWUP("size not delimited"); 15030Sstevel@tonic-gate if (targisdir) { 15040Sstevel@tonic-gate need = strlen(targ) + sizeof ("/") + strlen(cp); 15050Sstevel@tonic-gate if (need > namebuf_sz) { 15060Sstevel@tonic-gate if ((namebuf = realloc(namebuf, need)) == NULL) { 15070Sstevel@tonic-gate error("rcp: out of memory\n"); 15080Sstevel@tonic-gate exit(1); 15090Sstevel@tonic-gate } 15100Sstevel@tonic-gate namebuf_sz = need; 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate (void) snprintf(namebuf, need, "%s%s%s", targ, 15130Sstevel@tonic-gate *targ ? "/" : "", cp); 15140Sstevel@tonic-gate np = namebuf; 15150Sstevel@tonic-gate } else { 15160Sstevel@tonic-gate np = targ; 15170Sstevel@tonic-gate } 15180Sstevel@tonic-gate 15190Sstevel@tonic-gate exists = stat(np, &stb) == 0; 15200Sstevel@tonic-gate if (buf[0] == 'D') { 15210Sstevel@tonic-gate if (exists) { 15220Sstevel@tonic-gate if ((stb.st_mode&S_IFMT) != S_IFDIR) { 1523789Sahrens if (aclflag | acl_aclflag) { 15240Sstevel@tonic-gate /* 15250Sstevel@tonic-gate * consume acl in the pipe 15260Sstevel@tonic-gate * fd = -1 to indicate the 15270Sstevel@tonic-gate * special case 15280Sstevel@tonic-gate */ 15290Sstevel@tonic-gate if (recvacl(-1, exists, pflag) 15300Sstevel@tonic-gate == ACL_FAIL) { 15310Sstevel@tonic-gate goto bad; 15320Sstevel@tonic-gate } 15330Sstevel@tonic-gate } 15340Sstevel@tonic-gate errno = ENOTDIR; 15350Sstevel@tonic-gate goto bad; 15360Sstevel@tonic-gate } 15370Sstevel@tonic-gate if (pflag) 15380Sstevel@tonic-gate (void) chmod(np, mode); 15390Sstevel@tonic-gate } else if (mkdir(np, mode) < 0) { 15400Sstevel@tonic-gate if (aclflag) { 15410Sstevel@tonic-gate /* consume acl in the pipe */ 15420Sstevel@tonic-gate (void) recvacl(-1, exists, pflag); 15430Sstevel@tonic-gate } 15440Sstevel@tonic-gate goto bad; 15450Sstevel@tonic-gate } 15460Sstevel@tonic-gate 15470Sstevel@tonic-gate /* acl support for directories */ 1548789Sahrens if (aclflag | acl_aclflag) { 15490Sstevel@tonic-gate int dfd; 15500Sstevel@tonic-gate 15510Sstevel@tonic-gate if ((dfd = open(np, O_RDONLY)) == -1) 15520Sstevel@tonic-gate goto bad; 15530Sstevel@tonic-gate 15540Sstevel@tonic-gate /* get acl and set it to ofd */ 15550Sstevel@tonic-gate if (recvacl(dfd, exists, pflag) == ACL_FAIL) { 15560Sstevel@tonic-gate (void) close(dfd); 15570Sstevel@tonic-gate if (!exists) 15580Sstevel@tonic-gate (void) rmdir(np); 15590Sstevel@tonic-gate goto bad; 15600Sstevel@tonic-gate } 15610Sstevel@tonic-gate (void) close(dfd); 15620Sstevel@tonic-gate } 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate vect[0] = np; 15650Sstevel@tonic-gate sink(1, vect); 15660Sstevel@tonic-gate if (setimes) { 15670Sstevel@tonic-gate setimes = 0; 15680Sstevel@tonic-gate if (utimes(np, tv) < 0) 15690Sstevel@tonic-gate error("rcp: can't set times on %s: %s\n", 15700Sstevel@tonic-gate np, strerror(errno)); 15710Sstevel@tonic-gate } 15720Sstevel@tonic-gate continue; 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 15760Sstevel@tonic-gate bad: 15770Sstevel@tonic-gate error("rcp: %s: %s\n", np, strerror(errno)); 15780Sstevel@tonic-gate continue; 15790Sstevel@tonic-gate } 15800Sstevel@tonic-gate 15810Sstevel@tonic-gate /* 15820Sstevel@tonic-gate * If the output file exists we have to force zflag off 15830Sstevel@tonic-gate * to avoid erroneously seeking past old data. 15840Sstevel@tonic-gate */ 15850Sstevel@tonic-gate zopen(ofd, zflag && !exists); 15860Sstevel@tonic-gate 15870Sstevel@tonic-gate if (exists && pflag) 15880Sstevel@tonic-gate (void) fchmod(ofd, mode); 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate /* 15930Sstevel@tonic-gate * ACL support: receiving 15940Sstevel@tonic-gate */ 1595789Sahrens if (aclflag | acl_aclflag) { 15960Sstevel@tonic-gate /* get acl and set it to ofd */ 15970Sstevel@tonic-gate if (recvacl(ofd, exists, pflag) == ACL_FAIL) { 15980Sstevel@tonic-gate (void) close(ofd); 15990Sstevel@tonic-gate if (!exists) 16000Sstevel@tonic-gate (void) unlink(np); 16010Sstevel@tonic-gate continue; 16020Sstevel@tonic-gate } 16030Sstevel@tonic-gate } 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) { 16060Sstevel@tonic-gate (void) close(ofd); 16070Sstevel@tonic-gate continue; 16080Sstevel@tonic-gate } 16090Sstevel@tonic-gate cp = bp->buf; 16100Sstevel@tonic-gate count = 0; 16110Sstevel@tonic-gate wrerr = 0; 16120Sstevel@tonic-gate for (i = 0; i < size; i += RCP_BUFSIZE) { 16130Sstevel@tonic-gate amt = RCP_BUFSIZE; 16140Sstevel@tonic-gate if (i + amt > size) 16150Sstevel@tonic-gate amt = size - i; 16160Sstevel@tonic-gate count += amt; 16170Sstevel@tonic-gate do { 16180Sstevel@tonic-gate j = desrcpread(rem, cp, amt); 16190Sstevel@tonic-gate if (j <= 0) { 16200Sstevel@tonic-gate int sverrno = errno; 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate /* 16230Sstevel@tonic-gate * Connection to supplier lost. 16240Sstevel@tonic-gate * Truncate file to correspond 16250Sstevel@tonic-gate * to amount already transferred. 16260Sstevel@tonic-gate * 16270Sstevel@tonic-gate * Note that we must call ftruncate() 16280Sstevel@tonic-gate * before any call to error() (which 16290Sstevel@tonic-gate * might result in a SIGPIPE and 16300Sstevel@tonic-gate * sudden death before we have a chance 16310Sstevel@tonic-gate * to correct the file's size). 16320Sstevel@tonic-gate */ 16330Sstevel@tonic-gate size = lseek(ofd, 0, SEEK_CUR); 16340Sstevel@tonic-gate if ((ftruncate(ofd, size) == -1) && 16350Sstevel@tonic-gate (errno != EINVAL) && 16360Sstevel@tonic-gate (errno != EACCES)) 16370Sstevel@tonic-gate #define TRUNCERR "rcp: can't truncate %s: %s\n" 16380Sstevel@tonic-gate error(TRUNCERR, np, 16390Sstevel@tonic-gate strerror(errno)); 16400Sstevel@tonic-gate error("rcp: %s\n", 16410Sstevel@tonic-gate j ? strerror(sverrno) : 16420Sstevel@tonic-gate "dropped connection"); 16430Sstevel@tonic-gate (void) close(ofd); 16440Sstevel@tonic-gate exit(1); 16450Sstevel@tonic-gate } 16460Sstevel@tonic-gate amt -= j; 16470Sstevel@tonic-gate cp += j; 16480Sstevel@tonic-gate } while (amt > 0); 16490Sstevel@tonic-gate if (count == bp->cnt) { 16500Sstevel@tonic-gate cp = bp->buf; 16510Sstevel@tonic-gate if (wrerr == 0 && 16520Sstevel@tonic-gate zwrite(ofd, cp, count) < 0) 16530Sstevel@tonic-gate wrerr++; 16540Sstevel@tonic-gate count = 0; 16550Sstevel@tonic-gate } 16560Sstevel@tonic-gate } 16570Sstevel@tonic-gate if (count != 0 && wrerr == 0 && 16580Sstevel@tonic-gate zwrite(ofd, bp->buf, count) < 0) 16590Sstevel@tonic-gate wrerr++; 16600Sstevel@tonic-gate if (zclose(ofd) < 0) 16610Sstevel@tonic-gate wrerr++; 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate 16640Sstevel@tonic-gate if ((ftruncate(ofd, size) == -1) && (errno != EINVAL) && 16650Sstevel@tonic-gate (errno != EACCES)) { 16660Sstevel@tonic-gate error(TRUNCERR, np, strerror(errno)); 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate (void) close(ofd); 16690Sstevel@tonic-gate (void) response(); 16700Sstevel@tonic-gate if (setimes) { 16710Sstevel@tonic-gate setimes = 0; 16720Sstevel@tonic-gate if (utimes(np, tv) < 0) 16730Sstevel@tonic-gate error("rcp: can't set times on %s: %s\n", 16740Sstevel@tonic-gate np, strerror(errno)); 16750Sstevel@tonic-gate } 16760Sstevel@tonic-gate if (wrerr) 16770Sstevel@tonic-gate error("rcp: %s: %s\n", np, strerror(errno)); 16780Sstevel@tonic-gate else 16790Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 16800Sstevel@tonic-gate } 16810Sstevel@tonic-gate screwup: 16820Sstevel@tonic-gate error("rcp: protocol screwup: %s\n", why); 16830Sstevel@tonic-gate exit(1); 16840Sstevel@tonic-gate } 16850Sstevel@tonic-gate 16860Sstevel@tonic-gate #ifndef roundup 16870Sstevel@tonic-gate #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 16880Sstevel@tonic-gate #endif /* !roundup */ 16890Sstevel@tonic-gate 16900Sstevel@tonic-gate static BUF * 16910Sstevel@tonic-gate allocbuf(BUF *bp, int fd, int blksize) 16920Sstevel@tonic-gate { 16930Sstevel@tonic-gate struct stat stb; 16940Sstevel@tonic-gate int size; 16950Sstevel@tonic-gate 16960Sstevel@tonic-gate if (fstat(fd, &stb) < 0) { 16970Sstevel@tonic-gate error("rcp: fstat: %s\n", strerror(errno)); 16980Sstevel@tonic-gate return (0); 16990Sstevel@tonic-gate } 17000Sstevel@tonic-gate size = roundup(stb.st_blksize, blksize); 17010Sstevel@tonic-gate if (size == 0) 17020Sstevel@tonic-gate size = blksize; 17030Sstevel@tonic-gate if (bp->cnt < size) { 17040Sstevel@tonic-gate if (bp->buf != 0) 17050Sstevel@tonic-gate free(bp->buf); 17060Sstevel@tonic-gate bp->buf = (char *)malloc((uint_t)size); 17070Sstevel@tonic-gate if (!bp->buf) { 17080Sstevel@tonic-gate error("rcp: malloc: out of memory\n"); 17090Sstevel@tonic-gate return (0); 17100Sstevel@tonic-gate } 17110Sstevel@tonic-gate } 17120Sstevel@tonic-gate bp->cnt = size; 17130Sstevel@tonic-gate return (bp); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate 17160Sstevel@tonic-gate static void 17170Sstevel@tonic-gate usage(void) 17180Sstevel@tonic-gate { 17190Sstevel@tonic-gate (void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"), 17200Sstevel@tonic-gate gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] " 17210Sstevel@tonic-gate #ifdef DEBUG 17220Sstevel@tonic-gate "[-D port] " 17230Sstevel@tonic-gate #endif /* DEBUG */ 17240Sstevel@tonic-gate "f1 f2; or:\n"), 17250Sstevel@tonic-gate gettext("\trcp [-r] [-p] [-a] [-x] " 17260Sstevel@tonic-gate #ifdef DEBUG 17270Sstevel@tonic-gate "[-D port] " 17280Sstevel@tonic-gate #endif /* DEBUG */ 17290Sstevel@tonic-gate "[-k realm] [-PN / -PO] f1...fn d2\n")); 17300Sstevel@tonic-gate exit(1); 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate 17340Sstevel@tonic-gate /* 17350Sstevel@tonic-gate * sparse file support 17360Sstevel@tonic-gate */ 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate static off_t zbsize; 17390Sstevel@tonic-gate static off_t zlastseek; 17400Sstevel@tonic-gate 17410Sstevel@tonic-gate /* is it ok to try to create holes? */ 17420Sstevel@tonic-gate static void 17430Sstevel@tonic-gate zopen(int fd, int flag) 17440Sstevel@tonic-gate { 17450Sstevel@tonic-gate struct stat st; 17460Sstevel@tonic-gate 17470Sstevel@tonic-gate zbsize = 0; 17480Sstevel@tonic-gate zlastseek = 0; 17490Sstevel@tonic-gate 17500Sstevel@tonic-gate if (flag && 17510Sstevel@tonic-gate fstat(fd, &st) == 0 && 17520Sstevel@tonic-gate (st.st_mode & S_IFMT) == S_IFREG) 17530Sstevel@tonic-gate zbsize = st.st_blksize; 17540Sstevel@tonic-gate } 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate /* write and/or seek */ 17570Sstevel@tonic-gate static int 17580Sstevel@tonic-gate zwrite(int fd, char *buf, int nbytes) 17590Sstevel@tonic-gate { 17600Sstevel@tonic-gate off_t block = zbsize ? zbsize : nbytes; 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate do { 17630Sstevel@tonic-gate if (block > nbytes) 17640Sstevel@tonic-gate block = nbytes; 17650Sstevel@tonic-gate nbytes -= block; 17660Sstevel@tonic-gate 17670Sstevel@tonic-gate if (!zbsize || notzero(buf, block)) { 17680Sstevel@tonic-gate register int n, count = block; 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate do { 17710Sstevel@tonic-gate if ((n = write(fd, buf, count)) < 0) 17720Sstevel@tonic-gate return (-1); 17730Sstevel@tonic-gate buf += n; 17740Sstevel@tonic-gate } while ((count -= n) > 0); 17750Sstevel@tonic-gate zlastseek = 0; 17760Sstevel@tonic-gate } else { 17770Sstevel@tonic-gate if (lseek(fd, (off_t)block, SEEK_CUR) < 0) 17780Sstevel@tonic-gate return (-1); 17790Sstevel@tonic-gate buf += block; 17800Sstevel@tonic-gate zlastseek = 1; 17810Sstevel@tonic-gate } 17820Sstevel@tonic-gate } while (nbytes > 0); 17830Sstevel@tonic-gate 17840Sstevel@tonic-gate return (0); 17850Sstevel@tonic-gate } 17860Sstevel@tonic-gate 17870Sstevel@tonic-gate /* write last byte of file if necessary */ 17880Sstevel@tonic-gate static int 17890Sstevel@tonic-gate zclose(int fd) 17900Sstevel@tonic-gate { 17910Sstevel@tonic-gate zbsize = 0; 17920Sstevel@tonic-gate 17930Sstevel@tonic-gate if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 || 17940Sstevel@tonic-gate zwrite(fd, "", 1) < 0)) 17950Sstevel@tonic-gate return (-1); 17960Sstevel@tonic-gate else 17970Sstevel@tonic-gate return (0); 17980Sstevel@tonic-gate } 17990Sstevel@tonic-gate 18000Sstevel@tonic-gate /* return true if buffer is not all zeros */ 18010Sstevel@tonic-gate static int 18020Sstevel@tonic-gate notzero(char *p, int n) 18030Sstevel@tonic-gate { 18040Sstevel@tonic-gate register int result = 0; 18050Sstevel@tonic-gate 18060Sstevel@tonic-gate while ((int)p & 3 && --n >= 0) 18070Sstevel@tonic-gate result |= *p++; 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate while ((n -= 4 * sizeof (int)) >= 0) { 18100Sstevel@tonic-gate /* LINTED */ 18110Sstevel@tonic-gate result |= ((int *)p)[0]; 18120Sstevel@tonic-gate /* LINTED */ 18130Sstevel@tonic-gate result |= ((int *)p)[1]; 18140Sstevel@tonic-gate /* LINTED */ 18150Sstevel@tonic-gate result |= ((int *)p)[2]; 18160Sstevel@tonic-gate /* LINTED */ 18170Sstevel@tonic-gate result |= ((int *)p)[3]; 18180Sstevel@tonic-gate if (result) 18190Sstevel@tonic-gate return (result); 18200Sstevel@tonic-gate p += 4 * sizeof (int); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate n += 4 * sizeof (int); 18230Sstevel@tonic-gate 18240Sstevel@tonic-gate while (--n >= 0) 18250Sstevel@tonic-gate result |= *p++; 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate return (result); 18280Sstevel@tonic-gate } 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate /* 18310Sstevel@tonic-gate * New functions to support ACLs 18320Sstevel@tonic-gate */ 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate /* 18350Sstevel@tonic-gate * Get acl from f and send it over. 18360Sstevel@tonic-gate * ACL record includes acl entry count, acl text length, and acl text. 18370Sstevel@tonic-gate */ 18380Sstevel@tonic-gate static int 18390Sstevel@tonic-gate sendacl(int f) 18400Sstevel@tonic-gate { 18410Sstevel@tonic-gate int aclcnt; 18420Sstevel@tonic-gate char *acltext; 18430Sstevel@tonic-gate char buf[BUFSIZ]; 1844789Sahrens acl_t *aclp; 1845789Sahrens char acltype; 1846789Sahrens int aclerror; 1847789Sahrens int trivial; 18480Sstevel@tonic-gate 1849789Sahrens 1850789Sahrens aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp); 1851789Sahrens if (aclerror != 0) { 1852789Sahrens error("can't retrieve ACL: %s \n", acl_strerror(aclerror)); 18530Sstevel@tonic-gate return (ACL_FAIL); 18540Sstevel@tonic-gate } 18550Sstevel@tonic-gate 1856789Sahrens /* 1857789Sahrens * if acl type is not ACLENT_T and were operating in acl_aclflag == 0 1858789Sahrens * then don't do the malloc and facl(fd, getcntcmd,...); 1859789Sahrens * since the remote side doesn't support alternate style ACL's. 1860789Sahrens */ 1861789Sahrens 1862789Sahrens if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) { 1863789Sahrens aclcnt = MIN_ACL_ENTRIES; 1864789Sahrens acltype = 'A'; 1865789Sahrens trivial = ACL_IS_TRIVIAL; 1866789Sahrens } else { 1867789Sahrens 1868789Sahrens aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0; 1869789Sahrens 1870789Sahrens if (aclp) { 1871789Sahrens acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A'; 1872789Sahrens aclcnt = acl_cnt(aclp); 1873789Sahrens trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL); 1874789Sahrens } else { 1875789Sahrens acltype = 'A'; 1876789Sahrens aclcnt = MIN_ACL_ENTRIES; 1877789Sahrens trivial = ACL_IS_TRIVIAL; 1878789Sahrens } 1879789Sahrens 1880789Sahrens } 1881789Sahrens 18820Sstevel@tonic-gate /* send the acl count over */ 1883789Sahrens (void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt); 18840Sstevel@tonic-gate (void) desrcpwrite(rem, buf, strlen(buf)); 18850Sstevel@tonic-gate 1886789Sahrens /* 1887789Sahrens * only send acl when we have an aclp, which would 1888789Sahrens * imply its not trivial. 1889789Sahrens */ 1890789Sahrens if (aclp && (trivial != ACL_IS_TRIVIAL)) { 18911420Smarks acltext = acl_totext(aclp, 0); 18920Sstevel@tonic-gate if (acltext == NULL) { 18930Sstevel@tonic-gate error("rcp: failed to convert to text\n"); 1894789Sahrens acl_free(aclp); 18950Sstevel@tonic-gate return (ACL_FAIL); 18960Sstevel@tonic-gate } 18970Sstevel@tonic-gate 18980Sstevel@tonic-gate /* send ACLs over: send the length first */ 1899789Sahrens (void) snprintf(buf, sizeof (buf), "%c%d\n", 1900789Sahrens acltype, strlen(acltext)); 19010Sstevel@tonic-gate 19020Sstevel@tonic-gate (void) desrcpwrite(rem, buf, strlen(buf)); 19030Sstevel@tonic-gate (void) desrcpwrite(rem, acltext, strlen(acltext)); 19040Sstevel@tonic-gate free(acltext); 1905789Sahrens if (response() < 0) { 1906789Sahrens acl_free(aclp); 19070Sstevel@tonic-gate return (ACL_FAIL); 1908789Sahrens } 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate } 1911789Sahrens 1912789Sahrens if (aclp) 1913789Sahrens acl_free(aclp); 19140Sstevel@tonic-gate return (ACL_OK); 19150Sstevel@tonic-gate } 19160Sstevel@tonic-gate 19170Sstevel@tonic-gate /* 19180Sstevel@tonic-gate * Use this routine to get acl entry count and acl text size (in bytes) 19190Sstevel@tonic-gate */ 19200Sstevel@tonic-gate static int 1921789Sahrens getaclinfo(int *cnt, int *acltype) 19220Sstevel@tonic-gate { 19230Sstevel@tonic-gate char buf[BUFSIZ]; 19240Sstevel@tonic-gate char *cp; 19250Sstevel@tonic-gate char ch; 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate /* get acl count */ 19280Sstevel@tonic-gate cp = buf; 19290Sstevel@tonic-gate if (desrcpread(rem, cp, 1) <= 0) 19300Sstevel@tonic-gate return (ACL_FAIL); 1931789Sahrens 1932789Sahrens switch (*cp++) { 1933789Sahrens case 'A': 1934789Sahrens *acltype = 0; 1935789Sahrens break; 1936789Sahrens case 'Z': 1937789Sahrens *acltype = 1; 1938789Sahrens break; 1939789Sahrens default: 19400Sstevel@tonic-gate error("rcp: expect an ACL record, but got %c\n", *cp); 19410Sstevel@tonic-gate return (ACL_FAIL); 19420Sstevel@tonic-gate } 19430Sstevel@tonic-gate do { 19440Sstevel@tonic-gate if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) { 19450Sstevel@tonic-gate error("rcp: lost connection ..\n"); 19460Sstevel@tonic-gate return (ACL_FAIL); 19470Sstevel@tonic-gate } 19480Sstevel@tonic-gate *cp++ = ch; 19490Sstevel@tonic-gate } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 19500Sstevel@tonic-gate if (ch != '\n') { 19510Sstevel@tonic-gate error("rcp: ACL record corrupted \n"); 19520Sstevel@tonic-gate return (ACL_FAIL); 19530Sstevel@tonic-gate } 19540Sstevel@tonic-gate cp = &buf[1]; 19550Sstevel@tonic-gate *cnt = strtol(cp, &cp, 0); 19560Sstevel@tonic-gate if (*cp != '\n') { 19570Sstevel@tonic-gate error("rcp: ACL record corrupted \n"); 19580Sstevel@tonic-gate return (ACL_FAIL); 19590Sstevel@tonic-gate } 19600Sstevel@tonic-gate return (ACL_OK); 19610Sstevel@tonic-gate } 19620Sstevel@tonic-gate 19630Sstevel@tonic-gate 19640Sstevel@tonic-gate /* 19650Sstevel@tonic-gate * Receive acl from the pipe and set it to f 19660Sstevel@tonic-gate */ 19670Sstevel@tonic-gate static int 19680Sstevel@tonic-gate recvacl(int f, int exists, int preserve) 19690Sstevel@tonic-gate { 19700Sstevel@tonic-gate int aclcnt; /* acl entry count */ 19710Sstevel@tonic-gate int aclsize; /* acl text length */ 19720Sstevel@tonic-gate int j; 19730Sstevel@tonic-gate char *tp; 19740Sstevel@tonic-gate char *acltext; /* external format */ 1975789Sahrens acl_t *aclp; 1976789Sahrens int acltype; 1977789Sahrens int min_entries; 1978789Sahrens int aclerror; 19790Sstevel@tonic-gate 19800Sstevel@tonic-gate /* get acl count */ 1981789Sahrens if (getaclinfo(&aclcnt, &acltype) != ACL_OK) 19820Sstevel@tonic-gate return (ACL_FAIL); 19830Sstevel@tonic-gate 1984789Sahrens if (acltype == 0) { 1985789Sahrens min_entries = MIN_ACL_ENTRIES; 1986789Sahrens } else { 1987789Sahrens min_entries = 1; 1988789Sahrens } 1989789Sahrens 1990789Sahrens if (aclcnt > min_entries) { 19910Sstevel@tonic-gate /* get acl text size */ 1992789Sahrens if (getaclinfo(&aclsize, &acltype) != ACL_OK) 19930Sstevel@tonic-gate return (ACL_FAIL); 19940Sstevel@tonic-gate if ((acltext = malloc(aclsize + 1)) == NULL) { 19950Sstevel@tonic-gate error("rcp: cant allocate memory: %d\n", aclsize); 19960Sstevel@tonic-gate return (ACL_FAIL); 19970Sstevel@tonic-gate } 19980Sstevel@tonic-gate 19990Sstevel@tonic-gate tp = acltext; 20000Sstevel@tonic-gate do { 20010Sstevel@tonic-gate j = desrcpread(rem, tp, aclsize); 20020Sstevel@tonic-gate if (j <= 0) { 20030Sstevel@tonic-gate error("rcp: %s\n", j ? strerror(errno) : 20040Sstevel@tonic-gate "dropped connection"); 20050Sstevel@tonic-gate exit(1); 20060Sstevel@tonic-gate } 20070Sstevel@tonic-gate aclsize -= j; 20080Sstevel@tonic-gate tp += j; 20090Sstevel@tonic-gate } while (aclsize > 0); 20100Sstevel@tonic-gate *tp = '\0'; 20110Sstevel@tonic-gate 20120Sstevel@tonic-gate if (preserve || !exists) { 2013789Sahrens aclerror = acl_fromtext(acltext, &aclp); 2014789Sahrens if (aclerror != 0) { 2015789Sahrens error("rcp: failed to parse acl : %s\n", 2016789Sahrens acl_strerror(aclerror)); 20173226Smp204432 free(acltext); 20180Sstevel@tonic-gate return (ACL_FAIL); 20190Sstevel@tonic-gate } 2020789Sahrens 20210Sstevel@tonic-gate if (f != -1) { 2022789Sahrens if (facl_set(f, aclp) < 0) { 20230Sstevel@tonic-gate error("rcp: failed to set acl\n"); 20243226Smp204432 acl_free(aclp); 20253226Smp204432 free(acltext); 20260Sstevel@tonic-gate return (ACL_FAIL); 20270Sstevel@tonic-gate } 20280Sstevel@tonic-gate } 20290Sstevel@tonic-gate /* -1 means that just consume the data in the pipe */ 2030789Sahrens acl_free(aclp); 20310Sstevel@tonic-gate } 20320Sstevel@tonic-gate free(acltext); 20330Sstevel@tonic-gate (void) desrcpwrite(rem, "", 1); 20340Sstevel@tonic-gate } 20350Sstevel@tonic-gate return (ACL_OK); 20360Sstevel@tonic-gate } 20370Sstevel@tonic-gate 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate static char * 20400Sstevel@tonic-gate search_char(unsigned char *cp, unsigned char chr) 20410Sstevel@tonic-gate { 20420Sstevel@tonic-gate int len; 20430Sstevel@tonic-gate 20440Sstevel@tonic-gate while (*cp) { 20450Sstevel@tonic-gate if (*cp == chr) 20460Sstevel@tonic-gate return ((char *)cp); 20470Sstevel@tonic-gate if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 20480Sstevel@tonic-gate len = 1; 20490Sstevel@tonic-gate cp += len; 20500Sstevel@tonic-gate } 20510Sstevel@tonic-gate return (0); 20520Sstevel@tonic-gate } 20530Sstevel@tonic-gate 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate static int 20560Sstevel@tonic-gate desrcpread(int fd, char *buf, int len) 20570Sstevel@tonic-gate { 20580Sstevel@tonic-gate return ((int)desread(fd, buf, len, 0)); 20590Sstevel@tonic-gate } 20600Sstevel@tonic-gate 20610Sstevel@tonic-gate static int 20620Sstevel@tonic-gate desrcpwrite(int fd, char *buf, int len) 20630Sstevel@tonic-gate { 20640Sstevel@tonic-gate /* 20650Sstevel@tonic-gate * Note that rcp depends on the same file descriptor being both 20660Sstevel@tonic-gate * input and output to the remote side. This is bogus, especially 20670Sstevel@tonic-gate * when rcp is being run by a rsh that pipes. Fix it here because 20680Sstevel@tonic-gate * it would require significantly more work in other places. 20690Sstevel@tonic-gate * --hartmans 1/96 20700Sstevel@tonic-gate */ 20710Sstevel@tonic-gate 20720Sstevel@tonic-gate if (fd == 0) 20730Sstevel@tonic-gate fd = 1; 20740Sstevel@tonic-gate return ((int)deswrite(fd, buf, len, 0)); 20750Sstevel@tonic-gate } 20760Sstevel@tonic-gate 20770Sstevel@tonic-gate static char ** 20780Sstevel@tonic-gate save_argv(int argc, char **argv) 20790Sstevel@tonic-gate { 20800Sstevel@tonic-gate int i; 20810Sstevel@tonic-gate 20820Sstevel@tonic-gate char **local_argv = (char **)calloc((unsigned)argc + 1, 20830Sstevel@tonic-gate (unsigned)sizeof (char *)); 20840Sstevel@tonic-gate 20850Sstevel@tonic-gate /* 20860Sstevel@tonic-gate * allocate an extra pointer, so that it is initialized to NULL and 20870Sstevel@tonic-gate * execv() will work 20880Sstevel@tonic-gate */ 20890Sstevel@tonic-gate for (i = 0; i < argc; i++) { 20900Sstevel@tonic-gate local_argv[i] = strsave(argv[i]); 20910Sstevel@tonic-gate } 20920Sstevel@tonic-gate 20930Sstevel@tonic-gate return (local_argv); 20940Sstevel@tonic-gate } 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate #define SIZEOF_INADDR sizeof (struct in_addr) 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate static void 20990Sstevel@tonic-gate answer_auth(char *config_file, char *ccache_file) 21000Sstevel@tonic-gate { 21010Sstevel@tonic-gate krb5_data pname_data, msg; 21020Sstevel@tonic-gate krb5_creds creds, *new_creds; 21030Sstevel@tonic-gate krb5_ccache cc; 21040Sstevel@tonic-gate krb5_auth_context auth_context = NULL; 21050Sstevel@tonic-gate 21060Sstevel@tonic-gate if (config_file) { 21070Sstevel@tonic-gate const char *filenames[2]; 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate filenames[1] = NULL; 21100Sstevel@tonic-gate filenames[0] = config_file; 21110Sstevel@tonic-gate if (krb5_set_config_files(bsd_context, filenames)) 21120Sstevel@tonic-gate exit(1); 21130Sstevel@tonic-gate } 21140Sstevel@tonic-gate (void) memset((char *)&creds, 0, sizeof (creds)); 21150Sstevel@tonic-gate 21160Sstevel@tonic-gate if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data)) 21170Sstevel@tonic-gate exit(1); 21180Sstevel@tonic-gate 21190Sstevel@tonic-gate if (krb5_read_message(bsd_context, (krb5_pointer) &rem, 21200Sstevel@tonic-gate &creds.second_ticket)) 21210Sstevel@tonic-gate exit(1); 21220Sstevel@tonic-gate 21230Sstevel@tonic-gate if (ccache_file == NULL) { 21240Sstevel@tonic-gate if (krb5_cc_default(bsd_context, &cc)) 21250Sstevel@tonic-gate exit(1); 21260Sstevel@tonic-gate } else { 21270Sstevel@tonic-gate if (krb5_cc_resolve(bsd_context, ccache_file, &cc)) 21280Sstevel@tonic-gate exit(1); 21290Sstevel@tonic-gate } 21300Sstevel@tonic-gate 21310Sstevel@tonic-gate if (krb5_cc_get_principal(bsd_context, cc, &creds.client)) 21320Sstevel@tonic-gate exit(1); 21330Sstevel@tonic-gate 21340Sstevel@tonic-gate if (krb5_parse_name(bsd_context, pname_data.data, &creds.server)) 21350Sstevel@tonic-gate exit(1); 21360Sstevel@tonic-gate 21370Sstevel@tonic-gate krb5_xfree(pname_data.data); 21380Sstevel@tonic-gate if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds, 21390Sstevel@tonic-gate &new_creds)) 21400Sstevel@tonic-gate exit(1); 21410Sstevel@tonic-gate 21420Sstevel@tonic-gate if (krb5_mk_req_extended(bsd_context, &auth_context, 21430Sstevel@tonic-gate AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg)) 21440Sstevel@tonic-gate exit(1); 21450Sstevel@tonic-gate 21460Sstevel@tonic-gate if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) { 21470Sstevel@tonic-gate krb5_xfree(msg.data); 21480Sstevel@tonic-gate exit(1); 21490Sstevel@tonic-gate } 21500Sstevel@tonic-gate /* setup eblock for des_read and write */ 2151*11415SSurya.Prakki@Sun.COM (void) krb5_copy_keyblock(bsd_context, 2152*11415SSurya.Prakki@Sun.COM &new_creds->keyblock, &session_key); 21530Sstevel@tonic-gate 21540Sstevel@tonic-gate /* OK process key */ 21550Sstevel@tonic-gate eblock.crypto_entry = session_key->enctype; 21560Sstevel@tonic-gate eblock.key = (krb5_keyblock *)session_key; 21570Sstevel@tonic-gate 21580Sstevel@tonic-gate init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL, 21590Sstevel@tonic-gate &desinbuf, &desoutbuf, CLIENT, &eblock); 21600Sstevel@tonic-gate /* cleanup */ 21610Sstevel@tonic-gate krb5_free_cred_contents(bsd_context, &creds); 21620Sstevel@tonic-gate krb5_free_creds(bsd_context, new_creds); 21630Sstevel@tonic-gate krb5_xfree(msg.data); 21640Sstevel@tonic-gate } 21650Sstevel@tonic-gate 21660Sstevel@tonic-gate 21670Sstevel@tonic-gate static void 21680Sstevel@tonic-gate try_normal_rcp(int cur_argc, char **cur_argv) 21690Sstevel@tonic-gate { 21700Sstevel@tonic-gate char *target; 21710Sstevel@tonic-gate 21720Sstevel@tonic-gate /* 21730Sstevel@tonic-gate * Reset all KRB5 relevant flags and set the 21740Sstevel@tonic-gate * cmd-buffer so that normal rcp works 21750Sstevel@tonic-gate */ 21760Sstevel@tonic-gate krb5auth_flag = encrypt_flag = encrypt_done = 0; 21770Sstevel@tonic-gate cmd = cmd_orig; 21780Sstevel@tonic-gate cmd_sunw = cmd_sunw_orig; 21790Sstevel@tonic-gate 21800Sstevel@tonic-gate if (cur_argc < 2) 21810Sstevel@tonic-gate usage(); 21820Sstevel@tonic-gate 21830Sstevel@tonic-gate if (cur_argc > 2) 21840Sstevel@tonic-gate targetshouldbedirectory = 1; 21850Sstevel@tonic-gate 21860Sstevel@tonic-gate rem = -1; 21870Sstevel@tonic-gate 21880Sstevel@tonic-gate prev_argc = cur_argc; 21890Sstevel@tonic-gate prev_argv = save_argv(cur_argc, cur_argv); 21900Sstevel@tonic-gate 21910Sstevel@tonic-gate (void) init_service(krb5auth_flag); 21920Sstevel@tonic-gate 21930Sstevel@tonic-gate if (target = colon(cur_argv[cur_argc - 1])) { 21940Sstevel@tonic-gate toremote(target, cur_argc, cur_argv); 21950Sstevel@tonic-gate } else { 21960Sstevel@tonic-gate tolocal(cur_argc, cur_argv); 21970Sstevel@tonic-gate if (targetshouldbedirectory) 21980Sstevel@tonic-gate verifydir(cur_argv[cur_argc - 1]); 21990Sstevel@tonic-gate } 22000Sstevel@tonic-gate exit(errs); 22010Sstevel@tonic-gate /* NOTREACHED */ 22020Sstevel@tonic-gate } 22030Sstevel@tonic-gate 22040Sstevel@tonic-gate 22050Sstevel@tonic-gate static int 22060Sstevel@tonic-gate init_service(int krb5flag) 22070Sstevel@tonic-gate { 22080Sstevel@tonic-gate struct servent *sp; 22090Sstevel@tonic-gate boolean_t success = B_FALSE; 22100Sstevel@tonic-gate 22110Sstevel@tonic-gate if (krb5flag > 0) { 22120Sstevel@tonic-gate sp = getservbyname("kshell", "tcp"); 22130Sstevel@tonic-gate if (sp == NULL) { 22140Sstevel@tonic-gate (void) fprintf(stderr, 22150Sstevel@tonic-gate gettext("rcp: kshell/tcp: unknown service.\n" 22160Sstevel@tonic-gate "trying normal shell/tcp service\n")); 22170Sstevel@tonic-gate } else { 22180Sstevel@tonic-gate portnumber = sp->s_port; 22190Sstevel@tonic-gate success = B_TRUE; 22200Sstevel@tonic-gate } 22210Sstevel@tonic-gate } else { 22220Sstevel@tonic-gate portnumber = htons(IPPORT_CMDSERVER); 22230Sstevel@tonic-gate success = B_TRUE; 22240Sstevel@tonic-gate } 22250Sstevel@tonic-gate return (success); 22260Sstevel@tonic-gate } 22270Sstevel@tonic-gate 22280Sstevel@tonic-gate /*PRINTFLIKE1*/ 22290Sstevel@tonic-gate static void 22300Sstevel@tonic-gate error(char *fmt, ...) 22310Sstevel@tonic-gate { 22320Sstevel@tonic-gate va_list ap; 22330Sstevel@tonic-gate char buf[RCP_BUFSIZE]; 22340Sstevel@tonic-gate char *cp = buf; 22350Sstevel@tonic-gate 22360Sstevel@tonic-gate va_start(ap, fmt); 22370Sstevel@tonic-gate errs++; 22380Sstevel@tonic-gate *cp++ = 1; 22390Sstevel@tonic-gate (void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap); 22400Sstevel@tonic-gate va_end(ap); 22410Sstevel@tonic-gate 22420Sstevel@tonic-gate (void) desrcpwrite(rem, buf, strlen(buf)); 22430Sstevel@tonic-gate if (iamremote == 0) 22440Sstevel@tonic-gate (void) write(2, buf + 1, strlen(buf + 1)); 22450Sstevel@tonic-gate } 22464619Ssn199410 22474619Ssn199410 static void 22484619Ssn199410 addargs(char **arglist, ...) 22494619Ssn199410 { 22504619Ssn199410 va_list ap; 22514619Ssn199410 int i = 0; 22524619Ssn199410 char *pm; 22534619Ssn199410 22544619Ssn199410 va_start(ap, arglist); 22554619Ssn199410 while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL) 22564619Ssn199410 if (strcmp(pm, "")) 22574619Ssn199410 arglist[i++] = pm; 22584619Ssn199410 arglist[i] = NULL; 22594619Ssn199410 va_end(ap); 22604619Ssn199410 } 2261