Remove the need for rdist(1) to run setuid, thus completely closing any

possibility of a security hole.  It now does what rdist-6 does, and calls
/usr/bin/rsh if not running as root.  There are NO protocol changes, this
is 100% compatable with the old rdist, except that it does not need setuid
root privs.

However, there are some minor differences to the base rdist-6 code in that
if it is being run by root, it will call rcmd(3) directly rather than
piping everything through rsh(1).  This is a little more efficient as it
doesn't involve context switching on pipe reads/writes.

Also, the -P option was added from rdist-6.1.2, which allows an alternative
rsh program to be specified, such as ssh.  Note that it requires the fixes
to the ssh port to disable the unconditional USE_PIPES option that was
recently added.  The rcmd(3) optimisation is disabled if a non-rsh program
is speficied.
This commit is contained in:
Peter Wemm 1996-08-10 07:54:17 +00:00
parent b05a2d987d
commit fb9e3ade6c
11 changed files with 250 additions and 43 deletions

View File

@ -2,11 +2,15 @@
PROG= rdist
CFLAGS+=-I${.CURDIR}
SRCS= docmd.c expand.c lookup.c main.c server.c
SRCS= docmd.c expand.c lookup.c main.c rshrcmd.c server.c
OBJS+= gram.o
BINOWN= root
BINMODE=4555
INSTALLFLAGS=-fschg
CLEANFILES=y.tab.h
# To use the old method, which requires setuid-root and all the baggage and
# security holes that goes with it, uncomment:
# CFLAGS+= -DDIRECT_RCMD
# BINOWN= root
# BINMODE=4555
# INSTALLFLAGS=-fschg
.include <bsd.prog.mk>

View File

