/*
 *	$Source: /afs/rel-eng.athena.mit.edu/project/release/current/source/athena/athena.lib/kerberos/appl/bsd/RCS/rlogind.c,v $
 *	$Header: /afs/rel-eng.athena.mit.edu/project/release/current/source/athena/athena.lib/kerberos/appl/bsd/RCS/rlogind.c,v 4.24 91/08/20 18:08:04 probe Exp $
 */

#ifndef lint
static char *rcsid_rlogind_c = "$Header: /afs/rel-eng.athena.mit.edu/project/release/current/source/athena/athena.lib/kerberos/appl/bsd/RCS/rlogind.c,v 4.24 91/08/20 18:08:04 probe Exp $";
#endif	lint

/*
 * 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 */

#ifndef lint
static char sccsid[] = "@(#)rlogind.c	5.17 (Berkeley) 8/31/88";
#endif /* not lint */

/*
 * remote login server:
 *	remuser\0
 *	locuser\0
 *	terminal info\0
 *	data
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>

#include <netinet/in.h>

#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <sgtty.h>
#include <netdb.h>
#include <syslog.h>
#include <strings.h>

#ifdef POSIX
#include <stdlib.h>
#include <termios.h>
#ifdef _AIX
#include <termio.h>
#endif
#endif

#ifdef KERBEROS
#include <sys/param.h>
#include <utmp.h>
#include <krb.h>

#ifndef LOGIN_PROGRAM
#define LOGIN_PROGRAM "/usr/athena/etc/login.krb"
#endif
#define	SECURE_MESSAGE	"This rlogin session is using DES encryption for all data transmissions.\r\n"
#define NMAX	sizeof(wtmp.ut_name)
struct utmp	wtmp;
char		lusername[NMAX+1];
char		term[64];
int 		Klogin = 0, klogin = 0, eklogin = 0;
AUTH_DAT	*kdata;
KTEXT		ticket;
Key_schedule	schedule;
void		do_krb_login();
#ifdef NOENCRYPTION
#define	des_read	read
#define	des_write	write
#endif /* NOENCRYPTION */
#else /* !KERBEROS */
#define	des_read	read
#define	des_write	write
#endif KERBEROS

# ifndef TIOCPKT_WINDOW
# define TIOCPKT_WINDOW 0x80
# endif TIOCPKT_WINDOW

extern	int errno;
int	reapchild();
struct	passwd *getpwnam();

static	int Pfd;

#if (defined(_AIX) && defined(i386)) || defined(ibm032) || (defined(vax) && !defined(ultrix))
#define VHANG_FIRST
#endif

#if defined(ultrix)
#define VHANG_LAST		/* vhangup must occur on close, not open */
#endif


/* ARGSUSED */
main(argc, argv)
	int argc;
	char **argv;
{
	int on = 1, fromlen;
	struct sockaddr_in from;

#ifdef KERBEROS
	/*
	 * if the command name is klogind or Klogind or eklogind, the
	 * connection has 
	 * been made thru the Kerberos authentication port for rlogin.  The
	 * protocol is different, to provide the transmission for the
	 * ticket used for authentication.
	 *
	 * If the name of the program is "klogind", and the Kerberos 
	 * authentication fails, we will allow the user password access
	 * to this host.  If the program name is "Klogind", and the
	 * Kerberos authentication fails, we will NOT allow password access;
	 * this is for protection of the Kerberos server and other highly
	 * paranoid hosts.
	 *
	 * If the name of the program is "eklogind", all data passing over
	 * the network pipe are encrypted.
	 */
#ifdef NOENCRYPTION
	klogin = (!strcmp(*argv,"eklogind") ||
		  !strcmp(*argv,"klogind"));	/* pass -k flag to login */
#else
	eklogin = !strcmp(*argv,"eklogind");	/* pass -e flag to login */
	klogin = !strcmp(*argv,"klogind");	/* pass -k flag to login */
#endif
	Klogin = !strcmp(*argv,"Klogind");	/* pass -K flag to login */
	/* 
	 * if klogin, Klogin, and eklogin are zero (ie, the program name was
	 * probably rlogind instead), pass the -r flag to login
	 */

#ifndef LOG_AUTH /* 4.2 syslog */
#ifdef NOENCRYPTION
	openlog(klogin ? "klogind" : (Klogin ? "Klogind" : "rlogind"),
		LOG_PID);
#else /* !NOENCRYPTION */
	openlog(eklogin ? "eklogind" : (klogin ? "klogind" :
					(Klogin ? "Klogind" : "rlogind")),
		LOG_PID);
#endif /* NOENCRYPTION */
#else
#ifdef NOENCRYPTION
	openlog(klogin ? "klogind" : (Klogin ? "Klogind" : "rlogind"),
		LOG_PID | LOG_AUTH, LOG_AUTH);
#else /* !NOENCRYPTION */
	openlog(eklogin ? "eklogind" : (klogin ? "klogind" :
					(Klogin ? "Klogind" : "rlogind")),
		LOG_PID | LOG_AUTH, LOG_AUTH);
#endif /* NOENCRYPTION */
#endif /* 4.2 syslog */
#else
#ifndef LOG_AUTH /* 4.2 syslog */
	openlog("rlogind", LOG_PID);
#else
	openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH);
