|
________________________________________________________________ THE COMPUTER INCIDENT ADVISORY CAPABILITY CIAC ADVISORY NOTICE ________________________________________________________________ NOTICE OF VULNERABILITY INVOLVING RCP AND RDIST The DOE Computer Incident Advisory Capability (CIAC) has learned of a UNIX security problem involving rcp and rdist in 4.3BSD, 4.3BSD-tahoe, and all versions of UNIX using BSD networking code, as well as SunOS (all versions). This vulnerability allows someone to overwrite system files without being detected. Because this problem has potentially serious consequences, CIAC recommends that you install a patch to correct the problems with rcp and rdist. A patch is now available for BSD systems. CIAC will send this patch to you via e-mail if you contact: Gene Schultz, CIAC Manager gschultz%nsspa@icdc.llnl.gov (415) 422-8193 or (FTS) 532-8193 or send e-mail to: ciac@tiger.llnl.gov Although there are no SunOS workarounds currently available for rcp and rdist, Sun is developing a patch. If you would like to be notified when this patch is available, please contact CIAC. ------- Forwarded Message Date: Thu, 25 May 89 15:33:48 GMT >From: bostic@okeeffe.Berkeley.EDU (Keith Bostic) Message-Id: <8905251533.AA15064@okeeffe.Berkeley.EDU> To: cert@sei.cmu.edu Subject: V1.82 (Rcp/rdist security patch) Subject: Rcp/rdist security patch Index: bin/rcp.c 4.3BSD Index: ucb/rdist/server.c 4.3BSD Description: There's a security problem associated with rcp and rdist in the 4.3BSD, 4.3BSD-tahoe, and first BSD networking distributions. Fix: Replace your current bin/rcp.c with the attached rcp.c. Patch the server.c module in your current rdist with the attached patched. Recompile and re-install. # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # rcp.c # rdist.server.c.diff # echo x - rcp.c sed 's/^X//' >rcp.c << 'END-of-rcp.c' X/* X * Copyright (c) 1983 The Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. X */ X X#ifndef lint Xchar copyright[] = X"@(#) Copyright (c) 1983 The Regents of the University of California.\n\ X All rights reserved.\n"; X#endif /* not lint */ X X#ifndef lint Xstatic char sccsid[] = "@(#)rcp.c 5.20 (Berkeley) 5/23/89"; X#endif /* not lint */ X X/* X * rcp X */ X#include <sys/param.h> X#include <sys/file.h> X#include <sys/stat.h> X#include <sys/time.h> X#include <sys/ioctl.h> X#include <sys/dir.h> X#include <sys/signal.h> X#include <netinet/in.h> X#include <pwd.h> X#include <netdb.h> X#include <errno.h> X#include <string.h> X#include <stdio.h> X#include <ctype.h> X#include "pathnames.h" X X#ifdef KERBEROS X#include <kerberos/krb.h> X Xchar krb_realm[REALM_SZ]; Xint use_kerberos = 1, encrypt = 0; XCREDENTIALS cred; XKey_schedule schedule; X#endif X Xextern int errno; Xextern char *sys_errlist[]; Xstruct passwd *pwd; Xint errs, pflag, port, rem, userid; Xint iamremote, iamrecursive, targetshouldbedirectory; X X#define CMDNEEDS 20 Xchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ X Xtypedef struct _buf { X int cnt; X char *buf; X} BUF; X Xmain(argc, argv) X int argc; X char **argv; X{ X extern int optind; X struct servent *sp; X int ch, fflag, tflag; X char *targ, *colon(); X struct passwd *getpwuid(); X int lostconn(); X X#ifdef KERBEROS X sp = getservbyname("kshell", "tcp"); X if (sp == NULL) { X use_kerberos = 0; X old_warning("kshell service unknown"); X sp = getservbyname("kshell", "tcp"); X } X#else X sp = getservbyname("shell", "tcp"); X#endif X if (!sp) { X (void)fprintf(stderr, "rcp: shell/tcp: unknown service\n"); X exit(1); X } X port = sp->s_port; X X if (!(pwd = getpwuid(userid = getuid()))) { X (void)fprintf(stderr, "rcp: unknown user %d.\n", userid); X exit(1); X } X X fflag = tflag = 0; X while ((ch = getopt(argc, argv, "dfkprtx")) != EOF) X switch(ch) { X case 'd': X targetshouldbedirectory = 1; X break; X case 'f': /* "from" */ X fflag = 1; X break; X#ifdef KERBEROS X case 'k': X strncpy(krb_realm, ++argv, REALM_SZ); X break; X#endif X case 'p': /* preserve access/mod times */ X ++pflag; X break; X case 'r': X ++iamrecursive; X break; X case 't': /* "to" */ X tflag = 1; X break; X#ifdef KERBEROS X case 'x': X encrypt = 1; X des_set_key(cred.session, schedule); X break; X#endif X case '?': X default: X usage(); X } X argc -= optind; X argv += optind; X X if (fflag) { X iamremote = 1; X (void)response(); X (void)setuid(userid); X source(argc, argv); X exit(errs); X } X X if (tflag) { X iamremote = 1; X (void)setuid(userid); X sink(argc, argv); X exit(errs); X } X X if (argc < 2) X usage(); X if (argc > 2) X targetshouldbedirectory = 1; X X rem = -1; X (void)sprintf(cmd, "rcp%s%s%s", iamrecursive ? " -r" : "", X pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); X X (void)signal(SIGPIPE, lostconn); X X if (targ = colon(argv[argc - 1])) X toremote(targ, argc, argv); X else { X tolocal(argc, argv); X if (targetshouldbedirectory) X verifydir(argv[argc - 1]); X } X exit(errs); X} X Xtoremote(targ, argc, argv) X char *targ; X int argc; X char **argv; X{ X int i; X char *bp, *host, *src, *suser, *thost, *tuser; X char *colon(), *malloc(); X X *targ++ = 0; X if (*targ == 0) X targ = "."; X X if (thost = index(argv[argc - 1], '@')) { X *thost++ = 0; X tuser = argv[argc - 1]; X if (*tuser == '\0') X tuser = NULL; X else if (!okname(tuser)) X exit(1); X } else { X thost = argv[argc - 1]; X tuser = NULL; X } X X for (i = 0; i < argc - 1; i++) { X src = colon(argv[i]); X if (src) { /* remote to remote */ X *src++ = 0; X if (*src == 0) X src = "."; X host = index(argv[i], '@'); X if (!(bp = malloc((u_int)(strlen(_PATH_RSH) + X strlen(argv[i]) + strlen(src) + X strlen(tuser) + strlen(thost) + X strlen(targ)) + CMDNEEDS + 20))) X nospace(); X if (host) { X *host++ = 0; X suser = argv[i]; X if (*suser == '\0') X suser = pwd->pw_name; X else if (!okname(suser)) X continue; X (void)sprintf(bp, X "%s %s -l %s -n %s %s '%s%s%s:%s'", X _PATH_RSH, host, suser, cmd, src, X tuser ? tuser : "", tuser ? "@" : "", X thost, targ); X } else X (void)sprintf(bp, "%s %s -n %s %s '%s%s%s:%s'", X _PATH_RSH, argv[i], cmd, src, X tuser ? tuser : "", tuser ? "@" : "", X thost, targ); X (void)susystem(bp); X (void)free(bp); X } else { /* local to remote */ X if (rem == -1) { X if (!(bp = malloc((u_int)strlen(targ) + X CMDNEEDS + 20))) X nospace(); X (void)sprintf(bp, "%s -t %s", cmd, targ); X host = thost; X#ifdef KERBEROS X if (use_kerberos) X kerberos(bp, X tuser ? tuser : pwd->pw_name); X else X#endif X rem = rcmd(&host, port, pwd->pw_name, X tuser ? tuser : pwd->pw_name, X bp, 0); X if (rem < 0) X exit(1); X if (response() < 0) X exit(1); X (void)free(bp); X (void)setuid(userid); X } X source(1, argv+i); X } X } X} X Xtolocal(argc, argv) X int argc; X char **argv; X{ X int i; X char *bp, *host, *src, *suser; X char *colon(), *malloc(); X X for (i = 0; i < argc - 1; i++) { X if (!(src = colon(argv[i]))) { /* local to local */ X if (!(bp = malloc((u_int)(strlen(_PATH_CP) + X strlen(argv[i]) + strlen(argv[argc - 1])) + 20))) X nospace(); X (void)sprintf(bp, "%s%s%s %s %s", _PATH_CP, X iamrecursive ? " -r" : "", pflag ? " -p" : "", X argv[i], argv[argc - 1]); X (void)susystem(bp); X (void)free(bp); X continue; X } X *src++ = 0; X if (*src == 0) X src = "."; X host = index(argv[i], '@'); X if (host) { X *host++ = 0; X suser = argv[i]; X if (*suser == '\0') X suser = pwd->pw_name; X else if (!okname(suser)) X continue; X } else { X host = argv[i]; X suser = pwd->pw_name; X } X if (!(bp = malloc((u_int)(strlen(src)) + CMDNEEDS + 20))) X nospace(); X (void)sprintf(bp, "%s -f %s", cmd, src); X#ifdef KERBEROS X if (use_kerberos) X kerberos(bp, suser); X else X#endif X rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0); X (void)free(bp); X if (rem < 0) X continue; X (void)setreuid(0, userid); X sink(1, argv + argc - 1); X (void)setreuid(userid, 0); X (void)close(rem); X rem = -1; X } X} X X#ifdef KERBEROS Xkerberos(bp, user) X char *bp, *user; X{ X struct servent *sp; X char *host; X Xagain: rem = KSUCCESS; X if (krb_realm[0] == '\0') X rem = krb_get_lrealm(krb_realm, 1); X if (rem == KSUCCESS) { X if (encrypt) X rem = krcmd_mutual(&host, port, user, bp, 0, X krb_realm, &cred, schedule); X else X rem = krcmd(&host, port, user, bp, 0, krb_realm); X } else { X (void)fprintf(stderr, X "rcp: error getting local realm %s\n", krb_err_txt[rem]); X exit(1); X } X if (rem < 0 && errno == ECONNREFUSED) { X use_kerberos = 0; X old_warning("remote host doesn't support Kerberos"); X sp = getservbyname("shell", "tcp"); X if (sp == NULL) { X (void)fprintf(stderr, X "rcp: unknown service shell/tcp\n"); X exit(1); X } X port = sp->s_port; X goto again; X } X} X#endif /* KERBEROS */ X Xverifydir(cp) X char *cp; X{ X struct stat stb; X X if (stat(cp, &stb) >= 0) { X if ((stb.st_mode & S_IFMT) == S_IFDIR) X return; X errno = ENOTDIR; X } X error("rcp: %s: %s.\n", cp, sys_errlist[errno]); X exit(1); X} X Xchar * Xcolon(cp) X register char *cp; X{ X for (; *cp; ++cp) { X if (*cp == ':') X return(cp); X if (*cp == '/') X return(0); X } X return(0); X} X Xokname(cp0) X char *cp0; X{ X register char *cp = cp0; X register int c; X X do { X c = *cp; X if (c & 0200) X goto bad; X if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') X goto bad; X } while (*++cp); X return(1); Xbad: X (void)fprintf(stderr, "rcp: invalid user name %s\n", cp0); X return(0); X} X Xsusystem(s) X char *s; X{ X int status, pid, w; X register int (*istat)(), (*qstat)(); X X if ((pid = vfork()) == 0) { X (void)setuid(userid); X execl(_PATH_BSHELL, "sh", "-c", s, (char *)0); X _exit(127); X } X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X while ((w = wait(&status)) != pid && w != -1) X ; X if (w == -1) X status = -1; X (void)signal(SIGINT, istat); X (void)signal(SIGQUIT, qstat); X return(status); X} X Xsource(argc, argv) X int argc; X char **argv; X{ X struct stat stb; X static BUF buffer; X BUF *bp; X off_t i; X int x, readerr, f, amt; X char *last, *name, buf[BUFSIZ]; X BUF *allocbuf(); X X for (x = 0; x < argc; x++) { X name = argv[x]; X if ((f = open(name, O_RDONLY, 0)) < 0) { X error("rcp: %s: %s\n", name, sys_errlist[errno]); X continue; X } X if (fstat(f, &stb) < 0) X goto notreg; X switch (stb.st_mode&S_IFMT) { X X case S_IFREG: X break; X X case S_IFDIR: X if (iamrecursive) { X (void)close(f); X rsource(name, &stb); X continue; X } X /* FALLTHROUGH */ X default: Xnotreg: (void)close(f); X error("rcp: %s: not a plain file\n", name); X continue; X } X last = rindex(name, '/'); X if (last == 0) X last = name; X else X last++; X if (pflag) { X /* X * Make it compatible with possible future X * versions expecting microseconds. X */ X (void)sprintf(buf, "T%ld 0 %ld 0\n", stb.st_mtime, X stb.st_atime); X (void)write(rem, buf, strlen(buf)); X if (response() < 0) { X (void)close(f); X continue; X } X } X (void)sprintf(buf, "C%04o %ld %s\n", stb.st_mode&07777, X stb.st_size, last); X (void)write(rem, buf, strlen(buf)); X if (response() < 0) { X (void)close(f); X continue; X } X if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) { X (void)close(f); X continue; X } X readerr = 0; X for (i = 0; i < stb.st_size; i += bp->cnt) { X amt = bp->cnt; X if (i + amt > stb.st_size) X amt = stb.st_size - i; X if (readerr == 0 && read(f, bp->buf, amt) != amt) X readerr = errno; X (void)write(rem, bp->buf, amt); X } X (void)close(f); X if (readerr == 0) X (void)write(rem, "", 1); X else X error("rcp: %s: %s\n", name, sys_errlist[readerr]); X (void)response(); X } X} X Xrsource(name, statp) X char *name; X struct stat *statp; X{ X DIR *d; X struct direct *dp; X char *last, *vect[1], path[MAXPATHLEN]; X X if (!(d = opendir(name))) { X error("rcp: %s: %s\n", name, sys_errlist[errno]); X return; X } X last = rindex(name, '/'); X if (last == 0) X last = name; X else X last++; X if (pflag) { X (void)sprintf(path, "T%ld 0 %ld 0\n", statp->st_mtime, X statp->st_atime); X (void)write(rem, path, strlen(path)); X if (response() < 0) { X closedir(d); X return; X } X } X (void)sprintf(path, "D%04o %d %s\n", statp->st_mode&07777, 0, last); X (void)write(rem, path, strlen(path)); X if (response() < 0) { X closedir(d); X return; X } X while (dp = readdir(d)) { X if (dp->d_ino == 0) X continue; X if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) X continue; X if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { X error("%s/%s: name too long.\n", name, dp->d_name); X continue; X } X (void)sprintf(path, "%s/%s", name, dp->d_name); X vect[0] = path; X source(1, vect); X } X closedir(d); X (void)write(rem, "E\n", 2); X (void)response(); X} X Xresponse() X{ X register char *cp; X char ch, resp, rbuf[BUFSIZ]; X X if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) X lostconn(); X X cp = rbuf; X switch(resp) { X case 0: /* ok */ X return(0); X default: X *cp++ = resp; X /* FALLTHROUGH */ X case 1: /* error, followed by err msg */ X case 2: /* fatal error, "" */ X do { X if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) X lostconn(); X *cp++ = ch; X } while (cp < &rbuf[BUFSIZ] && ch != '\n'); X X if (!iamremote) X (void)write(2, rbuf, cp - rbuf); X ++errs; X if (resp == 1) X return(-1); X exit(1); X } X /*NOTREACHED*/ X} X Xlostconn() X{ X if (!iamremote) X (void)fprintf(stderr, "rcp: lost connection\n"); X exit(1); X} X Xsink(argc, argv) X int argc; X char **argv; X{ X register char *cp; X static BUF buffer; X struct stat stb; X struct timeval tv[2]; X BUF *bp, *allocbuf(); X off_t i, j; X char ch, *targ, *why; X int amt, count, exists, first, mask, mode; X int ofd, setimes, size, targisdir, wrerr; X char *np, *vect[1], buf[BUFSIZ], *malloc(); X X#define atime tv[0] X#define mtime tv[1] X#define SCREWUP(str) { why = str; goto screwup; } X X setimes = targisdir = 0; X mask = umask(0); X if (!pflag) X (void)umask(mask); X if (argc != 1) { X error("rcp: ambiguous target\n"); X exit(1); X } X targ = *argv; X if (targetshouldbedirectory) X verifydir(targ); X (void)write(rem, "", 1); X if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) X targisdir = 1; X for (first = 1;; first = 0) { X cp = buf; X if (read(rem, cp, 1) <= 0) X return; X if (*cp++ == '\n') X SCREWUP("unexpected <newline>"); X do { X if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) X SCREWUP("lost connection"); X *cp++ = ch; X } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); X *cp = 0; X X if (buf[0] == '\01' || buf[0] == '\02') { X if (iamremote == 0) X (void)write(2, buf + 1, strlen(buf + 1)); X if (buf[0] == '\02') X exit(1); X errs++; X continue; X } X if (buf[0] == 'E') { X (void)write(rem, "", 1); X return; X } X X if (ch == '\n') X *--cp = 0; X X#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); X cp = buf; X if (*cp == 'T') { X setimes++; X cp++; X getnum(mtime.tv_sec); X if (*cp++ != ' ') X SCREWUP("mtime.sec not delimited"); X getnum(mtime.tv_usec); X if (*cp++ != ' ') X SCREWUP("mtime.usec not delimited"); X getnum(atime.tv_sec); X if (*cp++ != ' ') X SCREWUP("atime.sec not delimited"); X getnum(atime.tv_usec); X if (*cp++ != '\0') X SCREWUP("atime.usec not delimited"); X (void)write(rem, "", 1); X continue; X } X if (*cp != 'C' && *cp != 'D') { X /* X * Check for the case "rcp remote:foo\* local:bar". X * In this case, the line "No match." can be returned X * by the shell before the rcp command on the remote is X * executed so the ^Aerror_message convention isn't X * followed. X */ X if (first) { X error("%s\n", cp); X exit(1); X } X SCREWUP("expected control record"); X } X mode = 0; X for (++cp; cp < buf + 5; cp++) { X if (*cp < '0' || *cp > '7') X SCREWUP("bad mode"); X mode = (mode << 3) | (*cp - '0'); X } X if (*cp++ != ' ') X SCREWUP("mode not delimited"); X size = 0; X while (isdigit(*cp)) X size = size * 10 + (*cp++ - '0'); X if (*cp++ != ' ') X SCREWUP("size not delimited"); X if (targisdir) { X static char *namebuf; X static int cursize; X int need; X X need = strlen(targ) + strlen(cp) + 250; X if (need > cursize) { X if (!(namebuf = malloc((u_int)need))) X error("out of memory\n"); X } X (void)sprintf(namebuf, "%s%s%s", targ, X *targ ? "/" : "", cp); X np = namebuf; X } X else X np = targ; X exists = stat(np, &stb) == 0; X if (buf[0] == 'D') { X if (exists) { X if ((stb.st_mode&S_IFMT) != S_IFDIR) { X errno = ENOTDIR; X goto bad; X } X if (pflag) X (void)chmod(np, mode); X } else if (mkdir(np, mode) < 0) X goto bad; X vect[0] = np; X sink(1, vect); X if (setimes) { X setimes = 0; X if (utimes(np, tv) < 0) X error("rcp: can't set times on %s: %s\n", X np, sys_errlist[errno]); X } X continue; X } X if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { Xbad: error("rcp: %s: %s\n", np, sys_errlist[errno]); X continue; X } X if (exists && pflag) X (void)fchmod(ofd, mode); X (void)write(rem, "", 1); X if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == 0) { X (void)close(ofd); X continue; X } X cp = bp->buf; X count = 0; X wrerr = 0; X for (i = 0; i < size; i += BUFSIZ) { X amt = BUFSIZ; X if (i + amt > size) X amt = size - i; X count += amt; X do { X j = read(rem, cp, amt); X if (j <= 0) { X error("rcp: %s\n", X j ? sys_errlist[errno] : X "dropped connection"); X exit(1); X } X amt -= j; X cp += j; X } while (amt > 0); X if (count == bp->cnt) { X if (wrerr == 0 && X write(ofd, bp->buf, count) != count) X wrerr++; X count = 0; X cp = bp->buf; X } X } X if (count != 0 && wrerr == 0 && X write(ofd, bp->buf, count) != count) X wrerr++; X if (ftruncate(ofd, size)) X error("rcp: can't truncate %s: %s\n", np, X sys_errlist[errno]); X (void)close(ofd); X (void)response(); X if (setimes) { X setimes = 0; X if (utimes(np, tv) < 0) X error("rcp: can't set times on %s: %s\n", X np, sys_errlist[errno]); X } X if (wrerr) X error("rcp: %s: %s\n", np, sys_errlist[errno]); X else X (void)write(rem, "", 1); X } Xscrewup: X error("rcp: protocol screwup: %s\n", why); X exit(1); X} X XBUF * Xallocbuf(bp, fd, blksize) X BUF *bp; X int fd, blksize; X{ X struct stat stb; X int size; X char *malloc(); X X if (fstat(fd, &stb) < 0) { X error("rcp: fstat: %s\n", sys_errlist[errno]); X return(0); X } X size = roundup(stb.st_blksize, blksize); X if (size == 0) X size = blksize; X if (bp->cnt < size) { X if (bp->buf != 0) X free(bp->buf); X bp->buf = (char *)malloc((u_int)size); X if (!bp->buf) { X error("rcp: malloc: out of memory\n"); X return(0); X } X } X bp->cnt = size; X return(bp); X} X X/* VARARGS1 */ Xerror(fmt, a1, a2, a3) X char *fmt; X int a1, a2, a3; X{ X static FILE *fp; X X ++errs; X if (!fp && !(fp = fdopen(rem, "w"))) X return; X (void)fprintf(fp, "%c", 0x01); X (void)fprintf(fp, fmt, a1, a2, a3); X (void)fflush(fp); X if (!iamremote) X (void)fprintf(stderr, fmt, a1, a2, a3); X} X Xnospace() X{ X (void)fprintf(stderr, "rcp: out of memory.\n"); X exit(1); X} X X#ifdef KERBEROS Xold_warning(str) X char *str; X{ X (void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str); X} X#endif X Xusage() X{ X#ifdef KERBEROS X (void)fprintf(stderr, "%s\n\t%s\n", X "usage: rcp [-k realm] [-px] f1 f2", X "or: rcp [-k realm] [-rpx] f1 ... fn directory"); X#else X (void)fprintf(stderr, X "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n"); X#endif X exit(1); X} END-of-rcp.c echo x - rdist.server.c.diff sed 's/^X//' >rdist.server.c.diff << 'END-of-rdist.server.c.diff' X*** old_server.c Wed Jun 29 20:19:34 1988 X--- server.c Wed May 24 20:17:51 1989 X*************** X*** 1369,1384 **** X char *fmt; X int a1, a2, a3; X { X! nerrs++; X! strcpy(buf, "\1rdist: "); X! (void) sprintf(buf+8, fmt, a1, a2, a3); X! if (!iamremote) { X fflush(stdout); X! (void) write(2, buf+1, strlen(buf+1)); X! } else X! (void) write(rem, buf, strlen(buf)); X! if (lfp != NULL) X! (void) fwrite(buf+1, 1, strlen(buf+1), lfp); X } X X /*VARARGS1*/ X--- 1369,1395 ---- X char *fmt; X int a1, a2, a3; X { X! static FILE *fp; X! X! ++nerrs; X! if (!fp && !(fp = fdopen(rem, "w"))) X! return; X! if (iamremote) { X! (void)fprintf(fp, "%crdist: ", 0x01); X! (void)fprintf(fp, fmt, a1, a2, a3); X! fflush(fp); X! } X! else { X fflush(stdout); X! (void)fprintf(stderr, "rdist: "); X! (void)fprintf(stderr, fmt, a1, a2, a3); X! fflush(stderr); X! } X! if (lfp != NULL) { X! (void)fprintf(lfp, "rdist: "); X! (void)fprintf(lfp, fmt, a1, a2, a3); X! fflush(lfp); X! } X } X X /*VARARGS1*/ X*************** X*** 1386,1401 **** X char *fmt; X int a1, a2, a3; X { X! nerrs++; X! strcpy(buf, "\2rdist: "); X! (void) sprintf(buf+8, fmt, a1, a2, a3); X! if (!iamremote) { X fflush(stdout); X! (void) write(2, buf+1, strlen(buf+1)); X! } else X! (void) write(rem, buf, strlen(buf)); X! if (lfp != NULL) X! (void) fwrite(buf+1, 1, strlen(buf+1), lfp); X cleanup(); X } X X--- 1397,1423 ---- X char *fmt; X int a1, a2, a3; X { X! static FILE *fp; X! X! ++nerrs; X! if (!fp && !(fp = fdopen(rem, "w"))) X! return; X! if (iamremote) { X! (void)fprintf(fp, "%crdist: ", 0x02); X! (void)fprintf(fp, fmt, a1, a2, a3); X! fflush(fp); X! } X! else { X fflush(stdout); X! (void)fprintf(stderr, "rdist: "); X! (void)fprintf(stderr, fmt, a1, a2, a3); X! fflush(stderr); X! } X! if (lfp != NULL) { X! (void)fprintf(lfp, "rdist: "); X! (void)fprintf(lfp, fmt, a1, a2, a3); X! fflush(lfp); X! } X cleanup(); X } X END-of-rdist.server.c.diff exit