xref: /plan9/sys/src/cmd/ssh2/ssh2.c (revision 515d8088f74049961cf7be2f37185376da6b74b2)
1*515d8088SDavid du Colombier /*
2*515d8088SDavid du Colombier  * ssh - remote login via SSH v2
3*515d8088SDavid du Colombier  *	/net/ssh does most of the work; we copy bytes back and forth
4*515d8088SDavid du Colombier  */
5*515d8088SDavid du Colombier #include <u.h>
6*515d8088SDavid du Colombier #include <libc.h>
7*515d8088SDavid du Colombier #include <auth.h>
8*515d8088SDavid du Colombier #include "ssh2.h"
9*515d8088SDavid du Colombier 
10*515d8088SDavid du Colombier int doauth(int, char *);
11*515d8088SDavid du Colombier int isatty(int);
12*515d8088SDavid du Colombier 
13*515d8088SDavid du Colombier char *user, *remote;
14*515d8088SDavid du Colombier char *netdir = "/net";
15*515d8088SDavid du Colombier int debug = 0;
16*515d8088SDavid du Colombier 
17*515d8088SDavid du Colombier static int stripcr = 0;
18*515d8088SDavid du Colombier static int mflag = 0;
19*515d8088SDavid du Colombier static int iflag = -1;
20*515d8088SDavid du Colombier static int nopw = 0, nopka = 0;
21*515d8088SDavid du Colombier static int chpid;
22*515d8088SDavid du Colombier static int reqfd, dfd1, cfd1, dfd2, cfd2, consfd, kconsfd, cctlfd, notefd, keyfd;
23*515d8088SDavid du Colombier 
24*515d8088SDavid du Colombier void
usage(void)25*515d8088SDavid du Colombier usage(void)
26*515d8088SDavid du Colombier {
27*515d8088SDavid du Colombier 	fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr "
28*515d8088SDavid du Colombier 		"[cmd [args]]\n", argv0);
29*515d8088SDavid du Colombier 	exits("usage");
30*515d8088SDavid du Colombier }
31*515d8088SDavid du Colombier 
32*515d8088SDavid du Colombier /*
33*515d8088SDavid du Colombier  * this is probably overkill except writing "kill" to notefd;
34*515d8088SDavid du Colombier  * file descriptors are closed by the kernel upon exit.
35*515d8088SDavid du Colombier  */
36*515d8088SDavid du Colombier static void
shutdown(void)37*515d8088SDavid du Colombier shutdown(void)
38*515d8088SDavid du Colombier {
39*515d8088SDavid du Colombier 	if (cctlfd > 0) {
40*515d8088SDavid du Colombier 		fprint(cctlfd, "rawoff");
41*515d8088SDavid du Colombier 		close(cctlfd);
42*515d8088SDavid du Colombier 	}
43*515d8088SDavid du Colombier 	if (consfd > 0)
44*515d8088SDavid du Colombier 		close(consfd);
45*515d8088SDavid du Colombier 	if (reqfd > 0) {
46*515d8088SDavid du Colombier 		fprint(reqfd, "close");
47*515d8088SDavid du Colombier 		close(reqfd);
48*515d8088SDavid du Colombier 	}
49*515d8088SDavid du Colombier 	close(dfd2);
50*515d8088SDavid du Colombier 	close(dfd1);
51*515d8088SDavid du Colombier 	close(cfd2);
52*515d8088SDavid du Colombier 	close(cfd1);
53*515d8088SDavid du Colombier 
54*515d8088SDavid du Colombier 	fprint(notefd, "kill");
55*515d8088SDavid du Colombier 	close(notefd);
56*515d8088SDavid du Colombier }
57*515d8088SDavid du Colombier 
58*515d8088SDavid du Colombier static void
bail(char * sts)59*515d8088SDavid du Colombier bail(char *sts)
60*515d8088SDavid du Colombier {
61*515d8088SDavid du Colombier 	shutdown();
62*515d8088SDavid du Colombier 	exits(sts);
63*515d8088SDavid du Colombier }
64*515d8088SDavid du Colombier 
65*515d8088SDavid du Colombier int
handler(void *,char * s)66*515d8088SDavid du Colombier handler(void *, char *s)
67*515d8088SDavid du Colombier {
68*515d8088SDavid du Colombier 	char *nf;
69*515d8088SDavid du Colombier 	int fd;
70*515d8088SDavid du Colombier 
71*515d8088SDavid du Colombier 	if (strstr(s, "alarm") != nil)
72*515d8088SDavid du Colombier 		return 0;
73*515d8088SDavid du Colombier 	if (chpid) {
74*515d8088SDavid du Colombier 		nf = esmprint("/proc/%d/note", chpid);
75*515d8088SDavid du Colombier 		fd = open(nf, OWRITE);
76*515d8088SDavid du Colombier 		fprint(fd, "interrupt");
77*515d8088SDavid du Colombier 		close(fd);
78*515d8088SDavid du Colombier 		free(nf);
79*515d8088SDavid du Colombier 	}
80*515d8088SDavid du Colombier 	shutdown();
81*515d8088SDavid du Colombier 	return 1;
82*515d8088SDavid du Colombier }
83*515d8088SDavid du Colombier 
84*515d8088SDavid du Colombier static void
parseargs(void)85*515d8088SDavid du Colombier parseargs(void)
86*515d8088SDavid du Colombier {
87*515d8088SDavid du Colombier 	int n;
88*515d8088SDavid du Colombier 	char *p, *q;
89*515d8088SDavid du Colombier 
90*515d8088SDavid du Colombier 	q = strchr(remote, '@');
91*515d8088SDavid du Colombier 	if (q != nil) {
92*515d8088SDavid du Colombier 		user = remote;
93*515d8088SDavid du Colombier 		*q++ = 0;
94*515d8088SDavid du Colombier 		remote = q;
95*515d8088SDavid du Colombier 	}
96*515d8088SDavid du Colombier 
97*515d8088SDavid du Colombier 	q = strchr(remote, '!');
98*515d8088SDavid du Colombier 	if (q) {
99*515d8088SDavid du Colombier 		n = q - remote;
100*515d8088SDavid du Colombier 		netdir = malloc(n+1);
101*515d8088SDavid du Colombier 		if (netdir == nil)
102*515d8088SDavid du Colombier 			sysfatal("out of memory");
103*515d8088SDavid du Colombier 		strncpy(netdir, remote, n+1);
104*515d8088SDavid du Colombier 		netdir[n] = '\0';
105*515d8088SDavid du Colombier 
106*515d8088SDavid du Colombier 		p = strrchr(netdir, '/');
107*515d8088SDavid du Colombier 		if (p == nil) {
108*515d8088SDavid du Colombier 			free(netdir);
109*515d8088SDavid du Colombier 			netdir = "/net";
110*515d8088SDavid du Colombier 		} else if (strcmp(p+1, "ssh") == 0)
111*515d8088SDavid du Colombier 			*p = '\0';
112*515d8088SDavid du Colombier 		else
113*515d8088SDavid du Colombier 			remote = esmprint("%s/ssh", netdir);
114*515d8088SDavid du Colombier 	}
115*515d8088SDavid du Colombier 
116*515d8088SDavid du Colombier }
117*515d8088SDavid du Colombier 
118*515d8088SDavid du Colombier static int
catcher(void *,char * s)119*515d8088SDavid du Colombier catcher(void *, char *s)
120*515d8088SDavid du Colombier {
121*515d8088SDavid du Colombier 	return strstr(s, "alarm") != nil;
122*515d8088SDavid du Colombier }
123*515d8088SDavid du Colombier 
124*515d8088SDavid du Colombier static int
timedmount(int fd,int afd,char * mntpt,int flag,char * aname)125*515d8088SDavid du Colombier timedmount(int fd, int afd, char *mntpt, int flag, char *aname)
126*515d8088SDavid du Colombier {
127*515d8088SDavid du Colombier 	int oalarm, ret;
128*515d8088SDavid du Colombier 
129*515d8088SDavid du Colombier 	atnotify(catcher, 1);
130*515d8088SDavid du Colombier 	oalarm = alarm(5*1000);		/* don't get stuck here */
131*515d8088SDavid du Colombier 	ret = mount(fd, afd, mntpt, flag, aname);
132*515d8088SDavid du Colombier 	alarm(oalarm);
133*515d8088SDavid du Colombier 	atnotify(catcher, 0);
134*515d8088SDavid du Colombier 	return ret;
135*515d8088SDavid du Colombier }
136*515d8088SDavid du Colombier 
137*515d8088SDavid du Colombier static void
mounttunnel(char * srv)138*515d8088SDavid du Colombier mounttunnel(char *srv)
139*515d8088SDavid du Colombier {
140*515d8088SDavid du Colombier 	int fd;
141*515d8088SDavid du Colombier 
142*515d8088SDavid du Colombier 	if (debug)
143*515d8088SDavid du Colombier 		fprint(2, "%s: mounting %s on /net\n", argv0, srv);
144*515d8088SDavid du Colombier 	fd = open(srv, OREAD);
145*515d8088SDavid du Colombier 	if (fd < 0) {
146*515d8088SDavid du Colombier 		if (debug)
147*515d8088SDavid du Colombier 			fprint(2, "%s: can't open %s: %r\n", argv0, srv);
148*515d8088SDavid du Colombier 	} else if (timedmount(fd, -1, netdir, MBEFORE, "") < 0) {
149*515d8088SDavid du Colombier 		fprint(2, "can't mount %s on %s: %r\n", srv, netdir);
150*515d8088SDavid du Colombier 		close(fd);
151*515d8088SDavid du Colombier 	}
152*515d8088SDavid du Colombier }
153*515d8088SDavid du Colombier 
154*515d8088SDavid du Colombier static void
newtunnel(char * myname)155*515d8088SDavid du Colombier newtunnel(char *myname)
156*515d8088SDavid du Colombier {
157*515d8088SDavid du Colombier 	int kid, pid;
158*515d8088SDavid du Colombier 
159*515d8088SDavid du Colombier 	if(debug)
160*515d8088SDavid du Colombier 		fprint(2, "%s: starting new netssh for key access\n", argv0);
161*515d8088SDavid du Colombier 	kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */);
162*515d8088SDavid du Colombier 	if (kid == 0) {
163*515d8088SDavid du Colombier //		for (fd = 3; fd < 40; fd++)
164*515d8088SDavid du Colombier //			close(fd);
165*515d8088SDavid du Colombier 		execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil);
166*515d8088SDavid du Colombier 		sysfatal("no /bin/netssh: %r");
167*515d8088SDavid du Colombier 	} else if (kid < 0)
168*515d8088SDavid du Colombier 		sysfatal("fork failed: %r");
169*515d8088SDavid du Colombier 	while ((pid = waitpid()) != kid && pid >= 0)
170*515d8088SDavid du Colombier 		;
171*515d8088SDavid du Colombier }
172*515d8088SDavid du Colombier 
173*515d8088SDavid du Colombier static void
starttunnel(void)174*515d8088SDavid du Colombier starttunnel(void)
175*515d8088SDavid du Colombier {
176*515d8088SDavid du Colombier 	char *keys, *mysrv, *myname;
177*515d8088SDavid du Colombier 
178*515d8088SDavid du Colombier 	keys = esmprint("%s/ssh/keys", netdir);
179*515d8088SDavid du Colombier 	myname = esmprint("ssh.%s", getuser());
180*515d8088SDavid du Colombier 	mysrv = esmprint("/srv/%s", myname);
181*515d8088SDavid du Colombier 
182*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
183*515d8088SDavid du Colombier 		mounttunnel("/srv/netssh");		/* old name */
184*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
185*515d8088SDavid du Colombier 		mounttunnel("/srv/ssh");
186*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
187*515d8088SDavid du Colombier 		mounttunnel(mysrv);
188*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
189*515d8088SDavid du Colombier 		newtunnel(myname);
190*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
191*515d8088SDavid du Colombier 		mounttunnel(mysrv);
192*515d8088SDavid du Colombier 
193*515d8088SDavid du Colombier 	/* if we *still* can't see our own tunnel, throw a tantrum. */
194*515d8088SDavid du Colombier 	if (access(keys, ORDWR) < 0)
195*515d8088SDavid du Colombier 		sysfatal("%s inaccessible: %r", keys);		/* WTF? */
196*515d8088SDavid du Colombier 
197*515d8088SDavid du Colombier 	free(myname);
198*515d8088SDavid du Colombier 	free(mysrv);
199*515d8088SDavid du Colombier 	free(keys);
200*515d8088SDavid du Colombier }
201*515d8088SDavid du Colombier 
202*515d8088SDavid du Colombier int
cmdmode(void)203*515d8088SDavid du Colombier cmdmode(void)
204*515d8088SDavid du Colombier {
205*515d8088SDavid du Colombier 	int n, m;
206*515d8088SDavid du Colombier 	char buf[Arbbufsz];
207*515d8088SDavid du Colombier 
208*515d8088SDavid du Colombier 	for(;;) {
209*515d8088SDavid du Colombier reprompt:
210*515d8088SDavid du Colombier 		print("\n>>> ");
211*515d8088SDavid du Colombier 		n = 0;
212*515d8088SDavid du Colombier 		do {
213*515d8088SDavid du Colombier 			m = read(0, buf + n, sizeof buf - n - 1);
214*515d8088SDavid du Colombier 			if (m <= 0)
215*515d8088SDavid du Colombier 				return 1;
216*515d8088SDavid du Colombier 			write(1, buf + n, m);
217*515d8088SDavid du Colombier 			n += m;
218*515d8088SDavid du Colombier 			buf[n] = '\0';
219*515d8088SDavid du Colombier 			if (buf[n-1] == ('u' & 037))
220*515d8088SDavid du Colombier 				goto reprompt;
221*515d8088SDavid du Colombier 		} while (buf[n-1] != '\n' && buf[n-1] != '\r');
222*515d8088SDavid du Colombier 		switch (buf[0]) {
223*515d8088SDavid du Colombier 		case '\n':
224*515d8088SDavid du Colombier 		case '\r':
225*515d8088SDavid du Colombier 			break;
226*515d8088SDavid du Colombier 		case 'q':
227*515d8088SDavid du Colombier 			return 1;
228*515d8088SDavid du Colombier 		case 'c':
229*515d8088SDavid du Colombier 			return 0;
230*515d8088SDavid du Colombier 		case 'r':
231*515d8088SDavid du Colombier 			stripcr = !stripcr;
232*515d8088SDavid du Colombier 			return 0;
233*515d8088SDavid du Colombier 		case 'h':
234*515d8088SDavid du Colombier 			print("c - continue\n");
235*515d8088SDavid du Colombier 			print("h - help\n");
236*515d8088SDavid du Colombier 			print("q - quit\n");
237*515d8088SDavid du Colombier 			print("r - toggle carriage return stripping\n");
238*515d8088SDavid du Colombier 			break;
239*515d8088SDavid du Colombier 		default:
240*515d8088SDavid du Colombier 			print("unknown command\n");
241*515d8088SDavid du Colombier 			break;
242*515d8088SDavid du Colombier 		}
243*515d8088SDavid du Colombier 	}
244*515d8088SDavid du Colombier }
245*515d8088SDavid du Colombier 
246*515d8088SDavid du Colombier static void
keyprompt(char * buf,int size,int n)247*515d8088SDavid du Colombier keyprompt(char *buf, int size, int n)
248*515d8088SDavid du Colombier {
249*515d8088SDavid du Colombier 	if (*buf == 'c') {
250*515d8088SDavid du Colombier 		fprint(kconsfd, "The following key has been offered by the server:\n");
251*515d8088SDavid du Colombier 		write(kconsfd, buf+5, n);
252*515d8088SDavid du Colombier 		fprint(kconsfd, "\n\n");
253*515d8088SDavid du Colombier 		fprint(kconsfd, "Add this key? (yes, no, session) ");
254*515d8088SDavid du Colombier 	} else {
255*515d8088SDavid du Colombier 		fprint(kconsfd, "The following key does NOT match the known "
256*515d8088SDavid du Colombier 			"key(s) for the server:\n");
257*515d8088SDavid du Colombier 		write(kconsfd, buf+5, n);
258*515d8088SDavid du Colombier 		fprint(kconsfd, "\n\n");
259*515d8088SDavid du Colombier 		fprint(kconsfd, "Add this key? (yes, no, session, replace) ");
260*515d8088SDavid du Colombier 	}
261*515d8088SDavid du Colombier 	n = read(kconsfd, buf, size - 1);
262*515d8088SDavid du Colombier 	if (n <= 0)
263*515d8088SDavid du Colombier 		return;
264*515d8088SDavid du Colombier 	write(keyfd, buf, n);		/* user's response -> /net/ssh/keys */
265*515d8088SDavid du Colombier 	seek(keyfd, 0, 2);
266*515d8088SDavid du Colombier 	if (readn(keyfd, buf, 5) <= 0)
267*515d8088SDavid du Colombier 		return;
268*515d8088SDavid du Colombier 	buf[5] = 0;
269*515d8088SDavid du Colombier 	n = strtol(buf+1, nil, 10);
270*515d8088SDavid du Colombier 	n = readn(keyfd, buf+5, n);
271*515d8088SDavid du Colombier 	if (n <= 0)
272*515d8088SDavid du Colombier 		return;
273*515d8088SDavid du Colombier 	buf[n+5] = 0;
274*515d8088SDavid du Colombier 
275*515d8088SDavid du Colombier 	switch (*buf) {
276*515d8088SDavid du Colombier 	case 'b':
277*515d8088SDavid du Colombier 	case 'f':
278*515d8088SDavid du Colombier 		fprint(kconsfd, "%s\n", buf+5);
279*515d8088SDavid du Colombier 	case 'o':
280*515d8088SDavid du Colombier 		close(keyfd);
281*515d8088SDavid du Colombier 		close(kconsfd);
282*515d8088SDavid du Colombier 	}
283*515d8088SDavid du Colombier }
284*515d8088SDavid du Colombier 
285*515d8088SDavid du Colombier /* talk the undocumented /net/ssh/keys protocol */
286*515d8088SDavid du Colombier static void
keyproc(char * buf,int size)287*515d8088SDavid du Colombier keyproc(char *buf, int size)
288*515d8088SDavid du Colombier {
289*515d8088SDavid du Colombier 	int n;
290*515d8088SDavid du Colombier 	char *p;
291*515d8088SDavid du Colombier 
292*515d8088SDavid du Colombier 	if (size < 6)
293*515d8088SDavid du Colombier 		exits("keyproc buffer too small");
294*515d8088SDavid du Colombier 	p = esmprint("%s/ssh/keys", netdir);
295*515d8088SDavid du Colombier 	keyfd = open(p, ORDWR);
296*515d8088SDavid du Colombier 	if (keyfd < 0) {
297*515d8088SDavid du Colombier 		chpid = 0;
298*515d8088SDavid du Colombier 		sysfatal("failed to open ssh keys in %s: %r", p);
299*515d8088SDavid du Colombier 	}
300*515d8088SDavid du Colombier 
301*515d8088SDavid du Colombier 	kconsfd = open("/dev/cons", ORDWR);
302*515d8088SDavid du Colombier 	if (kconsfd < 0)
303*515d8088SDavid du Colombier 		nopw = 1;
304*515d8088SDavid du Colombier 
305*515d8088SDavid du Colombier 	buf[0] = 0;
306*515d8088SDavid du Colombier 	n = read(keyfd, buf, 5);		/* reading /net/ssh/keys */
307*515d8088SDavid du Colombier 	if (n < 0)
308*515d8088SDavid du Colombier 		sysfatal("%s read: %r", p);
309*515d8088SDavid du Colombier 	buf[5] = 0;
310*515d8088SDavid du Colombier 	n = strtol(buf+1, nil, 10);
311*515d8088SDavid du Colombier 	n = readn(keyfd, buf+5, n);
312*515d8088SDavid du Colombier 	buf[n < 0? 5: n+5] = 0;
313*515d8088SDavid du Colombier 	free(p);
314*515d8088SDavid du Colombier 
315*515d8088SDavid du Colombier 	switch (*buf) {
316*515d8088SDavid du Colombier 	case 'f':
317*515d8088SDavid du Colombier 		if (kconsfd >= 0)
318*515d8088SDavid du Colombier 			fprint(kconsfd, "%s\n", buf+5);
319*515d8088SDavid du Colombier 		/* fall through */
320*515d8088SDavid du Colombier 	case 'o':
321*515d8088SDavid du Colombier 		close(keyfd);
322*515d8088SDavid du Colombier 		if (kconsfd >= 0)
323*515d8088SDavid du Colombier 			close(kconsfd);
324*515d8088SDavid du Colombier 		break;
325*515d8088SDavid du Colombier 	default:
326*515d8088SDavid du Colombier 		if (kconsfd >= 0)
327*515d8088SDavid du Colombier 			keyprompt(buf, size, n);
328*515d8088SDavid du Colombier 		else {
329*515d8088SDavid du Colombier 			fprint(keyfd, "n");
330*515d8088SDavid du Colombier 			close(keyfd);
331*515d8088SDavid du Colombier 		}
332*515d8088SDavid du Colombier 		break;
333*515d8088SDavid du Colombier 	}
334*515d8088SDavid du Colombier 	chpid = 0;
335*515d8088SDavid du Colombier 	exits(nil);
336*515d8088SDavid du Colombier }
337*515d8088SDavid du Colombier 
338*515d8088SDavid du Colombier /*
339*515d8088SDavid du Colombier  * start a subproc to copy from network to stdout
340*515d8088SDavid du Colombier  * while we copy from stdin to network.
341*515d8088SDavid du Colombier  */
342*515d8088SDavid du Colombier static void
bidircopy(char * buf,int size)343*515d8088SDavid du Colombier bidircopy(char *buf, int size)
344*515d8088SDavid du Colombier {
345*515d8088SDavid du Colombier 	int i, n, lstart;
346*515d8088SDavid du Colombier 	char *path, *p, *q;
347*515d8088SDavid du Colombier 
348*515d8088SDavid du Colombier 	rfork(RFNOTEG);
349*515d8088SDavid du Colombier 	path = esmprint("/proc/%d/notepg", getpid());
350*515d8088SDavid du Colombier 	notefd = open(path, OWRITE);
351*515d8088SDavid du Colombier 
352*515d8088SDavid du Colombier 	switch (rfork(RFPROC|RFMEM|RFNOWAIT)) {
353*515d8088SDavid du Colombier 	case 0:
354*515d8088SDavid du Colombier 		while ((n = read(dfd2, buf, size - 1)) > 0) {
355*515d8088SDavid du Colombier 			if (!stripcr)
356*515d8088SDavid du Colombier 				p = buf + n;
357*515d8088SDavid du Colombier 			else
358*515d8088SDavid du Colombier 				for (i = 0, p = buf, q = buf; i < n; ++i, ++q)
359*515d8088SDavid du Colombier 					if (*q != '\r')
360*515d8088SDavid du Colombier 						*p++ = *q;
361*515d8088SDavid du Colombier 			if (p != buf)
362*515d8088SDavid du Colombier 				write(1, buf, p-buf);
363*515d8088SDavid du Colombier 		}
364*515d8088SDavid du Colombier 		/*
365*515d8088SDavid du Colombier 		 * don't bother; it will be obvious when the user's prompt
366*515d8088SDavid du Colombier 		 * changes.
367*515d8088SDavid du Colombier 		 *
368*515d8088SDavid du Colombier 		 * fprint(2, "%s: Connection closed by server\n", argv0);
369*515d8088SDavid du Colombier 		 */
370*515d8088SDavid du Colombier 		break;
371*515d8088SDavid du Colombier 	default:
372*515d8088SDavid du Colombier 		lstart = 1;
373*515d8088SDavid du Colombier 		while ((n = read(0, buf, size - 1)) > 0) {
374*515d8088SDavid du Colombier 			if (!mflag && lstart && buf[0] == 0x1c)
375*515d8088SDavid du Colombier 				if (cmdmode())
376*515d8088SDavid du Colombier 					break;
377*515d8088SDavid du Colombier 				else
378*515d8088SDavid du Colombier 					continue;
379*515d8088SDavid du Colombier 			lstart = (buf[n-1] == '\n' || buf[n-1] == '\r');
380*515d8088SDavid du Colombier 			write(dfd2, buf, n);
381*515d8088SDavid du Colombier 		}
382*515d8088SDavid du Colombier 		/*
383*515d8088SDavid du Colombier 		 * don't bother; it will be obvious when the user's prompt
384*515d8088SDavid du Colombier 		 * changes.
385*515d8088SDavid du Colombier 		 *
386*515d8088SDavid du Colombier 		 * fprint(2, "%s: EOF on client side\n", argv0);
387*515d8088SDavid du Colombier 		 */
388*515d8088SDavid du Colombier 		break;
389*515d8088SDavid du Colombier 	case -1:
390*515d8088SDavid du Colombier 		fprint(2, "%s: fork error: %r\n", argv0);
391*515d8088SDavid du Colombier 		break;
392*515d8088SDavid du Colombier 	}
393*515d8088SDavid du Colombier 
394*515d8088SDavid du Colombier 	bail(nil);
395*515d8088SDavid du Colombier }
396*515d8088SDavid du Colombier 
397*515d8088SDavid du Colombier static int
connect(char * buf,int size)398*515d8088SDavid du Colombier connect(char *buf, int size)
399*515d8088SDavid du Colombier {
400*515d8088SDavid du Colombier 	int nfd, n;
401*515d8088SDavid du Colombier 	char *dir, *ds, *nf;
402*515d8088SDavid du Colombier 
403*515d8088SDavid du Colombier 	dir = esmprint("%s/ssh", netdir);
404*515d8088SDavid du Colombier 	ds = netmkaddr(remote, dir, "22");		/* tcp port 22 is ssh */
405*515d8088SDavid du Colombier 	free(dir);
406*515d8088SDavid du Colombier 
407*515d8088SDavid du Colombier 	dfd1 = dial(ds, nil, nil, &cfd1);
408*515d8088SDavid du Colombier 	if (dfd1 < 0) {
409*515d8088SDavid du Colombier 		fprint(2, "%s: dial conn %s: %r\n", argv0, ds);
410*515d8088SDavid du Colombier 		if (chpid) {
411*515d8088SDavid du Colombier 			nf = esmprint("/proc/%d/note", chpid);
412*515d8088SDavid du Colombier 			nfd = open(nf, OWRITE);
413*515d8088SDavid du Colombier 			fprint(nfd, "interrupt");
414*515d8088SDavid du Colombier 			close(nfd);
415*515d8088SDavid du Colombier 		}
416*515d8088SDavid du Colombier 		exits("can't dial");
417*515d8088SDavid du Colombier 	}
418*515d8088SDavid du Colombier 
419*515d8088SDavid du Colombier 	seek(cfd1, 0, 0);
420*515d8088SDavid du Colombier 	n = read(cfd1, buf, size - 1);
421*515d8088SDavid du Colombier 	buf[n >= 0? n: 0] = 0;
422*515d8088SDavid du Colombier 	return atoi(buf);
423*515d8088SDavid du Colombier }
424*515d8088SDavid du Colombier 
425*515d8088SDavid du Colombier static int
chanconnect(int conn,char * buf,int size)426*515d8088SDavid du Colombier chanconnect(int conn, char *buf, int size)
427*515d8088SDavid du Colombier {
428*515d8088SDavid du Colombier 	int n;
429*515d8088SDavid du Colombier 	char *path;
430*515d8088SDavid du Colombier 
431*515d8088SDavid du Colombier 	path = esmprint("%s/ssh/%d!session", netdir, conn);
432*515d8088SDavid du Colombier 	dfd2 = dial(path, nil, nil, &cfd2);
433*515d8088SDavid du Colombier 	if (dfd2 < 0) {
434*515d8088SDavid du Colombier 		fprint(2, "%s: dial chan %s: %r\n", argv0, path);
435*515d8088SDavid du Colombier 		bail("dial");
436*515d8088SDavid du Colombier 	}
437*515d8088SDavid du Colombier 	free(path);
438*515d8088SDavid du Colombier 
439*515d8088SDavid du Colombier 	n = read(cfd2, buf, size - 1);
440*515d8088SDavid du Colombier 	buf[n >= 0? n: 0] = 0;
441*515d8088SDavid du Colombier 	return atoi(buf);
442*515d8088SDavid du Colombier }
443*515d8088SDavid du Colombier 
444*515d8088SDavid du Colombier static void
remotecmd(int argc,char * argv[],int conn,int chan,char * buf,int size)445*515d8088SDavid du Colombier remotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size)
446*515d8088SDavid du Colombier {
447*515d8088SDavid du Colombier 	int i;
448*515d8088SDavid du Colombier 	char *path, *q, *ep;
449*515d8088SDavid du Colombier 
450*515d8088SDavid du Colombier 	path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan);
451*515d8088SDavid du Colombier 	reqfd = open(path, OWRITE);
452*515d8088SDavid du Colombier 	if (reqfd < 0)
453*515d8088SDavid du Colombier 		bail("can't open request chan");
454*515d8088SDavid du Colombier 	if (argc == 0)
455*515d8088SDavid du Colombier 		if (readfile("/env/TERM", buf, size) < 0)
456*515d8088SDavid du Colombier 			fprint(reqfd, "shell");
457*515d8088SDavid du Colombier 		else
458*515d8088SDavid du Colombier 			fprint(reqfd, "shell %s", buf);
459*515d8088SDavid du Colombier 	else {
460*515d8088SDavid du Colombier 		assert(size >= Bigbufsz);
461*515d8088SDavid du Colombier 		ep = buf + Bigbufsz;
462*515d8088SDavid du Colombier 		q = seprint(buf, ep, "exec");
463*515d8088SDavid du Colombier 		for (i = 0; i < argc; ++i)
464*515d8088SDavid du Colombier 			q = seprint(q, ep, " %q", argv[i]);
465*515d8088SDavid du Colombier 		if (q >= ep) {
466*515d8088SDavid du Colombier 			fprint(2, "%s: command too long\n", argv0);
467*515d8088SDavid du Colombier 			fprint(reqfd, "close");
468*515d8088SDavid du Colombier 			bail("cmd too long");
469*515d8088SDavid du Colombier 		}
470*515d8088SDavid du Colombier 		write(reqfd, buf, q - buf);
471*515d8088SDavid du Colombier 	}
472*515d8088SDavid du Colombier }
473*515d8088SDavid du Colombier 
474*515d8088SDavid du Colombier void
main(int argc,char * argv[])475*515d8088SDavid du Colombier main(int argc, char *argv[])
476*515d8088SDavid du Colombier {
477*515d8088SDavid du Colombier 	char *whichkey;
478*515d8088SDavid du Colombier 	int conn, chan, n;
479*515d8088SDavid du Colombier 	char buf[Copybufsz];
480*515d8088SDavid du Colombier 
481*515d8088SDavid du Colombier 	quotefmtinstall();
482*515d8088SDavid du Colombier 	reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd =
483*515d8088SDavid du Colombier 		notefd = keyfd = -1;
484*515d8088SDavid du Colombier 	whichkey = nil;
485*515d8088SDavid du Colombier 	ARGBEGIN {
486*515d8088SDavid du Colombier 	case 'A':			/* auth protos */
487*515d8088SDavid du Colombier 	case 'c':			/* ciphers */
488*515d8088SDavid du Colombier 		fprint(2, "%s: sorry, -%c is not supported\n", argv0, ARGC());
489*515d8088SDavid du Colombier 		break;
490*515d8088SDavid du Colombier 	case 'a':			/* compat? */
491*515d8088SDavid du Colombier 	case 'C':			/* cooked mode */
492*515d8088SDavid du Colombier 	case 'f':			/* agent forwarding */
493*515d8088SDavid du Colombier 	case 'p':			/* force pty */
494*515d8088SDavid du Colombier 	case 'P':			/* force no pty */
495*515d8088SDavid du Colombier 	case 'R':			/* force raw mode on pty */
496*515d8088SDavid du Colombier 	case 'v':			/* scp compat */
497*515d8088SDavid du Colombier 	case 'w':			/* send window-size changes */
498*515d8088SDavid du Colombier 	case 'x':			/* unix compat: no x11 forwarding */
499*515d8088SDavid du Colombier 		break;
500*515d8088SDavid du Colombier 	case 'd':
501*515d8088SDavid du Colombier 		debug++;
502*515d8088SDavid du Colombier 		break;
503*515d8088SDavid du Colombier 	case 'I':			/* non-interactive */
504*515d8088SDavid du Colombier 		iflag = 0;
505*515d8088SDavid du Colombier 		break;
506*515d8088SDavid du Colombier 	case 'i':			/* interactive: scp & rx do it */
507*515d8088SDavid du Colombier 		iflag = 1;
508*515d8088SDavid du Colombier 		break;
509*515d8088SDavid du Colombier 	case 'l':
510*515d8088SDavid du Colombier 	case 'u':
511*515d8088SDavid du Colombier 		user = EARGF(usage());
512*515d8088SDavid du Colombier 		break;
513*515d8088SDavid du Colombier 	case 'k':
514*515d8088SDavid du Colombier 		nopka = 1;
515*515d8088SDavid du Colombier 		break;
516*515d8088SDavid du Colombier 	case 'K':
517*515d8088SDavid du Colombier 		nopw = 1;
518*515d8088SDavid du Colombier 		break;
519*515d8088SDavid du Colombier 	case 'm':
520*515d8088SDavid du Colombier 		mflag = 1;
521*515d8088SDavid du Colombier 		break;
522*515d8088SDavid du Colombier 	case 'n':
523*515d8088SDavid du Colombier 		netdir = EARGF(usage());
524*515d8088SDavid du Colombier 		break;
525*515d8088SDavid du Colombier 	case 'r':
526*515d8088SDavid du Colombier 		stripcr = 1;
527*515d8088SDavid du Colombier 		break;
528*515d8088SDavid du Colombier 	case 'z':
529*515d8088SDavid du Colombier 		whichkey = EARGF(usage());
530*515d8088SDavid du Colombier 		break;
531*515d8088SDavid du Colombier 	default:
532*515d8088SDavid du Colombier 		usage();
533*515d8088SDavid du Colombier 	} ARGEND;
534*515d8088SDavid du Colombier 	if (argc == 0)
535*515d8088SDavid du Colombier 		usage();
536*515d8088SDavid du Colombier 
537*515d8088SDavid du Colombier 	if (iflag == -1)
538*515d8088SDavid du Colombier 		iflag = isatty(0);
539*515d8088SDavid du Colombier 	remote = *argv++;
540*515d8088SDavid du Colombier 	--argc;
541*515d8088SDavid du Colombier 
542*515d8088SDavid du Colombier 	parseargs();
543*515d8088SDavid du Colombier 
544*515d8088SDavid du Colombier 	if (!user)
545*515d8088SDavid du Colombier 		user = getuser();
546*515d8088SDavid du Colombier 	if (user == nil || remote == nil)
547*515d8088SDavid du Colombier 		sysfatal("out of memory");
548*515d8088SDavid du Colombier 
549*515d8088SDavid du Colombier 	starttunnel();
550*515d8088SDavid du Colombier 
551*515d8088SDavid du Colombier 	/* fork subproc to handle keys; don't wait for it */
552*515d8088SDavid du Colombier 	if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0)
553*515d8088SDavid du Colombier 		keyproc(buf, sizeof buf);
554*515d8088SDavid du Colombier 	chpid = n;
555*515d8088SDavid du Colombier 	atnotify(handler, 1);
556*515d8088SDavid du Colombier 
557*515d8088SDavid du Colombier 	/* connect and learn connection number */
558*515d8088SDavid du Colombier 	conn = connect(buf, sizeof buf);
559*515d8088SDavid du Colombier 
560*515d8088SDavid du Colombier 	consfd = open("/dev/cons", ORDWR);
561*515d8088SDavid du Colombier 	cctlfd = open("/dev/consctl", OWRITE);
562*515d8088SDavid du Colombier 	fprint(cctlfd, "rawon");
563*515d8088SDavid du Colombier 	if (doauth(cfd1, whichkey) < 0)
564*515d8088SDavid du Colombier 		bail("doauth");
565*515d8088SDavid du Colombier 
566*515d8088SDavid du Colombier 	/* connect a channel of conn and learn channel number */
567*515d8088SDavid du Colombier 	chan = chanconnect(conn, buf, sizeof buf);
568*515d8088SDavid du Colombier 
569*515d8088SDavid du Colombier 	/* open request channel, request shell or command execution */
570*515d8088SDavid du Colombier 	remotecmd(argc, argv, conn, chan, buf, sizeof buf);
571*515d8088SDavid du Colombier 
572*515d8088SDavid du Colombier 	bidircopy(buf, sizeof buf);
573*515d8088SDavid du Colombier }
574*515d8088SDavid du Colombier 
575*515d8088SDavid du Colombier int
isatty(int fd)576*515d8088SDavid du Colombier isatty(int fd)
577*515d8088SDavid du Colombier {
578*515d8088SDavid du Colombier 	char buf[64];
579*515d8088SDavid du Colombier 
580*515d8088SDavid du Colombier 	buf[0] = '\0';
581*515d8088SDavid du Colombier 	fd2path(fd, buf, sizeof buf);
582*515d8088SDavid du Colombier 	return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
583*515d8088SDavid du Colombier }
584*515d8088SDavid du Colombier 
585*515d8088SDavid du Colombier int
doauth(int cfd1,char * whichkey)586*515d8088SDavid du Colombier doauth(int cfd1, char *whichkey)
587*515d8088SDavid du Colombier {
588*515d8088SDavid du Colombier 	UserPasswd *up;
589*515d8088SDavid du Colombier 	int n;
590*515d8088SDavid du Colombier 	char path[Arbpathlen];
591*515d8088SDavid du Colombier 
592*515d8088SDavid du Colombier  	if (!nopka) {
593*515d8088SDavid du Colombier 		if (whichkey)
594*515d8088SDavid du Colombier 			n = fprint(cfd1, "ssh-userauth K %q %q", user, whichkey);
595*515d8088SDavid du Colombier 		else
596*515d8088SDavid du Colombier 			n = fprint(cfd1, "ssh-userauth K %q", user);
597*515d8088SDavid du Colombier 		if (n >= 0)
598*515d8088SDavid du Colombier 			return 0;
599*515d8088SDavid du Colombier 	}
600*515d8088SDavid du Colombier 	if (nopw)
601*515d8088SDavid du Colombier 		return -1;
602*515d8088SDavid du Colombier 	up = auth_getuserpasswd(iflag? auth_getkey: nil,
603*515d8088SDavid du Colombier 		"proto=pass service=ssh server=%q user=%q", remote, user);
604*515d8088SDavid du Colombier 	if (up == nil) {
605*515d8088SDavid du Colombier 		fprint(2, "%s: didn't get password: %r\n", argv0);
606*515d8088SDavid du Colombier 		return -1;
607*515d8088SDavid du Colombier 	}
608*515d8088SDavid du Colombier 	n = fprint(cfd1, "ssh-userauth k %q %q", user, up->passwd);
609*515d8088SDavid du Colombier 	if (n >= 0)
610*515d8088SDavid du Colombier 		return 0;
611*515d8088SDavid du Colombier 
612*515d8088SDavid du Colombier 	path[0] = '\0';
613*515d8088SDavid du Colombier 	fd2path(cfd1, path, sizeof path);
614*515d8088SDavid du Colombier 	fprint(2, "%s: auth ctl msg `ssh-userauth k %q <password>' for %q: %r\n",
615*515d8088SDavid du Colombier 		argv0, user, path);
616*515d8088SDavid du Colombier 	return -1;
617*515d8088SDavid du Colombier }
618