#endif /* 4.2 syslog */
#endif KERBEROS
	fromlen = sizeof (from);
	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
		fprintf(stderr, "%s: ", argv[0]);
		perror("getpeername");
		_exit(1);
	}
	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
	}
	doit(0, &from);
}

int	child;
int	cleanup();
int	netf;
char	*line;
extern	char	*inet_ntoa();

struct winsize win = { 0, 0, 0, 0 };


doit(f, fromp)
	int f;
	struct sockaddr_in *fromp;
{
	int i, p, t, pid, on = 1;
	register struct hostent *hp;
	struct hostent hostent;
	char c;

	alarm(60);
	read(f, &c, 1);
	if (c != 0)
		exit(1);
	alarm(0);
	fromp->sin_port = ntohs((u_short)fromp->sin_port);
	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
		fromp->sin_family);
	if (hp == 0) {
		/*
		 * Only the name is used below.
		 */
		hp = &hostent;
		hp->h_name = inet_ntoa(fromp->sin_addr);
	}
#ifdef KERBEROS
	/* Don't care about reserved port for kerberos logins */
#ifdef NOENCRYPTION
	if (fromp->sin_family != AF_INET ||
	    (!klogin && !Klogin &&
	     (fromp->sin_port >= IPPORT_RESERVED ||
	     fromp->sin_port < IPPORT_RESERVED/2)))
#else /* !NOENCRYPTION */
	if (fromp->sin_family != AF_INET ||
	    (!klogin && !Klogin && !eklogin &&
	     (fromp->sin_port >= IPPORT_RESERVED ||
	     fromp->sin_port < IPPORT_RESERVED/2)))
#endif /* NOENCRYPTION */
#else /* !KERBEROS */
	if (fromp->sin_family != AF_INET ||
	    fromp->sin_port >= IPPORT_RESERVED ||
	    fromp->sin_port < IPPORT_RESERVED/2)
#endif KERBEROS
		fatal(f, "Permission denied");
#if defined(KERBEROS) && !defined(NOENCRYPTION)
	/*
	 * If encrypting, we need to respond here, since we have to send
	 * the mutual authentication stuff before the response
	 */
	if (eklogin)
	    do_krb_login(hp->h_name);
#endif KERBEROS
	write(f, "", 1);
#if defined(_AIX) && defined(_IBMR2)
	if ((p = open("/dev/ptc", O_RDWR)) >= 0) {
		line = ttyname(p);
		goto gotpty;
	}
#else
	for (c = 'p'; c <= 's'; c++) {
		struct stat stb;
		line = "/dev/ptyXX";
		line[strlen("/dev/pty")] = c;
		line[strlen("/dev/ptyp")] = '0';
		if (stat(line, &stb) < 0)
			break;
		for (i = 0; i < 16; i++) {
 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
 			p = open(line, O_RDWR);
			if (p > 0)
				goto gotpty;
		}
	}
