[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

LESM - Link Encrypted Session Manager (3rd try)



I tried to distribute LESM to the cypherpunks mailing list twice, and 
neither of them made it to the nntp.hks.com list archive (my 
laster posts HAVE).  What is going on here?  This is a second repost.

-----BEGIN PGP SIGNED MESSAGE-----

I finished hacking Matt's ESM to do link encryption yesterday, but 
for some reason the mail I sent to cypherpunks didn't get echoed back 
to me, so I'm sending this again.

To install: get ESM and RSAREF, make sure you can compile ESM, 
then replace the esm.c with my hacked version and recompile.

To use: same as ESM, but there's an extra option "-b bandwidth", the 
default is 100 cps.

I've attached the modified esm.c here since it doesn't include any 
crypto code.  To get ESM, write to [email protected] and state the 
following:

	- you are in the US or Canada, and
	- you are a US or Canadian citizen or legal permanent
	  resident, and
	- You've read and understand the license and export
	  conditions.

RSAREF is in ftp://rsa.com/rsaref/

Wei Dai


-----BEGIN PGP SIGNATURE-----
Version: 2.6.2

iQCVAwUBLzqTtTl0sXKgdnV5AQEmbgP8CRxE+RCGsDXwwShWhZKGytSywyWZ8RXB
RPBHZmk6UgdpCR2VVo8wXjcPuEv4SHp9ZjhFTq53JZ/s3YcV5FxF33/ivSlh1Ra4
P8GSYnoEaIJoFrJyagukbBUaY6zWCx0PcnsLoVArCxujwRYwfrQvHdiNx13z7bJh
OSEhhoE9djg=
=PY8P
-----END PGP SIGNATURE-----


-------------- Enclosure number 1 ----------------
/*
 * LESM - Link Encrypted Session Manager
 * v0.6a.01
 * Wei Dai
 * 2/8/1995
 *
 * This program is a quick and dirty hack of:
 */

/*
 * ESM - Encrypted Session Manager
 * v0.6a
 * matt blaze
 * January 1995
 */

/*
 * The author of this software is Matt Blaze.
 *              Copyright (c) 1995 by AT&T.
 * Permission to use, copy, and modify this software without fee
 * is hereby granted, provided that this entire notice is included in
 * all copies of any software which is or includes a copy or
 * modification of this software and in all copies of the supporting
 * documentation for such software.
 *
 * This software is subject to United States export controls.
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
/*
 * Some of this file was stolen from the BSD "script" program, which
 * is covered under the following notice:
 *
 * Copyright (c) 1980 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. 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.
 */

#ifndef lint
char copyright1[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
char copyright2[] =
"@(#) Copyright (c) 1995 AT&T\nAll rights reserved.\n";
#endif /* not lint */

#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#ifndef SUN
#include <sys/ioctl.h>
#endif
#include <sys/time.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <unistd.h>
#include <stdio.h>
#include "global.h"
#include "rsaref.h"
#include "esm.h"


char	*shell;
int	master;
int	slave;
int	subchild;

int escape=036; /* ^^ */
int ciphstate=0;
int ciphbyte=0;

int keyed=0;

struct	termios tt;
struct	winsize win;
int	lb;
int	l;
char	line[] = "/dev/ptyXX";
int	aflg;

#define REMOTE 0
#define LOCAL 1
#define CALC 2

int mode=LOCAL;
int paranoid=0;

#define SL_START 0
#define SL_GOT1 1
#define SL_GOT2 2
#define SL_GOT3 3
#define SL_GOT4 4
#define SL_KEYING 5
#define SL_CRYPT 6
int sloutstate=SL_START;
char *cmd=NULL;

#define TRANS 0
#define CMDWAIT 1
#define CIPHER 2
#define KEYWAIT 3

#define IV0 0
#define IV1 1
#define IV2 2
#define IV3 3
#define C0  4
#define C1  5

int state=TRANS;
int cstate=IV0;

FILE *fpmaster;

struct timeval tvPause={0,10000};
struct timeval tvNoPause={0,0};

unsigned char filler = (unsigned char) 31;

#define bwrite(fp,buf,len) (fwrite(buf,len,1,fp))

main(argc, argv)
	int argc;
	char *argv[];
{
	extern char *optarg;
	extern int optind;
	int ch, result;
	void finish();
	char *getenv();
	fd_set fds;
        struct timeval tv;

	while ((ch = getopt(argc, argv, "e:b:splrci")) != EOF)
	  	switch((char)ch) {
	  	    case 'e':
	  		cmd=optarg;
	  		break;
                    case 'b':
                    	tvPause.tv_usec=(long)1000000/atol(optarg);
                        break;
	  	    case 'i':
	  	    case 's':
	  	    case 'p':
	  		paranoid=1;
	  	    case 'r':
	  		mode=REMOTE;
	  		break;
	  	    case 'l':
	  		mode=LOCAL;
	  		break;
	  	    case 'c':
	  		mode=CALC;
	  		break;
	  	    case '?':
	  	    default:
	  		fprintf(stderr,
	  		   "usage: lesm [-rlc] [-b bandwidth] [-e program\n");
	  		exit(1);
	  	}
	argc -= optind;
	argv += optind;

	if ((shell=getenv("SHELL")) == NULL)
		shell = "/bin/sh";
	
      	getmaster();

	(void) signal(SIGCHLD, finish);
	subchild = fork();
	if (subchild < 0) {
		perror("fork");
		fail();
	}
	if (subchild==0)
		doshell();
	else {
		/* main loop */
		printf("LESM v0.6a.01, hacked by Wei Dai from ESM v0.6a - encrypted session manager\n");
		printf("    by Matt Blaze, AT&T Bell Labs, January 1995\n");
	       	randinit();
		cipherinit();
		switch (mode) {
		    case REMOTE:
			if (paranoid)
			     printf("remote server ready\n");
			else
			     printf("remote server ready; ctl-^ to escape\n");
			break;
		    case LOCAL:
			printf("local layer ready (run 'lesm -s' on remote)\n");
			break;  
		    default:  /* not yet */
			printf("lesm ready\n");
		}
		rawtty();
		fpmaster=fdopen(master,"w");
		if (fpmaster == NULL)
			done();
		if (paranoid)
			startsession();
            
                while(1)
                {
			FD_ZERO(&fds);
			FD_SET(0,&fds);
                        
                        tv=tvNoPause;
                        result=select (FD_SETSIZE,&fds,NULL,NULL,&tv);

                        if (result==1)
                        	doinput();

      	                if (mode==LOCAL && sloutstate==SL_CRYPT && result==0)
                        	doslavein(filler);
                                
                        if (result<0)
                        	break;

	       		FD_ZERO(&fds);
	       		FD_SET(master,&fds);
                        
                        tv=tvNoPause;
                        result=select (FD_SETSIZE,&fds,NULL,NULL,&tv);
	       		                    	
                        if (result==1)
                                dooutput();

                        if (mode==REMOTE && state==CIPHER && result==0)
                        	domasterout(filler);

                        if (result<0)
                        	break;
		}
		done();
	}                                          
}

doinput()
{
	int cc;
	int i;
	static unsigned char ibuf[512];
	
	if ((cc = read(0, ibuf, 512)) > 0) {
		switch (mode) {
		    case REMOTE:
			for (i=0; i<cc; i++)
				domasterin(ibuf[i]);
			break;
		    case LOCAL:
			for (i=0; i<cc; i++)
				doslavein(ibuf[i]);
			break;
		    default:
			for (i=0; i<cc; i++)
				bwrite(fpmaster,&ibuf[i],1);
			break;
		}
		fflush(fpmaster);
		fflush(stdout);
	} else
		done();
}

doslavein(ibuf)
     unsigned char ibuf;
{
	int c;
	static int count=0;
        struct timeval tv;
	
	switch (sloutstate) {
	    case SL_CRYPT:
		if (ibuf==escape) {
			bwrite(stdout,">>",2);
			fflush(stdout);
			if (slescape()) {
				printf("q\r\nEntering CLEARTEXT mode\r\n");
				bwrite(fpmaster,"PPPPPPPPPPPPPPPP",16);
				sloutstate=SL_START;
			}
			break;
		}			
		c=cfb8_encrypt(ibuf);
		if (!(++count % 8))
			bwrite(fpmaster,"!",1);
		sendhex(fpmaster,c);
        	tv=tvPause;
                select (FD_SETSIZE,NULL,NULL,NULL,&tv);
		break;
	    default:
		bwrite(fpmaster,&ibuf,1);
		break;
	}
}

slescape()
{
	char buf;
	int c;
	int escaped=0;
	
	while (read(0,&buf,1)>0) {
		if (escaped) {
			escaped=0;
			bwrite(fpmaster,&buf,1);
			continue;
		}
		if (buf==escape) {
			c=cfb8_encrypt(buf);
			sendhex(fpmaster,c);
			return 0;
		}
		bwrite(stdout,&buf,1);
		fflush(stdout);
		if (buf=='\\') {
			mode=escaped;
			continue;
		}
		if (buf=='\r')
			return 0;
		if (buf=='C') {
			return 1;
		}
		printf("\r\nType one of the following:\r\n");
		printf("  \\[char] to send char as cleartext\r\n");
		printf("  ctrl-^ to send escape character\r\n");
		printf("  'C' to return to CLEARTEXT session\r\n");
		printf("  <enter> to return to encrypted session\r\n");
	}
	return 1; /* should never happen */
}

			       

domasterin(ibuf)
     unsigned char ibuf;
{
	int c;
	char ch;
	static int bad=0;
	
	switch (state) {
	    case TRANS:
	  	if (ibuf != escape)
	  		bwrite(fpmaster, &ibuf, 1);
	  	else {
	  		state=CMDWAIT;
	  		bwrite(stdout,">>",2);
	  		cstate=IV0;
	  	}
	  	break;
	    case CMDWAIT:
	  	if (ibuf == escape) {
	  		bwrite(fpmaster, &ibuf, 1);
	  		state=TRANS;
	  	} else switch (ibuf) {
	  	    case '\r':
	  	    case '\n':
	  		bwrite(stdout,"\r\n",2);
	  		state=TRANS;
	  		break;
	  	    case 's':
	  	    case 'S':
	  		startsession(LONG);
	  		break;
	  	    case 'Q':
	  		done();
	  		break;
	  	    default:
	  		printf("Type 's' to start encrypted session\r\n");
	  		printf("     'Q' to terminate remote session\r\n");
	  		printf("     ctrl-^ to send escape character\r\n");
	  		printf("     <enter> to return to session\r\n");
	  		break;
	  	}
	  	break;
	    case CIPHER:
	  	if (strchr("0123456789abcdef!",ibuf)!=NULL) {
	  		bad=0;
	  		if ((c = cipherout(ibuf)) >= 0) {
	  		      	ch=c;
                                if (ch!=filler)
	  			      	bwrite(fpmaster,&ch,1);
	  		}
	  	} else if (bad++ > 16) {
	  		delkey();
	  		bwrite(stdout,"XXXXXXXXXXXXXXXX",16);
	  		if (paranoid)
	  		      	done();
	  		state=TRANS;
	  	} else
	  		ciphstate=0;
	  	break;
	    case KEYWAIT:
	  	if (strchr("0123456789abcdef:",ibuf)!=NULL) {
			masterkeyin(ibuf);
		} else {
			delkey();
			bwrite(stdout,"XXXXXXXXXXXXXXXX",16);
			if (paranoid)
				done();
			state=TRANS;
		}
		break;
	}
}

int pubstat=0;
int pubpos=0;
unsigned char pubbyte=0;
int pksize = -1;

startsession()
{
	static unsigned char buf[5] = {0177, '~', 0177, '~', 'L'};
	static unsigned char colon=':';
	int i;

	pklen=dhparams[LONG].primeLen;
	printf("Starting remote side of %d bit key exchange.\r\n",pklen*8);
	if (createdh(LONG) < 0)
		return -1;
	bwrite(stdout,buf,5);
	for (i=0; i<pklen; i++)
		sendhex(stdout,ourpub[i]);
	bwrite(stdout,&colon,1);
	pksize=LONG;
	pubstat=0;
	pubpos=0;
	pubbyte=0;
	state=KEYWAIT;
	return 0;
}

sltranspub(len)
{
	static unsigned char colon=':';
	int i;
	
	printf("\r\nStarting local key exchange.\r\n");
	for (i=0; i<len; i++)
		sendhex(fpmaster,ourpub[i]);
	bwrite(fpmaster,&colon,1);
	printf("entering ENCRYPTED mode; type ctrl-^ to escape\r\n");
}

masterkeyin(c)
     char c;
{
	int bits;

	if (c==':') {
		if (pubpos!=(pklen+2)) /* +2 for trailing checkword */
			goto abort;
		if (mcalckeys(pksize)<0) /* also verifies */
			goto abort;
		ciphstate=0;
		state=CIPHER;
		return;
	} else if (pubpos<MAXPUBKEY) {
		bits=atoh(c);
		if (bits<0)
			goto abort;
		if (pubstat) {
			pubbyte |= bits;
			otherpub[pubpos]=pubbyte;
			pubpos++;
		} else {
			pubbyte = bits<<4;
		}
		pubstat = 1-pubstat;
		return;
	}
    abort:
	bwrite(stdout,"XXXXXXXXXXXXXXXX",16);
	if (paranoid)
		done();
	state=TRANS;
}

int mcalckeys(len)
     int len;
{
	int i;
	
	if (dhagree(len)<0) /* sets up session keys */
		return -1;
	for (i=0; i<8; i++) {
		ivin[i]=0;
		ivout[i]=0xff;
	}
	if (cfb8_decrypt(otherpub[pklen]) ||
	    cfb8_decrypt(otherpub[pklen+1]))
		return -1;
	return 0;
}

int slcalckeys(len)
     int len;
{
	int i;
	
	if (dhagree(len)<0) /* sets up session keys */
		return -1;
	for (i=0; i<8; i++) {
		ivout[i]=0;
		ivin[i]=0xff;
	}
	ourpub[pklen]=cfb8_encrypt(0);
	ourpub[pklen+1]=cfb8_encrypt(0);
	return 0;
}

initslkey(param)
     int param;
{
	if ((param<0) || (param>2))
		return -1;
	pklen=dhparams[param].primeLen;
	pksize=param;
	pubstat=0;
	pubpos=0;
	pubbyte=0;
}

slkeyin(c)
     char c;
{
	int bits;

	if (c==':') {
		if (pubpos!=pklen)
			goto abort;
		if (createdh(pksize)<0)
			goto abort;
		if (slcalckeys(pksize)<0)
			goto abort;
		sltranspub(pklen+2);
		ciphstate=0;
		sloutstate=SL_CRYPT;
		return;
	} else if (pubpos<MAXPUBKEY){
		bits=atoh(c);
		if (bits<0)
			goto abort;
		if (pubstat) {
			pubbyte |= bits;
			otherpub[pubpos]=pubbyte;
			pubpos++;
		} else {
			pubbyte = bits<<4;
		}
		pubstat = 1-pubstat;
		return;
	}
    abort:
	bwrite(stdout,"X",1);
	state=SL_START;

}



int cipherout(ch)
     char ch;
{
	int bits;
	
	if (ch=='!') {
		ciphstate=0;
		return -1;
	}
	bits=atoh(ch);
	if (ciphstate) {
		ciphbyte |= bits;
		ciphstate=0;
		return(cfb8_decrypt(ciphbyte));
	} else {
		ciphbyte = bits<<4;
		ciphstate=1;
		return -1;
	}
}

int ciphercalcin(ch)
     unsigned char ch;
{
	static unsigned char iv[8];
	static unsigned int cbuf;
	int c;
	int i;

	c = atoh(ch);
	switch (cstate) {
	    case IV0:
		for(i=0; i<8; i++)
			iv[i]=0;
		cbuf = (c&0xf)<<4;
		cstate=IV1;
		return '.';
	    case IV1:
		cbuf = cbuf | (c&0xf);
		iv[7]=cbuf;
		cstate=IV2;
		return '.';
	    case IV2:
		cbuf = (c&0xf)<<4;
		cstate=IV3;
		return '.';
	    case IV3:
		cbuf = cbuf | (c&0xf);
		iv[6]=cbuf;
		cstate=C0;
		return '.';
	    case C0:
		cbuf = (c&0xf)<<4;
		cstate=C1;
		return '.';
	    case C1:
		cbuf = cbuf | c&0xf;
		c=cfb8_decrypt(cbuf);
		cstate=C0;
		if (isprint(c)) {
			bwrite(fpmaster,&c,1);
			return '_';
		} else {
			return '?';
		}
	    default:
		return '?';
	}
}

#include <sys/wait.h>

void
finish()
{
	union wait status;
	register int pid;
	register int die = 0;

	while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
		if (pid == subchild)
			die = 1;

	if (die)
		done();
}

dooutput()
{
	register int cc;
	int i;
	unsigned char obuf[512];
	
	if ((cc = read(master, obuf, 512)) > 0) {
		switch (mode) {
		    case REMOTE:
			for (i=0; i<cc; i++)
				domasterout(obuf[i]);
			break;
		    case LOCAL:
			for (i=0; i<cc; i++)
				doslaveout(obuf[i]);
			break;
		    default:
			for (i=0; i<cc; i++)
				bwrite(stdout, &obuf[i], 1);
			break;
		}
		fflush(fpmaster);
		fflush(stdout);
	} else
		done();
}

domasterout(obuf)
     unsigned char obuf;
{
	int c;
	static int count=0;
        struct timeval tv;
	
	switch (state) {
	    case CIPHER:
		c=cfb8_encrypt(obuf);
		if (!(++count % 8))
			bwrite(stdout,"!",1);
		sendhex(stdout,c);
        	tv=tvPause;
                select (FD_SETSIZE,NULL,NULL,NULL,&tv);
		break;
	    case TRANS:
		bwrite(stdout, &obuf, 1);
		break;
	    default:
		/* throw away since i/o screws up keying */
		break;
	}
}


doslaveout(obuf)
     unsigned char obuf;
{
	static int bad=0;
	int c;
	char ch;
	
	switch (sloutstate) {
	    case SL_START:
	   	bwrite(stdout, &obuf, 1);
	   	if (obuf==0177)
	   		sloutstate=SL_GOT1;
	   	break;
	    case SL_GOT1:
	   	bwrite(stdout, &obuf, 1);
	   	if (obuf=='~')
	   		sloutstate=SL_GOT2;
	   	else
	   		sloutstate=SL_START;
	   	break;
	    case SL_GOT2:
	   	if (obuf==0177)
	   		sloutstate=SL_GOT3;
	   	else {
	   		bwrite(stdout, &obuf, 1);
	   		sloutstate=SL_START;
	   	}
	   	break;
	    case SL_GOT3:
	   	if (obuf=='~')
	   		sloutstate=SL_GOT4;
	   	else {
	   		bwrite(stdout, &obuf, 1);
	   		sloutstate=SL_START;
	   	}
	   	break;
	    case SL_GOT4: /* key size indicator */
	   	bwrite(stdout, &obuf, 1);
	   	if (obuf=='S') {
	   		initslkey(SHORT);
	   		sloutstate=SL_KEYING;
	   	} else if (obuf=='M') {
	   		initslkey(MEDIUM);
	   		sloutstate=SL_KEYING;
	   	} else if (obuf=='L') {
	   		initslkey(LONG);
	   		sloutstate=SL_KEYING;
	   	} else
	   		sloutstate=SL_START;
	   	break;
	    case SL_KEYING:
	   	if (strchr("0123456789abcdef:",obuf) != NULL) {
	   		slkeyin(obuf);
	   	} else {
	   		bwrite(stdout,"U",1);
	   		sloutstate=SL_START;
	   	}
	   	break;
	    case SL_CRYPT:
	   	if (strchr("0123456789abcdef!",obuf) != NULL) {
	   		bad=0;
	   		if ((c = cipherout(obuf))>=0) {
	   			ch=c;
                                if (ch!=filler)
		   			bwrite(stdout,&ch,1);
	   		}
	   	} else if (bad++ > 8) {
	   		fprintf(stderr,"\r\nEncrypted session terminated -");
	   		fprintf(stderr,"\r\npress enter for CLEARTEXT mode: ");
	   		waitenter();
	   		delkey();
	   		sloutstate=SL_START;
	   	} else
	   		ciphstate=0;
	   	break;
	    default:
	   	bwrite(stdout, &obuf, 1);
	   	break;
	}
}



doshell() 
{
	int t;

	/***
	t = open(_PATH_TTY, O_RDWR);
	if (t >= 0) {
		(void) ioctl(t, TIOCNOTTY, (char *)0);
		(void) close(t);
	}
	***/
	getslave();
	(void) close(master);
	(void) dup2(slave, 0);
	(void) dup2(slave, 1);
	(void) dup2(slave, 2);
	(void) close(slave);
	if (cmd==NULL)
		execl(shell, "sh", "-i", 0);
	else
		system(cmd);
	perror(shell);
	fail();
}

rawtty()
{

	struct termios sbuf;

        sbuf = tt;
        sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IXON);
        sbuf.c_oflag &= ~OPOST;
        sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
        sbuf.c_cc[VMIN] = 1;
        sbuf.c_cc[VTIME] = 0;
	(void) tcsetattr(0, TCSAFLUSH, &sbuf);
}

cookedtty()
{
	(void) tcsetattr(0, TCSAFLUSH, &tt);
}

fail()
{

	(void) kill(0, SIGTERM);
	done();
}

done()
{
	cookedtty();
	switch (mode) {
	    case REMOTE:
		if (state==CIPHER)
			printf("XXXXXXXXXXXXXXXX\n");
		printf("lesm remote ");
		break;
	    case LOCAL:
		printf("lesm local ");
		break;
	}
	printf("done\n");
	exit(0);
}

getmaster()
{
	char *pty, *bank, *cp;
	struct stat stb;

	pty = &line[strlen("/dev/ptyp")];
	for (bank = "pqrstuvwxyz"; *bank; bank++) {
		line[strlen("/dev/pty")] = *bank;
		*pty = '0';
		if (stat(line, &stb) < 0)
			break;
		for (cp = "0123456789abcdef"; *cp; cp++) {
			*pty = *cp;
			master = open(line, O_RDWR);
			if (master >= 0) {
				char *tp = &line[strlen("/dev/")];
				int ok;

				/* verify slave side is usable */
				*tp = 't';
				ok = access(line, R_OK|W_OK) == 0;
				*tp = 'p';
				if (ok) {
					(void) tcgetattr(0, &tt);
				    	(void) ioctl(0, TIOCGWINSZ, 
						(char *)&win);
					return;
				}
				(void) close(master);
			}
		}
	}
	fprintf(stderr, "Out of pty's\n");
	fail();
}

getslave()
{

	line[strlen("/dev/")] = 't';
	slave = open(line, O_RDWR);
	if (slave < 0) {
		perror(line);
		fail();
	}
	(void) tcsetattr(slave, TCSAFLUSH, &tt);
	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
	(void) setsid();
	(void) ioctl(slave, TIOCSCTTY, 0);
}

waitenter()
{
	unsigned char b;
	do {
		read(0,&b,1);
	} while (b!='\r');
	printf("\r\n");
}

sendhex(fp,c)
     FILE *fp;
     unsigned int c;
{
	static char buf[16];

	sprintf(buf,"%02x",c);
	bwrite(fp,buf,2);
}