rcp.c revision 104559
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 496196Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596196Sdes * All rights reserved. 61556Srgrimes * 796196Sdes * Portions of this software were developed for the FreeBSD Project by 896196Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996196Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096196Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196196Sdes * 121556Srgrimes * Redistribution and use in source and binary forms, with or without 131556Srgrimes * modification, are permitted provided that the following conditions 141556Srgrimes * are met: 151556Srgrimes * 1. Redistributions of source code must retain the above copyright 161556Srgrimes * notice, this list of conditions and the following disclaimer. 171556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181556Srgrimes * notice, this list of conditions and the following disclaimer in the 191556Srgrimes * documentation and/or other materials provided with the distribution. 201556Srgrimes * 3. All advertising materials mentioning features or use of this software 211556Srgrimes * must display the following acknowledgement: 221556Srgrimes * This product includes software developed by the University of 231556Srgrimes * California, Berkeley and its contributors. 241556Srgrimes * 4. Neither the name of the University nor the names of its contributors 251556Srgrimes * may be used to endorse or promote products derived from this software 261556Srgrimes * without specific prior written permission. 271556Srgrimes * 281556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381556Srgrimes * SUCH DAMAGE. 391556Srgrimes */ 401556Srgrimes 411556Srgrimes#ifndef lint 4220420Sstevestatic char const copyright[] = 431556Srgrimes"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ 441556Srgrimes The Regents of the University of California. All rights reserved.\n"; 451556Srgrimes#endif /* not lint */ 461556Srgrimes 47104559Scharnier#if 0 481556Srgrimes#ifndef lint 4936149Scharnierstatic char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; 50104559Scharnier#endif /* not lint */ 5136149Scharnier#endif 52104559Scharnier 5399110Sobrien#include <sys/cdefs.h> 5499110Sobrien__FBSDID("$FreeBSD: head/bin/rcp/rcp.c 104559 2002-10-06 09:09:27Z charnier $"); 551556Srgrimes 561556Srgrimes#include <sys/param.h> 571556Srgrimes#include <sys/stat.h> 581556Srgrimes#include <sys/time.h> 591556Srgrimes#include <sys/socket.h> 601556Srgrimes#include <netinet/in.h> 611556Srgrimes#include <netinet/in_systm.h> 621556Srgrimes#include <netinet/ip.h> 631556Srgrimes 641556Srgrimes#include <ctype.h> 651556Srgrimes#include <dirent.h> 661556Srgrimes#include <err.h> 671556Srgrimes#include <errno.h> 681556Srgrimes#include <fcntl.h> 6977462Simp#include <libutil.h> 7077462Simp#include <limits.h> 711556Srgrimes#include <netdb.h> 7296196Sdes#include <paths.h> 731556Srgrimes#include <pwd.h> 741556Srgrimes#include <signal.h> 75104549Stjr#include <stdint.h> 761556Srgrimes#include <stdio.h> 771556Srgrimes#include <stdlib.h> 781556Srgrimes#include <string.h> 791556Srgrimes#include <string.h> 801556Srgrimes#include <unistd.h> 811556Srgrimes 821556Srgrimes#include "extern.h" 831556Srgrimes 841556Srgrimes#ifdef KERBEROS 8557454Smarkm#include <openssl/des.h> 8629914Smarkm#include <krb.h> 8751434Smarkm#include "bsd_locl.h" 881556Srgrimes 891556Srgrimeschar dst_realm_buf[REALM_SZ]; 901556Srgrimeschar *dest_realm = NULL; 911556Srgrimesint use_kerberos = 1; 9296196SdesCREDENTIALS cred; 931556SrgrimesKey_schedule schedule; 941556Srgrimesextern char *krb_realmofhost(); 951556Srgrimes#ifdef CRYPT 961556Srgrimesint doencrypt = 0; 97101591Sume#define OPTIONS "46dfKk:prtx" 981556Srgrimes#else 99101591Sume#define OPTIONS "46dfKk:prt" 1001556Srgrimes#endif 1011556Srgrimes#else 102101591Sume#define OPTIONS "46dfprt" 1031556Srgrimes#endif 1041556Srgrimes 1051556Srgrimesstruct passwd *pwd; 1061556Srgrimesu_short port; 1071556Srgrimesuid_t userid; 1081556Srgrimesint errs, rem; 1091556Srgrimesint pflag, iamremote, iamrecursive, targetshouldbedirectory; 110101591Sumeint family = PF_UNSPEC; 1111556Srgrimes 11234898Smarkmstatic int argc_copy; 11334898Smarkmstatic char **argv_copy; 11434898Smarkm 1151556Srgrimes#define CMDNEEDS 64 1161556Srgrimeschar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 1171556Srgrimes 1181556Srgrimes#ifdef KERBEROS 11990110Simpint kerberos(char **, char *, char *, char *); 12090110Simpvoid oldw(const char *, ...) __printflike(1, 2); 1211556Srgrimes#endif 12290110Simpint response(void); 12390110Simpvoid rsource(char *, struct stat *); 12490110Simpvoid run_err(const char *, ...) __printflike(1, 2); 12590110Simpvoid sink(int, char *[]); 12690110Simpvoid source(int, char *[]); 12790110Simpvoid tolocal(int, char *[]); 12890110Simpvoid toremote(char *, int, char *[]); 12990110Simpvoid usage(void); 1301556Srgrimes 1311556Srgrimesint 13290110Simpmain(int argc, char *argv[]) 1331556Srgrimes{ 1341556Srgrimes struct servent *sp; 13548560Sbde int ch, fflag, i, tflag; 1361556Srgrimes char *targ, *shell; 13740101Smarkm#ifdef KERBEROS 13840101Smarkm char *k; 13940101Smarkm#endif 1401556Srgrimes 14134898Smarkm /* 14234898Smarkm * Prepare for execing ourselves. 14334898Smarkm */ 14434898Smarkm argc_copy = argc + 1; 14534898Smarkm argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); 14634898Smarkm if (argv_copy == NULL) 14734898Smarkm err(1, "malloc"); 14834898Smarkm argv_copy[0] = argv[0]; 14934898Smarkm argv_copy[1] = "-K"; 15048560Sbde for (i = 1; i < argc; ++i) { 15134898Smarkm argv_copy[i + 1] = strdup(argv[i]); 15234898Smarkm if (argv_copy[i + 1] == NULL) 15334898Smarkm errx(1, "strdup: out of memory"); 15434898Smarkm } 15534898Smarkm argv_copy[argc + 1] = NULL; 15634898Smarkm 1571556Srgrimes fflag = tflag = 0; 15824348Simp while ((ch = getopt(argc, argv, OPTIONS)) != -1) 1591556Srgrimes switch(ch) { /* User-visible flags. */ 160101591Sume case '4': 161101591Sume family = PF_INET; 162101591Sume break; 163101591Sume 164101591Sume case '6': 165101591Sume family = PF_INET6; 166101591Sume break; 167101591Sume 1681556Srgrimes case 'K': 1691556Srgrimes#ifdef KERBEROS 1701556Srgrimes use_kerberos = 0; 1711556Srgrimes#endif 1721556Srgrimes break; 1731556Srgrimes#ifdef KERBEROS 1741556Srgrimes case 'k': 1751556Srgrimes dest_realm = dst_realm_buf; 17625612Sjoerg (void)strncpy(dst_realm_buf, optarg, REALM_SZ - 1); 17725612Sjoerg dst_realm_buf[REALM_SZ - 1] = '\0'; 1781556Srgrimes break; 1791556Srgrimes#ifdef CRYPT 1801556Srgrimes case 'x': 1811556Srgrimes doencrypt = 1; 1821556Srgrimes /* des_set_key(cred.session, schedule); */ 1831556Srgrimes break; 1841556Srgrimes#endif 1851556Srgrimes#endif 1861556Srgrimes case 'p': 1871556Srgrimes pflag = 1; 1881556Srgrimes break; 1891556Srgrimes case 'r': 1901556Srgrimes iamrecursive = 1; 1911556Srgrimes break; 1921556Srgrimes /* Server options. */ 1931556Srgrimes case 'd': 1941556Srgrimes targetshouldbedirectory = 1; 1951556Srgrimes break; 1961556Srgrimes case 'f': /* "from" */ 1971556Srgrimes iamremote = 1; 1981556Srgrimes fflag = 1; 1991556Srgrimes break; 2001556Srgrimes case 't': /* "to" */ 2011556Srgrimes iamremote = 1; 2021556Srgrimes tflag = 1; 2031556Srgrimes break; 2041556Srgrimes case '?': 2051556Srgrimes default: 2061556Srgrimes usage(); 2071556Srgrimes } 2081556Srgrimes argc -= optind; 2091556Srgrimes argv += optind; 2101556Srgrimes 2111556Srgrimes#ifdef KERBEROS 21240101Smarkm k = auth_getval("auth_list"); 21340101Smarkm if (k && !strstr(k, "kerberos")) 21440101Smarkm use_kerberos = 0; 2151556Srgrimes if (use_kerberos) { 2161556Srgrimes#ifdef CRYPT 2171556Srgrimes shell = doencrypt ? "ekshell" : "kshell"; 2181556Srgrimes#else 2191556Srgrimes shell = "kshell"; 2201556Srgrimes#endif 2211556Srgrimes if ((sp = getservbyname(shell, "tcp")) == NULL) { 2221556Srgrimes use_kerberos = 0; 2231556Srgrimes oldw("can't get entry for %s/tcp service", shell); 2241556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2251556Srgrimes } 2261556Srgrimes } else 2271556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2281556Srgrimes#else 2291556Srgrimes sp = getservbyname(shell = "shell", "tcp"); 2301556Srgrimes#endif 2311556Srgrimes if (sp == NULL) 2321556Srgrimes errx(1, "%s/tcp: unknown service", shell); 2331556Srgrimes port = sp->s_port; 2341556Srgrimes 2351556Srgrimes if ((pwd = getpwuid(userid = getuid())) == NULL) 2361556Srgrimes errx(1, "unknown user %d", (int)userid); 2371556Srgrimes 2381556Srgrimes rem = STDIN_FILENO; /* XXX */ 2391556Srgrimes 2401556Srgrimes if (fflag) { /* Follow "protocol", send data. */ 2411556Srgrimes (void)response(); 2421556Srgrimes (void)setuid(userid); 2431556Srgrimes source(argc, argv); 2441556Srgrimes exit(errs); 2451556Srgrimes } 2461556Srgrimes 2471556Srgrimes if (tflag) { /* Receive data. */ 2481556Srgrimes (void)setuid(userid); 2491556Srgrimes sink(argc, argv); 2501556Srgrimes exit(errs); 2511556Srgrimes } 2521556Srgrimes 2531556Srgrimes if (argc < 2) 2541556Srgrimes usage(); 2551556Srgrimes if (argc > 2) 2561556Srgrimes targetshouldbedirectory = 1; 2571556Srgrimes 2581556Srgrimes rem = -1; 2591556Srgrimes /* Command to be executed on remote system using "rsh". */ 2601556Srgrimes#ifdef KERBEROS 2611556Srgrimes (void)snprintf(cmd, sizeof(cmd), 2621556Srgrimes "rcp%s%s%s%s", iamrecursive ? " -r" : "", 2631556Srgrimes#ifdef CRYPT 2641556Srgrimes (doencrypt && use_kerberos ? " -x" : ""), 2651556Srgrimes#else 2661556Srgrimes "", 2671556Srgrimes#endif 2681556Srgrimes pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 2691556Srgrimes#else 2701556Srgrimes (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", 2711556Srgrimes iamrecursive ? " -r" : "", pflag ? " -p" : "", 2721556Srgrimes targetshouldbedirectory ? " -d" : ""); 2731556Srgrimes#endif 2741556Srgrimes 2751556Srgrimes (void)signal(SIGPIPE, lostconn); 2761556Srgrimes 2777165Sjoerg if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 2781556Srgrimes toremote(targ, argc, argv); 2791556Srgrimes else { 2801556Srgrimes tolocal(argc, argv); /* Dest is local host. */ 2811556Srgrimes if (targetshouldbedirectory) 2821556Srgrimes verifydir(argv[argc - 1]); 2831556Srgrimes } 2841556Srgrimes exit(errs); 2851556Srgrimes} 2861556Srgrimes 2871556Srgrimesvoid 28890110Simptoremote(char *targ, int argc, char *argv[]) 2891556Srgrimes{ 2901556Srgrimes int i, len, tos; 2911556Srgrimes char *bp, *host, *src, *suser, *thost, *tuser; 2921556Srgrimes 2931556Srgrimes *targ++ = 0; 2941556Srgrimes if (*targ == 0) 2951556Srgrimes targ = "."; 2961556Srgrimes 2977165Sjoerg if ((thost = strchr(argv[argc - 1], '@'))) { 2981556Srgrimes /* user@host */ 2991556Srgrimes *thost++ = 0; 3001556Srgrimes tuser = argv[argc - 1]; 3011556Srgrimes if (*tuser == '\0') 3021556Srgrimes tuser = NULL; 3031556Srgrimes else if (!okname(tuser)) 3041556Srgrimes exit(1); 3051556Srgrimes } else { 3061556Srgrimes thost = argv[argc - 1]; 3071556Srgrimes tuser = NULL; 3081556Srgrimes } 3091556Srgrimes 3101556Srgrimes for (i = 0; i < argc - 1; i++) { 3111556Srgrimes src = colon(argv[i]); 3121556Srgrimes if (src) { /* remote to remote */ 3131556Srgrimes *src++ = 0; 3141556Srgrimes if (*src == 0) 3151556Srgrimes src = "."; 3161556Srgrimes host = strchr(argv[i], '@'); 3171556Srgrimes len = strlen(_PATH_RSH) + strlen(argv[i]) + 3181556Srgrimes strlen(src) + (tuser ? strlen(tuser) : 0) + 3191556Srgrimes strlen(thost) + strlen(targ) + CMDNEEDS + 20; 3201556Srgrimes if (!(bp = malloc(len))) 32199744Sdillon err(1, "malloc"); 3221556Srgrimes if (host) { 3231556Srgrimes *host++ = 0; 3241556Srgrimes suser = argv[i]; 3251556Srgrimes if (*suser == '\0') 3261556Srgrimes suser = pwd->pw_name; 32777491Spirzyk else if (!okname(suser)) { 32877491Spirzyk ++errs; 3291556Srgrimes continue; 33077491Spirzyk } 3311556Srgrimes (void)snprintf(bp, len, 3321556Srgrimes "%s %s -l %s -n %s %s '%s%s%s:%s'", 3331556Srgrimes _PATH_RSH, host, suser, cmd, src, 3341556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3351556Srgrimes thost, targ); 3361556Srgrimes } else 3371556Srgrimes (void)snprintf(bp, len, 3381556Srgrimes "exec %s %s -n %s %s '%s%s%s:%s'", 3391556Srgrimes _PATH_RSH, argv[i], cmd, src, 3401556Srgrimes tuser ? tuser : "", tuser ? "@" : "", 3411556Srgrimes thost, targ); 3421556Srgrimes (void)susystem(bp, userid); 3431556Srgrimes (void)free(bp); 3441556Srgrimes } else { /* local to remote */ 3451556Srgrimes if (rem == -1) { 3461556Srgrimes len = strlen(targ) + CMDNEEDS + 20; 3471556Srgrimes if (!(bp = malloc(len))) 34899744Sdillon err(1, "malloc"); 3491556Srgrimes (void)snprintf(bp, len, "%s -t %s", cmd, targ); 3501556Srgrimes host = thost; 3511556Srgrimes#ifdef KERBEROS 3521556Srgrimes if (use_kerberos) 3531556Srgrimes rem = kerberos(&host, bp, 3541556Srgrimes pwd->pw_name, 3551556Srgrimes tuser ? tuser : pwd->pw_name); 3561556Srgrimes else 3571556Srgrimes#endif 358101591Sume rem = rcmd_af(&host, port, 359101591Sume pwd->pw_name, 3601556Srgrimes tuser ? tuser : pwd->pw_name, 361101591Sume bp, 0, family); 3621556Srgrimes if (rem < 0) 3631556Srgrimes exit(1); 364101591Sume if (family == PF_INET) { 365101591Sume tos = IPTOS_THROUGHPUT; 366101591Sume if (setsockopt(rem, IPPROTO_IP, IP_TOS, 367101591Sume &tos, sizeof(int)) < 0) 368101591Sume warn("TOS (ignored)"); 369101591Sume } 3701556Srgrimes if (response() < 0) 3711556Srgrimes exit(1); 3721556Srgrimes (void)free(bp); 3731556Srgrimes (void)setuid(userid); 3741556Srgrimes } 3751556Srgrimes source(1, argv+i); 3761556Srgrimes } 3771556Srgrimes } 3781556Srgrimes} 3791556Srgrimes 3801556Srgrimesvoid 38190110Simptolocal(int argc, char *argv[]) 3821556Srgrimes{ 3831556Srgrimes int i, len, tos; 3841556Srgrimes char *bp, *host, *src, *suser; 3851556Srgrimes 3861556Srgrimes for (i = 0; i < argc - 1; i++) { 3871556Srgrimes if (!(src = colon(argv[i]))) { /* Local to local. */ 3881556Srgrimes len = strlen(_PATH_CP) + strlen(argv[i]) + 3891556Srgrimes strlen(argv[argc - 1]) + 20; 3901556Srgrimes if (!(bp = malloc(len))) 39199744Sdillon err(1, "malloc"); 3921556Srgrimes (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 39331633Swosch iamrecursive ? " -PR" : "", pflag ? " -p" : "", 3941556Srgrimes argv[i], argv[argc - 1]); 3951556Srgrimes if (susystem(bp, userid)) 3961556Srgrimes ++errs; 3971556Srgrimes (void)free(bp); 3981556Srgrimes continue; 3991556Srgrimes } 4001556Srgrimes *src++ = 0; 4011556Srgrimes if (*src == 0) 4021556Srgrimes src = "."; 4031556Srgrimes if ((host = strchr(argv[i], '@')) == NULL) { 4041556Srgrimes host = argv[i]; 4051556Srgrimes suser = pwd->pw_name; 4061556Srgrimes } else { 4071556Srgrimes *host++ = 0; 4081556Srgrimes suser = argv[i]; 4091556Srgrimes if (*suser == '\0') 4101556Srgrimes suser = pwd->pw_name; 41177491Spirzyk else if (!okname(suser)) { 41277491Spirzyk ++errs; 4131556Srgrimes continue; 41477491Spirzyk } 4151556Srgrimes } 4161556Srgrimes len = strlen(src) + CMDNEEDS + 20; 4171556Srgrimes if ((bp = malloc(len)) == NULL) 41899744Sdillon err(1, "malloc"); 4191556Srgrimes (void)snprintf(bp, len, "%s -f %s", cmd, src); 4208855Srgrimes rem = 4211556Srgrimes#ifdef KERBEROS 4228855Srgrimes use_kerberos ? 4238855Srgrimes kerberos(&host, bp, pwd->pw_name, suser) : 4241556Srgrimes#endif 425101591Sume rcmd_af(&host, port, pwd->pw_name, suser, bp, 0, 426101591Sume family); 4271556Srgrimes (void)free(bp); 4281556Srgrimes if (rem < 0) { 4291556Srgrimes ++errs; 4301556Srgrimes continue; 4311556Srgrimes } 4321556Srgrimes (void)seteuid(userid); 433101591Sume if (family == PF_INET) { 434101591Sume tos = IPTOS_THROUGHPUT; 435101591Sume if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, 436101591Sume sizeof(int)) < 0) 437101591Sume warn("TOS (ignored)"); 438101591Sume } 4391556Srgrimes sink(1, argv + argc - 1); 4401556Srgrimes (void)seteuid(0); 4411556Srgrimes (void)close(rem); 4421556Srgrimes rem = -1; 4431556Srgrimes } 4441556Srgrimes} 4451556Srgrimes 4461556Srgrimesvoid 44790110Simpsource(int argc, char *argv[]) 4481556Srgrimes{ 4491556Srgrimes struct stat stb; 4501556Srgrimes static BUF buffer; 4511556Srgrimes BUF *bp; 4521556Srgrimes off_t i; 4531556Srgrimes int amt, fd, haderr, indx, result; 4541556Srgrimes char *last, *name, buf[BUFSIZ]; 4551556Srgrimes 4561556Srgrimes for (indx = 0; indx < argc; ++indx) { 45796196Sdes name = argv[indx]; 4581556Srgrimes if ((fd = open(name, O_RDONLY, 0)) < 0) 4591556Srgrimes goto syserr; 4601556Srgrimes if (fstat(fd, &stb)) { 4611556Srgrimessyserr: run_err("%s: %s", name, strerror(errno)); 4621556Srgrimes goto next; 4631556Srgrimes } 4641556Srgrimes switch (stb.st_mode & S_IFMT) { 4651556Srgrimes case S_IFREG: 4661556Srgrimes break; 4671556Srgrimes case S_IFDIR: 4681556Srgrimes if (iamrecursive) { 4691556Srgrimes rsource(name, &stb); 4701556Srgrimes goto next; 4711556Srgrimes } 4721556Srgrimes /* FALLTHROUGH */ 4731556Srgrimes default: 4741556Srgrimes run_err("%s: not a regular file", name); 4751556Srgrimes goto next; 4761556Srgrimes } 4771556Srgrimes if ((last = strrchr(name, '/')) == NULL) 4781556Srgrimes last = name; 4791556Srgrimes else 4801556Srgrimes ++last; 4811556Srgrimes if (pflag) { 4821556Srgrimes /* 4831556Srgrimes * Make it compatible with possible future 4841556Srgrimes * versions expecting microseconds. 4851556Srgrimes */ 4861556Srgrimes (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 48738018Sbde (long)stb.st_mtimespec.tv_sec, 48838018Sbde (long)stb.st_atimespec.tv_sec); 4891556Srgrimes (void)write(rem, buf, strlen(buf)); 4901556Srgrimes if (response() < 0) 4911556Srgrimes goto next; 4921556Srgrimes } 4931556Srgrimes#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) 494104549Stjr (void)snprintf(buf, sizeof(buf), "C%04o %jd %s\n", 495104549Stjr stb.st_mode & MODEMASK, (intmax_t)stb.st_size, last); 4961556Srgrimes (void)write(rem, buf, strlen(buf)); 4971556Srgrimes if (response() < 0) 4981556Srgrimes goto next; 4991556Srgrimes if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 5001556Srgrimesnext: (void)close(fd); 5011556Srgrimes continue; 5021556Srgrimes } 5031556Srgrimes 5041556Srgrimes /* Keep writing after an error so that we stay sync'd up. */ 5051556Srgrimes for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 5061556Srgrimes amt = bp->cnt; 5071556Srgrimes if (i + amt > stb.st_size) 5081556Srgrimes amt = stb.st_size - i; 5091556Srgrimes if (!haderr) { 5101556Srgrimes result = read(fd, bp->buf, amt); 5111556Srgrimes if (result != amt) 5121556Srgrimes haderr = result >= 0 ? EIO : errno; 5131556Srgrimes } 5141556Srgrimes if (haderr) 5151556Srgrimes (void)write(rem, bp->buf, amt); 5161556Srgrimes else { 5171556Srgrimes result = write(rem, bp->buf, amt); 5181556Srgrimes if (result != amt) 5191556Srgrimes haderr = result >= 0 ? EIO : errno; 5201556Srgrimes } 5211556Srgrimes } 5221556Srgrimes if (close(fd) && !haderr) 5231556Srgrimes haderr = errno; 5241556Srgrimes if (!haderr) 5251556Srgrimes (void)write(rem, "", 1); 5261556Srgrimes else 5271556Srgrimes run_err("%s: %s", name, strerror(haderr)); 5281556Srgrimes (void)response(); 5291556Srgrimes } 5301556Srgrimes} 5311556Srgrimes 5321556Srgrimesvoid 53390110Simprsource(char *name, struct stat *statp) 5341556Srgrimes{ 5351556Srgrimes DIR *dirp; 5361556Srgrimes struct dirent *dp; 53777462Simp char *last, *vect[1], path[PATH_MAX]; 5381556Srgrimes 5391556Srgrimes if (!(dirp = opendir(name))) { 5401556Srgrimes run_err("%s: %s", name, strerror(errno)); 5411556Srgrimes return; 5421556Srgrimes } 5431556Srgrimes last = strrchr(name, '/'); 5441556Srgrimes if (last == 0) 5451556Srgrimes last = name; 5461556Srgrimes else 5471556Srgrimes last++; 5481556Srgrimes if (pflag) { 5491556Srgrimes (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 55038018Sbde (long)statp->st_mtimespec.tv_sec, 55138018Sbde (long)statp->st_atimespec.tv_sec); 5521556Srgrimes (void)write(rem, path, strlen(path)); 5531556Srgrimes if (response() < 0) { 5541556Srgrimes closedir(dirp); 5551556Srgrimes return; 5561556Srgrimes } 5571556Srgrimes } 5581556Srgrimes (void)snprintf(path, sizeof(path), 5591556Srgrimes "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 5601556Srgrimes (void)write(rem, path, strlen(path)); 5611556Srgrimes if (response() < 0) { 5621556Srgrimes closedir(dirp); 5631556Srgrimes return; 5641556Srgrimes } 5657165Sjoerg while ((dp = readdir(dirp))) { 5661556Srgrimes if (dp->d_ino == 0) 5671556Srgrimes continue; 5681556Srgrimes if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 5691556Srgrimes continue; 57077462Simp if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path)) { 5711556Srgrimes run_err("%s/%s: name too long", name, dp->d_name); 5721556Srgrimes continue; 5731556Srgrimes } 5741556Srgrimes (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 5751556Srgrimes vect[0] = path; 5761556Srgrimes source(1, vect); 5771556Srgrimes } 5781556Srgrimes (void)closedir(dirp); 5791556Srgrimes (void)write(rem, "E\n", 2); 5801556Srgrimes (void)response(); 5811556Srgrimes} 5821556Srgrimes 5831556Srgrimesvoid 58490110Simpsink(int argc, char *argv[]) 5851556Srgrimes{ 5861556Srgrimes static BUF buffer; 5871556Srgrimes struct stat stb; 5881556Srgrimes struct timeval tv[2]; 5891556Srgrimes enum { YES, NO, DISPLAYED } wrerr; 5901556Srgrimes BUF *bp; 59146057Sdt off_t i, j, size; 5921556Srgrimes int amt, count, exists, first, mask, mode, ofd, omode; 59346057Sdt int setimes, targisdir, wrerrno = 0; 594104559Scharnier char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ], path[PATH_MAX]; 5951556Srgrimes 5961556Srgrimes#define atime tv[0] 5971556Srgrimes#define mtime tv[1] 5981556Srgrimes#define SCREWUP(str) { why = str; goto screwup; } 5991556Srgrimes 6001556Srgrimes setimes = targisdir = 0; 6011556Srgrimes mask = umask(0); 6021556Srgrimes if (!pflag) 6031556Srgrimes (void)umask(mask); 6041556Srgrimes if (argc != 1) { 6051556Srgrimes run_err("ambiguous target"); 6061556Srgrimes exit(1); 6071556Srgrimes } 6081556Srgrimes targ = *argv; 6091556Srgrimes if (targetshouldbedirectory) 6101556Srgrimes verifydir(targ); 6111556Srgrimes (void)write(rem, "", 1); 6121556Srgrimes if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 6131556Srgrimes targisdir = 1; 6141556Srgrimes for (first = 1;; first = 0) { 6151556Srgrimes cp = buf; 6161556Srgrimes if (read(rem, cp, 1) <= 0) 6171556Srgrimes return; 6181556Srgrimes if (*cp++ == '\n') 6191556Srgrimes SCREWUP("unexpected <newline>"); 6201556Srgrimes do { 6211556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 6221556Srgrimes SCREWUP("lost connection"); 6231556Srgrimes *cp++ = ch; 6241556Srgrimes } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 6251556Srgrimes *cp = 0; 6261556Srgrimes 6271556Srgrimes if (buf[0] == '\01' || buf[0] == '\02') { 6281556Srgrimes if (iamremote == 0) 6291556Srgrimes (void)write(STDERR_FILENO, 6301556Srgrimes buf + 1, strlen(buf + 1)); 6311556Srgrimes if (buf[0] == '\02') 6321556Srgrimes exit(1); 6331556Srgrimes ++errs; 6341556Srgrimes continue; 6351556Srgrimes } 6361556Srgrimes if (buf[0] == 'E') { 6371556Srgrimes (void)write(rem, "", 1); 6381556Srgrimes return; 6391556Srgrimes } 6401556Srgrimes 6411556Srgrimes if (ch == '\n') 6421556Srgrimes *--cp = 0; 6431556Srgrimes 6441556Srgrimes cp = buf; 6451556Srgrimes if (*cp == 'T') { 6461556Srgrimes setimes++; 6471556Srgrimes cp++; 64813978Spst mtime.tv_sec = strtol(cp, &cp, 10); 64913978Spst if (!cp || *cp++ != ' ') 6501556Srgrimes SCREWUP("mtime.sec not delimited"); 65113978Spst mtime.tv_usec = strtol(cp, &cp, 10); 65213978Spst if (!cp || *cp++ != ' ') 6531556Srgrimes SCREWUP("mtime.usec not delimited"); 65413978Spst atime.tv_sec = strtol(cp, &cp, 10); 65513978Spst if (!cp || *cp++ != ' ') 6561556Srgrimes SCREWUP("atime.sec not delimited"); 65713978Spst atime.tv_usec = strtol(cp, &cp, 10); 65813978Spst if (!cp || *cp++ != '\0') 6591556Srgrimes SCREWUP("atime.usec not delimited"); 6601556Srgrimes (void)write(rem, "", 1); 6611556Srgrimes continue; 6621556Srgrimes } 6631556Srgrimes if (*cp != 'C' && *cp != 'D') { 6641556Srgrimes /* 6651556Srgrimes * Check for the case "rcp remote:foo\* local:bar". 6661556Srgrimes * In this case, the line "No match." can be returned 6671556Srgrimes * by the shell before the rcp command on the remote is 6681556Srgrimes * executed so the ^Aerror_message convention isn't 6691556Srgrimes * followed. 6701556Srgrimes */ 6711556Srgrimes if (first) { 6721556Srgrimes run_err("%s", cp); 6731556Srgrimes exit(1); 6741556Srgrimes } 6751556Srgrimes SCREWUP("expected control record"); 6761556Srgrimes } 6771556Srgrimes mode = 0; 6781556Srgrimes for (++cp; cp < buf + 5; cp++) { 6791556Srgrimes if (*cp < '0' || *cp > '7') 6801556Srgrimes SCREWUP("bad mode"); 6811556Srgrimes mode = (mode << 3) | (*cp - '0'); 6821556Srgrimes } 6831556Srgrimes if (*cp++ != ' ') 6841556Srgrimes SCREWUP("mode not delimited"); 6851556Srgrimes 6861556Srgrimes for (size = 0; isdigit(*cp);) 6871556Srgrimes size = size * 10 + (*cp++ - '0'); 6881556Srgrimes if (*cp++ != ' ') 6891556Srgrimes SCREWUP("size not delimited"); 6901556Srgrimes if (targisdir) { 691104559Scharnier if (strlen(targ) + (*targ ? 1 : 0) + strlen(cp) 692104559Scharnier >= sizeof(path)) { 693104559Scharnier run_err("%s%s%s: name too long", targ, 694104559Scharnier *targ ? "/" : "", cp); 695104559Scharnier exit(1); 6961556Srgrimes } 697104559Scharnier (void)snprintf(path, sizeof(path), "%s%s%s", targ, 6981556Srgrimes *targ ? "/" : "", cp); 699104559Scharnier np = path; 7001556Srgrimes } else 7011556Srgrimes np = targ; 7021556Srgrimes exists = stat(np, &stb) == 0; 7031556Srgrimes if (buf[0] == 'D') { 7041556Srgrimes int mod_flag = pflag; 7051556Srgrimes if (exists) { 7061556Srgrimes if (!S_ISDIR(stb.st_mode)) { 7071556Srgrimes errno = ENOTDIR; 7081556Srgrimes goto bad; 7091556Srgrimes } 7101556Srgrimes if (pflag) 7111556Srgrimes (void)chmod(np, mode); 7121556Srgrimes } else { 7131556Srgrimes /* Handle copying from a read-only directory */ 7141556Srgrimes mod_flag = 1; 7151556Srgrimes if (mkdir(np, mode | S_IRWXU) < 0) 7161556Srgrimes goto bad; 7171556Srgrimes } 7181556Srgrimes vect[0] = np; 7191556Srgrimes sink(1, vect); 7201556Srgrimes if (setimes) { 7211556Srgrimes setimes = 0; 7221556Srgrimes if (utimes(np, tv) < 0) 7231556Srgrimes run_err("%s: set times: %s", 7241556Srgrimes np, strerror(errno)); 7251556Srgrimes } 7261556Srgrimes if (mod_flag) 7271556Srgrimes (void)chmod(np, mode); 7281556Srgrimes continue; 7291556Srgrimes } 7301556Srgrimes omode = mode; 7311556Srgrimes mode |= S_IWRITE; 7321556Srgrimes if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 7331556Srgrimesbad: run_err("%s: %s", np, strerror(errno)); 7341556Srgrimes continue; 7351556Srgrimes } 7361556Srgrimes (void)write(rem, "", 1); 7371556Srgrimes if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 7381556Srgrimes (void)close(ofd); 7391556Srgrimes continue; 7401556Srgrimes } 7411556Srgrimes cp = bp->buf; 7421556Srgrimes wrerr = NO; 7431556Srgrimes for (count = i = 0; i < size; i += BUFSIZ) { 7441556Srgrimes amt = BUFSIZ; 7451556Srgrimes if (i + amt > size) 7461556Srgrimes amt = size - i; 7471556Srgrimes count += amt; 7481556Srgrimes do { 7491556Srgrimes j = read(rem, cp, amt); 7501556Srgrimes if (j <= 0) { 7511556Srgrimes run_err("%s", j ? strerror(errno) : 7521556Srgrimes "dropped connection"); 7531556Srgrimes exit(1); 7541556Srgrimes } 7551556Srgrimes amt -= j; 7561556Srgrimes cp += j; 7571556Srgrimes } while (amt > 0); 7581556Srgrimes if (count == bp->cnt) { 7591556Srgrimes /* Keep reading so we stay sync'd up. */ 7601556Srgrimes if (wrerr == NO) { 7611556Srgrimes j = write(ofd, bp->buf, count); 7621556Srgrimes if (j != count) { 7631556Srgrimes wrerr = YES; 7648855Srgrimes wrerrno = j >= 0 ? EIO : errno; 7651556Srgrimes } 7661556Srgrimes } 7671556Srgrimes count = 0; 7681556Srgrimes cp = bp->buf; 7691556Srgrimes } 7701556Srgrimes } 7711556Srgrimes if (count != 0 && wrerr == NO && 7721556Srgrimes (j = write(ofd, bp->buf, count)) != count) { 7731556Srgrimes wrerr = YES; 7748855Srgrimes wrerrno = j >= 0 ? EIO : errno; 7751556Srgrimes } 7761556Srgrimes if (ftruncate(ofd, size)) { 7771556Srgrimes run_err("%s: truncate: %s", np, strerror(errno)); 7781556Srgrimes wrerr = DISPLAYED; 7791556Srgrimes } 7801556Srgrimes if (pflag) { 7811556Srgrimes if (exists || omode != mode) 7821556Srgrimes if (fchmod(ofd, omode)) 7831556Srgrimes run_err("%s: set mode: %s", 7841556Srgrimes np, strerror(errno)); 7851556Srgrimes } else { 7861556Srgrimes if (!exists && omode != mode) 7871556Srgrimes if (fchmod(ofd, omode & ~mask)) 7881556Srgrimes run_err("%s: set mode: %s", 7891556Srgrimes np, strerror(errno)); 7901556Srgrimes } 7911556Srgrimes (void)close(ofd); 7921556Srgrimes (void)response(); 7931556Srgrimes if (setimes && wrerr == NO) { 7941556Srgrimes setimes = 0; 7951556Srgrimes if (utimes(np, tv) < 0) { 7961556Srgrimes run_err("%s: set times: %s", 7971556Srgrimes np, strerror(errno)); 7981556Srgrimes wrerr = DISPLAYED; 7991556Srgrimes } 8001556Srgrimes } 8011556Srgrimes switch(wrerr) { 8021556Srgrimes case YES: 8031556Srgrimes run_err("%s: %s", np, strerror(wrerrno)); 8041556Srgrimes break; 8051556Srgrimes case NO: 8061556Srgrimes (void)write(rem, "", 1); 8071556Srgrimes break; 8081556Srgrimes case DISPLAYED: 8091556Srgrimes break; 8101556Srgrimes } 8111556Srgrimes } 8121556Srgrimesscrewup: 8131556Srgrimes run_err("protocol error: %s", why); 8141556Srgrimes exit(1); 8151556Srgrimes} 8161556Srgrimes 8171556Srgrimes#ifdef KERBEROS 8181556Srgrimesint 81990110Simpkerberos(char **host, char *bp, char *locuser, char *user) 8201556Srgrimes{ 8211556Srgrimes if (use_kerberos) { 82234898Smarkm setuid(getuid()); 8231556Srgrimes rem = KSUCCESS; 8241556Srgrimes errno = 0; 8251556Srgrimes if (dest_realm == NULL) 8261556Srgrimes dest_realm = krb_realmofhost(*host); 8278855Srgrimes rem = 8281556Srgrimes#ifdef CRYPT 8298855Srgrimes doencrypt ? 8301556Srgrimes krcmd_mutual(host, 8311556Srgrimes port, user, bp, 0, dest_realm, &cred, schedule) : 8321556Srgrimes#endif 8331556Srgrimes krcmd(host, port, user, bp, 0, dest_realm); 8341556Srgrimes 8351556Srgrimes if (rem < 0) { 8361556Srgrimes if (errno == ECONNREFUSED) 8371556Srgrimes oldw("remote host doesn't support Kerberos"); 8381556Srgrimes else if (errno == ENOENT) 8391556Srgrimes oldw("can't provide Kerberos authentication data"); 84034898Smarkm execv(_PATH_RCP, argv_copy); 84148560Sbde err(1, "execv: %s", _PATH_RCP); 8421556Srgrimes } 8431556Srgrimes } else { 8441556Srgrimes#ifdef CRYPT 8451556Srgrimes if (doencrypt) 8461556Srgrimes errx(1, 8471556Srgrimes "the -x option requires Kerberos authentication"); 8481556Srgrimes#endif 849101591Sume rem = rcmd_af(host, port, locuser, user, bp, 0, family); 8501556Srgrimes } 8511556Srgrimes return (rem); 8521556Srgrimes} 8531556Srgrimes#endif /* KERBEROS */ 8541556Srgrimes 8551556Srgrimesint 85690110Simpresponse(void) 8571556Srgrimes{ 8581556Srgrimes char ch, *cp, resp, rbuf[BUFSIZ]; 8591556Srgrimes 8601556Srgrimes if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) 8611556Srgrimes lostconn(0); 8621556Srgrimes 8631556Srgrimes cp = rbuf; 8641556Srgrimes switch(resp) { 8651556Srgrimes case 0: /* ok */ 8661556Srgrimes return (0); 8671556Srgrimes default: 8681556Srgrimes *cp++ = resp; 8691556Srgrimes /* FALLTHROUGH */ 8701556Srgrimes case 1: /* error, followed by error msg */ 8711556Srgrimes case 2: /* fatal error, "" */ 8721556Srgrimes do { 8731556Srgrimes if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 8741556Srgrimes lostconn(0); 8751556Srgrimes *cp++ = ch; 8761556Srgrimes } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 8771556Srgrimes 8781556Srgrimes if (!iamremote) 8791556Srgrimes (void)write(STDERR_FILENO, rbuf, cp - rbuf); 8801556Srgrimes ++errs; 8811556Srgrimes if (resp == 1) 8821556Srgrimes return (-1); 8831556Srgrimes exit(1); 8841556Srgrimes } 8851556Srgrimes /* NOTREACHED */ 8861556Srgrimes} 8871556Srgrimes 8881556Srgrimesvoid 88990110Simpusage(void) 8901556Srgrimes{ 8911556Srgrimes#ifdef KERBEROS 8921556Srgrimes#ifdef CRYPT 89326466Scharnier (void)fprintf(stderr, "%s\n%s\n", 894101591Sume "usage: rcp [-46Kpx] [-k realm] f1 f2", 895101591Sume " rcp [-46Kprx] [-k realm] f1 ... fn directory"); 8961556Srgrimes#else 89726466Scharnier (void)fprintf(stderr, "%s\n%s\n", 898101591Sume "usage: rcp [-46Kp] [-k realm] f1 f2", 899101591Sume " rcp [-46Kpr] [-k realm] f1 ... fn directory"); 9001556Srgrimes#endif 9011556Srgrimes#else 90226466Scharnier (void)fprintf(stderr, "%s\n%s\n", 903101591Sume "usage: rcp [-46p] f1 f2", 904101591Sume " rcp [-46pr] f1 ... fn directory"); 9051556Srgrimes#endif 9061556Srgrimes exit(1); 9071556Srgrimes} 9081556Srgrimes 9091556Srgrimes#include <stdarg.h> 9101556Srgrimes 9111556Srgrimes#ifdef KERBEROS 9121556Srgrimesvoid 9131556Srgrimesoldw(const char *fmt, ...) 9141556Srgrimes{ 9151556Srgrimes va_list ap; 9161556Srgrimes va_start(ap, fmt); 9171556Srgrimes (void)fprintf(stderr, "rcp: "); 9181556Srgrimes (void)vfprintf(stderr, fmt, ap); 9191556Srgrimes (void)fprintf(stderr, ", using standard rcp\n"); 9201556Srgrimes va_end(ap); 9211556Srgrimes} 9221556Srgrimes#endif 9231556Srgrimes 9241556Srgrimesvoid 9251556Srgrimesrun_err(const char *fmt, ...) 9261556Srgrimes{ 9271556Srgrimes static FILE *fp; 9281556Srgrimes va_list ap; 9291556Srgrimes va_start(ap, fmt); 9301556Srgrimes 9311556Srgrimes ++errs; 9321556Srgrimes if (fp == NULL && !(fp = fdopen(rem, "w"))) 9331556Srgrimes return; 9341556Srgrimes (void)fprintf(fp, "%c", 0x01); 9351556Srgrimes (void)fprintf(fp, "rcp: "); 9361556Srgrimes (void)vfprintf(fp, fmt, ap); 9371556Srgrimes (void)fprintf(fp, "\n"); 9381556Srgrimes (void)fflush(fp); 9391556Srgrimes 9401556Srgrimes if (!iamremote) 9411556Srgrimes vwarnx(fmt, ap); 9421556Srgrimes 9431556Srgrimes va_end(ap); 9441556Srgrimes} 945