/* * appl/bsd/krcp.c */ /* * Copyright (c) 1983 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ /* based on @(#)rcp.c 5.10 (Berkeley) 9/20/88 */ /* * rcp */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #ifndef _TYPES_ #include #define _TYPES_ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_VFORK_H #include #endif #ifdef HAVE_STDARG_H #include #else #include #endif #include #ifdef KERBEROS #include #include #include #ifdef KRB5_KRB4_COMPAT #include #endif #include "defines.h" #define RCP_BUFSIZ 4096 int sock; struct sockaddr_in local, foreign; /* set up by kcmd used by v4_send_auth */ char *krb_realm = NULL; char *krb_cache = NULL; char *krb_config = NULL; krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ krb5_context bsd_context; #ifdef KRB5_KRB4_COMPAT Key_schedule v4_schedule; CREDENTIALS v4_cred; KTEXT_ST v4_ticket; MSG_DAT v4_msg_data; #endif void v4_send_auth(char *, char *), try_normal(char **); char **save_argv(int, char **); #ifndef HAVE_STRSAVE char *strsave(); #endif int rcmd_stream_write(), rcmd_stream_read(); void usage(void), sink(int, char **), source(int, char **), rsource(char *, struct stat *), verifydir(char *), answer_auth(char *, char *); int response(void), hosteq(char *, char *), okname(char *), susystem(char *); int encryptflag = 0; #ifndef UCB_RCP #define UCB_RCP "/bin/rcp" #endif #endif /* KERBEROS */ int rem; char *colon(char *); int errs; krb5_sigtype lostconn(int); int iamremote, targetshouldbedirectory; int iamrecursive; int pflag; int forcenet; struct passwd *pwd; int userid; int port = 0; struct buffer { unsigned int cnt; char *buf; }; struct buffer *allocbuf(struct buffer *, int, int); #define NULLBUF (struct buffer *) 0 void error (char *fmt, ...) #if !defined (__cplusplus) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) __attribute__ ((__format__ (__printf__, 1, 2))) #endif ; #define ga() (void) rcmd_stream_write(rem, "", 1, 0) int main(argc, argv) int argc; char **argv; { char *targ, *host, *src; char *suser, *tuser, *thost; int i; unsigned int cmdsiz = 30; char buf[RCP_BUFSIZ], cmdbuf[30]; char *cmd = cmdbuf; struct servent *sp; static char curhost[256]; #ifdef POSIX_SIGNALS struct sigaction sa; #endif #ifdef KERBEROS krb5_flags authopts; krb5_error_code status; int euid; char **orig_argv = save_argv(argc, argv); krb5_auth_context auth_context; enum kcmd_proto kcmd_proto = KCMD_PROTOCOL_COMPAT_HACK; status = krb5_init_context(&bsd_context); if (status) { com_err(argv[0], status, "while initializing krb5"); exit(1); } #endif pwd = getpwuid(userid = getuid()); if (pwd == 0) { fprintf(stderr, "who are you?\n"); exit(1); } for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { (*argv)++; while (**argv) switch (*(*argv)++) { case 'r': iamrecursive++; break; case 'p': /* preserve mtimes and atimes */ pflag++; break; case 'D': argc--, argv++; if (argc == 0) usage(); port = htons(atoi(*argv)); goto next_arg; case 'N': forcenet++; break; #ifdef KERBEROS case 'x': encryptflag++; break; case 'k': /* Change kerberos realm */ argc--, argv++; if (argc == 0) usage(); if(!(krb_realm = (char *)malloc(strlen(*argv) + 1))){ fprintf(stderr, "rcp: Cannot malloc.\n"); exit(1); } strcpy(krb_realm, *argv); goto next_arg; case 'c': /* Change default ccache file */ argc--, argv++; if (argc == 0) usage(); if(!(krb_cache = (char *)malloc(strlen(*argv) + 1))){ fprintf(stderr, "rcp: Cannot malloc.\n"); exit(1); } strcpy(krb_cache, *argv); goto next_arg; case 'C': /* Change default config file */ argc--, argv++; if (argc == 0) usage(); if(!(krb_config = (char *)malloc(strlen(*argv) + 1))){ fprintf(stderr, "rcp: Cannot malloc.\n"); exit(1); } strcpy(krb_config, *argv); goto next_arg; case 'P': if (!strcmp (*argv, "O")) kcmd_proto = KCMD_OLD_PROTOCOL; else if (!strcmp (*argv, "N")) kcmd_proto = KCMD_NEW_PROTOCOL; else usage (); goto next_arg; #endif /* KERBEROS */ /* The rest of these are not for users. */ case 'd': targetshouldbedirectory = 1; break; case 'f': /* "from" */ iamremote = 1; rcmd_stream_init_normal(); #if defined(KERBEROS) if (encryptflag) answer_auth(krb_config, krb_cache); #endif /* KERBEROS */ (void) response(); source(--argc, ++argv); exit(errs); case 't': /* "to" */ iamremote = 1; rcmd_stream_init_normal(); #if defined(KERBEROS) if (encryptflag) answer_auth(krb_config, krb_cache); #endif /* KERBEROS */ sink(--argc, ++argv); exit(errs); default: usage(); } next_arg: ; } if (argc < 2) usage(); if (argc > 2) targetshouldbedirectory = 1; rem = -1; if (port == 0) { #ifdef KERBEROS sp = getservbyname("kshell", "tcp"); #else sp = getservbyname("shell", "tcp"); #endif /* KERBEROS */ if (sp == NULL) { #ifdef KERBEROS fprintf(stderr, "rcp: kshell/tcp: unknown service\n"); try_normal(orig_argv); #else fprintf(stderr, "rcp: shell/tcp: unknown service\n"); exit(1); #endif /* KERBEROS */ } port = sp->s_port; } #ifdef KERBEROS if (krb_realm != NULL) cmdsiz += strlen(krb_realm); if (krb_cache != NULL) cmdsiz += strlen(krb_cache); if (krb_config != NULL) cmdsiz += strlen(krb_config); if ((cmd = (char *)malloc(cmdsiz)) == NULL) { fprintf(stderr, "rcp: Cannot malloc.\n"); exit(1); } (void) sprintf(cmd, "%srcp %s%s%s%s%s%s%s%s%s", encryptflag ? "-x " : "", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "", krb_realm != NULL ? " -k " : "", krb_realm != NULL ? krb_realm : "", krb_cache != NULL ? " -c " : "", krb_cache != NULL ? krb_cache : "", krb_config != NULL ? " -C " : "", krb_config != NULL ? krb_config : ""); #else /* !KERBEROS */ (void) sprintf(cmd, "rcp%s%s%s", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); #endif /* KERBEROS */ #ifdef POSIX_SIGNALS (void) sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = lostconn; (void) sigaction(SIGPIPE, &sa, (struct sigaction *)0); #else (void) signal(SIGPIPE, lostconn); #endif targ = colon(argv[argc - 1]); /* Check if target machine is the current machine. */ gethostname(curhost, sizeof(curhost)); if (targ) { /* ... to remote */ *targ++ = 0; if (hosteq(argv[argc - 1], curhost)) { /* If so, pretend there wasn't even one given * check for an argument of just "host:", it * should become "." */ if (*targ == 0) { targ = "."; argv[argc - 1] = targ; } else argv[argc - 1] = targ; targ = 0; } } if (targ) { /* Target machine is some remote machine */ if (*targ == 0) targ = "."; thost = strchr(argv[argc - 1], '@'); if (thost) { *thost++ = 0; tuser = argv[argc - 1]; if (*tuser == '\0') tuser = NULL; else if (!okname(tuser)) exit(1); } else { thost = argv[argc - 1]; tuser = NULL; } for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src) { /* remote to remote */ *src++ = 0; if (*src == 0) src = "."; host = strchr(argv[i], '@'); if (host) { *host++ = 0; suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname(suser)) continue; (void) sprintf(buf, #if defined(hpux) || defined(__hpux) "remsh %s -l %s -n %s %s '%s%s%s:%s'", #else "rsh %s -l %s -n %s %s '%s%s%s:%s'", #endif host, suser, cmd, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ); } else (void) sprintf(buf, #if defined(hpux) || defined(__hpux) "remsh %s -n %s %s '%s%s%s:%s'", #else "rsh %s -n %s %s '%s%s%s:%s'", #endif argv[i], cmd, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ); (void) susystem(buf); } else { /* local to remote */ krb5_creds *cred; if (rem == -1) { (void) sprintf(buf, "%s -t %s", cmd, targ); host = thost; #ifdef KERBEROS authopts = AP_OPTS_MUTUAL_REQUIRED; status = kcmd(&sock, &host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, buf, 0, "host", krb_realm, &cred, 0, /* No seq # */ 0, /* No server seq # */ &local, &foreign, &auth_context, authopts, 0, /* Not any port # */ 0, &kcmd_proto); if (status) { if (kcmd_proto == KCMD_NEW_PROTOCOL) /* Don't fall back to less safe methods. */ exit (1); #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Trying krb4 rcp...\n"); if (strncmp(buf, "-x rcp", 6) == 0) memcpy(buf, "rcp -x", 6); status = k4cmd(&sock, &host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, buf, 0, &v4_ticket, "rcmd", krb_realm, NULL, NULL, NULL, &local, &foreign, 0L, 0); if (status) try_normal(orig_argv); if (encryptflag) v4_send_auth(host, krb_realm); rcmd_stream_init_krb4(v4_cred.session, encryptflag, 0, 0); #else try_normal(orig_argv); #endif } else { krb5_boolean similar; krb5_keyblock *key = &cred->keyblock; status = krb5_c_enctype_compare(bsd_context, ENCTYPE_DES_CBC_CRC, cred->keyblock.enctype, &similar); if (status) try_normal(orig_argv); /* doesn't return */ if (!similar) { status = krb5_auth_con_getlocalsubkey (bsd_context, auth_context, &key); if ((status || !key) && encryptflag) try_normal(orig_argv); } if (key == 0) key = &cred->keyblock; rcmd_stream_init_krb5(key, encryptflag, 0, 1, kcmd_proto); } rem = sock; #else rem = rcmd(&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, buf, 0); if (rem < 0) exit(1); #endif /* KERBEROS */ if (response() < 0) exit(1); } source(1, argv+i); } } } else { /* ... to local */ if (targetshouldbedirectory) verifydir(argv[argc - 1]); for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); /* Check if source machine is current machine */ if (src) { *src++ = 0; if (hosteq(argv[i], curhost)) { /* If so, pretend src machine never given */ if (*src == 0) { error("rcp: no path given in arg: %s:\n", argv[i]); errs++; continue; } argv[i] = src; src = 0; } else { /* not equiv, return colon */ *(--src) = ':'; } } if (src == 0) { /* local to local */ (void) sprintf(buf, "/bin/cp%s%s %s %s", iamrecursive ? " -r" : "", pflag ? " -p" : "", argv[i], argv[argc - 1]); (void) susystem(buf); } else { /* remote to local */ krb5_creds *cred; *src++ = 0; if (*src == 0) src = "."; host = strchr(argv[i], '@'); if (host) { *host++ = 0; suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname(suser)) continue; } else { host = argv[i]; suser = pwd->pw_name; } (void) sprintf(buf, "%s -f %s", cmd, src); #ifdef KERBEROS authopts = AP_OPTS_MUTUAL_REQUIRED; status = kcmd(&sock, &host, port, pwd->pw_name, suser, buf, 0, "host", krb_realm, &cred, 0, /* No seq # */ 0, /* No server seq # */ (struct sockaddr_in *) 0, &foreign, &auth_context, authopts, 0, /* Not any port # */ 0, &kcmd_proto); if (status) { if (kcmd_proto == KCMD_NEW_PROTOCOL) /* Don't fall back to less safe methods. */ exit (1); #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Trying krb4 rcp...\n"); if (strncmp(buf, "-x rcp", 6) == 0) memcpy(buf, "rcp -x", 6); status = k4cmd(&sock, &host, port, pwd->pw_name, suser, buf, 0, &v4_ticket, "rcmd", krb_realm, NULL, NULL, NULL, &local, &foreign, 0L, 0); if (status) try_normal(orig_argv); if (encryptflag) v4_send_auth(host, krb_realm); rcmd_stream_init_krb4(v4_cred.session, encryptflag, 0, 0); #else try_normal(orig_argv); #endif } else { krb5_keyblock *key = &cred->keyblock; if (kcmd_proto == KCMD_NEW_PROTOCOL) { status = krb5_auth_con_getlocalsubkey (bsd_context, auth_context, &key); if (status) { com_err (argv[0], status, "determining subkey for session"); exit (1); } if (!key) { com_err (argv[0], 0, "no subkey negotiated for connection"); exit (1); } } rcmd_stream_init_krb5(key, encryptflag, 0, 1, kcmd_proto); } rem = sock; euid = geteuid(); if (euid == 0) { (void) setuid(0); if(krb5_seteuid(userid)) { perror("rcp seteuid user"); errs++; exit(errs); } } sink(1, argv+argc-1); if (euid == 0) { if(krb5_seteuid(0)) { perror("rcp seteuid 0"); errs++; exit(errs); } } #else rem = rcmd(&host, port, pwd->pw_name, suser, buf, 0); if (rem < 0) continue; rcmd_stream_init_normal(); #ifdef HAVE_SETREUID (void) setreuid(0, userid); sink(1, argv+argc-1); (void) setreuid(userid, 0); #else (void) setuid(0); if(seteuid(userid)) { perror("rcp seteuid user"); errs++; exit(errs); } sink(1, argv+argc-1); if(seteuid(0)) { perror("rcp seteuid 0"); errs++; exit(errs); } #endif #endif /* KERBEROS */ (void) close(rem); rem = -1; } } } exit(errs); } void verifydir(cp) char *cp; { struct stat stb; if (stat(cp, &stb) >= 0) { if ((stb.st_mode & S_IFMT) == S_IFDIR) return; errno = ENOTDIR; } error("rcp: %s: %s.\n", cp, error_message(errno)); exit(1); } char *colon(cp) char *cp; { while (*cp) { if (*cp == ':') return (cp); if (*cp == '/') return (0); cp++; } return (0); } int okname(cp0) char *cp0; { register char *cp = cp0; register int c; do { c = *cp; if (c & 0200) goto bad; if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') goto bad; cp++; } while (*cp); return (1); bad: fprintf(stderr, "rcp: invalid user name %s\n", cp0); return (0); } int susystem(s) char *s; { int status; pid_t pid, w; #ifdef POSIX_SIGNALS struct sigaction sa, isa, qsa; #else register krb5_sigtype (bsd_context, *istat)(), (*qstat)(); #endif if ((pid = vfork()) == 0) { execl("/bin/sh", "sh", "-c", s, (char *)0); _exit(127); } #ifdef POSIX_SIGNALS (void) sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; (void) sigaction(SIGINT, &sa, &isa); (void) sigaction(SIGQUIT, &sa, &qsa); #else istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); #endif #ifdef HAVE_WAITPID w = waitpid(pid, &status, 0); #else while ((w = wait(&status)) != pid && w != -1) /*void*/ ; #endif if (w == (pid_t)-1) status = -1; #ifdef POSIX_SIGNALS (void) sigaction(SIGINT, &isa, (struct sigaction *)0); (void) sigaction(SIGQUIT, &qsa, (struct sigaction *)0); #else (void) signal(SIGINT, istat); (void) signal(SIGQUIT, qstat); #endif return (status); } void source(argc, argv) int argc; char **argv; { char *last, *name; struct stat stb; static struct buffer buffer; struct buffer *bp; int x, readerr, f; unsigned int amt; off_t i; char buf[RCP_BUFSIZ]; for (x = 0; x < argc; x++) { name = argv[x]; if ((f = open(name, 0)) < 0) { error("rcp: %s: %s\n", name, error_message(errno)); continue; } if (fstat(f, &stb) < 0) goto notreg; switch (stb.st_mode&S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { (void) close(f); rsource(name, &stb); continue; } /* fall into ... */ default: notreg: (void) close(f); error("rcp: %s: not a plain file\n", name); continue; } last = strrchr(name, '/'); if (last == 0) last = name; else last++; if (pflag) { /* * Make it compatible with possible future * versions expecting microseconds. */ (void) sprintf(buf, "T%ld 0 %ld 0\n", stb.st_mtime, stb.st_atime); (void) rcmd_stream_write(rem, buf, strlen(buf), 0); if (response() < 0) { (void) close(f); continue; } } (void) sprintf(buf, "C%04o %ld %s\n", (int) stb.st_mode&07777, (long ) stb.st_size, last); (void) rcmd_stream_write(rem, buf, strlen(buf), 0); if (response() < 0) { (void) close(f); continue; } if ((bp = allocbuf(&buffer, f, RCP_BUFSIZ)) == NULLBUF) { (void) close(f); continue; } readerr = 0; for (i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + amt > stb.st_size) amt = stb.st_size - i; if (readerr == 0 && read(f, bp->buf, amt) != amt) readerr = errno; (void) rcmd_stream_write(rem, bp->buf, amt, 0); } (void) close(f); if (readerr == 0) ga(); else error("rcp: %s: %s\n", name, error_message(readerr)); (void) response(); } } #ifndef USE_DIRENT_H #include #else #include #endif void rsource(name, statp) char *name; struct stat *statp; { DIR *d = opendir(name); char *last; #ifdef USE_DIRENT_H struct dirent *dp; #else struct direct *dp; #endif char buf[RCP_BUFSIZ]; char *bufv[1]; if (d == 0) { error("rcp: %s: %s\n", name, error_message(errno)); return; } last = strrchr(name, '/'); if (last == 0) last = name; else last++; if (pflag) { (void) sprintf(buf, "T%ld 0 %ld 0\n", statp->st_mtime, statp->st_atime); (void) rcmd_stream_write(rem, buf, strlen(buf), 0); if (response() < 0) { closedir(d); return; } } (void) sprintf(buf, "D%04lo %d %s\n", (long) statp->st_mode&07777, 0, last); (void) rcmd_stream_write(rem, buf, strlen(buf), 0); if (response() < 0) { closedir(d); return; } while ((dp = readdir(d)) != NULL) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; if (strlen(name) + 1 + strlen(dp->d_name) >= RCP_BUFSIZ - 1) { error("%s/%s: Name too long.\n", name, dp->d_name); continue; } (void) sprintf(buf, "%s/%s", name, dp->d_name); bufv[0] = buf; source(1, bufv); } closedir(d); (void) rcmd_stream_write(rem, "E\n", 2, 0); (void) response(); } int response() { char resp, c, rbuf[RCP_BUFSIZ], *cp = rbuf; if (rcmd_stream_read(rem, &resp, 1, 0) != 1) lostconn(0); switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* fall into... */ case 1: /* error, followed by err msg */ case 2: /* fatal error, "" */ do { if (rcmd_stream_read(rem, &c, 1, 0) != 1) lostconn(0); *cp++ = c; } while (cp < &rbuf[RCP_BUFSIZ] && c != '\n'); if (iamremote == 0) (void) write(2, rbuf, (unsigned) (cp - rbuf)); errs++; if (resp == 1) return (-1); exit(1); } /*NOTREACHED*/ } krb5_sigtype lostconn(signumber) int signumber; { if (iamremote == 0) fprintf(stderr, "rcp: lost connection\n"); exit(1); } #if !defined(HAVE_UTIMES) #include #include /* * We emulate utimes() instead of utime() as necessary because * utimes() is more powerful than utime(), and rcp actually tries to * set the microsecond values; we don't want to take away * functionality unnecessarily. */ int utimes(file, tvp) const char *file; struct timeval *tvp; { struct utimbuf times; times.actime = tvp[0].tv_sec; times.modtime = tvp[1].tv_sec; return(utime(file, ×)); } #endif void sink(argc, argv) int argc; char **argv; { mode_t mode; mode_t mask = umask(0); off_t i, j; char *targ, *whopp, *cp; int of, wrerr, exists, first; off_t size; unsigned int amt, count; struct buffer *bp; static struct buffer buffer; struct stat stb; int targisdir = 0; char *myargv[1]; char cmdbuf[RCP_BUFSIZ], nambuf[RCP_BUFSIZ]; int setimes = 0; struct timeval tv[2]; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { whopp = str; goto screwup; } if (!pflag) (void) umask(mask); if (argc != 1) { error("rcp: ambiguous target\n"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); ga(); if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) targisdir = 1; for (first = 1; ; first = 0) { cp = cmdbuf; if (rcmd_stream_read(rem, cp, 1, 0) <= 0) return; if (*cp++ == '\n') SCREWUP("unexpected '\\n'"); do { if (rcmd_stream_read(rem, cp, 1, 0) != 1) SCREWUP("lost connection"); } while (*cp++ != '\n'); *cp = 0; if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { if (iamremote == 0) (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); if (cmdbuf[0] == '\02') exit(1); errs++; continue; } *--cp = 0; cp = cmdbuf; if (*cp == 'E') { ga(); return; } #define getnum(t) (t) = 0; while (isdigit((int) *cp)) (t) = (t) * 10 + (*cp++ - '0'); if (*cp == 'T') { setimes++; cp++; getnum(mtime.tv_sec); if (*cp++ != ' ') SCREWUP("mtime.sec not delimited"); getnum(mtime.tv_usec); if (*cp++ != ' ') SCREWUP("mtime.usec not delimited"); getnum(atime.tv_sec); if (*cp++ != ' ') SCREWUP("atime.sec not delimited"); getnum(atime.tv_usec); if (*cp++ != '\0') SCREWUP("atime.usec not delimited"); ga(); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { error("%s\n", cp); exit(1); } SCREWUP("expected control record"); } cp++; mode = 0; for (; cp < cmdbuf+5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); size = 0; while (isdigit((int) *cp)) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if (targisdir) { if(strlen(targ) + strlen(cp) + 2 >= sizeof(nambuf)) SCREWUP("target name too long"); (void) sprintf(nambuf, "%s%s%s", targ, *targ ? "/" : "", cp); } else { if (strlen(targ) + 1 >= sizeof (nambuf)) SCREWUP("target name too long"); (void) strncpy(nambuf, targ, sizeof(nambuf) - 1); } nambuf[sizeof(nambuf) - 1] = '\0'; exists = stat(nambuf, &stb) == 0; if (cmdbuf[0] == 'D') { if (exists) { if ((stb.st_mode&S_IFMT) != S_IFDIR) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(nambuf, mode); } else if (mkdir(nambuf, mode) < 0) goto bad; myargv[0] = nambuf; sink(1, myargv); if (setimes) { setimes = 0; if (utimes(nambuf, tv) < 0) error("rcp: can't set times on %s: %s\n", nambuf, error_message(errno)); } continue; } if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) { bad: error("rcp: %s: %s\n", nambuf, error_message(errno)); continue; } if (exists && pflag) { #ifdef NOFCHMOD (void) chmod(nambuf, mode); #else (void) fchmod(of, mode); #endif } ga(); if ((bp = allocbuf(&buffer, of, RCP_BUFSIZ)) == NULLBUF) { (void) close(of); continue; } cp = bp->buf; count = 0; wrerr = 0; for (i = 0; i < size; i += RCP_BUFSIZ) { amt = RCP_BUFSIZ; if (i + amt > size) amt = size - i; count += amt; do { j = rcmd_stream_read(rem, cp, amt, 0); if (j <= 0) { if (j == 0) error("rcp: dropped connection"); else error("rcp: %s\n", error_message(errno)); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { if (wrerr == 0 && write(of, bp->buf, count) != count) wrerr++; count = 0; cp = bp->buf; } } if (count != 0 && wrerr == 0 && write(of, bp->buf, count) != count) wrerr++; if (ftruncate(of, size)) error("rcp: can't truncate %s: %s\n", nambuf, error_message(errno)); (void) close(of); (void) response(); if (setimes) { setimes = 0; if (utimes(nambuf, tv) < 0) error("rcp: can't set times on %s: %s\n", nambuf, error_message(errno)); } if (wrerr) error("rcp: %s: %s\n", nambuf, error_message(errno)); else ga(); } screwup: error("rcp: protocol screwup: %s\n", whopp); exit(1); } struct buffer *allocbuf(bp, fd, blksize) struct buffer *bp; int fd, blksize; { struct stat stb; int size; if (fstat(fd, &stb) < 0) { error("rcp: fstat: %s\n", error_message(errno)); return (NULLBUF); } size = blksize; if (bp->cnt < size) { if (bp->buf != 0) free(bp->buf); bp->buf = (char *)malloc((unsigned) size); if (bp->buf == 0) { error("rcp: malloc: out of memory\n"); return (NULLBUF); } } bp->cnt = size; return (bp); } void #ifdef HAVE_STDARG_H error(char *fmt, ...) #else /*VARARGS1*/ error(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; char buf[RCP_BUFSIZ], *cp = buf; #ifdef HAVE_STDARG_H va_start(ap, fmt); #else va_start(ap); #endif errs++; *cp++ = 1; (void) vsprintf(cp, fmt, ap); va_end(ap); if (iamremote) (void) rcmd_stream_write(rem, buf, strlen(buf), 0); else (void) write(2, buf+1, strlen(buf+1)); } void usage() { #ifdef KERBEROS fprintf(stderr, "Usage: \trcp [-PN | -PO] [-p] [-x] [-k realm] f1 f2; or:\n\trcp [-PN | -PO] [-r] [-p] [-x] [-k realm] f1 ... fn d2\n"); #else fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr); #endif exit(1); } int hosteq(h1, h2) char *h1, *h2; { struct hostent *h_ptr; char hname1[256]; if (forcenet) return(0); /* get the official names for the two hosts */ if ((h_ptr = gethostbyname(h1)) == NULL) return(0); strncpy(hname1, h_ptr->h_name, sizeof (hname1)); hname1[sizeof (hname1) - 1] = '\0'; if ((h_ptr = gethostbyname(h2)) == NULL) return(0); /*return if they are equal (strcmp returns 0 for equal - I return 1) */ return(!strcmp(hname1, h_ptr->h_name)); } #ifdef KERBEROS void try_normal(argv) char **argv; { register int i; #ifndef KRB5_ATHENA_COMPAT if (!encryptflag) #endif { fprintf(stderr,"trying normal rcp (%s)\n", UCB_RCP); fflush(stderr); /* close all but stdin, stdout, stderr */ for (i = getdtablesize(); i > 2; i--) (void) close(i); execv(UCB_RCP, argv); perror("exec"); } exit(1); } char **save_argv(argc, argv) int argc; char **argv; { register int i; char **local_argv = (char **)calloc((unsigned) argc+1, (unsigned) sizeof(char *)); /* allocate an extra pointer, so that it is initialized to NULL and execv() will work */ for (i = 0; i < argc; i++) local_argv[i] = strsave(argv[i]); return(local_argv); } #ifdef unicos61 #define SIZEOF_INADDR SIZEOF_in_addr #else #define SIZEOF_INADDR sizeof(struct in_addr) #endif /* This function is mostly vestigial, since under normal operation * the -x flag doesn't get set for the server process for encrypted * rcp. It only gets called by beta clients attempting user-to-user * authentication. */ void answer_auth(config_file, ccache_file) char *config_file; char *ccache_file; { krb5_data pname_data, msg; krb5_creds creds, *new_creds; krb5_ccache cc; krb5_error_code status; krb5_auth_context auth_context = NULL; if (config_file) { const char * filenames[2]; filenames[1] = NULL; filenames[0] = config_file; if ((status = krb5_set_config_files(bsd_context, filenames))) exit(1); } memset ((char*)&creds, 0, sizeof(creds)); if ((status = krb5_read_message(bsd_context, (krb5_pointer)&rem, &pname_data))) exit(1); if ((status = krb5_read_message(bsd_context, (krb5_pointer) &rem, &creds.second_ticket))) exit(1); if (ccache_file == NULL) { if ((status = krb5_cc_default(bsd_context, &cc))) exit(1); } else { if ((status = krb5_cc_resolve(bsd_context, ccache_file, &cc))) exit(1); } if ((status = krb5_cc_get_principal(bsd_context, cc, &creds.client))) exit(1); if ((status = krb5_parse_name(bsd_context, pname_data.data, &creds.server)) ) exit(1); krb5_free_data_contents(bsd_context, &pname_data); if ((status = krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds, &new_creds))) exit(1); if ((status = krb5_mk_req_extended(bsd_context, &auth_context, AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))) exit(1); if ((status = krb5_write_message(bsd_context, (krb5_pointer) &rem, &msg))) { krb5_free_data_contents(bsd_context, &msg); exit(1); } rcmd_stream_init_krb5(&new_creds->keyblock, encryptflag, 0, 0, KCMD_OLD_PROTOCOL); /* cleanup */ krb5_free_cred_contents(bsd_context, &creds); krb5_free_creds(bsd_context, new_creds); krb5_free_data_contents(bsd_context, &msg); return; } char storage[2*RCP_BUFSIZ]; /* storage for the decryption */ int nstored = 0; char *store_ptr = storage; #ifdef KRB5_KRB4_COMPAT void v4_send_auth(host,realm) char *host; char *realm; { long authopts; if ((realm == NULL) || (realm[0] == '\0')) realm = krb_realmofhost(host); /* this needs to be sent again, because the rcp process needs the key. the rshd has grabbed the first one. */ authopts = KOPT_DO_MUTUAL; if ((rem = krb_sendauth(authopts, sock, &v4_ticket, "rcmd", host, realm, (unsigned long) getpid(), &v4_msg_data, &v4_cred, v4_schedule, &local, &foreign, "KCMDV0.1")) != KSUCCESS) { fprintf(stderr, "krb_sendauth mutual fail: %s\n", krb_get_err_text(rem)); exit(1); } } #endif /* KRB5_KRB4_COMPAT */ #endif /* KERBEROS */