xref: /plan9/sys/src/cmd/ssh2/sshsession.c (revision 24d7e15e5e21820296ecc4fa071edfc938789ae0)
163afb9a5SDavid du Colombier /*
263afb9a5SDavid du Colombier  * ssh server - serve SSH protocol v2
363afb9a5SDavid du Colombier  *	/net/ssh does most of the work; we copy bytes back and forth
463afb9a5SDavid du Colombier  */
563afb9a5SDavid du Colombier #include <u.h>
663afb9a5SDavid du Colombier #include <libc.h>
763afb9a5SDavid du Colombier #include <ip.h>
863afb9a5SDavid du Colombier #include <auth.h>
963afb9a5SDavid du Colombier #include "ssh2.h"
1063afb9a5SDavid du Colombier 
1163afb9a5SDavid du Colombier char *confine(char *, char *);
1263afb9a5SDavid du Colombier char *get_string(char *, char *);
1363afb9a5SDavid du Colombier void newchannel(int, char *, int);
1463afb9a5SDavid du Colombier void runcmd(int, int, char *, char *, char *, char *);
1563afb9a5SDavid du Colombier 
1663afb9a5SDavid du Colombier int errfd, toppid, sflag, tflag, prevent;
1763afb9a5SDavid du Colombier int debug;
1863afb9a5SDavid du Colombier char *idstring;
19*24d7e15eSDavid du Colombier char *netdir;				/* /net/ssh/<conn> */
2063afb9a5SDavid du Colombier char *nsfile = nil;
2163afb9a5SDavid du Colombier char *restdir;
2263afb9a5SDavid du Colombier char *shell;
2363afb9a5SDavid du Colombier char *srvpt;
2463afb9a5SDavid du Colombier char *uname;
2563afb9a5SDavid du Colombier 
2663afb9a5SDavid du Colombier void
usage(void)2763afb9a5SDavid du Colombier usage(void)
2863afb9a5SDavid du Colombier {
2963afb9a5SDavid du Colombier 	fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
3063afb9a5SDavid du Colombier 		"[-n ns] [-t] [netdir]\n", argv0);
3163afb9a5SDavid du Colombier 	exits("usage");
3263afb9a5SDavid du Colombier }
3363afb9a5SDavid du Colombier 
3463afb9a5SDavid du Colombier static int
getctlfd(void)3563afb9a5SDavid du Colombier getctlfd(void)
3663afb9a5SDavid du Colombier {
3763afb9a5SDavid du Colombier 	int ctlfd;
3863afb9a5SDavid du Colombier 	char *name;
3963afb9a5SDavid du Colombier 
4063afb9a5SDavid du Colombier 	name = smprint("%s/clone", netdir);
4163afb9a5SDavid du Colombier 	ctlfd = -1;
4263afb9a5SDavid du Colombier 	if (name)
4363afb9a5SDavid du Colombier 		ctlfd = open(name, ORDWR);
4463afb9a5SDavid du Colombier 	if (ctlfd < 0) {
4563afb9a5SDavid du Colombier 		syslog(0, "ssh", "server can't clone: %s: %r", name);
4663afb9a5SDavid du Colombier 		exits("open clone");
4763afb9a5SDavid du Colombier 	}
4863afb9a5SDavid du Colombier 	free(name);
4963afb9a5SDavid du Colombier 	return ctlfd;
5063afb9a5SDavid du Colombier }
5163afb9a5SDavid du Colombier 
5263afb9a5SDavid du Colombier static int
getdatafd(int ctlfd)5363afb9a5SDavid du Colombier getdatafd(int ctlfd)
5463afb9a5SDavid du Colombier {
5563afb9a5SDavid du Colombier 	int fd;
5663afb9a5SDavid du Colombier 	char *name;
5763afb9a5SDavid du Colombier 
5863afb9a5SDavid du Colombier 	name = smprint("%s/data", netdir);
5963afb9a5SDavid du Colombier 	fd = -1;
6063afb9a5SDavid du Colombier 	if (name)
6163afb9a5SDavid du Colombier 		fd = open(name, OREAD);
6263afb9a5SDavid du Colombier 	if (fd < 0) {
6363afb9a5SDavid du Colombier 		syslog(0, "ssh", "can't open %s: %r", name);
6463afb9a5SDavid du Colombier 		hangup(ctlfd);
6563afb9a5SDavid du Colombier 		exits("open data");
6663afb9a5SDavid du Colombier 	}
6763afb9a5SDavid du Colombier 	free(name);
6863afb9a5SDavid du Colombier 	return fd;
6963afb9a5SDavid du Colombier }
7063afb9a5SDavid du Colombier 
7163afb9a5SDavid du Colombier static void
auth(char * buf,int n,int ctlfd)7263afb9a5SDavid du Colombier auth(char *buf, int n, int ctlfd)
7363afb9a5SDavid du Colombier {
7463afb9a5SDavid du Colombier 	int fd;
7563afb9a5SDavid du Colombier 
7663afb9a5SDavid du Colombier 	fd = open("#¤/capuse", OWRITE);
7763afb9a5SDavid du Colombier 	if (fd < 0) {
7863afb9a5SDavid du Colombier 		syslog(0, "ssh", "server can't open capuse: %r");
7963afb9a5SDavid du Colombier 		hangup(ctlfd);
8063afb9a5SDavid du Colombier 		exits("capuse");
8163afb9a5SDavid du Colombier 	}
8263afb9a5SDavid du Colombier 	if (write(fd, buf, n) != n) {
8363afb9a5SDavid du Colombier 		syslog(0, "ssh", "server write `%.*s' to capuse failed: %r",
8463afb9a5SDavid du Colombier 			n, buf);
8563afb9a5SDavid du Colombier 		hangup(ctlfd);
8663afb9a5SDavid du Colombier 		exits("capuse");
8763afb9a5SDavid du Colombier 	}
8863afb9a5SDavid du Colombier 	close(fd);
8963afb9a5SDavid du Colombier }
9063afb9a5SDavid du Colombier 
9163afb9a5SDavid du Colombier /*
92*24d7e15eSDavid du Colombier  * mount tunnel if there isn't one visible.
9363afb9a5SDavid du Colombier  */
9463afb9a5SDavid du Colombier static void
mounttunnel(int ctlfd)9563afb9a5SDavid du Colombier mounttunnel(int ctlfd)
9663afb9a5SDavid du Colombier {
9763afb9a5SDavid du Colombier 	int fd;
98*24d7e15eSDavid du Colombier 	char *p, *np, *q;
9963afb9a5SDavid du Colombier 
10063afb9a5SDavid du Colombier 	if (access(netdir, AEXIST) >= 0)
10163afb9a5SDavid du Colombier 		return;
10263afb9a5SDavid du Colombier 
10363afb9a5SDavid du Colombier 	p = smprint("/srv/%s", srvpt? srvpt: "ssh");
104*24d7e15eSDavid du Colombier 	np = strdup(netdir);
105*24d7e15eSDavid du Colombier 	if (p == nil || np == nil)
106*24d7e15eSDavid du Colombier 		sysfatal("out of memory");
107*24d7e15eSDavid du Colombier 	q = strstr(np, "/ssh");
108*24d7e15eSDavid du Colombier 	if (q != nil)
109*24d7e15eSDavid du Colombier 		*q = '\0';
11063afb9a5SDavid du Colombier 	fd = open(p, ORDWR);
11163afb9a5SDavid du Colombier 	if (fd < 0) {
11263afb9a5SDavid du Colombier 		syslog(0, "ssh", "can't open %s: %r", p);
11363afb9a5SDavid du Colombier 		hangup(ctlfd);
11463afb9a5SDavid du Colombier 		exits("open");
11563afb9a5SDavid du Colombier 	}
116*24d7e15eSDavid du Colombier 	if (mount(fd, -1, np, MBEFORE, "") < 0) {
117*24d7e15eSDavid du Colombier 		syslog(0, "ssh", "can't mount %s in %s: %r", p, np);
11863afb9a5SDavid du Colombier 		hangup(ctlfd);
11963afb9a5SDavid du Colombier 		exits("can't mount");
12063afb9a5SDavid du Colombier 	}
121*24d7e15eSDavid du Colombier 	free(p);
122*24d7e15eSDavid du Colombier 	free(np);
12363afb9a5SDavid du Colombier }
12463afb9a5SDavid du Colombier 
12563afb9a5SDavid du Colombier static int
authnewns(int ctlfd,char * buf,int size,int n)12663afb9a5SDavid du Colombier authnewns(int ctlfd, char *buf, int size, int n)
12763afb9a5SDavid du Colombier {
12863afb9a5SDavid du Colombier 	char *p, *q;
12963afb9a5SDavid du Colombier 
13063afb9a5SDavid du Colombier 	USED(size);
13163afb9a5SDavid du Colombier 	if (n <= 0)
13263afb9a5SDavid du Colombier 		return 0;
13363afb9a5SDavid du Colombier 	buf[n] = '\0';
13463afb9a5SDavid du Colombier 	if (strcmp(buf, "n/a") == 0)
13563afb9a5SDavid du Colombier 		return 0;
13663afb9a5SDavid du Colombier 
13763afb9a5SDavid du Colombier 	auth(buf, n, ctlfd);
13863afb9a5SDavid du Colombier 
13963afb9a5SDavid du Colombier 	p = strchr(buf, '@');
14063afb9a5SDavid du Colombier 	if (p == nil)
14163afb9a5SDavid du Colombier 		return 0;
14263afb9a5SDavid du Colombier 	++p;
14363afb9a5SDavid du Colombier 	q = strchr(p, '@');
14463afb9a5SDavid du Colombier 	if (q) {
14563afb9a5SDavid du Colombier 		*q = '\0';
14663afb9a5SDavid du Colombier 		uname = strdup(p);
14763afb9a5SDavid du Colombier 	}
14863afb9a5SDavid du Colombier 	if (!tflag && newns(p, nsfile) < 0) {
14963afb9a5SDavid du Colombier 		syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile);
15063afb9a5SDavid du Colombier 		return -1;
15163afb9a5SDavid du Colombier 	}
15263afb9a5SDavid du Colombier 	return 0;
15363afb9a5SDavid du Colombier }
15463afb9a5SDavid du Colombier 
15563afb9a5SDavid du Colombier static void
listenloop(char * listfile,int ctlfd,char * buf,int size)15663afb9a5SDavid du Colombier listenloop(char *listfile, int ctlfd, char *buf, int size)
15763afb9a5SDavid du Colombier {
15863afb9a5SDavid du Colombier 	int fd, n;
15963afb9a5SDavid du Colombier 
16063afb9a5SDavid du Colombier 	while ((fd = open(listfile, ORDWR)) >= 0) {
16163afb9a5SDavid du Colombier 		n = read(fd, buf, size - 1);
16263afb9a5SDavid du Colombier 		fprint(errfd, "read from listen file returned %d\n", n);
16363afb9a5SDavid du Colombier 		if (n <= 0) {
16463afb9a5SDavid du Colombier 			syslog(0, "ssh", "read on listen failed: %r");
16563afb9a5SDavid du Colombier 			break;
16663afb9a5SDavid du Colombier 		}
16763afb9a5SDavid du Colombier 		buf[n >= 0? n: 0] = '\0';
16863afb9a5SDavid du Colombier 		fprint(errfd, "read %s\n", buf);
16963afb9a5SDavid du Colombier 
17063afb9a5SDavid du Colombier 		switch (fork()) {
17163afb9a5SDavid du Colombier 		case 0:					/* child */
17263afb9a5SDavid du Colombier 			close(ctlfd);
17363afb9a5SDavid du Colombier 			newchannel(fd, netdir, atoi(buf));  /* never returns */
17463afb9a5SDavid du Colombier 		case -1:
17563afb9a5SDavid du Colombier 			syslog(0, "ssh", "fork failed: %r");
17663afb9a5SDavid du Colombier 			hangup(ctlfd);
17763afb9a5SDavid du Colombier 			exits("fork");
17863afb9a5SDavid du Colombier 		}
17963afb9a5SDavid du Colombier 		close(fd);
18063afb9a5SDavid du Colombier 	}
18163afb9a5SDavid du Colombier 	if (fd < 0)
18263afb9a5SDavid du Colombier 		syslog(0, "ssh", "listen failed: %r");
18363afb9a5SDavid du Colombier }
18463afb9a5SDavid du Colombier 
18563afb9a5SDavid du Colombier void
main(int argc,char * argv[])18663afb9a5SDavid du Colombier main(int argc, char *argv[])
18763afb9a5SDavid du Colombier {
18863afb9a5SDavid du Colombier 	char *listfile;
18963afb9a5SDavid du Colombier 	int ctlfd, fd, n;
19063afb9a5SDavid du Colombier 	char buf[Arbpathlen], path[Arbpathlen];
19163afb9a5SDavid du Colombier 
19263afb9a5SDavid du Colombier 	rfork(RFNOTEG);
19363afb9a5SDavid du Colombier 	toppid = getpid();
19463afb9a5SDavid du Colombier 	shell = "/bin/rc -il";
19563afb9a5SDavid du Colombier 	ARGBEGIN {
19663afb9a5SDavid du Colombier 	case 'd':
19763afb9a5SDavid du Colombier 		debug++;
19863afb9a5SDavid du Colombier 		break;
19963afb9a5SDavid du Colombier 	case 'i':
20063afb9a5SDavid du Colombier 		idstring = EARGF(usage());
20163afb9a5SDavid du Colombier 		break;
20263afb9a5SDavid du Colombier 	case 'n':
20363afb9a5SDavid du Colombier 		nsfile = EARGF(usage());
20463afb9a5SDavid du Colombier 		break;
20563afb9a5SDavid du Colombier 	case 'R':
20663afb9a5SDavid du Colombier 		prevent = 1;
20763afb9a5SDavid du Colombier 		/* fall through */
20863afb9a5SDavid du Colombier 	case 'r':
20963afb9a5SDavid du Colombier 		restdir = EARGF(usage());
21063afb9a5SDavid du Colombier 		break;
21163afb9a5SDavid du Colombier 	case 's':
21263afb9a5SDavid du Colombier 		sflag = 1;
21363afb9a5SDavid du Colombier 		shell = EARGF(usage());
21463afb9a5SDavid du Colombier 		break;
21563afb9a5SDavid du Colombier 	case 'S':
21663afb9a5SDavid du Colombier 		srvpt = EARGF(usage());
21763afb9a5SDavid du Colombier 		break;
21863afb9a5SDavid du Colombier 	case 't':
21963afb9a5SDavid du Colombier 		tflag = 1;
22063afb9a5SDavid du Colombier 		break;
22163afb9a5SDavid du Colombier 	default:
22263afb9a5SDavid du Colombier 		usage();
22363afb9a5SDavid du Colombier 		break;
22463afb9a5SDavid du Colombier 	} ARGEND;
22563afb9a5SDavid du Colombier 
22663afb9a5SDavid du Colombier 	errfd = -1;
22763afb9a5SDavid du Colombier 	if (debug)
22863afb9a5SDavid du Colombier 		errfd = 2;
22963afb9a5SDavid du Colombier 
23063afb9a5SDavid du Colombier 	/* work out network connection's directory */
23163afb9a5SDavid du Colombier 	if (argc >= 1)
23263afb9a5SDavid du Colombier 		netdir = argv[0];
23363afb9a5SDavid du Colombier 	else				/* invoked by listen1 */
23463afb9a5SDavid du Colombier 		netdir = getenv("net");
23563afb9a5SDavid du Colombier 	if (netdir == nil) {
23663afb9a5SDavid du Colombier 		syslog(0, "ssh", "server netdir is nil");
23763afb9a5SDavid du Colombier 		exits("nil netdir");
23863afb9a5SDavid du Colombier 	}
23963afb9a5SDavid du Colombier 	syslog(0, "ssh", "server netdir is %s", netdir);
24063afb9a5SDavid du Colombier 
24163afb9a5SDavid du Colombier 	uname = getenv("user");
24263afb9a5SDavid du Colombier 	if (uname == nil)
24363afb9a5SDavid du Colombier 		uname = "none";
24463afb9a5SDavid du Colombier 
24563afb9a5SDavid du Colombier 	/* extract dfd and cfd from netdir */
24663afb9a5SDavid du Colombier 	ctlfd = getctlfd();
24763afb9a5SDavid du Colombier 	fd = getdatafd(ctlfd);
24863afb9a5SDavid du Colombier 
24963afb9a5SDavid du Colombier 	n = read(fd, buf, sizeof buf - 1);
25063afb9a5SDavid du Colombier 	if (n < 0) {
25163afb9a5SDavid du Colombier 		syslog(0, "ssh", "server read error for data file: %r");
25263afb9a5SDavid du Colombier 		hangup(ctlfd);
25363afb9a5SDavid du Colombier 		exits("read cap");
25463afb9a5SDavid du Colombier 	}
25563afb9a5SDavid du Colombier 	close(fd);
25663afb9a5SDavid du Colombier 	authnewns(ctlfd, buf, sizeof buf, n);
25763afb9a5SDavid du Colombier 
25863afb9a5SDavid du Colombier 	/* get connection number in buf */
25963afb9a5SDavid du Colombier 	n = read(ctlfd, buf, sizeof buf - 1);
26063afb9a5SDavid du Colombier 	buf[n >= 0? n: 0] = '\0';
26163afb9a5SDavid du Colombier 
26263afb9a5SDavid du Colombier 	/* tell netssh our id string */
26363afb9a5SDavid du Colombier 	fd2path(ctlfd, path, sizeof path);
26463afb9a5SDavid du Colombier 	if (0 && idstring) {			/* was for coexistence */
26563afb9a5SDavid du Colombier 		syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s",
26663afb9a5SDavid du Colombier 			buf, idstring, path);
26763afb9a5SDavid du Colombier 		fprint(ctlfd, "id %s", idstring);
26863afb9a5SDavid du Colombier 	}
26963afb9a5SDavid du Colombier 
27063afb9a5SDavid du Colombier 	/* announce */
27163afb9a5SDavid du Colombier 	fprint(ctlfd, "announce session");
27263afb9a5SDavid du Colombier 
27363afb9a5SDavid du Colombier 	/* construct listen file name */
27463afb9a5SDavid du Colombier 	listfile = smprint("%s/%s/listen", netdir, buf);
27563afb9a5SDavid du Colombier 	if (listfile == nil) {
27663afb9a5SDavid du Colombier 		syslog(0, "ssh", "out of memory");
27763afb9a5SDavid du Colombier 		exits("out of memory");
27863afb9a5SDavid du Colombier 	}
27963afb9a5SDavid du Colombier 	syslog(0, "ssh", "server listen is %s", listfile);
28063afb9a5SDavid du Colombier 
28163afb9a5SDavid du Colombier 	mounttunnel(ctlfd);
28263afb9a5SDavid du Colombier 	listenloop(listfile, ctlfd, buf, sizeof buf);
28363afb9a5SDavid du Colombier 	hangup(ctlfd);
28463afb9a5SDavid du Colombier 	exits(nil);
28563afb9a5SDavid du Colombier }
28663afb9a5SDavid du Colombier 
28763afb9a5SDavid du Colombier /* an abbreviation.  note the assumed variables. */
28863afb9a5SDavid du Colombier #define REPLY(s)	if (want_reply) fprint(reqfd, s);
28963afb9a5SDavid du Colombier 
29063afb9a5SDavid du Colombier static void
forkshell(char * cmd,int reqfd,int datafd,int want_reply)29163afb9a5SDavid du Colombier forkshell(char *cmd, int reqfd, int datafd, int want_reply)
29263afb9a5SDavid du Colombier {
29363afb9a5SDavid du Colombier 	switch (fork()) {
29463afb9a5SDavid du Colombier 	case 0:
29563afb9a5SDavid du Colombier 		if (sflag)
29663afb9a5SDavid du Colombier 			snprint(cmd, sizeof cmd, "-s%s", shell);
29763afb9a5SDavid du Colombier 		else
29863afb9a5SDavid du Colombier 			cmd[0] = '\0';
29963afb9a5SDavid du Colombier 		USED(cmd);
30063afb9a5SDavid du Colombier 		syslog(0, "ssh", "server starting ssh shell for %s", uname);
30163afb9a5SDavid du Colombier 		/* runcmd doesn't return */
30263afb9a5SDavid du Colombier 		runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil);
30363afb9a5SDavid du Colombier 	case -1:
30463afb9a5SDavid du Colombier 		REPLY("failure");
30563afb9a5SDavid du Colombier 		syslog(0, "ssh", "server can't fork: %r");
30663afb9a5SDavid du Colombier 		exits("fork");
30763afb9a5SDavid du Colombier 	}
30863afb9a5SDavid du Colombier }
30963afb9a5SDavid du Colombier 
31063afb9a5SDavid du Colombier static void
forkcmd(char * cmd,char * p,int reqfd,int datafd,int want_reply)31163afb9a5SDavid du Colombier forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply)
31263afb9a5SDavid du Colombier {
31363afb9a5SDavid du Colombier 	char *q;
31463afb9a5SDavid du Colombier 
31563afb9a5SDavid du Colombier 	switch (fork()) {
31663afb9a5SDavid du Colombier 	case 0:
31763afb9a5SDavid du Colombier 		if (restdir && chdir(restdir) < 0) {
31863afb9a5SDavid du Colombier 			syslog(0, "ssh", "can't chdir(%s): %r", restdir);
31963afb9a5SDavid du Colombier 			exits("can't chdir");
32063afb9a5SDavid du Colombier 		}
32163afb9a5SDavid du Colombier 		if (!prevent || (q = getenv("sshsession")) &&
32263afb9a5SDavid du Colombier 		    strcmp(q, "allow") == 0)
32363afb9a5SDavid du Colombier 			get_string(p+1, cmd);
32463afb9a5SDavid du Colombier 		else
32563afb9a5SDavid du Colombier 			confine(p+1, cmd);
32663afb9a5SDavid du Colombier 		syslog(0, "ssh", "server running `%s' for %s", cmd, uname);
32763afb9a5SDavid du Colombier 		/* runcmd doesn't return */
32863afb9a5SDavid du Colombier 		runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd);
32963afb9a5SDavid du Colombier 	case -1:
33063afb9a5SDavid du Colombier 		REPLY("failure");
33163afb9a5SDavid du Colombier 		syslog(0, "ssh", "server can't fork: %r");
33263afb9a5SDavid du Colombier 		exits("fork");
33363afb9a5SDavid du Colombier 	}
33463afb9a5SDavid du Colombier }
33563afb9a5SDavid du Colombier 
33663afb9a5SDavid du Colombier void
newchannel(int fd,char * conndir,int channum)33763afb9a5SDavid du Colombier newchannel(int fd, char *conndir, int channum)
33863afb9a5SDavid du Colombier {
33963afb9a5SDavid du Colombier 	char *p, *reqfile, *datafile;
34063afb9a5SDavid du Colombier 	int n, reqfd, datafd, want_reply, already_done;
34163afb9a5SDavid du Colombier 	char buf[Maxpayload], cmd[Bigbufsz];
34263afb9a5SDavid du Colombier 
34363afb9a5SDavid du Colombier 	close(fd);
34463afb9a5SDavid du Colombier 
34563afb9a5SDavid du Colombier 	already_done = 0;
34663afb9a5SDavid du Colombier 	reqfile = smprint("%s/%d/request", conndir, channum);
34763afb9a5SDavid du Colombier 	if (reqfile == nil)
34863afb9a5SDavid du Colombier 		sysfatal("out of memory");
34963afb9a5SDavid du Colombier 	reqfd = open(reqfile, ORDWR);
35063afb9a5SDavid du Colombier 	if (reqfd < 0) {
35163afb9a5SDavid du Colombier 		syslog(0, "ssh", "can't open request file %s: %r", reqfile);
35263afb9a5SDavid du Colombier 		exits("net");
35363afb9a5SDavid du Colombier 	}
35463afb9a5SDavid du Colombier 	datafile = smprint("%s/%d/data", conndir, channum);
35563afb9a5SDavid du Colombier 	if (datafile == nil)
35663afb9a5SDavid du Colombier 		sysfatal("out of memory");
35763afb9a5SDavid du Colombier 	datafd = open(datafile, ORDWR);
35863afb9a5SDavid du Colombier 	if (datafd < 0) {
35963afb9a5SDavid du Colombier 		syslog(0, "ssh", "can't open data file %s: %r", datafile);
36063afb9a5SDavid du Colombier 		exits("net");
36163afb9a5SDavid du Colombier 	}
36263afb9a5SDavid du Colombier 	while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) {
36363afb9a5SDavid du Colombier 		fprint(errfd, "read from request file returned %d\n", n);
36463afb9a5SDavid du Colombier 		for (p = buf; p < buf + n && *p != ' '; ++p)
36563afb9a5SDavid du Colombier 			;
36663afb9a5SDavid du Colombier 		*p++ = '\0';
36763afb9a5SDavid du Colombier 		want_reply = (*p == 't');
36863afb9a5SDavid du Colombier 		/* shell, exec, and various flavours of failure */
36963afb9a5SDavid du Colombier 		if (strcmp(buf, "shell") == 0) {
37063afb9a5SDavid du Colombier 			if (already_done) {
37163afb9a5SDavid du Colombier 				REPLY("failure");
37263afb9a5SDavid du Colombier 				continue;
37363afb9a5SDavid du Colombier 			}
37463afb9a5SDavid du Colombier 			forkshell(cmd, reqfd, datafd, want_reply);
37563afb9a5SDavid du Colombier 			already_done = 1;
37663afb9a5SDavid du Colombier 			REPLY("success");
37763afb9a5SDavid du Colombier 		} else if (strcmp(buf, "exec") == 0) {
37863afb9a5SDavid du Colombier 			if (already_done) {
37963afb9a5SDavid du Colombier 				REPLY("failure");
38063afb9a5SDavid du Colombier 				continue;
38163afb9a5SDavid du Colombier 			}
38263afb9a5SDavid du Colombier 			forkcmd(cmd, p, reqfd, datafd, want_reply);
38363afb9a5SDavid du Colombier 			already_done = 1;
38463afb9a5SDavid du Colombier 			REPLY("success");
38563afb9a5SDavid du Colombier 		} else if (strcmp(buf, "pty-req") == 0 ||
38663afb9a5SDavid du Colombier 		    strcmp(buf, "window-change") == 0) {
38763afb9a5SDavid du Colombier 			REPLY("success");
38863afb9a5SDavid du Colombier 		} else if (strcmp(buf, "x11-req") == 0 ||
38963afb9a5SDavid du Colombier 		    strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) {
39063afb9a5SDavid du Colombier 			REPLY("failure");
39163afb9a5SDavid du Colombier 		} else if (strcmp(buf, "xon-xoff") == 0 ||
39263afb9a5SDavid du Colombier 		    strcmp(buf, "signal") == 0 ||
39363afb9a5SDavid du Colombier 		    strcmp(buf, "exit-status") == 0 ||
39463afb9a5SDavid du Colombier 		    strcmp(buf, "exit-signal") == 0) {
39563afb9a5SDavid du Colombier 			;
39663afb9a5SDavid du Colombier 		} else
39763afb9a5SDavid du Colombier 			syslog(0, "ssh", "server unknown channel request: %s",
39863afb9a5SDavid du Colombier 				buf);
39963afb9a5SDavid du Colombier 	}
40063afb9a5SDavid du Colombier 	if (n < 0)
40163afb9a5SDavid du Colombier 		syslog(0, "ssh", "server read failed: %r");
40263afb9a5SDavid du Colombier 	exits(nil);
40363afb9a5SDavid du Colombier }
40463afb9a5SDavid du Colombier 
40563afb9a5SDavid du Colombier char *
get_string(char * q,char * s)40663afb9a5SDavid du Colombier get_string(char *q, char *s)
40763afb9a5SDavid du Colombier {
40863afb9a5SDavid du Colombier 	int n;
40963afb9a5SDavid du Colombier 
41063afb9a5SDavid du Colombier 	n = nhgetl(q);
41163afb9a5SDavid du Colombier 	q += 4;
41263afb9a5SDavid du Colombier 	memmove(s, q, n);
41363afb9a5SDavid du Colombier 	s[n] = '\0';
41463afb9a5SDavid du Colombier 	q += n;
41563afb9a5SDavid du Colombier 	return q;
41663afb9a5SDavid du Colombier }
41763afb9a5SDavid du Colombier 
41863afb9a5SDavid du Colombier char *
confine(char * q,char * s)41963afb9a5SDavid du Colombier confine(char *q, char *s)
42063afb9a5SDavid du Colombier {
42163afb9a5SDavid du Colombier 	int i, n, m;
42263afb9a5SDavid du Colombier 	char *p, *e, *r, *buf, *toks[Maxtoks];
42363afb9a5SDavid du Colombier 
42463afb9a5SDavid du Colombier 	n = nhgetl(q);
42563afb9a5SDavid du Colombier 	q += 4;
42663afb9a5SDavid du Colombier 	buf = malloc(n+1);
42763afb9a5SDavid du Colombier 	if (buf == nil)
42863afb9a5SDavid du Colombier 		return nil;
42963afb9a5SDavid du Colombier 	memmove(buf, q, n);
43063afb9a5SDavid du Colombier 	buf[n]  = 0;
43163afb9a5SDavid du Colombier 	m = tokenize(buf, toks, nelem(toks));
43263afb9a5SDavid du Colombier 	e = s + n + 1;
43363afb9a5SDavid du Colombier 	for (i = 0, r = s; i < m; ++i) {
43463afb9a5SDavid du Colombier 		p = strrchr(toks[i], '/');
43563afb9a5SDavid du Colombier 		if (p == nil)
43663afb9a5SDavid du Colombier 			r = seprint(r, e, "%s ", toks[i]);
43763afb9a5SDavid du Colombier 		else if (p[0] != '\0' && p[1] != '\0')
43863afb9a5SDavid du Colombier 			r = seprint(r, e, "%s ", p+1);
43963afb9a5SDavid du Colombier 		else
44063afb9a5SDavid du Colombier 			r = seprint(r, e, ". ");
44163afb9a5SDavid du Colombier 	}
44263afb9a5SDavid du Colombier 	free(buf);
44363afb9a5SDavid du Colombier 	q += n;
44463afb9a5SDavid du Colombier 	return q;
44563afb9a5SDavid du Colombier }
44663afb9a5SDavid du Colombier 
44763afb9a5SDavid du Colombier void
runcmd(int reqfd,int datafd,char * svc,char * cmd,char * arg1,char * arg2)44863afb9a5SDavid du Colombier runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1, char *arg2)
44963afb9a5SDavid du Colombier {
45063afb9a5SDavid du Colombier 	char *p;
45163afb9a5SDavid du Colombier 	int fd, cmdpid, child;
45263afb9a5SDavid du Colombier 
45363afb9a5SDavid du Colombier 	cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG);
45463afb9a5SDavid du Colombier 	switch (cmdpid) {
45563afb9a5SDavid du Colombier 	case -1:
45663afb9a5SDavid du Colombier 		syslog(0, "ssh", "fork failed: %r");
45763afb9a5SDavid du Colombier 		exits("fork");
45863afb9a5SDavid du Colombier 	case 0:
45963afb9a5SDavid du Colombier 		if (restdir == nil) {
46063afb9a5SDavid du Colombier 			p = smprint("/usr/%s", uname);
46163afb9a5SDavid du Colombier 			if (p && access(p, AREAD) == 0 && chdir(p) < 0) {
46263afb9a5SDavid du Colombier 				syslog(0, "ssh", "can't chdir(%s): %r", p);
46363afb9a5SDavid du Colombier 				exits("can't chdir");
46463afb9a5SDavid du Colombier 			}
46563afb9a5SDavid du Colombier 			free(p);
46663afb9a5SDavid du Colombier 		}
46763afb9a5SDavid du Colombier 		p = strrchr(cmd, '/');
46863afb9a5SDavid du Colombier 		if (p)
46963afb9a5SDavid du Colombier 			++p;
47063afb9a5SDavid du Colombier 		else
47163afb9a5SDavid du Colombier 			p = cmd;
47263afb9a5SDavid du Colombier 
47363afb9a5SDavid du Colombier 		dup(datafd, 0);
47463afb9a5SDavid du Colombier 		dup(datafd, 1);
47563afb9a5SDavid du Colombier 		dup(datafd, 2);
47663afb9a5SDavid du Colombier 		close(datafd);
47763afb9a5SDavid du Colombier 		putenv("service", svc);
47863afb9a5SDavid du Colombier 		fprint(errfd, "starting %s\n", cmd);
47963afb9a5SDavid du Colombier 		execl(cmd, p, arg1, arg2, nil);
48063afb9a5SDavid du Colombier 
48163afb9a5SDavid du Colombier 		syslog(0, "ssh", "cannot exec %s: %r", cmd);
48263afb9a5SDavid du Colombier 		exits("exec");
48363afb9a5SDavid du Colombier 	default:
48463afb9a5SDavid du Colombier 		close(datafd);
48563afb9a5SDavid du Colombier 		fprint(errfd, "waiting for child %d\n", cmdpid);
48663afb9a5SDavid du Colombier 		while ((child = waitpid()) != cmdpid && child != -1)
48763afb9a5SDavid du Colombier 			fprint(errfd, "child %d passed\n", child);
48863afb9a5SDavid du Colombier 		if (child == -1)
48963afb9a5SDavid du Colombier 			fprint(errfd, "wait failed: %r\n");
49063afb9a5SDavid du Colombier 
49163afb9a5SDavid du Colombier 		syslog(0, "ssh", "server closing ssh session for %s", uname);
49263afb9a5SDavid du Colombier 		fprint(errfd, "closing connection\n");
49363afb9a5SDavid du Colombier 		fprint(reqfd, "close");
49463afb9a5SDavid du Colombier 		p = smprint("/proc/%d/notepg", toppid);
49563afb9a5SDavid du Colombier 		if (p) {
49663afb9a5SDavid du Colombier 			fd = open(p, OWRITE);
49763afb9a5SDavid du Colombier 			fprint(fd, "interrupt");
49863afb9a5SDavid du Colombier 			close(fd);
49963afb9a5SDavid du Colombier 		}
50063afb9a5SDavid du Colombier 		exits(nil);
50163afb9a5SDavid du Colombier 	}
50263afb9a5SDavid du Colombier }
503