@ -105,6 +105,13 @@
#define ALLOC(x) (struct x *) malloc(sizeof(struct x))
/*
* RSH Time Out interval (in seconds).
* Should be long enough to allow rsh to even the slowest hosts.
*/
#define RTIMEOUT 180
struct namelist { /* for making lists of strings */
char *n_name;
struct namelist *n_next;
@ -150,6 +157,7 @@ extern struct passwd *pw; /* pointer to static area used by getpwent */
extern struct group *gr; /* pointer to static area used by getgrent */
extern char host[]; /* host name of master copy */
extern char buf[BUFSIZ]; /* general purpose buffer */
extern char *path_rsh; /* rsh command to use */
int any __P((int, char *));
char *colon __P((char *));
@ -178,3 +186,4 @@ void prnames __P((struct namelist *));
void server __P((void));
void yyerror __P((char *));
int yyparse __P((void));
int rshrcmd __P((char **, u_short, char *, char *, char *, int *));

View File

@ -34,7 +34,7 @@
#ifndef lint
/*static char sccsid[] = "From: @(#)docmd.c 8.1 (Berkeley) 6/9/93";*/
static const char rcsid[] =
"$Id: docmd.c,v 1.3 1995/05/30 06:33:02 rgrimes Exp $";
"$Id: docmd.c,v 1.4 1996/07/12 04:00:13 nate Exp $";
#endif /* not lint */
#include "defs.h"
@ -56,6 +56,7 @@ static void dodcolon __P((char **,
struct namelist *, char *, struct subcmd *));
static void notify __P((char *, char *, struct namelist *, time_t));
static void rcmptime __P((struct stat *));
static int remotecmd __P((char *, char *, char *, char *));
/*
* Do the commands in cmds (initialized by yyparse).
@ -129,7 +130,7 @@ doarrow(filev, files, rhost, cmds)
int n, ddir, opts = options;
if (debug)
printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
printf("doarrow(%p, %s, %p)\n", files, rhost, cmds);
if (files == NULL) {
error("no files to be updated\n");
@ -194,6 +195,54 @@ doarrow(filev, files, rhost, cmds)
}
}
static int remotecmd(rhost, luser, ruser, cmd)
char *rhost;
char *luser, *ruser;
char *cmd;
{
int desc;
static int port = -1;
if (debug) {
printf("local user = %s remote user = %s\n", luser, ruser);
printf("Remote command = '%s'\n", cmd);
}
(void) fflush(stdout);
(void) fflush(stderr);
(void) signal(SIGALRM, cleanup);
(void) alarm(RTIMEOUT);
if (geteuid() == 0 && strcmp(path_rsh, _PATH_RSH) == 0) {
if (debug)
printf("I am root, therefore direct rcmd\n");
(void) signal(SIGPIPE, cleanup);
if (port < 0) {
struct servent *sp;
if ((sp = getservbyname("shell", "tcp")) == NULL)
fatal("shell/tcp: unknown service");
port = sp->s_port;
}
desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
} else {
if (debug)
printf("Remote shell command = '%s'\n", path_rsh);
(void) signal(SIGPIPE, SIG_IGN);
desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
if (desc > 0)
(void) signal(SIGPIPE, cleanup);
}
(void) alarm(0);
return(desc);
}
/*
* Create a connection to the rdist server on the machine rhost.
*/
@ -207,7 +256,9 @@ makeconn(rhost)
char tuser[20];
int n;
extern char user[];
#if defined(DIRECT_RCMD)
extern int userid;
#endif
if (debug)
printf("makeconn(%s)\n", rhost);
@ -251,9 +302,13 @@ makeconn(rhost)
}
fflush(stdout);
#if defined(DIRECT_RCMD)
seteuid(0);
rem = rcmd(&rhost, port, user, ruser, buf, 0);
seteuid(userid);
#else
rem = remotecmd(rhost, user, ruser, buf);
#endif
if (rem < 0)
return(0);
cp = buf;
@ -463,7 +518,7 @@ rcmptime(st)
int len;
if (debug)
printf("rcmptime(%x)\n", st);
printf("rcmptime(%p)\n", st);
if ((d = opendir(target)) == NULL) {
error("%s: %s\n", target, strerror(errno));
@ -471,7 +526,7 @@ rcmptime(st)
}
otp = tp;
len = tp - target;
while (dp = readdir(d)) {
while ((dp = readdir(d))) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
@ -481,7 +536,7 @@ rcmptime(st)
tp = otp;
*tp++ = '/';
cp = dp->d_name;
while (*tp++ = *cp++)
while ((*tp++ = *cp++))
;
tp--;
cmptime(target);

View File

@ -91,7 +91,7 @@ expand(list, wh)
char *argvbuf[GAVSIZ];
if (debug) {
printf("expand(%x, %d)\nlist = ", list, wh);
printf("expand(%p, %d)\nlist = ", list, wh);
prnames(list);
}
@ -217,7 +217,7 @@ expstr(s)
cp1 = pw->pw_dir;
s = cp;
}
for (cp = path; *cp++ = *cp1++; )
for (cp = path; (*cp++ = *cp1++); )
;
tpathp = pathp = cp - 1;
} else {
@ -451,7 +451,7 @@ amatch(s, p)
case '[':
ok = 0;
lc = 077777;
while (cc = *p++) {
while ((cc = *p++)) {
if (cc == ']') {
if (ok)
break;
@ -534,7 +534,7 @@ smatch(s, p)
case '[':
ok = 0;
lc = 077777;
while (cc = *p++) {
while ((cc = *p++)) {
if (cc == ']') {
if (ok)
break;
@ -592,10 +592,10 @@ Cat(s1, s2)
eargv[eargc - 1] = s = malloc(len);
if (s == NULL)
fatal("ran out of memory\n");
while (*s++ = *s1++ & TRIM)
while ((*s++ = *s1++ & TRIM))
;
s--;
while (*s++ = *s2++ & TRIM)
while ((*s++ = *s2++ & TRIM))
;
}
@ -655,12 +655,12 @@ exptilde(buf, file)
*s3 = '/';
s2 = pw->pw_dir;
}
for (s1 = buf; *s1++ = *s2++; )
for (s1 = buf; (*s1++ = *s2++); )
;
s2 = --s1;
if (s3 != NULL) {
s2++;
while (*s1++ = *s3++)
while ((*s1++ = *s3++))
;
}
return(s2);

View File

@ -171,8 +171,8 @@ cmd: INSTALL options opt_namelist SM = {
char errbuf[_POSIX2_LINE_MAX];
for (nl = $2; nl != NULL; nl = nl->n_next) {
if (val = regcomp(&rx, nl->n_name,
REG_EXTENDED)) {
if ((val = regcomp(&rx, nl->n_name,
REG_EXTENDED))) {
regerror(val, &rx, errbuf,
sizeof errbuf);
yyerror(errbuf);
@ -477,7 +477,7 @@ makestr(str)
str = cp = malloc(strlen(s = str) + 1);
if (cp == NULL)
fatal("ran out of memory\n");
while (*cp++ = *s++)
while ((*cp++ = *s++))
;
return(str);
}

View File

@ -59,7 +59,7 @@ define(name)
{
register char *cp, *s;
register struct namelist *nl;
struct namelist *value;
struct namelist *value = NULL;
if (debug)
printf("define(%s)\n", name);
@ -129,7 +129,7 @@ lookup(name, action, value)
char buf[256];
if (debug)
printf("lookup(%s, %d, %x)\n", name, action, value);
printf("lookup(%s, %d, %p)\n", name, action, value);
n = 0;
for (cp = name; *cp; )

View File

@ -68,6 +68,7 @@ char user[10]; /* user's name */
char homedir[128]; /* user's home directory */
int userid; /* user's user ID */
int groupid; /* user's group ID */
char *path_rsh = _PATH_RSH; /* rsh (or equiv command) path */
struct passwd *pw; /* pointer to static area used by getpwent */
struct group *gr; /* pointer to static area used by getgrent */
@ -107,6 +108,12 @@ main(argc, argv)
iamremote++;
else while (*++arg)
switch (*arg) {
case 'P':
if (--argc <= 0)
usage();
path_rsh = *++argv;
break;
case 'f':
if (--argc <= 0)
usage();
@ -222,8 +229,9 @@ main(argc, argv)
static void
usage()
{
printf("Usage: rdist [-nqbhirvwyD] [-f distfile] [-d var=value] [-m host] [file ...]\n");
printf("or: rdist [-nqbhirvwyD] -c source [...] machine[:dest]\n");
printf("Usage: rdist [-nqbhirvwyD] [-P /path/to/rsh ] [-f distfile] [-d var=value]\n");
printf(" [-m host] [file ...]\n");
printf("or: rdist [-nqbhirvwyD] [-P /path/to/rsh ] -c source [...] machine[:dest]\n");
exit(1);
}
@ -237,7 +245,7 @@ docmdargs(nargs, args)
{
register struct namelist *nl, *prev;
register char *cp;
struct namelist *files, *hosts;
struct namelist *files = NULL, *hosts;
struct subcmd *cmds;
char *dest;
static struct namelist tnl = { NULL, NULL };

View File

@ -36,3 +36,4 @@
#include <paths.h>
#define _PATH_RDIST "rdist"
#define _PATH_RSH "/usr/bin/rsh"

View File

@ -40,12 +40,14 @@
.Sh SYNOPSIS
.Nm rdist
.Op Fl nqbRhivwy
.Op Fl P Ar rshcmd
.Op Fl f Ar distfile
.Op Fl d Ar var=value
.Op Fl m Ar host
.Op Ar name ...
.Nm rdist
.Op Fl nqbRhivwy
.Op Fl P Ar rshcmd
.Fl c
.Ar name ...
.Oo login@ Oc Ns Ar host Ns Op :dest
@ -118,9 +120,13 @@ The equivalent distfile is as follows.
Options common to both forms:
.Pp
.Bl -tag -width Ic
.It Fl b
Binary comparison. Perform a binary comparison and update files if they differ
rather than comparing dates and sizes.
.It Fl P Ar rshcmd
Alternative program to provide
.Xr rsh 1 -like
transport to the remote server. It must provide a binary-transparent path
to the remote server, and must have a command argument syntax that is
compatable with
.Xr rsh 1 .
.It Fl d Ar var=value
Define
.Ar var

124
usr.bin/rdist/rshrcmd.c Normal file
View File

@ -0,0 +1,124 @@
/*
* This is an rcmd() replacement originally by
* Chris Siebenmann <cks@utcc.utoronto.ca>.
*/
#ifndef lint
static char RCSid[] =
"$Id: rshrcmd.c,v 1.7 1995/12/12 00:20:55 mcooper Exp $";
#endif
#include "defs.h"
#if !defined(DIRECT_RCMD)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static char *
xbasename(s)
char *s;
{
char *ret;
ret = strrchr(s, '/');
if (ret && ret[1])
return (ret + 1);
return s;
}
/*
* This is a replacement rcmd() function that uses the rsh(1c)
* program in place of a direct rcmd() function call so as to
* avoid having to be root.
*/
int
rshrcmd(ahost, port, luser, ruser, cmd, fd2p)
char **ahost;
u_short port;
char *luser, *ruser, *cmd;
int *fd2p;
{
int cpid;
struct hostent *hp;
int sp[2];
/* insure that we are indeed being used as we thought. */
if (fd2p != 0)
return -1;
/* validate remote hostname. */
hp = gethostbyname(*ahost);
if (hp == 0) {
error("%s: unknown host", *ahost);
return -1;
}
/* *ahost = hp->h_name; *//* This makes me nervous. */
/* get a socketpair we'll use for stdin and stdout. */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
error("socketpair(AF_UNIX, SOCK_STREAM, 0) failed: %s.",
strerror(errno));
return -1;
}
cpid = fork();
if (cpid < 0) {
error("fork failed: %s.", strerror(errno));
return -1; /* error. */
}
if (cpid == 0) {
/* child. we use sp[1] to be stdin/stdout, and close
sp[0]. */
(void) close(sp[0]);
if (dup2(sp[1], 0) < 0 || dup2(0,1) < 0 || dup2(0, 2) < 0) {
error("dup2 failed: %s.", strerror(errno));
_exit(255);
}
/* fork again to lose parent. */
cpid = fork();
if (cpid < 0) {
error("fork to lose parent failed: %s.", strerror(errno));
_exit(255);
}
if (cpid > 0)
_exit(0);
/* in grandchild here. */
/*
* If we are rdist'ing to "localhost" as the same user
* as we are, then avoid running remote shell for efficiency.
*/
if (strcmp(*ahost, "localhost") == 0 &&
strcmp(luser, ruser) == 0) {
execlp(_PATH_BSHELL, xbasename(_PATH_BSHELL), "-c",
cmd, (char *) NULL);
error("execlp %s failed: %s.", _PATH_BSHELL, strerror(errno));
} else {
execlp(path_rsh, xbasename(path_rsh),
*ahost, "-l", ruser, cmd, (char *) NULL);
error("execlp %s failed: %s.", path_rsh,
strerror(errno));
}
_exit(255);
}
if (cpid > 0) {
/* parent. close sp[1], return sp[0]. */
(void) close(sp[1]);
/* reap child. */
(void) wait(0);
return sp[0];
}
/*NOTREACHED*/
return (-1);
}
#endif /* !DIRECT_RCMD */

View File

@ -371,7 +371,7 @@ sendf(rname, opts)
otp = tp;
len = tp - target;
while (dp = readdir(d)) {
while ((dp = readdir(d))) {
if (!strcmp(dp->d_name, ".") ||
!strcmp(dp->d_name, ".."))
continue;
@ -383,7 +383,7 @@ sendf(rname, opts)
tp = otp;
*tp++ = '/';
cp = dp->d_name;
while (*tp++ = *cp++)
while ((*tp++ = *cp++))
;
tp--;
sendf(dp->d_name, opts);
@ -498,7 +498,7 @@ sendf(rname, opts)
} else
ack();
f = response();
if (f < 0 || f == 0 && (opts & COMPARE))
if (f < 0 || (f == 0 && (opts & COMPARE)))
return;
dospecial:
for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
@ -564,7 +564,7 @@ update(rname, opts, stp)
register time_t mtime;
if (debug)
printf("update(%s, %x, %x)\n", rname, opts, stp);
printf("update(%s, %x, %p)\n", rname, opts, stp);
/*
* Check to see if the file exists on the remote machine.
@ -697,7 +697,7 @@ recvf(cmd, type)
int type;
{
register char *cp;
int f, mode, opts, wrerr, olderrno;
int f = -1, mode, opts, wrerr, olderrno;
off_t i, size;
time_t mtime;
struct stat stb;
@ -761,7 +761,7 @@ recvf(cmd, type)
stp[catname] = tp;
if (catname++) {
*tp++ = '/';
while (*tp++ = *cp++)
while ((*tp++ = *cp++))
;
tp--;
}
@ -783,8 +783,8 @@ recvf(cmd, type)
return;
}
errno = ENOTDIR;
} else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
chkparent(target) == 0 && mkdir(target, mode) == 0)) {
} else if ((errno == ENOENT && mkdir(target, mode) == 0) ||
(chkparent(target) == 0 && mkdir(target, mode) == 0)) {
if (fchog(-1, target, owner, group, mode) == 0)
ack();
return;
@ -922,7 +922,7 @@ differ: buf[0] = '\0';
note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
if (fchog(f, new, owner, group, mode) < 0) {
badnew2: (void) close(f);
badnew2: if (f != -1) (void) close(f);
(void) unlink(new);
return;
}
@ -1084,10 +1084,10 @@ fchog(fd, file, owner, group, mode)
mode &= ~02000;
gid = -1;
}
ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
ok: if ((fd != -1 && fchown(fd, uid, gid) < 0) || chown(file, uid, gid) < 0)
note("%s: %s chown: %s", host, file, strerror(errno));
else if (mode & 07000 &&
(fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
((fd != -1 && fchmod(fd, mode) < 0) || chmod(file, mode) < 0))
note("%s: %s chmod: %s", host, file, strerror(errno));
return(0);
}
@ -1204,7 +1204,7 @@ clean(cp)
otp = tp;
len = tp - target;
while (dp = readdir(d)) {
while ((dp = readdir(d))) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
@ -1215,7 +1215,7 @@ clean(cp)
tp = otp;
*tp++ = '/';
cp = dp->d_name;;
while (*tp++ = *cp++)
while ((*tp++ = *cp++))
;
tp--;
if (lstat(target, &stb) < 0) {
@ -1284,7 +1284,7 @@ removeit(stp)
otp = tp;
len = tp - target;
while (dp = readdir(d)) {
while ((dp = readdir(d))) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
@ -1295,7 +1295,7 @@ removeit(stp)
tp = otp;
*tp++ = '/';
cp = dp->d_name;;
while (*tp++ = *cp++)
while ((*tp++ = *cp++))
;
tp--;
if (lstat(target, &stb) < 0) {