From fb9e3ade6c800a20206bf6e1d77c02ba1dc87250 Mon Sep 17 00:00:00 2001 From: Peter Wemm Date: Sat, 10 Aug 1996 07:54:17 +0000 Subject: 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. --- usr.bin/rdist/Makefile | 12 +++-- usr.bin/rdist/defs.h | 9 ++++ usr.bin/rdist/docmd.c | 65 ++++++++++++++++++++++-- usr.bin/rdist/expand.c | 16 +++--- usr.bin/rdist/gram.y | 6 +-- usr.bin/rdist/lookup.c | 4 +- usr.bin/rdist/main.c | 14 ++++-- usr.bin/rdist/pathnames.h | 1 + usr.bin/rdist/rdist.1 | 12 +++-- usr.bin/rdist/rshrcmd.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ usr.bin/rdist/server.c | 30 +++++------ 11 files changed, 250 insertions(+), 43 deletions(-) create mode 100644 usr.bin/rdist/rshrcmd.c (limited to 'usr.bin/rdist') diff --git a/usr.bin/rdist/Makefile b/usr.bin/rdist/Makefile index 853439c9f763..83582e09e55a 100644 --- a/usr.bin/rdist/Makefile +++ b/usr.bin/rdist/Makefile @@ -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 diff --git a/usr.bin/rdist/defs.h b/usr.bin/rdist/defs.h index c99ddd60fc93..e23ae6f5f505 100644 --- a/usr.bin/rdist/defs.h +++ b/usr.bin/rdist/defs.h @@ -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 *)); diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c index 7b3219dd3839..e941d78159d3 100644 --- a/usr.bin/rdist/docmd.c +++ b/usr.bin/rdist/docmd.c @@ -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 @@ done: } } +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); diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c index 555dbf14776d..4d1819b7732b 100644 --- a/usr.bin/rdist/expand.c +++ b/usr.bin/rdist/expand.c @@ -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); diff --git a/usr.bin/rdist/gram.y b/usr.bin/rdist/gram.y index 6e5491174759..22d0ee87ac2f 100644 --- a/usr.bin/rdist/gram.y +++ b/usr.bin/rdist/gram.y @@ -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); } diff --git a/usr.bin/rdist/lookup.c b/usr.bin/rdist/lookup.c index 8b55afc164fd..dc2f1342e334 100644 --- a/usr.bin/rdist/lookup.c +++ b/usr.bin/rdist/lookup.c @@ -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; ) diff --git a/usr.bin/rdist/main.c b/usr.bin/rdist/main.c index 44b3279a2652..3badde74f73c 100644 --- a/usr.bin/rdist/main.c +++ b/usr.bin/rdist/main.c @@ -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 }; diff --git a/usr.bin/rdist/pathnames.h b/usr.bin/rdist/pathnames.h index 2e1b067840d5..c7725393543b 100644 --- a/usr.bin/rdist/pathnames.h +++ b/usr.bin/rdist/pathnames.h @@ -36,3 +36,4 @@ #include #define _PATH_RDIST "rdist" +#define _PATH_RSH "/usr/bin/rsh" diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1 index 00a3339c2c1a..72ab58726666 100644 --- a/usr.bin/rdist/rdist.1 +++ b/usr.bin/rdist/rdist.1 @@ -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 diff --git a/usr.bin/rdist/rshrcmd.c b/usr.bin/rdist/rshrcmd.c new file mode 100644 index 000000000000..2cb8ef2788ab --- /dev/null +++ b/usr.bin/rdist/rshrcmd.c @@ -0,0 +1,124 @@ + +/* + * This is an rcmd() replacement originally by + * Chris Siebenmann . + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 */ diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c index 46f3ea4b3bf8..a51117446d38 100644 --- a/usr.bin/rdist/server.c +++ b/usr.bin/rdist/server.c @@ -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 @@ done: } 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) { -- cgit v1.2.3