/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/rcp.c
1 522 строки
33 KB
Collin Funk
rcp: prefer lchmod to chmod
13 мар 2026, 09:39
13 мар 2026, 09:39
19ed637
Код
Авторство
О чём код?
/* Copyright (C) 1995-2026 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. */ /* * Copyright (c) 1983, 1990, 1992, 1993, 2002 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <config.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #ifdef HAVE_NETINET_IN_SYSTM_H # include <netinet/in_systm.h> #endif #ifdef HAVE_NETINET_IP_H # include <netinet/ip.h> #endif #include <ctype.h> #include <dirent.h> #include <error.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdint.h> /* intmax_t */ #include <string.h> #include <string.h> #include <unistd.h> #ifndef HAVE_UTIMES # include <utime.h> /* If we don't have utimes(), use utime(). */ #endif #include <progname.h> #include <attribute.h> #include <libinetutils.h> #include <argp.h> #include <error.h> #include <xalloc.h> #include "intprops.h" #include "stat-time.h" #include "timespec.h" typedef struct { int cnt; char *buf; } BUF; BUF *allocbuf (BUF *, int, int); char *colon (char *); void lostconn (int); void nospace (void); int okname (char *); void run_err (const char *, ...); int susystem (char *, int); void verifydir (char *); #ifdef SHISHI # include <shishi.h> # include "shishi_def.h" #endif /* SHISHI */ #ifdef KERBEROS # ifdef HAVE_KERBEROSIV_DES_H # include <kerberosIV/des.h> # endif # ifdef HAVE_KERBEROSIV_KRB_H # include <kerberosIV/krb.h> # endif #endif /* KERBEROS */ #if defined KERBEROS || defined SHISHI const char *dest_realm = NULL; int use_kerberos = 1; int doencrypt = 0; # ifdef KERBEROS CREDENTIALS cred; Key_schedule schedule; extern char *krb_realmofhost (const char *); # elif defined SHISHI /* !KERBEROS */ Shishi *h; Shishi_key *enckey; shishi_ivector iv1, iv2, iv3, iv4; shishi_ivector *ivtab[4]; int keytype; int keylen; int rc; int wlen; # endif/* SHISHI */ #endif /* KERBEROS || SHISHI */ const char doc[] = "Remote copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."; const char arg_doc[] = "SOURCE DEST\n" "SOURCE... DIRECTORY\n" "--target-directory=DIRECTORY SOURCE..."; char *target = NULL; int preserve_option; int from_option, to_option; int iamremote, iamrecursive, targetshouldbedirectory; #if defined WITH_ORCMD_AF || defined WITH_RCMD_AF || defined SHISHI sa_family_t family = AF_UNSPEC; #endif static struct argp_option options[] = { #define GRID 0 {"recursive", 'r', NULL, 0, "if any of the source files are directories, copies" " each subtree rooted at that name; in this case the" " destination must be a directory", GRID + 1}, {"preserve", 'p', NULL, 0, "attempt to preserve (duplicate) in its copies the" " modification times and modes of the source files", GRID + 1}, {"target-directory", 'd', "DIRECTORY", OPTION_ARG_OPTIONAL, "copy all SOURCE arguments into DIRECTORY", GRID + 1}, {"from", 'f', NULL, 0, "copying from remote host (server use only)", GRID + 1}, {"to", 't', NULL, 0, "copying to remote host (server use only)", GRID + 1}, #if defined WITH_ORCMD_AF || defined WITH_RCMD_AF || defined SHISHI {"ipv4", '4', NULL, 0, "use only IPv4", GRID + 1}, {"ipv6", '6', NULL, 0, "use only IPv6", GRID + 1}, #endif /* WITH_ORCMD_AF || WITH_RCMD_AF || SHISHI */ #undef GRID #if defined KERBEROS || defined SHISHI # define GRID 10 {"kerberos", 'K', NULL, 0, "turns off all Kerberos authentication", GRID + 1}, {"realm", 'k', "REALM", 0, "obtain tickets for a remote host in REALM instead of the remote host's realm", GRID + 1}, # ifdef ENCRYPTION {"encrypt", 'x', NULL, 0, "encrypt all data transfer", GRID + 1}, # endif # undef GRID #endif {NULL, 0, NULL, 0, NULL, 0} }; static error_t parse_opt (int key, char *arg, struct argp_state *state MAYBE_UNUSED) { switch (key) { #if defined WITH_ORCMD_AF || defined WITH_RCMD_AF || defined SHISHI case '4': family = AF_INET; break; case '6': family = AF_INET6; break; #endif /* WITH_ORCMD_AF || WITH_RCMD_AF || SHISHI */ #if defined KERBEROS || defined SHISHI case 'K': use_kerberos = 0; break; case 'k': dest_realm = arg; break; # ifdef ENCRYPTION case 'x': doencrypt = 1; # ifdef KERBEROS des_set_key (cred.session, schedule); # endif break; # endif/* ENCRYPTION */ #endif /* KERBEROS || SHISHI */ case 'p': preserve_option = 1; break; case 'r': iamrecursive = 1; break; /* Server options. */ case 'd': targetshouldbedirectory = 1; if (arg && strlen (arg)) target = xstrdup (arg); /* Client side use. */ break; case 'f': /* "from" */ iamremote = 1; from_option = 1; break; case 't': /* "to" */ iamremote = 1; to_option = 1; break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, arg_doc, doc, NULL, NULL, NULL }; struct passwd *pwd; unsigned short port; uid_t userid, effuid; int errs, rem; char *command; #if defined KERBEROS || defined SHISHI int kerberos (char **, char *, char *, char *); void oldw (const char *, ...); #endif /* KERBEROS || SHISHI */ int response (void); void rsource (char *, struct stat *); void sink (int, char *[]); void source (int, char *[]); void tolocal (int, char *[]); void toremote (char *, int, char *[]); int main (int argc, char *argv[]) { struct servent *sp; char *targ; const char *shell; int index, rc; set_program_name (argv[0]); from_option = to_option = 0; iu_argp_init ("rcp", default_program_authors); argp_parse (&argp, argc, argv, 0, &index, NULL); argc -= index; argv += index; #if defined KERBEROS || defined SHISHI if (use_kerberos) { # if defined ENCRYPTION && defined KERBEROS shell = doencrypt ? "ekshell" : "kshell"; # else/* SHISHI */ shell = "kshell"; /* Libshishi uses a single service. */ # endif sp = getservbyname (shell, "tcp"); if (sp == NULL) { use_kerberos = 0; oldw ("can't get entry for %s/tcp service", shell); sp = getservbyname (shell = "shell", "tcp"); } } else sp = getservbyname (shell = "shell", "tcp"); #else /* !KERBEROS && !SHISHI */ sp = getservbyname (shell = "shell", "tcp"); #endif if (sp == NULL) error (EXIT_FAILURE, 0, "%s/tcp: unknown service", shell); port = sp->s_port; effuid = geteuid (); userid = getuid (); pwd = getpwuid (userid); if (pwd == NULL) error (EXIT_FAILURE, 0, "unknown user %d", (int) userid); rem = STDIN_FILENO; /* XXX */ if (from_option) { /* Follow "protocol", send data. */ response (); if (setuid (userid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (setuid() failed)"); } source (argc, argv); exit (errs); } if (to_option) { /* Receive data. */ if (setuid (userid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (setuid() failed)"); } sink (argc, argv); exit (errs); } if (argc < 1 || (argc < 2 && !(target && strlen (target)))) error (EXIT_FAILURE, 0, "not enough arguments"); if (argc > 2) targetshouldbedirectory = 1; #if defined KERBEROS || defined SHISHI if (doencrypt && !use_kerberos) error (EXIT_FAILURE, 0, "encryption must use Kerberos"); #endif /* Command to be executed on remote system using "rsh". */ rc = asprintf (&command, "rcp%s%s%s", iamrecursive ? " -r" : "", preserve_option ? " -p" : "", targetshouldbedirectory ? " -d" : ""); if (rc < 0) xalloc_die (); rem = -1; signal (SIGPIPE, lostconn); /* Without a specified target, the last argument * is extracted to serve as target. */ if (!target || !strlen (target)) { target = xstrdup (argv[argc - 1]); argc--; /* Do not count target directory. */ } targ = colon (target); if (targ) /* Dest is remote host. */ toremote (targ, argc, argv); else { tolocal (argc, argv); /* Dest is local host. */ if (targetshouldbedirectory) verifydir (target); } exit (errs); } void toremote (char *targ, int argc, char *argv[]) { int i, tos; char *bp, *host, *src, *suser, *thost, *tuser; #if defined IP_TOS && defined IPPROTO_IP && defined IPTOS_THROUGHPUT struct sockaddr_storage ss; socklen_t sslen; #endif *targ++ = 0; if (*targ == 0) targ = "."; thost = strchr (target, '@'); if (thost) { /* user@host */ *thost++ = 0; tuser = target; if (*tuser == '\0') tuser = NULL; else if (!okname (tuser)) exit (EXIT_FAILURE); } else { thost = target; tuser = NULL; } for (i = 0; i < argc; 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; if (asprintf (&bp, #if defined ENCRYPTION && (defined KERBEROS || defined SHISHI) "%s%s -l %s -n %s %s %s '%s%s%s:%s'", #else "%s -l %s -n %s %s %s '%s%s%s:%s'", #endif PATH_RSH, #if ENCRYPTION && (defined KERBEROS || defined SHISHI) doencrypt ? " -x" : "", #endif suser, host, command, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ) < 0) xalloc_die (); } else { if (asprintf (&bp, #if defined ENCRYPTION && (defined KERBEROS || defined SHISHI) "exec %s%s -n %s %s %s '%s%s%s:%s'", #else "exec %s -n %s %s %s '%s%s%s:%s'", #endif PATH_RSH, #if ENCRYPTION && (defined KERBEROS || defined SHISHI) doencrypt ? " -x" : "", #endif argv[i], command, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ) < 0) xalloc_die (); } susystem (bp, userid); free (bp); } else { /* local to remote */ if (rem == -1) { if (asprintf (&bp, "%s -t %s", command, targ) < 0) xalloc_die (); host = thost; #if defined KERBEROS || defined SHISHI if (use_kerberos) rem = kerberos (&host, bp, pwd->pw_name, tuser ? tuser : pwd->pw_name); else #endif /* KERBEROS || SHISHI */ #ifdef WITH_ORCMD_AF rem = orcmd_af (&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, bp, 0, family); #elif defined WITH_RCMD_AF rem = rcmd_af (&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, bp, 0, family); #elif defined WITH_ORCMD rem = orcmd (&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, bp, 0); #else /* !WITH_ORCMD_AF && !WITH_RCMD_AF && !WITH_ORCMD */ rem = rcmd (&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, bp, 0); #endif if (rem < 0) { /* rcmd() provides its own error messages, * but we add a vital addition, caused by * insufficient capabilities. */ if (errno == EACCES) error (EXIT_FAILURE, 0, "No access to privileged ports."); exit (EXIT_FAILURE); } #if defined IP_TOS && defined IPPROTO_IP && defined IPTOS_THROUGHPUT sslen = sizeof (ss); (void) getpeername (rem, (struct sockaddr *) &ss, &sslen); tos = IPTOS_THROUGHPUT; if (ss.ss_family == AF_INET && setsockopt (rem, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)) < 0) if (errno != ENOPROTOOPT) error (0, errno, "TOS (ignored)"); #endif if (response () < 0) exit (EXIT_FAILURE); free (bp); if (setuid (userid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (setuid() failed)"); } } source (1, argv + i); close (rem); rem = -1; #ifdef SHISHI if (use_kerberos) { shishi_done (h); # ifdef ENCRYPTION if (doencrypt) { shishi_key_done (enckey); for (i = 0; i < 4; i++) { shishi_crypto_close (ivtab[i]->ctx); free (ivtab[i]->iv); } } # endif/* ENCRYPTION */ } #endif /* SHISHI */ } } } void tolocal (int argc, char *argv[]) { int i, tos; char *bp, *host, *src, *suser, *vect[1]; #if defined IP_TOS && defined IPPROTO_IP && defined IPTOS_THROUGHPUT struct sockaddr_storage ss; socklen_t sslen; #endif for (i = 0; i < argc; i++) { src = colon (argv[i]); if (!src) { /* Local to local. */ if (asprintf (&bp, "exec %s%s%s %s %s", PATH_CP, iamrecursive ? " -R" : "", preserve_option ? " -p" : "", argv[i], target) < 0) xalloc_die (); if (susystem (bp, userid)) ++errs; free (bp); continue; } *src++ = 0; if (*src == 0) src = "."; host = strchr (argv[i], '@'); if (host == NULL) { host = argv[i]; suser = pwd->pw_name; } else { *host++ = 0; suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname (suser)) continue; } if (asprintf (&bp, "%s -f %s", command, src) < 0) xalloc_die (); #if defined KERBEROS || defined SHISHI if (use_kerberos) rem = kerberos (&host, bp, pwd->pw_name, suser); else #elif defined WITH_ORCMD_AF rem = orcmd_af (&host, port, pwd->pw_name, suser, bp, 0, family); #elif defined WITH_RCMD_AF rem = rcmd_af (&host, port, pwd->pw_name, suser, bp, 0, family); #elif defined WITH_ORCMD rem = orcmd (&host, port, pwd->pw_name, suser, bp, 0); #else /* !WITH_ORCMD_AF && !WITH_RCMD_AF && !WITH_ORCMD */ rem = rcmd (&host, port, pwd->pw_name, suser, bp, 0); #endif free (bp); if (rem < 0) { ++errs; continue; } if (seteuid (userid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid() failed)"); } #if defined IP_TOS && defined IPPROTO_IP && defined IPTOS_THROUGHPUT sslen = sizeof (ss); (void) getpeername (rem, (struct sockaddr *) &ss, &sslen); tos = IPTOS_THROUGHPUT; if (ss.ss_family == AF_INET && setsockopt (rem, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)) < 0) if (errno != ENOPROTOOPT) error (0, errno, "TOS (ignored)"); #endif vect[0] = target; sink (1, vect); if (seteuid (effuid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (seteuid() failed)"); } close (rem); rem = -1; #ifdef SHISHI if (use_kerberos) shishi_done (h); { # ifdef ENCRYPTION if (doencrypt) { shishi_key_done (enckey); for (i = 0; i < 4; i++) { shishi_crypto_close (ivtab[i]->ctx); free (ivtab[i]->iv); } } # endif/* ENCRYPTION */ } #endif /* SHISHI */ } } static int write_stat_time (int fd, struct stat *st) { /* 'T' + 4 integers + 3 spaces + '\n' + '\0'. */ char buffer[4 * INT_STRLEN_BOUND (intmax_t) + 1 + 3 + 2]; struct timespec atime = get_stat_atime (st); struct timespec mtime = get_stat_mtime (st); int len = sprintf (buffer, "T%jd %jd %jd %jd\n", (intmax_t) mtime.tv_sec, (intmax_t) (mtime.tv_nsec / 1000), (intmax_t) atime.tv_sec, (intmax_t) (mtime.tv_nsec / 1000)); return write (fd, buffer, len); } void source (int argc, char *argv[]) { struct stat stb; static BUF buffer; BUF *bp; off_t i; int amt, fd, haderr, indx, result; char *last, *name, buf[BUFSIZ]; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; fd = open (name, O_RDONLY, 0); if (fd < 0) goto syserr; if (fstat (fd, &stb)) { syserr: run_err ("%s: %s", name, strerror (errno)); goto next; } switch (stb.st_mode & S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { rsource (name, &stb); goto next; } default: run_err ("%s: not a regular file", name); goto next; } last = strrchr (name, '/'); if (last == NULL) last = name; else ++last; if (preserve_option) { write_stat_time (rem, &stb); if (response () < 0) goto next; } #define RCP_MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) snprintf (buf, sizeof buf, "C%04o %jd %s\n", (int) stb.st_mode & RCP_MODEMASK, (intmax_t) stb.st_size, last); write (rem, buf, strlen (buf)); if (response () < 0) goto next; bp = allocbuf (&buffer, fd, BUFSIZ); if (bp == NULL) { next: close (fd); continue; } /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { result = read (fd, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; } if (haderr) write (rem, bp->buf, amt); else { result = write (rem, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; } } if (close (fd) && !haderr) haderr = errno; if (!haderr) write (rem, "", 1); else run_err ("%s: %s", name, strerror (haderr)); response (); } } void rsource (char *name, struct stat *statp) { DIR *dirp; struct dirent *dp; char *last, *vect[1]; char *buf; int buf_len; dirp = opendir (name); if (!dirp) { run_err ("%s: %s", name, strerror (errno)); return; } last = strrchr (name, '/'); if (last == 0) last = name; else last++; if (preserve_option) { write_stat_time (rem, statp); if (response () < 0) { closedir (dirp); return; } } buf_len = 1 + sizeof (int) * 3 + 1 + sizeof (int) * 3 + 1 + strlen (last) + 2; buf = malloc (buf_len); if (!buf) { run_err ("malloc failed for %d bytes", buf_len); closedir (dirp); return; } sprintf (buf, "D%04o %d %s\n", (int) statp->st_mode & RCP_MODEMASK, 0, last); write (rem, buf, strlen (buf)); free (buf); if (response () < 0) { closedir (dirp); return; } while ((dp = readdir (dirp))) { if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) continue; buf_len = strlen (name) + 1 + strlen (dp->d_name) + 1; buf = malloc (buf_len); if (!buf) { run_err ("malloc_failed for %d bytes", buf_len); continue; } sprintf (buf, "%s/%s", name, dp->d_name); vect[0] = buf; source (1, vect); free (buf); } closedir (dirp); write (rem, "E\n", 2); response (); } void sink (int argc, char *argv[]) { static BUF buffer; struct stat stb; struct timeval tv[2]; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i, j, size; int amt, count, exists, first, mask, mode, ofd, omode; int setimes, targisdir, wrerrno; char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ]; const char *why; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask (0); if (!preserve_option) umask (mask); if (argc != 1) { run_err ("ambiguous target"); exit (EXIT_FAILURE); } targ = *argv; if (targetshouldbedirectory) verifydir (targ); write (rem, "", 1); if (stat (targ, &stb) == 0 && S_ISDIR (stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (read (rem, cp, 1) <= 0) return; if (*cp++ == '\n') SCREWUP ("unexpected <newline>"); do { if (read (rem, &ch, sizeof ch) != sizeof ch) SCREWUP ("lost connection"); *cp++ = ch; } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); *cp = 0; if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) write (STDERR_FILENO, buf + 1, strlen (buf + 1)); if (buf[0] == '\02') exit (EXIT_FAILURE); ++errs; continue; } if (buf[0] == 'E') { write (rem, "", 1); return; } if (ch == '\n') *--cp = 0; #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); cp = buf; 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"); write (rem, "", 1); 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) { run_err ("%s", cp); exit (EXIT_FAILURE); } SCREWUP ("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP ("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP ("mode not delimited"); for (size = 0; isdigit (*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP ("size not delimited"); if (targisdir) { static char *namebuf = NULL; static size_t cursize = 0; size_t need; need = strlen (targ) + strlen (cp) + 250; if (need > cursize) { if (!(namebuf = malloc (need))) run_err ("%s", strerror (errno)); } snprintf (namebuf, need, "%s%s%s", targ, *targ ? "/" : "", cp); np = namebuf; } else np = targ; exists = stat (np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = preserve_option; if (exists) { if (!S_ISDIR (stb.st_mode)) { errno = ENOTDIR; goto bad; } if (preserve_option) lchmod (np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir (np, mode | S_IRWXU) < 0) goto bad; } vect[0] = np; sink (1, vect); if (setimes) { #ifndef HAVE_UTIMES struct utimbuf utbuf; utbuf.actime = atime.tv_sec; utbuf.modtime = mtime.tv_sec; #endif setimes = 0; #ifdef HAVE_UTIMES if (utimes (np, tv) < 0) #else if (utime (np, &utbuf) < 0) #endif run_err ("%s: set times: %s", np, strerror (errno)); } if (mod_flag) lchmod (np, mode); continue; } omode = mode; mode |= S_IWRITE; ofd = open (np, O_WRONLY | O_CREAT, mode); if (ofd < 0) { bad: run_err ("%s: %s", np, strerror (errno)); continue; } write (rem, "", 1); bp = allocbuf (&buffer, ofd, BUFSIZ); if (bp == NULL) { close (ofd); continue; } cp = bp->buf; wrerr = NO; for (count = i = 0; i < size; i += BUFSIZ) { amt = BUFSIZ; if (i + amt > size) amt = size - i; count += amt; do { j = read (rem, cp, amt); if (j <= 0) { run_err ("%s", j ? strerror (errno) : "dropped connection"); exit (EXIT_FAILURE); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { j = write (ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } } count = 0; cp = bp->buf; } } if (count != 0 && wrerr == NO && (j = write (ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } if (ftruncate (ofd, size)) { run_err ("%s: truncate: %s", np, strerror (errno)); wrerr = DISPLAYED; } if (preserve_option) { if (exists || omode != mode) if (fchmod (ofd, omode)) run_err ("%s: set mode: %s", np, strerror (errno)); } else { if (!exists && omode != mode) if (fchmod (ofd, omode & ~mask)) run_err ("%s: set mode: %s", np, strerror (errno)); } close (ofd); response (); if (setimes && wrerr == NO) { #ifndef HAVE_UTIMES struct utimbuf utbuf; utbuf.actime = atime.tv_sec; utbuf.modtime = mtime.tv_sec; #endif setimes = 0; #ifdef HAVE_UTIMES if (utimes (np, tv) < 0) { #else if (utime (np, &utbuf) < 0) { #endif run_err ("%s: set times: %s", np, strerror (errno)); wrerr = DISPLAYED; } } switch (wrerr) { case YES: run_err ("%s: %s", np, strerror (wrerrno)); break; case NO: write (rem, "", 1); break; case DISPLAYED: break; } } screwup: run_err ("protocol error: %s", why); exit (EXIT_FAILURE); } #if defined KERBEROS || defined SHISHI int kerberos (char **host, char *bp, char *locuser, char *user) { int krb_errno = 0; struct servent *sp; again: if (use_kerberos) { errno = 0; # ifdef KERBEROS rem = KSUCCESS; if (dest_realm == NULL) dest_realm = krb_realmofhost (*host); # elif defined SHISHI rem = SHISHI_OK; # endif # ifdef ENCRYPTION if (doencrypt) { # ifdef KERBEROS rem = krcmd_mutual (host, port, user, bp, 0, dest_realm, &cred, schedule): krb_errno = errno; # elif defined SHISHI int i; char *xbp = NULL; xbp = xmalloc (strlen (bp) + sizeof ("-x ")); sprintf (xbp, "%s%s", "-x ", bp); rem = krcmd_mutual (&h, host, port, &user, xbp, NULL, dest_realm, &enckey, family); krb_errno = errno; if (rem > 0) { keytype = shishi_key_type (enckey); keylen = shishi_cipher_blocksize (keytype); ivtab[0] = &iv1; ivtab[1] = &iv2; ivtab[2] = &iv3; ivtab[3] = &iv4; for (i = 0; i < 4; i++) { ivtab[i]->ivlen = keylen; switch (keytype) { case SHISHI_DES_CBC_CRC: case SHISHI_DES_CBC_MD4: case SHISHI_DES_CBC_MD5: case SHISHI_DES_CBC_NONE: case SHISHI_DES3_CBC_HMAC_SHA1_KD: ivtab[i]->keyusage = SHISHI_KEYUSAGE_KCMD_DES; ivtab[i]->iv = xmalloc (ivtab[i]->ivlen); memset (ivtab[i]->iv, 2 * i + 1 * (i < 2) - 4 * (i >= 2), ivtab[i]->ivlen); ivtab[i]->ctx = shishi_crypto (h, enckey, ivtab[i]->keyusage, shishi_key_type (enckey), ivtab[i]->iv, ivtab[i]->ivlen); break; case SHISHI_ARCFOUR_HMAC: case SHISHI_ARCFOUR_HMAC_EXP: ivtab[i]->keyusage = SHISHI_KEYUSAGE_KCMD_DES + 2 + 4 * i; ivtab[i]->ctx = shishi_crypto (h, enckey, ivtab[i]->keyusage, shishi_key_type (enckey), NULL, 0); break; default: ivtab[i]->keyusage = SHISHI_KEYUSAGE_KCMD_DES + 2 + 4 * i; ivtab[i]->iv = xmalloc (ivtab[i]->ivlen); memset (ivtab[i]->iv, 0, ivtab[i]->ivlen); ivtab[i]->ctx = shishi_crypto (h, enckey, ivtab[i]->keyusage, shishi_key_type (enckey), ivtab[i]->iv, ivtab[i]->ivlen); } } } free (xbp); # endif } else # endif/* ENCRYPTION */ { # ifdef KERBEROS rem = krcmd (host, port, user, bp, 0, dest_realm); # else/* SHISHI */ rem = krcmd (&h, host, port, &user, bp, NULL, dest_realm, family); # endif krb_errno = errno; } if (rem < 0) { use_kerberos = 0; if (krb_errno == ECONNREFUSED) oldw ("remote host doesn't support Kerberos"); else if (krb_errno == ENOENT) error (EXIT_FAILURE, 0, "Can't provide Kerberos authentication data."); else error (EXIT_FAILURE, 0, "Kerberos authentication failed."); sp = getservbyname ("shell", "tcp"); if (sp == NULL) error (EXIT_FAILURE, 0, "unknown service shell/tcp"); port = sp->s_port; goto again; } } else { char *p = strchr (*host, '/'); # ifdef ENCRYPTION if (doencrypt) error (EXIT_FAILURE, 0, "the -x option requires Kerberos authentication"); # endif if (p) *host = ++p; /* Skip prefix like `host/'. */ # ifdef WITH_ORCMD_AF rem = orcmd_af (host, port, locuser, user, bp, 0, family); # elif defined WITH_RCMD_AF rem = rcmd_af (host, port, locuser, user, bp, 0, family); # elif defined WITH_ORCMD rem = orcmd (host, port, locuser, user, bp, 0); # else/* !WITH_ORCMD_AF && !WITH_RCMD_AF && !WITH_ORCMD */ rem = rcmd (host, port, locuser, user, bp, 0); # endif } return rem; } #endif /* KERBEROS || SHISHI */ int response (void) { char ch, *cp, resp, rbuf[BUFSIZ]; if (read (rem, &resp, sizeof resp) != sizeof resp) lostconn (0); cp = rbuf; switch (resp) { case 0: /* ok */ return 0; default: *cp++ = resp; case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (read (rem, &ch, sizeof (ch)) != sizeof (ch)) lostconn (0); *cp++ = ch; } while (cp < &rbuf[BUFSIZ] && ch != '\n'); if (!iamremote) write (STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return -1; exit (EXIT_FAILURE); } } #if defined KERBEROS || defined SHISHI void oldw (const char *fmt, ...) { va_list ap; va_start (ap, fmt); fprintf (stderr, "rcp: "); vfprintf (stderr, fmt, ap); fprintf (stderr, ", using standard rcp\n"); va_end (ap); } #endif /* KERBEROS || SHISHI */ void run_err (const char *fmt, ...) { static FILE *fp; va_list ap; ++errs; if (fp == NULL && !(fp = fdopen (rem, "w"))) return; va_start (ap, fmt); fprintf (fp, "%c", 0x01); fprintf (fp, "rcp: "); vfprintf (fp, fmt, ap); va_end (ap); fprintf (fp, "\n"); fflush (fp); if (!iamremote) { fprintf (stderr, "%s: ", program_invocation_name); va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, "\n"); } } char * colon (char *cp) { if (*cp == ':') /* Leading colon is part of file name. */ return (0); for (; *cp; ++cp) { if (*cp == ':') return (cp); if (*cp == '/') return (0); } return (0); } void verifydir (char *cp) { struct stat stb; if (!stat (cp, &stb)) { if (S_ISDIR (stb.st_mode)) return; errno = ENOTDIR; } run_err ("%s: %s", cp, strerror (errno)); exit (EXIT_FAILURE); } int okname (char *cp0) { int c; char *cp; cp = cp0; do { c = *cp; if (c & 0200) goto bad; if (!isalpha (c) && !isdigit (c) && c != '_' && c != '-') goto bad; } while (*++cp); return (1); bad: error (0, 0, "%s: invalid user name", cp0); return (0); } int susystem (char *s, int userid) { sighandler_t istat, qstat; int status; pid_t pid; pid = vfork (); switch (pid) { case -1: return (127); case 0: if (setuid (userid) == -1) { error (EXIT_FAILURE, 0, "Could not drop privileges (setuid() failed)"); } execl (PATH_BSHELL, "sh", "-c", s, NULL); _exit (127); } istat = signal (SIGINT, SIG_IGN); qstat = signal (SIGQUIT, SIG_IGN); if (waitpid (pid, &status, 0) < 0) status = -1; signal (SIGINT, istat); signal (SIGQUIT, qstat); return (status); } BUF * allocbuf (BUF *bp, int fd, int blksize) { struct stat stb; size_t size; if (fstat (fd, &stb) < 0) { run_err ("fstat: %s", strerror (errno)); return (0); } #ifndef roundup # define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif size = roundup (BUFSIZ, blksize); if (size == 0) size = blksize; if ((size_t) bp->cnt >= size) return (bp); bp->buf = realloc (bp->buf, size); if (bp->buf == NULL) { bp->cnt = 0; run_err ("%s", strerror (errno)); return (0); } bp->cnt = size; return (bp); } void lostconn (int signo MAYBE_UNUSED) { if (!iamremote) error (0, 0, "lost connection"); exit (EXIT_FAILURE); }