626 lines · 12.1 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. */ /* * ssh - remote login via SSH v2 *	/net/ssh does most of the work; we copy bytes back and forth */#include <u.h>#include <libc.h>#include <auth.h>#include "ssh2.h" int doauth(int, char *);int isatty(int); char *user, *remote;char *netdir = "/net";int debug = 0; static int stripcr = 0;static int mflag = 0;static int iflag = -1;static int nopw = 0, nopka = 0;static int chpid;static int reqfd, dfd1, cfd1, dfd2, cfd2, consfd, kconsfd, cctlfd, notefd, keyfd; voidusage(void){	fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr "		"[cmd [args]]\n", argv0);	exits("usage");} /* * this is probably overkill except writing "kill" to notefd; * file descriptors are closed by the kernel upon exit. */static voidshutdown(void){	if (cctlfd > 0) {		fprint(cctlfd, "rawoff");		close(cctlfd);	}	if (consfd > 0)		close(consfd);	if (reqfd > 0) {		fprint(reqfd, "close");		close(reqfd);	}	close(dfd2);	close(dfd1);	close(cfd2);	close(cfd1); 	fprint(notefd, "kill");	close(notefd);} static voidbail(char *sts){	shutdown();	exits(sts);} inthandler(void *, char *s){	char *nf;	int fd; 	if (strstr(s, "alarm") != nil)		return 0;	if (chpid) {		nf = esmprint("/proc/%d/note", chpid);		fd = open(nf, OWRITE);		fprint(fd, "interrupt");		close(fd);		free(nf);	}	shutdown();	return 1;} static voidparseargs(void){	int n;	char *p, *q; 	q = strchr(remote, '@');	if (q != nil) {		user = remote;		*q++ = 0;		remote = q;	} 	q = strchr(remote, '!');	if (q) {		n = q - remote;		netdir = malloc(n+1);		if (netdir == nil)			sysfatal("out of memory");		strncpy(netdir, remote, n+1);		netdir[n] = '\0'; 		p = strrchr(netdir, '/');		if (p == nil) {			free(netdir);			netdir = "/net";		} else if (strcmp(p+1, "ssh") == 0)			*p = '\0';		else			remote = esmprint("%s/ssh", netdir);	} } static intcatcher(void *, char *s){	return strstr(s, "alarm") != nil;} static inttimedmount(int fd, int afd, char *mntpt, int flag, char *aname){	int oalarm, ret; 	atnotify(catcher, 1);	oalarm = alarm(5*1000);		/* don't get stuck here */	ret = mount(fd, afd, mntpt, flag, aname);	alarm(oalarm);	atnotify(catcher, 0);	return ret;} static voidmounttunnel(char *srv){	int fd; 	if (debug)		fprint(2, "%s: mounting %s on /net\n", argv0, srv);	fd = open(srv, OREAD);	if (fd < 0) {		if (debug)			fprint(2, "%s: can't open %s: %r\n", argv0, srv);	} else if (timedmount(fd, -1, netdir, MBEFORE, "") < 0) {		fprint(2, "can't mount %s on %s: %r\n", srv, netdir);		close(fd);	}} static voidnewtunnel(char *myname){	int kid, pid; 	if(debug)		fprint(2, "%s: starting new netssh for key access\n", argv0);	kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */);	if (kid == 0) {//		for (fd = 3; fd < 40; fd++)//			close(fd);		execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil);		sysfatal("no /bin/netssh: %r");	} else if (kid < 0)		sysfatal("fork failed: %r");	while ((pid = waitpid()) != kid && pid >= 0)		;} static voidstarttunnel(void){	char *keys, *mysrv, *myname; 	keys = esmprint("%s/ssh/keys", netdir);	myname = esmprint("ssh.%s", getuser());	mysrv = esmprint("/srv/%s", myname); 	if (access(keys, ORDWR) < 0)		mounttunnel("/srv/netssh");		/* old name */	if (access(keys, ORDWR) < 0)		mounttunnel("/srv/ssh");	if (access(keys, ORDWR) < 0)		mounttunnel(mysrv);	if (access(keys, ORDWR) < 0)		newtunnel(myname);	if (access(keys, ORDWR) < 0)		mounttunnel(mysrv); 	/* if we *still* can't see our own tunnel, throw a tantrum. */	if (access(keys, ORDWR) < 0)		sysfatal("%s inaccessible: %r", keys);		/* WTF? */ 	free(myname);	free(mysrv);	free(keys);} intcmdmode(void){	int n, m;	char buf[Arbbufsz]; 	for(;;) {reprompt:		print("\n>>> ");		n = 0;		do {			m = read(0, buf + n, sizeof buf - n - 1);			if (m <= 0)				return 1;			write(1, buf + n, m);			n += m;			buf[n] = '\0';			if (buf[n-1] == ('u' & 037))				goto reprompt;		} while (buf[n-1] != '\n' && buf[n-1] != '\r');		switch (buf[0]) {		case '\n':		case '\r':			break;		case 'q':			return 1;		case 'c':			return 0;		case 'r':			stripcr = !stripcr;			return 0;		case 'h':			print("c - continue\n");			print("h - help\n");			print("q - quit\n");			print("r - toggle carriage return stripping\n");			break;		default:			print("unknown command\n");			break;		}	}} static voidkeyprompt(char *buf, int size, int n){	if (*buf == 'c') {		fprint(kconsfd, "The following key has been offered by the server:\n");		write(kconsfd, buf+5, n);		fprint(kconsfd, "\n\n");		fprint(kconsfd, "Add this key? (yes, no, session) ");	} else {		fprint(kconsfd, "The following key does NOT match the known "			"key(s) for the server:\n");		write(kconsfd, buf+5, n);		fprint(kconsfd, "\n\n");		fprint(kconsfd, "Add this key? (yes, no, session, replace) ");	}	n = read(kconsfd, buf, size - 1);	if (n <= 0)		return;	write(keyfd, buf, n);		/* user's response -> /net/ssh/keys */	seek(keyfd, 0, 2);	if (readn(keyfd, buf, 5) <= 0)		return;	buf[5] = 0;	n = strtol(buf+1, nil, 10);	n = readn(keyfd, buf+5, n);	if (n <= 0)		return;	buf[n+5] = 0; 	switch (*buf) {	case 'b':	case 'f':		fprint(kconsfd, "%s\n", buf+5);	case 'o':		close(keyfd);		close(kconsfd);	}} /* talk the undocumented /net/ssh/keys protocol */static voidkeyproc(char *buf, int size){	int n;	char *p; 	if (size < 6)		exits("keyproc buffer too small");	p = esmprint("%s/ssh/keys", netdir);	keyfd = open(p, ORDWR);	if (keyfd < 0) {		chpid = 0;		sysfatal("failed to open ssh keys in %s: %r", p);	} 	kconsfd = open("/dev/cons", ORDWR);	if (kconsfd < 0)		nopw = 1; 	buf[0] = 0;	n = read(keyfd, buf, 5);		/* reading /net/ssh/keys */	if (n < 0)		sysfatal("%s read: %r", p);	buf[5] = 0;	n = strtol(buf+1, nil, 10);	n = readn(keyfd, buf+5, n);	buf[n < 0? 5: n+5] = 0;	free(p); 	switch (*buf) {	case 'f':		if (kconsfd >= 0)			fprint(kconsfd, "%s\n", buf+5);		/* fall through */	case 'o':		close(keyfd);		if (kconsfd >= 0)			close(kconsfd);		break;	default:		if (kconsfd >= 0)			keyprompt(buf, size, n);		else {			fprint(keyfd, "n");			close(keyfd);		}		break;	}	chpid = 0;	exits(nil);} /* * start a subproc to copy from network to stdout * while we copy from stdin to network. */static voidbidircopy(char *buf, int size){	int i, n, lstart;	char *path, *p, *q; 	rfork(RFNOTEG);	path = esmprint("/proc/%d/notepg", getpid());	notefd = open(path, OWRITE); 	switch (rfork(RFPROC|RFMEM|RFNOWAIT)) {	case 0:		while ((n = read(dfd2, buf, size - 1)) > 0) {			if (!stripcr)				p = buf + n;			else				for (i = 0, p = buf, q = buf; i < n; ++i, ++q)					if (*q != '\r')						*p++ = *q;			if (p != buf)				write(1, buf, p-buf);		}		/*		 * don't bother; it will be obvious when the user's prompt		 * changes.		 *		 * fprint(2, "%s: Connection closed by server\n", argv0);		 */		break;	default:		lstart = 1;		while ((n = read(0, buf, size - 1)) > 0) {			if (!mflag && lstart && buf[0] == 0x1c)				if (cmdmode())					break;				else					continue;			lstart = (buf[n-1] == '\n' || buf[n-1] == '\r');			write(dfd2, buf, n);		}		/*		 * don't bother; it will be obvious when the user's prompt		 * changes.		 *		 * fprint(2, "%s: EOF on client side\n", argv0);		 */		break;	case -1:		fprint(2, "%s: fork error: %r\n", argv0);		break;	} 	bail(nil);} static intconnect(char *buf, int size){	int nfd, n;	char *dir, *ds, *nf; 	dir = esmprint("%s/ssh", netdir);	ds = netmkaddr(remote, dir, "22");		/* tcp port 22 is ssh */	free(dir); 	dfd1 = dial(ds, nil, nil, &cfd1);	if (dfd1 < 0) {		fprint(2, "%s: dial conn %s: %r\n", argv0, ds);		if (chpid) {			nf = esmprint("/proc/%d/note", chpid);			nfd = open(nf, OWRITE);			fprint(nfd, "interrupt");			close(nfd);		}		exits("can't dial");	} 	seek(cfd1, 0, 0);	n = read(cfd1, buf, size - 1);	buf[n >= 0? n: 0] = 0;	return atoi(buf);} static intchanconnect(int conn, char *buf, int size){	int n;	char *path; 	path = esmprint("%s/ssh/%d!session", netdir, conn);	dfd2 = dial(path, nil, nil, &cfd2);	if (dfd2 < 0) {		fprint(2, "%s: dial chan %s: %r\n", argv0, path);		bail("dial");	}	free(path); 	n = read(cfd2, buf, size - 1);	buf[n >= 0? n: 0] = 0;	return atoi(buf);} static voidremotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size){	int i;	char *path, *q, *ep; 	path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan);	reqfd = open(path, OWRITE);	if (reqfd < 0)		bail("can't open request chan");	if (argc == 0)		if (readfile("/env/TERM", buf, size) < 0)			fprint(reqfd, "shell");		else			fprint(reqfd, "shell %s", buf);	else {		assert(size >= Bigbufsz);		ep = buf + Bigbufsz;		q = seprint(buf, ep, "exec");		for (i = 0; i < argc; ++i)			q = seprint(q, ep, " %q", argv[i]);		if (q >= ep) {			fprint(2, "%s: command too long\n", argv0);			fprint(reqfd, "close");			bail("cmd too long");		}		write(reqfd, buf, q - buf);	}} voidmain(int argc, char *argv[]){	char *whichkey;	int conn, chan, n;	char buf[Copybufsz]; 	quotefmtinstall();	reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd =		notefd = keyfd = -1;	whichkey = nil;	ARGBEGIN {	case 'A':			/* auth protos */	case 'c':			/* ciphers */		fprint(2, "%s: sorry, -%c is not supported\n", argv0, ARGC());		break;	case 'a':			/* compat? */	case 'C':			/* cooked mode */

Lines 1–500 of 626.