3229 lines · 73.2 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* *  /net/ssh */#include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include <mp.h>#include <auth.h>#include <authsrv.h>#include <libsec.h>#include <ip.h>#include "netssh.h" extern int nokeyverify; void stclunk(Fid *);void stend(Srv *);void stflush(Req *);void stopen(Req *);void stread(Req *);void stwrite(Req *); Srv netsshsrv = {	.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]; 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",}; int debug;int kflag;char *mntpt = "/net";char uid[32];Conn *connections[MAXCONN];File *rootfile, *clonefile, *ctlfile, *keysfile;Ioproc *io9p;MBox keymbox;QLock availlck;Rendez availrend; SSHChan *alloc_chan(Conn *);Conn *alloc_conn(void);int auth_req(Packet *, Conn *);int client_auth(Conn *, Ioproc *);int dohandshake(Conn *, char *);char *factlookup(int, int, char *[]);void filedup(Req *, File *);void readdata(void *);void reader(void *);void readreqrem(void *);void send_kexinit(Conn *);void server(char *, char *);void shutdown(Conn *);void stlisconn(void *);void stlischan(void *);int validatekex(Conn *, Packet *);int validatekexc(Packet *);int validatekexs(Packet *);void writectlproc(void *);void writedataproc(void *);void writereqremproc(void *); static int deferredinit(Conn *c); static voidsshlogint(Conn *c, char *file, char *p){	char *role, *id; 	if (c == nil)		role = "";	else if (c->role == Server)		role = "server ";	else		role = "client ";	if (c == nil)		id = strdup("");	else if (c->user || c->remote)		id = smprint("user %s@%s id %d ", c->user, c->remote, c->id);	else		id = smprint("id %d ", c->id); 	syslog(0, file, "%s: %s%s%s", argv0, role, id, p);	free(id);} voidsshlog(Conn *c, char *fmt, ...){	va_list args;	char *p; 	/* do this first in case fmt contains "%r" */	va_start(args, fmt);	p = vsmprint(fmt, args);	va_end(args); 	sshlogint(c, "ssh", p);	sshlogint(c, "sshdebug", p);	/* log in both places */	free(p);} voidsshdebug(Conn *c, char *fmt, ...){	va_list args;	char *p; 	if (!debug)		return; 	/* do this first in case fmt contains "%r" */	va_start(args, fmt);	p = vsmprint(fmt, args);	va_end(args); 	sshlogint(c, "sshdebug", p);	free(p);} voidusage(void){	fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0);	exits("usage");} voidthreadmain(int argc, char *argv[]){	char *p, *srvpt = nil; 	quotefmtinstall();	threadsetname("main");	nokeyverify = 1;	/* temporary until verification is fixed */	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;	case 'v':		nokeyverify = 1;		break;	case 'V':		nokeyverify = 0;		break;	default:		usage();		break;	} ARGEND; 	p = getenv("nosshkeyverify");	if (p && p[0] != '\0')		nokeyverify = 1;	free(p); 	if (readfile("/dev/user", uid, sizeof uid) <= 0)		strcpy(uid, "none"); 	keymbox.mchan = chancreate(4, 0);	availrend.l = &availlck;	dh_init(pkas); 	/* become a daemon */	if (rfork(RFNOTEG) < 0)		fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0);	server(mntpt, srvpt);	threadexits(nil);} intreadio(Ioproc *io, int fd, void *buf, int n){	if (io)		return ioread(io, fd, buf, n);	else		return read(fd, buf, n);} intwriteio(Ioproc *io, int fd, void *buf, int n){	if (io)		return iowrite(io, fd, buf, n);	else		return write(fd, buf, n);} 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; 	netsshsrv.tree = alloctree(uid, uid, 0777, nil);	rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,		(void*)Qroot);	clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);	ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);	keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); 	/*	 * needs to be MBEFORE in case there are previous, now defunct,	 * netssh processes mounted in mntpt.	 */	threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE); 	p = esmprint("%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 = esmprint("/srv/%s", srvpt);		dirwstat(p, &d);		free(p);	}	sshdebug(nil, "server started for %s", getuser());} static voidrespexit(Conn *c, Req *r, void *freeme, char *msg){	if (msg)		sshdebug(c, "%s", msg);	r->aux = 0;	respond(r, msg);	free(freeme);	threadexits(nil);	/* maybe use msg here */} voidstopen(Req *r){	int lev, xconn, fd;	uvlong qidpath;	char *p;	char buf[32];	Conn *c;	SSHChan *sc; 	qidpath = (uvlong)r->fid->file->aux;	lev = qidpath >> Levshift;	switch ((ulong)(qidpath & Qtypemask)) {	default:		respond(r, nil);		break;	case Qlisten:		r->aux = (void *)threadcreate((lev == Connection?			stlisconn: stlischan), r, Defstk);		break;	case Qclone:		switch (lev) {		case Top:			/* should use dial(2) instead of diddling /net/tcp */			p = esmprint("%s/tcp/clone", mntpt);			fd = open(p, ORDWR);			if (fd < 0) {				sshdebug(nil, "stopen: open %s failed: %r", p);				free(p);				responderror(r);				return;			}			free(p); 			c = alloc_conn();			if (c == nil) {				close(fd);				respond(r, "no more connections");				return;			}			c->ctlfd = fd;			c->poisoned = 0;			filedup(r, c->ctlfile);			sshlog(c, "new connection on fd %d", fd);			break;		case Connection:			xconn = (qidpath >> Connshift) & Connmask;			c = connections[xconn];			if (c == nil) {				respond(r, "bad connection");				return;			}			sc = alloc_chan(c);			if (sc == nil) {				respond(r, "no more channels");				return;			}			filedup(r, sc->ctl);			break;		default:			snprint(buf, sizeof buf, "bad level %d", lev);			readstr(r, buf);			break;		}		respond(r, nil);		break;	}} static voidlisterrexit(Req *r, Ioproc *io, Conn *cl){	r->aux = 0;	responderror(r);	closeioproc(io);	shutdown(cl);	threadexits(nil);} voidstlisconn(void *a){	int xconn, fd, n;	uvlong qidpath;	char *msg;	char buf[Numbsz], path[NETPATHLEN];	Conn *c, *cl;	Ioproc *io;	Req *r; 	threadsetname("stlisconn");	r = a;	qidpath = (uvlong)r->fid->file->aux;	xconn = (qidpath >> Connshift) & Connmask; 	cl = connections[xconn];	if (cl == nil) {		sshlog(cl, "bad connection");		respond(r, "bad connection");		threadexits("bad connection");	}	if (cl->poisoned) {		sshdebug(cl, "stlisconn conn %d poisoned", xconn);		r->aux = 0;		respond(r, "top level listen conn poisoned");		threadexits("top level listen conn poisoned");	}	if (cl->ctlfd < 0) {		sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn);		r->aux = 0;		respond(r, "top level listen with closed fd");		shutdown(cl);		cl->poisoned = 1;	/* no more use until ctlfd is set */		threadexits("top level listen with closed fd");	} 	io = ioproc(); 	/* read xconn's tcp conn's ctl file */	seek(cl->ctlfd, 0, 0);	n = ioread(io, cl->ctlfd, buf, sizeof buf - 1);	if (n == 0) {		sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd);		listerrexit(r, io, cl);	} else if (n < 0) {		sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd);		listerrexit(r, io, cl);	}	buf[n] = '\0'; 	cl->state = Listening;	/* should use dial(2) instead of diddling /net/tcp */	snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf);	for(;;) {		fd = ioopen(io, path, ORDWR);		if (fd < 0) 			listerrexit(r, io, cl);		c = alloc_conn();		if (c)			break;		n = ioread(io, fd, buf, sizeof buf - 1);		if (n <= 0)			listerrexit(r, io, cl);		buf[n] = '\0';		msg = smprint("reject %s no available connections", buf);		iowrite(io, fd, msg, strlen(msg));		free(msg);		close(fd);			/* surely ioclose? */	}	c->ctlfd = fd;	if (c->ctlfd < 0) {		sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn);		threadexitsall("stlisconn c->ctlfd < 0");	}	c->poisoned = 0;	c->stifle = 1;			/* defer server; was for coexistence */	filedup(r, c->ctlfile);	sshdebug(c, "responding to listen open");	r->aux = 0;	respond(r, nil);	closeioproc(io);	threadexits(nil);} voidstlischan(void *a){	Req *r;

Lines 1–500 of 3229.