#endif /* !AIX3.1 */
	fatal(f, "Out of ptys");
	/*NOTREACHED*/
gotpty:
	Pfd = p;
	(void) ioctl(p, TIOCSWINSZ, &win);
	netf = f;
#if !defined(_AIX) && !defined(_IBMR2)
	line[strlen("/dev/")] = 't';
#endif
#ifdef VHANG_FIRST
	t = open(line, O_RDWR);
 	if (t < 0)
 		fatalperror(f, line);
 	if (fchmod(t, 0))
 		fatalperror(f, line);
	/*
	 * On some systems you have to revoke tty access on close or
	 * you may lose access yourself.
	 */
 	(void)signal(SIGHUP, SIG_IGN);
#if defined(_AIX) && defined(i386)
	_vhangup();
#else
 	vhangup();
#endif
#endif /* VHANG_FIRST */
 	(void)signal(SIGHUP, SIG_DFL);
 	t = open(line, O_RDWR);
 	if (t < 0)
 		fatalperror(f, line);
#ifdef POSIX
	{
		struct termios new_termio;

		tcgetattr(t,&new_termio);
		new_termio.c_lflag &=  ~(ICANON|ECHO|ISIG);
		new_termio.c_iflag = 0;
		new_termio.c_oflag = 0;
		new_termio.c_cc[VMIN] = 1;
		new_termio.c_cc[VTIME] = 0;
		tcsetattr(t,TCSANOW,&new_termio);
	}
#else
 	{
 		struct sgttyb b;
 
 		(void)ioctl(t, TIOCGETP, &b);
 		b.sg_flags = RAW|ANYP;
 		(void)ioctl(t, TIOCSETP, &b);
	}
#endif
#ifdef DEBUG
	{
		int tt = open("/dev/tty", O_RDWR);
		if (tt > 0) {
		(void) ioctl(tt, TIOCNOTTY, 0);
		(void) close(tt);
	  }
	}
#endif
#ifdef POSIX
	setsid();
#endif
	t = open(line, 2);
	if (t < 0)
		fatalperror(f, line, errno);
#if defined(POSIX) && !defined(ultrix)
	{
		struct termios new_termio;

		tcgetattr(t,&new_termio);
		new_termio.c_lflag &=  ~(ICANON|ECHO|ISIG);
		new_termio.c_iflag = 0;
		new_termio.c_oflag = 0;
		new_termio.c_cc[VMIN] = 1;
		new_termio.c_cc[VTIME] = 0;
		tcsetattr(t,TCSANOW,&new_termio);
	}
#else
	{
		struct sgttyb b;
		gtty(t, &b); b.sg_flags = RAW|ANYP; stty(t, &b);
	}
#endif
	pid = fork();
	if (pid < 0)
		fatalperror(f, "", errno);
	if (pid == 0) {
		close(f), close(p);
		dup2(t, 0), dup2(t, 1), dup2(t, 2);
		if (t > 2)
		    close(t);

		/* Under Ultrix 3.0, the pgrp of the slave pty terminal
		 needs to be set explicitly.  Why rlogind works at all
		 without this on 4.3BSD is a mystery.
		 It seems to work fine on 4.3BSD with this code enabled.
		 */
		pid = getpgrp(getpid());
		ioctl(0, TIOCSPGRP, &pid);
#ifdef KERBEROS
		if (klogin) {
			execl(LOGIN_PROGRAM, "login", "-k", hp->h_name, 0);
		} else if (Klogin) {
			execl(LOGIN_PROGRAM, "login", "-K", hp->h_name, 0);
#ifndef NOENCRYPTION
		} else if (eklogin) {
		    struct passwd *pw;
		    pw = getpwnam(lusername);
		    if (pw && !pw->pw_uid)
			syslog(LOG_INFO, "ROOT LOGIN (krb) from %s, %s.%s@%s.",
			       hp->h_name, kdata->pname, kdata->pinst,
			       kdata->prealm);
		    execl(LOGIN_PROGRAM, "login", "-e", lusername, 0);
#endif
		} else {
			execl(LOGIN_PROGRAM, "login", "-r", hp->h_name, 0);
		}
		fatalperror(2, LOGIN_PROGRAM, errno);
#else
		execl("/bin/login", "login", "-r", hp->h_name, 0);
		fatalperror(2, "/bin/login", errno);
#endif KERBEROS
		/*NOTREACHED*/
	}
	close(t);

