2901 lines · 65.4 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <mp.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include <auth.h>#include <authsrv.h>#include <libsec.h>#include <ip.h>#include "sshtun.h" void stend(Srv *);void server(char *, char *);void stopen(Req *);void stlisten1(void *);void stlisten2(void *);void stread(Req *);void readreqrem(void *);void readdata(void *);void stwrite(Req *);void writectl(void *);void writereqrem(void *);void writedata(void *);void stclunk(Fid *);void stflush(Req *);void filedup(Req *, File *);Conn *alloc_conn(void);SSHChan *alloc_chan(Conn *);int dohandshake(Conn *, char *);void send_kexinit(Conn *);void reader(void *);int validatekex(Conn *, Packet *);int validatekexs(Packet *);int validatekexc(Packet *);int auth_req(Packet *, Conn *);int client_auth(Conn *, Ioproc *);char *factlookup(int, int, char *[]);void shutdown(Conn *); Srv sshtunsrv = {	.open = stopen,	.read = stread,	.write = stwrite,	.flush = stflush,	.destroyfid = stclunk,	.end = stend,}; Cipher *cryptos[] = {	&cipheraes128,	&cipheraes192,	&cipheraes256,//	&cipherblowfish,	&cipher3des,	&cipherrc4,}; Kex *kexes[] = {	&dh1sha1,	&dh14sha1,}; PKA *pkas[3]; #define Ctl(x)	(1+(x)-'A') static uchar stty_cooked[] = {	0x01, 0, 0, 0, 0x7f, 	/* interrupt = DEL */	0x02, 0, 0, 0, Ctl('Q'), 	/* quit */	0x03, 0, 0, 0, Ctl('H'), 	/* backspace */	0x04, 0, 0, 0, Ctl('U'), 	/* line kill */	0x05, 0, 0, 0, Ctl('D'), 	/* EOF */	0x35, 0, 0, 0, 0, 		/* echo */	0x48, 0, 0, 0, 0, 		/* opost */	0, 			/* end of list */}; char *macnames[] = {	"hmac-sha1",}; char *st_names[] = {[Empty]	"Empty",[Allocated]	"Allocated",[Initting]	"Initting",[Listening]	"Listening",[Opening]	"Opening",[Negotiating]	"Negotiating",[Authing]	"Authing",[Established]	"Established",[Eof]	"Eof",[Closing]	"Closing",[Closed]	"Closed",}; File *rootfile, *clonefile, *ctlfile, *keysfile;Conn *connections[MAXCONN];char *mntpt = "/net";int debug;int kflag;int slfd;char uid[32];MBox keymbox;QLock availlck;Rendez availrend; voidusage(void){	fprint(2, "usage: sshtun [-d] [-k] [-m mntpt] [-s srvpt]\n");	exits("usage");} voidthreadmain(int argc, char *argv[]){	char *srvpt = nil;	int fd, n; 	slfd = open("/dev/syslog", OWRITE);	ARGBEGIN {	case '9':		chatty9p = 1;		break;	case 'd':		debug++;		break;	case 'k':		kflag = 1;		break;	case 'm':		mntpt = EARGF(usage());		break;	case 's':		srvpt = EARGF(usage());		break;	default:		usage();		break;	} ARGEND; 	fd = open("/dev/user", OREAD);	if (fd < 0)		strcpy(uid, "none");	else {		n = read(fd, uid, 31);		if (n < 0)			strcpy(uid, "none");		else			uid[n] = '\0';		close(fd);	} 	keymbox.mchan = chancreate(4, 0);	availrend.l = &availlck;	dh_init(pkas); 	if (rfork(RFNOTEG) < 0)		fprint(2, "Failed to set process attributes: %r\n"); 	/* isolate us from any output pipe, e.g. "ssh fred | conswdir" pipeline */	close(0);	close(1); 	server(mntpt, srvpt);} Ioproc *io9p; intread9pmsg(int fd, void *abuf, uint n){	int m, len;	uchar *buf; 	if (io9p == nil)		io9p = ioproc(); 	buf = abuf; 	/* read count */	m = ioreadn(io9p, fd, buf, BIT32SZ);	if(m != BIT32SZ){		if(m < 0)			return -1;		return 0;	} 	len = GBIT32(buf);	if(len <= BIT32SZ || len > n){		werrstr("bad length in 9P2000 message header");		return -1;	}	len -= BIT32SZ;	m = ioreadn(io9p, fd, buf+BIT32SZ, len);	if(m < len)		return 0;	return BIT32SZ+m;} voidstend(Srv *){	closeioproc(io9p);	threadkillgrp(threadgetgrp());} voidserver(char *mntpt, char *srvpt){	Dir d;	char *p;	int fd; 	sshtunsrv.tree = alloctree(uid, uid, 0777, nil);	rootfile = createfile(sshtunsrv.tree->root, "ssh", uid, 0555|DMDIR, (void*)RootFile);	clonefile = createfile(rootfile, "clone", uid, 0666, (void*)CloneFile);	ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)CtlFile);	keysfile = createfile(rootfile, "keys", uid, 0600, (void *)ReqRemFile);	threadpostmountsrv(&sshtunsrv, srvpt, mntpt, MAFTER);	p = smprint("%s/cs", mntpt);	fd = open(p, OWRITE);	free(p);	if (fd >= 0) {		fprint(fd, "add ssh");		close(fd);	}	if (srvpt) {		nulldir(&d);		d.mode = 0666;		p = smprint("/srv/%s", srvpt);		dirwstat(p, &d);		free(p);	}} voidstopen(Req *r){	Conn *c;	SSHChan *sc;	char *p;	int lev, xconn, fnum, fd;	char buf[10]; 	fnum = (uintptr)r->fid->file->aux;	lev = fnum >> LEVSHIFT;	switch (fnum & FileMask) {	case CloneFile:		switch (lev) {		case 0:			p = smprint("%s/tcp/clone", mntpt);			fd = open(p, ORDWR);			free(p);			if (fd < 0) {				responderror(r);				return;			}			c = alloc_conn();			if (c == nil) {				respond(r, "No more connections");				return;			}			c->ctlfd = fd;			filedup(r, c->ctlfile);			if (debug)				fprint(2, "new connection: %d\n", c->id);			break;		case 1:			xconn = (fnum >> CONNSHIFT) & ConnMask;			c = connections[xconn];			if (c == nil) {				respond(r, "Invalid connection");				return;			}			sc = alloc_chan(c);			if (sc == nil) {				respond(r, "No more channels");				return;			}			filedup(r, sc->ctl);			break;		default:			snprint(buf, 10, "bad %d", lev);			readstr(r, buf);			break;		}		respond(r, nil);		break;	case ListenFile:		switch (lev) {		case 1:			r->aux = (void *)threadcreate(stlisten1, r, 8192);			break;		case 2:			r->aux = (void *)threadcreate(stlisten2, r, 8192);			break;		default:			respond(r, "not possible");			break;		}		break;	default:		respond(r, nil);		break;	}} voidstlisten1(void *a){	Req *r;	Conn *c, *cl;	Ioproc *io;	char *msg;	int fnum, xconn, fd, n;	char buf[10], path[40]; 	r = a;	fnum = (uintptr)r->fid->file->aux;	xconn = (fnum >> CONNSHIFT) & ConnMask;	cl = connections[xconn];	if (cl == nil) {		respond(r, "Invalid connection");		threadexits(nil);	}	memset(buf, '\0', sizeof(buf));	io = ioproc();	seek(cl->ctlfd, 0, 0);	if ((n = ioread(io, cl->ctlfd, buf, 10)) <= 0)		fprint(2, "read failed: %r\n");	buf[n] = '\0';	cl->state = Listening;	snprint(path, 40, "%s/tcp/%s/listen", mntpt, buf);	while (1) {		fd = ioopen(io, path, ORDWR);		if (fd < 0) {			r->aux = 0;			responderror(r);			closeioproc(io);			shutdown(cl);			threadexits(nil);		}		c = alloc_conn();		if (c)			break;		n = ioread(io, fd, buf, 10);		if (n <= 0) {			r->aux = 0;			responderror(r);			closeioproc(io);			shutdown(cl);			threadexits(nil);		}		else {			buf[n] = '\0';			msg = smprint("reject %s No available connections", buf);			iowrite(io, fd, msg, strlen(msg));			free(msg);		}		close(fd);	}	c->ctlfd = fd;	filedup(r, c->ctlfile);	if (debug)		fprint(2, "**** responding to listen open ***\n");	r->aux = 0;	respond(r, nil);	closeioproc(io);	threadexits(nil);} voidstlisten2(void *a){	Req *r;	Packet *p2;	Ioproc *io;	Conn *c;	SSHChan *sc;	int i, n, xconn, fnum; 	r = a;	fnum = (uintptr)r->fid->file->aux;	xconn = (fnum >> CONNSHIFT) & ConnMask;	c = connections[xconn];	if (c == nil) {		respond(r, "Invalid connection");		threadexits(nil);	}	if (c->state == Closed || c->state == Closing) {		r->aux = 0;		respond(r, "listen on a closed connection");		threadexits(nil);	}	sc = c->chans[fnum & ConnMask];	qlock(&c->l);	sc->lreq = r;	for (i = 0; i < c->nchan; ++i)		if (c->chans[i] && c->chans[i]->state == Opening && c->chans[i]->ann				&& strcmp(c->chans[i]->ann, sc->ann) == 0)			break;	if (i >= c->nchan) {		sc->state = Listening;		rsleep(&sc->r);		i = sc->waker;		if (i < 0) {			qunlock(&c->l);			r->aux = 0;			responderror(r);			threadexits(nil);		}	}	else		rwakeup(&c->chans[i]->r);	qunlock(&c->l);	if (c->state == Closed || c->state == Closing || c->state == Eof) {		r->aux = 0;		respond(r, "Listen on a closed connection");		threadexits(nil);	}	c->chans[i]->state = Established;	p2 = new_packet(c);	c->chans[i]->rwindow = 32*1024;	add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);	hnputl(p2->payload + 1, c->chans[i]->otherid);	hnputl(p2->payload + 5, c->chans[i]->id);	hnputl(p2->payload + 9, 32*1024);	hnputl(p2->payload + 13, 8192);	p2->rlength = 18;	n = finish_packet(p2);	filedup(r, c->chans[i]->ctl);	io = ioproc();	n = iowrite(io, c->datafd, p2->nlength, n);	free(p2);	closeioproc(io);	if (debug)		fprint(2, "*** Responding to chan listen open ***\n");	r->aux = 0;	if (n < 0)		responderror(r);	else		respond(r, nil);	threadexits(nil);} voidgetdata(Conn *c, SSHChan *sc, Req *r){	Packet *p;	Plist *d;	int n; 	n = r->ifcall.count;	if (sc->dataq->rem < n)		n = sc->dataq->rem;	if (n > 8192)		n = 8192;	r->ifcall.offset = 0;	readbuf(r, sc->dataq->st, n);	sc->dataq->st += n;	sc->dataq->rem -= n;	sc->inrqueue -= n;	if (sc->dataq->rem <= 0) {		d = sc->dataq;		sc->dataq = sc->dataq->next;		if (d->pack->tlength > sc->rwindow)			sc->rwindow = 0;		else			sc->rwindow -= d->pack->tlength;		free(d->pack);		free(d);	}	if (sc->rwindow < 16*1024) {		sc->rwindow += 32*1024;		if (debug)			fprint(2, "Increasing receive window to %lud, inq %lud\n", sc->rwindow, sc->inrqueue);		p = new_packet(c);		add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST);		hnputl(p->payload+1, sc->otherid);		hnputl(p->payload+5, 32*1024);		p->rlength += 8;		n = finish_packet(p);		iowrite(c->dio, c->datafd, p->nlength, n);		free(p);	}	r->aux = 0;	respond(r, nil);} voidstread(Req *r){	Conn *c;	SSHChan *sc;	int fd, n, lev,  cnum, xconn, fnum;	char buf[256], path[40]; 	fnum = (uintptr)r->fid->file->aux;

Lines 1–500 of 2901.