#if defined(KERBEROS) && !defined(NOENCRYPTION)
	if (eklogin)
	    (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE));
	else
	/*
	 * if encrypting, don't turn on NBIO, else the read/write routines
	 * will fail to work properly
	 */
#endif /* KERBEROS && !NOENCRYPTION */
	ioctl(f, FIONBIO, &on);
	ioctl(p, FIONBIO, &on);
	ioctl(p, TIOCPKT, &on);
	signal(SIGTSTP, SIG_IGN);
	signal(SIGCHLD, cleanup);
	setpgrp(0, 0);
#ifdef KERBEROS
	if (eklogin)
	    (void) write(p, term, strlen(term)+1); /* stuff term info down
						      to login */
#endif /* KERBEROS */
	protocol(f, p);
	signal(SIGCHLD, SIG_IGN);
	cleanup();
}

char	magic[2] = { 0377, 0377 };
char	oobdata[] = {TIOCPKT_WINDOW};

/*
 * Handle a "control" request (signaled by magic being present)
 * in the data stream.  For now, we are only willing to handle
 * window size changes.
 */
control(pty, cp, n)
	int pty;
	char *cp;
	int n;
{
	struct winsize w;

	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
		return (0);
	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
	bcopy(cp+4, (char *)&w, sizeof(w));
	w.ws_row = ntohs(w.ws_row);
	w.ws_col = ntohs(w.ws_col);
	w.ws_xpixel = ntohs(w.ws_xpixel);
	w.ws_ypixel = ntohs(w.ws_ypixel);
	(void)ioctl(pty, TIOCSWINSZ, &w);
	return (4+sizeof (w));
}

/*
 * rlogin "protocol" machine.
 */
protocol(f, p)
	int f, p;
{
	char pibuf[1024], fibuf[1024], *pbp, *fbp;
	register pcc = 0, fcc = 0;
	int cc;
	char cntl;

	/*
	 * Must ignore SIGTTOU, otherwise we'll stop
	 * when we try and set slave pty's window shape
	 * (our controlling tty is the master pty).
	 */
	(void) signal(SIGTTOU, SIG_IGN);
	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
	for (;;) {
		int ibits, obits, ebits;

		ibits = 0;
		obits = 0;
		if (fcc)
			obits |= (1<<p);
		else
			ibits |= (1<<f);
		if (pcc >= 0)
			if (pcc)
				obits |= (1<<f);
			else
				ibits |= (1<<p);
		ebits = (1<<p);
		if (select(16, &ibits, &obits, &ebits, 0) < 0) {
			if (errno == EINTR)
				continue;
			fatalperror(f, "select");
		}
		if (ibits == 0 && obits == 0 && ebits == 0) {
			/* shouldn't happen... */
			sleep(5);
			continue;
		}
#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
		if (ebits & (1<<p)) {
			cc = read(p, &cntl, 1);
			if (cc == 1 && pkcontrol(cntl)) {
				cntl |= oobdata[0];
				send(f, &cntl, 1, MSG_OOB);
				if (cntl & TIOCPKT_FLUSHWRITE) {
					pcc = 0;
					ibits &= ~(1<<p);
				}
			}
		}
		if (ibits & (1<<f)) {
			fcc = des_read(f, fibuf, sizeof (fibuf));
			if (fcc < 0 && errno == EWOULDBLOCK)
				fcc = 0;
			else {
				register char *cp;
				int left, n;

				if (fcc <= 0)
					break;
				fbp = fibuf;

			top:
				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
					if (cp[0] == magic[0] &&
					    cp[1] == magic[1]) {
						left = fcc - (cp-fibuf);
						n = control(p, cp, left);
						if (n) {
							left -= n;
							if (left > 0)
								bcopy(cp+n, cp, left);
							fcc -= n;
							goto top; /* n^2 */
						}
					}
			}
		}

		if ((obits & (1<<p)) && fcc > 0) {
			cc = write(p, fbp, fcc);
			if (cc > 0) {
				fcc -= cc;
				fbp += cc;
			}
		}

		if (ibits & (1<<p)) {
			pcc = read(p, pibuf, sizeof (pibuf));
			pbp = pibuf;
			if (pcc < 0 && errno == EWOULDBLOCK)
				pcc = 0;
			else if (pcc <= 0)
				break;
			else if (pibuf[0] == 0)
				pbp++, pcc--;
			else {
				if (pkcontrol(pibuf[0])) {
					pibuf[0] |= oobdata[0];
					send(f, &pibuf[0], 1, MSG_OOB);
				}
				pcc = 0;
			}
		}
		if ((obits & (1<<f)) && pcc > 0) {
			cc = des_write(f, pbp, pcc);
			if (cc < 0 && errno == EWOULDBLOCK) {
				/* also shouldn't happen */
				sleep(5);
				continue;
			}
			if (cc > 0) {
				pcc -= cc;
				pbp += cc;
			}
		}
	}
}

cleanup()
{
	char *p;

	p = line + sizeof("/dev/") - 1;
	if (!logout(p))
		logwtmp(p, "", "");
	(void)chmod(line, 0666);
	(void)chown(line, 0, 0);
	*p = 'p';
	(void)chmod(line, 0666);
	(void)chown(line, 0, 0);
#ifdef VHANG_LAST
	close(Pfd);
	vhangup();
#endif
	shutdown(netf, 2);
	exit(1);
}

fatal(f, msg)
	int f;
	char *msg;
{
	char buf[BUFSIZ];

	buf[0] = '\01';		/* error indicator */
	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
	(void) write(f, buf, strlen(buf));
	exit(1);
}

fatalperror(f, msg)
	int f;
	char *msg;
{
	char buf[BUFSIZ];
	extern int sys_nerr;
	extern char *sys_errlist[];

	if ((unsigned)errno < sys_nerr)
		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
	else
		(void) sprintf(buf, "%s: Error %d", msg, errno);
	fatal(f, buf);
}

#ifdef KERBEROS
void
do_krb_login(host)
	char *host;
{
	int rc;
	struct sockaddr_in sin, faddr;
	char instance[INST_SZ], version[9];
	long authoptions = KOPT_DO_MUTUAL;

	if (getuid()) {
		exit(1);
	}
	rc = sizeof(sin);
	if (getpeername(0, (struct sockaddr *)&sin, &rc)) {
		exit(1);
	}
	rc = sizeof(faddr);
	if (getsockname(0, (struct sockaddr *)&faddr, &rc)) {
		exit(1);
	}

	kdata = (AUTH_DAT *)malloc( sizeof(AUTH_DAT) );
	ticket = (KTEXT) malloc(sizeof(KTEXT_ST));

	strcpy(instance, "*");
	if (rc=krb_recvauth(authoptions, 0, ticket, "rcmd",
			    instance, &sin,
			    &faddr,
			    kdata, "", schedule, version)) {
		exit(1);
	}
	getstr(lusername, sizeof (lusername), "Local username");
	getstr(term, sizeof(term), "Terminal type");

	if (rc=kuserok(kdata,lusername)) {
		exit(1);
	}
	return;
}

getstr(buf, cnt, err)
	char *buf;
	int cnt;
	char *err;
{
	int ocnt = cnt;
	char *obuf = buf;
	char c;

	do {
		if (read(0, &c, 1) != 1) {
			exit(1);
		}
		if (--cnt < 0) {
			printf("%s '%.*s' too long, %d characters maximum.\r\n",
			       err, ocnt, obuf, ocnt-1);
			exit(1);
		}
		*buf++ = c;
	} while (c != 0);
}


#ifndef NOENCRYPTION
char des_inbuf[10240];			/* needs to be > largest read size */
char des_outbuf[10240];			/* needs to be > largest write size */
char storage[10240];			/* storage for the decryption */
int nstored = 0;
char *store_ptr;

int
des_read(fd, buf, len)
int fd;
register char *buf;
int len;
{
	int nreturned = 0;
	long net_len, rd_len;
	int cc;

	if (!eklogin)
		return(read(fd, buf, len));

	if (nstored >= len) {
		bcopy(store_ptr, buf, len);
		store_ptr += len;
		nstored -= len;
		return(len);
	} else if (nstored) {
		bcopy(store_ptr, buf, nstored);
		nreturned += nstored;
		buf += nstored;
		len -= nstored;
		nstored = 0;
	}
	
	if ((cc = krb_net_read(fd, &net_len, sizeof(net_len))) != sizeof(net_len)) {
		/* XXX can't read enough, pipe
		   must have closed */
		return(0);
	}
	net_len = ntohl(net_len);
	if (net_len < 0 || net_len > sizeof(des_inbuf)) {
		/* XXX preposterous length, probably out of sync.
		   act as if pipe closed */
		return(0);
	}
	/* the writer tells us how much real data we are getting, but
	   we need to read the pad bytes (8-byte boundary) */
	rd_len = roundup(net_len, 8);
	if ((cc = krb_net_read(fd, des_inbuf, rd_len)) != rd_len) {
		/* XXX can't read enough, pipe
		   must have closed */
		return(0);
	}
	(void) pcbc_encrypt(des_inbuf,
			    storage,
			    (net_len < 8) ? 8 : net_len,
			    schedule,
			    kdata->session,
			    DECRYPT);
	/* 
	 * when the cleartext block is < 8 bytes, it is "right-justified"
	 * in the block, so we need to adjust the pointer to the data
	 */
	if (net_len < 8)
		store_ptr = storage + 8 - net_len;
	else
		store_ptr = storage;
	nstored = net_len;
	if (nstored > len) {
		bcopy(store_ptr, buf, len);
		nreturned += len;
		store_ptr += len;
		nstored -= len;
	} else {
		bcopy(store_ptr, buf, nstored);
		nreturned += nstored;
		nstored = 0;
	}
	
	return(nreturned);
}

int
des_write(fd, buf, len)
int fd;
char *buf;
int len;
{
	long net_len;
	static int seeded = 0;
	static char garbage_buf[8];
	long garbage;

	if (!eklogin)
		return(write(fd, buf, len));

	/* 
	 * pcbc_encrypt outputs in 8-byte (64 bit) increments
	 *
	 * it zero-fills the cleartext to 8-byte padding,
	 * so if we have cleartext of < 8 bytes, we want
	 * to insert random garbage before it so that the ciphertext
	 * differs for each transmission of the same cleartext.
	 * if len < 8 - sizeof(long), sizeof(long) bytes of random
	 * garbage should be sufficient; leave the rest as-is in the buffer.
	 * if len > 8 - sizeof(long), just garbage fill the rest.
	 */

#define min(a,b) ((a < b) ? a : b)

	if (len < 8) {
		if (!seeded) {
			seeded = 1;
			srandom((int) time((long *)0));
		}
		garbage = random();
		/* insert random garbage */
		(void) bcopy(&garbage, garbage_buf, min(sizeof(long),8));

		/* this "right-justifies" the data in the buffer */
		(void) bcopy(buf, garbage_buf + 8 - len, len);
	}

	(void) pcbc_encrypt((len < 8) ? garbage_buf : buf,
			    des_outbuf,
			    (len < 8) ? 8 : len,
			    schedule,
			    kdata->session,
			    ENCRYPT);

	/* tell the other end the real amount, but send an 8-byte padded
	   packet */
	net_len = htonl(len);
	(void) write(fd, &net_len, sizeof(net_len));
	(void) write(fd, des_outbuf, roundup(len,8));
	return(len);
}
#endif /* NOENCRYPTION */
#endif KERBEROS
