xref: /plan9-contrib/sys/src/cmd/ssh2/netssh.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1*63afb9a5SDavid du Colombier /*
2*63afb9a5SDavid du Colombier  *  /net/ssh
3*63afb9a5SDavid du Colombier  */
4*63afb9a5SDavid du Colombier #include <u.h>
5*63afb9a5SDavid du Colombier #include <libc.h>
6*63afb9a5SDavid du Colombier #include <fcall.h>
7*63afb9a5SDavid du Colombier #include <thread.h>
8*63afb9a5SDavid du Colombier #include <9p.h>
9*63afb9a5SDavid du Colombier #include <mp.h>
10*63afb9a5SDavid du Colombier #include <auth.h>
11*63afb9a5SDavid du Colombier #include <authsrv.h>
12*63afb9a5SDavid du Colombier #include <libsec.h>
13*63afb9a5SDavid du Colombier #include <ip.h>
14*63afb9a5SDavid du Colombier #include "netssh.h"
15*63afb9a5SDavid du Colombier 
16*63afb9a5SDavid du Colombier extern int nokeyverify;
17*63afb9a5SDavid du Colombier 
18*63afb9a5SDavid du Colombier void stclunk(Fid *);
19*63afb9a5SDavid du Colombier void stend(Srv *);
20*63afb9a5SDavid du Colombier void stflush(Req *);
21*63afb9a5SDavid du Colombier void stopen(Req *);
22*63afb9a5SDavid du Colombier void stread(Req *);
23*63afb9a5SDavid du Colombier void stwrite(Req *);
24*63afb9a5SDavid du Colombier 
25*63afb9a5SDavid du Colombier Srv netsshsrv = {
26*63afb9a5SDavid du Colombier 	.open = stopen,
27*63afb9a5SDavid du Colombier 	.read = stread,
28*63afb9a5SDavid du Colombier 	.write = stwrite,
29*63afb9a5SDavid du Colombier 	.flush = stflush,
30*63afb9a5SDavid du Colombier 	.destroyfid = stclunk,
31*63afb9a5SDavid du Colombier 	.end = stend,
32*63afb9a5SDavid du Colombier };
33*63afb9a5SDavid du Colombier 
34*63afb9a5SDavid du Colombier Cipher *cryptos[] = {
35*63afb9a5SDavid du Colombier 	&cipheraes128,
36*63afb9a5SDavid du Colombier 	&cipheraes192,
37*63afb9a5SDavid du Colombier 	&cipheraes256,
38*63afb9a5SDavid du Colombier //	&cipherblowfish,
39*63afb9a5SDavid du Colombier 	&cipher3des,
40*63afb9a5SDavid du Colombier 	&cipherrc4,
41*63afb9a5SDavid du Colombier };
42*63afb9a5SDavid du Colombier 
43*63afb9a5SDavid du Colombier Kex *kexes[] = {
44*63afb9a5SDavid du Colombier 	&dh1sha1,
45*63afb9a5SDavid du Colombier 	&dh14sha1,
46*63afb9a5SDavid du Colombier };
47*63afb9a5SDavid du Colombier 
48*63afb9a5SDavid du Colombier PKA *pkas[3];
49*63afb9a5SDavid du Colombier 
50*63afb9a5SDavid du Colombier char *macnames[] = {
51*63afb9a5SDavid du Colombier 	"hmac-sha1",
52*63afb9a5SDavid du Colombier };
53*63afb9a5SDavid du Colombier 
54*63afb9a5SDavid du Colombier char *st_names[] = {
55*63afb9a5SDavid du Colombier [Empty]		"Empty",
56*63afb9a5SDavid du Colombier [Allocated]	"Allocated",
57*63afb9a5SDavid du Colombier [Initting]	"Initting",
58*63afb9a5SDavid du Colombier [Listening]	"Listening",
59*63afb9a5SDavid du Colombier [Opening]	"Opening",
60*63afb9a5SDavid du Colombier [Negotiating]	"Negotiating",
61*63afb9a5SDavid du Colombier [Authing]	"Authing",
62*63afb9a5SDavid du Colombier [Established]	"Established",
63*63afb9a5SDavid du Colombier [Eof]		"Eof",
64*63afb9a5SDavid du Colombier [Closing]	"Closing",
65*63afb9a5SDavid du Colombier [Closed]	"Closed",
66*63afb9a5SDavid du Colombier };
67*63afb9a5SDavid du Colombier 
68*63afb9a5SDavid du Colombier int debug;
69*63afb9a5SDavid du Colombier int kflag;
70*63afb9a5SDavid du Colombier char *mntpt = "/net";
71*63afb9a5SDavid du Colombier char uid[32];
72*63afb9a5SDavid du Colombier Conn *connections[MAXCONN];
73*63afb9a5SDavid du Colombier File *rootfile, *clonefile, *ctlfile, *keysfile;
74*63afb9a5SDavid du Colombier Ioproc *io9p;
75*63afb9a5SDavid du Colombier MBox keymbox;
76*63afb9a5SDavid du Colombier QLock availlck;
77*63afb9a5SDavid du Colombier Rendez availrend;
78*63afb9a5SDavid du Colombier 
79*63afb9a5SDavid du Colombier SSHChan *alloc_chan(Conn *);
80*63afb9a5SDavid du Colombier Conn *alloc_conn(void);
81*63afb9a5SDavid du Colombier int auth_req(Packet *, Conn *);
82*63afb9a5SDavid du Colombier int client_auth(Conn *, Ioproc *);
83*63afb9a5SDavid du Colombier int dohandshake(Conn *, char *);
84*63afb9a5SDavid du Colombier char *factlookup(int, int, char *[]);
85*63afb9a5SDavid du Colombier void filedup(Req *, File *);
86*63afb9a5SDavid du Colombier void readdata(void *);
87*63afb9a5SDavid du Colombier void reader(void *);
88*63afb9a5SDavid du Colombier void readreqrem(void *);
89*63afb9a5SDavid du Colombier void send_kexinit(Conn *);
90*63afb9a5SDavid du Colombier void server(char *, char *);
91*63afb9a5SDavid du Colombier void shutdown(Conn *);
92*63afb9a5SDavid du Colombier void stlisconn(void *);
93*63afb9a5SDavid du Colombier void stlischan(void *);
94*63afb9a5SDavid du Colombier int validatekex(Conn *, Packet *);
95*63afb9a5SDavid du Colombier int validatekexc(Packet *);
96*63afb9a5SDavid du Colombier int validatekexs(Packet *);
97*63afb9a5SDavid du Colombier void writectlproc(void *);
98*63afb9a5SDavid du Colombier void writedataproc(void *);
99*63afb9a5SDavid du Colombier void writereqremproc(void *);
100*63afb9a5SDavid du Colombier 
101*63afb9a5SDavid du Colombier static int deferredinit(Conn *c);
102*63afb9a5SDavid du Colombier 
103*63afb9a5SDavid du Colombier static void
104*63afb9a5SDavid du Colombier sshlogint(Conn *c, char *file, char *p)
105*63afb9a5SDavid du Colombier {
106*63afb9a5SDavid du Colombier 	char *role, *id;
107*63afb9a5SDavid du Colombier 
108*63afb9a5SDavid du Colombier 	if (c == nil)
109*63afb9a5SDavid du Colombier 		role = "";
110*63afb9a5SDavid du Colombier 	else if (c->role == Server)
111*63afb9a5SDavid du Colombier 		role = "server ";
112*63afb9a5SDavid du Colombier 	else
113*63afb9a5SDavid du Colombier 		role = "client ";
114*63afb9a5SDavid du Colombier 	if (c == nil)
115*63afb9a5SDavid du Colombier 		id = strdup("");
116*63afb9a5SDavid du Colombier 	else if (c->user || c->remote)
117*63afb9a5SDavid du Colombier 		id = smprint("user %s@%s id %d ", c->user, c->remote, c->id);
118*63afb9a5SDavid du Colombier 	else
119*63afb9a5SDavid du Colombier 		id = smprint("id %d ", c->id);
120*63afb9a5SDavid du Colombier 
121*63afb9a5SDavid du Colombier 	syslog(0, file, "%s: %s%s%s", argv0, role, id, p);
122*63afb9a5SDavid du Colombier 	free(id);
123*63afb9a5SDavid du Colombier }
124*63afb9a5SDavid du Colombier 
125*63afb9a5SDavid du Colombier void
126*63afb9a5SDavid du Colombier sshlog(Conn *c, char *fmt, ...)
127*63afb9a5SDavid du Colombier {
128*63afb9a5SDavid du Colombier 	va_list args;
129*63afb9a5SDavid du Colombier 	char *p;
130*63afb9a5SDavid du Colombier 
131*63afb9a5SDavid du Colombier 	/* do this first in case fmt contains "%r" */
132*63afb9a5SDavid du Colombier 	va_start(args, fmt);
133*63afb9a5SDavid du Colombier 	p = vsmprint(fmt, args);
134*63afb9a5SDavid du Colombier 	va_end(args);
135*63afb9a5SDavid du Colombier 
136*63afb9a5SDavid du Colombier 	sshlogint(c, "ssh", p);
137*63afb9a5SDavid du Colombier 	sshlogint(c, "sshdebug", p);	/* log in both places */
138*63afb9a5SDavid du Colombier 	free(p);
139*63afb9a5SDavid du Colombier }
140*63afb9a5SDavid du Colombier 
141*63afb9a5SDavid du Colombier void
142*63afb9a5SDavid du Colombier sshdebug(Conn *c, char *fmt, ...)
143*63afb9a5SDavid du Colombier {
144*63afb9a5SDavid du Colombier 	va_list args;
145*63afb9a5SDavid du Colombier 	char *p;
146*63afb9a5SDavid du Colombier 
147*63afb9a5SDavid du Colombier 	if (!debug)
148*63afb9a5SDavid du Colombier 		return;
149*63afb9a5SDavid du Colombier 
150*63afb9a5SDavid du Colombier 	/* do this first in case fmt contains "%r" */
151*63afb9a5SDavid du Colombier 	va_start(args, fmt);
152*63afb9a5SDavid du Colombier 	p = vsmprint(fmt, args);
153*63afb9a5SDavid du Colombier 	va_end(args);
154*63afb9a5SDavid du Colombier 
155*63afb9a5SDavid du Colombier 	sshlogint(c, "sshdebug", p);
156*63afb9a5SDavid du Colombier 	free(p);
157*63afb9a5SDavid du Colombier }
158*63afb9a5SDavid du Colombier 
159*63afb9a5SDavid du Colombier void
160*63afb9a5SDavid du Colombier usage(void)
161*63afb9a5SDavid du Colombier {
162*63afb9a5SDavid du Colombier 	fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0);
163*63afb9a5SDavid du Colombier 	exits("usage");
164*63afb9a5SDavid du Colombier }
165*63afb9a5SDavid du Colombier 
166*63afb9a5SDavid du Colombier void
167*63afb9a5SDavid du Colombier threadmain(int argc, char *argv[])
168*63afb9a5SDavid du Colombier {
169*63afb9a5SDavid du Colombier 	char *p, *srvpt = nil;
170*63afb9a5SDavid du Colombier 
171*63afb9a5SDavid du Colombier 	threadsetname("main");
172*63afb9a5SDavid du Colombier 	ARGBEGIN {
173*63afb9a5SDavid du Colombier 	case '9':
174*63afb9a5SDavid du Colombier 		chatty9p = 1;
175*63afb9a5SDavid du Colombier 		break;
176*63afb9a5SDavid du Colombier 	case 'd':
177*63afb9a5SDavid du Colombier 		debug++;
178*63afb9a5SDavid du Colombier 		break;
179*63afb9a5SDavid du Colombier 	case 'k':
180*63afb9a5SDavid du Colombier 		kflag = 1;
181*63afb9a5SDavid du Colombier 		break;
182*63afb9a5SDavid du Colombier 	case 'm':
183*63afb9a5SDavid du Colombier 		mntpt = EARGF(usage());
184*63afb9a5SDavid du Colombier 		break;
185*63afb9a5SDavid du Colombier 	case 's':
186*63afb9a5SDavid du Colombier 		srvpt = EARGF(usage());
187*63afb9a5SDavid du Colombier 		break;
188*63afb9a5SDavid du Colombier 	case 'v':
189*63afb9a5SDavid du Colombier 		nokeyverify = 1;
190*63afb9a5SDavid du Colombier 		break;
191*63afb9a5SDavid du Colombier 	default:
192*63afb9a5SDavid du Colombier 		usage();
193*63afb9a5SDavid du Colombier 		break;
194*63afb9a5SDavid du Colombier 	} ARGEND;
195*63afb9a5SDavid du Colombier 
196*63afb9a5SDavid du Colombier 	p = getenv("nosshkeyverify");
197*63afb9a5SDavid du Colombier 	if (p) {
198*63afb9a5SDavid du Colombier 		nokeyverify = 1;
199*63afb9a5SDavid du Colombier 		free(p);
200*63afb9a5SDavid du Colombier 	}
201*63afb9a5SDavid du Colombier 
202*63afb9a5SDavid du Colombier 	if (readfile("/dev/user", uid, sizeof uid) <= 0)
203*63afb9a5SDavid du Colombier 		strcpy(uid, "none");
204*63afb9a5SDavid du Colombier 
205*63afb9a5SDavid du Colombier 	keymbox.mchan = chancreate(4, 0);
206*63afb9a5SDavid du Colombier 	availrend.l = &availlck;
207*63afb9a5SDavid du Colombier 	dh_init(pkas);
208*63afb9a5SDavid du Colombier 
209*63afb9a5SDavid du Colombier 	/* become a daemon */
210*63afb9a5SDavid du Colombier 	if (rfork(RFNOTEG) < 0)
211*63afb9a5SDavid du Colombier 		fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0);
212*63afb9a5SDavid du Colombier 	server(mntpt, srvpt);
213*63afb9a5SDavid du Colombier }
214*63afb9a5SDavid du Colombier 
215*63afb9a5SDavid du Colombier int
216*63afb9a5SDavid du Colombier readio(Ioproc *io, int fd, void *buf, int n)
217*63afb9a5SDavid du Colombier {
218*63afb9a5SDavid du Colombier 	if (io)
219*63afb9a5SDavid du Colombier 		return ioread(io, fd, buf, n);
220*63afb9a5SDavid du Colombier 	else
221*63afb9a5SDavid du Colombier 		return read(fd, buf, n);
222*63afb9a5SDavid du Colombier }
223*63afb9a5SDavid du Colombier 
224*63afb9a5SDavid du Colombier int
225*63afb9a5SDavid du Colombier writeio(Ioproc *io, int fd, void *buf, int n)
226*63afb9a5SDavid du Colombier {
227*63afb9a5SDavid du Colombier 	if (io)
228*63afb9a5SDavid du Colombier 		return iowrite(io, fd, buf, n);
229*63afb9a5SDavid du Colombier 	else
230*63afb9a5SDavid du Colombier 		return write(fd, buf, n);
231*63afb9a5SDavid du Colombier }
232*63afb9a5SDavid du Colombier 
233*63afb9a5SDavid du Colombier int
234*63afb9a5SDavid du Colombier read9pmsg(int fd, void *abuf, uint n)
235*63afb9a5SDavid du Colombier {
236*63afb9a5SDavid du Colombier 	int m, len;
237*63afb9a5SDavid du Colombier 	uchar *buf;
238*63afb9a5SDavid du Colombier 
239*63afb9a5SDavid du Colombier 	if (io9p == nil)
240*63afb9a5SDavid du Colombier 		io9p = ioproc();
241*63afb9a5SDavid du Colombier 
242*63afb9a5SDavid du Colombier 	buf = abuf;
243*63afb9a5SDavid du Colombier 
244*63afb9a5SDavid du Colombier 	/* read count */
245*63afb9a5SDavid du Colombier 	m = ioreadn(io9p, fd, buf, BIT32SZ);
246*63afb9a5SDavid du Colombier 	if(m != BIT32SZ){
247*63afb9a5SDavid du Colombier 		if(m < 0)
248*63afb9a5SDavid du Colombier 			return -1;
249*63afb9a5SDavid du Colombier 		return 0;
250*63afb9a5SDavid du Colombier 	}
251*63afb9a5SDavid du Colombier 
252*63afb9a5SDavid du Colombier 	len = GBIT32(buf);
253*63afb9a5SDavid du Colombier 	if(len <= BIT32SZ || len > n){
254*63afb9a5SDavid du Colombier 		werrstr("bad length in 9P2000 message header");
255*63afb9a5SDavid du Colombier 		return -1;
256*63afb9a5SDavid du Colombier 	}
257*63afb9a5SDavid du Colombier 	len -= BIT32SZ;
258*63afb9a5SDavid du Colombier 	m = ioreadn(io9p, fd, buf+BIT32SZ, len);
259*63afb9a5SDavid du Colombier 	if(m < len)
260*63afb9a5SDavid du Colombier 		return 0;
261*63afb9a5SDavid du Colombier 	return BIT32SZ+m;
262*63afb9a5SDavid du Colombier }
263*63afb9a5SDavid du Colombier 
264*63afb9a5SDavid du Colombier void
265*63afb9a5SDavid du Colombier stend(Srv *)
266*63afb9a5SDavid du Colombier {
267*63afb9a5SDavid du Colombier 	closeioproc(io9p);
268*63afb9a5SDavid du Colombier 	threadkillgrp(threadgetgrp());
269*63afb9a5SDavid du Colombier }
270*63afb9a5SDavid du Colombier 
271*63afb9a5SDavid du Colombier void
272*63afb9a5SDavid du Colombier server(char *mntpt, char *srvpt)
273*63afb9a5SDavid du Colombier {
274*63afb9a5SDavid du Colombier 	Dir d;
275*63afb9a5SDavid du Colombier 	char *p;
276*63afb9a5SDavid du Colombier 	int fd;
277*63afb9a5SDavid du Colombier 
278*63afb9a5SDavid du Colombier 	netsshsrv.tree = alloctree(uid, uid, 0777, nil);
279*63afb9a5SDavid du Colombier 	rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,
280*63afb9a5SDavid du Colombier 		(void*)Qroot);
281*63afb9a5SDavid du Colombier 	clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);
282*63afb9a5SDavid du Colombier 	ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);
283*63afb9a5SDavid du Colombier 	keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem);
284*63afb9a5SDavid du Colombier 
285*63afb9a5SDavid du Colombier 	threadpostmountsrv(&netsshsrv, srvpt, mntpt, MAFTER);
286*63afb9a5SDavid du Colombier 
287*63afb9a5SDavid du Colombier 	p = esmprint("%s/cs", mntpt);
288*63afb9a5SDavid du Colombier 	fd = open(p, OWRITE);
289*63afb9a5SDavid du Colombier 	free(p);
290*63afb9a5SDavid du Colombier 	if (fd >= 0) {
291*63afb9a5SDavid du Colombier 		fprint(fd, "add ssh");
292*63afb9a5SDavid du Colombier 		close(fd);
293*63afb9a5SDavid du Colombier 	}
294*63afb9a5SDavid du Colombier 	if (srvpt) {
295*63afb9a5SDavid du Colombier 		nulldir(&d);
296*63afb9a5SDavid du Colombier 		d.mode = 0666;
297*63afb9a5SDavid du Colombier 		p = esmprint("/srv/%s", srvpt);
298*63afb9a5SDavid du Colombier 		dirwstat(p, &d);
299*63afb9a5SDavid du Colombier 		free(p);
300*63afb9a5SDavid du Colombier 	}
301*63afb9a5SDavid du Colombier 	sshdebug(nil, "server started for %s", getuser());
302*63afb9a5SDavid du Colombier }
303*63afb9a5SDavid du Colombier 
304*63afb9a5SDavid du Colombier static void
305*63afb9a5SDavid du Colombier respexit(Conn *c, Req *r, void *freeme, char *msg)
306*63afb9a5SDavid du Colombier {
307*63afb9a5SDavid du Colombier 	if (msg)
308*63afb9a5SDavid du Colombier 		sshdebug(c, "%s", msg);
309*63afb9a5SDavid du Colombier 	r->aux = 0;
310*63afb9a5SDavid du Colombier 	respond(r, msg);
311*63afb9a5SDavid du Colombier 	free(freeme);
312*63afb9a5SDavid du Colombier 	threadexits(nil);	/* maybe use msg here */
313*63afb9a5SDavid du Colombier }
314*63afb9a5SDavid du Colombier 
315*63afb9a5SDavid du Colombier void
316*63afb9a5SDavid du Colombier stopen(Req *r)
317*63afb9a5SDavid du Colombier {
318*63afb9a5SDavid du Colombier 	int lev, xconn, fd;
319*63afb9a5SDavid du Colombier 	uvlong qidpath;
320*63afb9a5SDavid du Colombier 	char *p;
321*63afb9a5SDavid du Colombier 	char buf[32];
322*63afb9a5SDavid du Colombier 	Conn *c;
323*63afb9a5SDavid du Colombier 	SSHChan *sc;
324*63afb9a5SDavid du Colombier 
325*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
326*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
327*63afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
328*63afb9a5SDavid du Colombier 	default:
329*63afb9a5SDavid du Colombier 		respond(r, nil);
330*63afb9a5SDavid du Colombier 		break;
331*63afb9a5SDavid du Colombier 	case Qlisten:
332*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate((lev == Connection?
333*63afb9a5SDavid du Colombier 			stlisconn: stlischan), r, Defstk);
334*63afb9a5SDavid du Colombier 		break;
335*63afb9a5SDavid du Colombier 	case Qclone:
336*63afb9a5SDavid du Colombier 		switch (lev) {
337*63afb9a5SDavid du Colombier 		case Top:
338*63afb9a5SDavid du Colombier 			/* should use dial(2) instead of diddling /net/tcp */
339*63afb9a5SDavid du Colombier 			p = esmprint("%s/tcp/clone", mntpt);
340*63afb9a5SDavid du Colombier 			fd = open(p, ORDWR);
341*63afb9a5SDavid du Colombier 			if (fd < 0) {
342*63afb9a5SDavid du Colombier 				sshdebug(nil, "stopen: open %s failed: %r", p);
343*63afb9a5SDavid du Colombier 				free(p);
344*63afb9a5SDavid du Colombier 				responderror(r);
345*63afb9a5SDavid du Colombier 				return;
346*63afb9a5SDavid du Colombier 			}
347*63afb9a5SDavid du Colombier 			free(p);
348*63afb9a5SDavid du Colombier 
349*63afb9a5SDavid du Colombier 			c = alloc_conn();
350*63afb9a5SDavid du Colombier 			if (c == nil) {
351*63afb9a5SDavid du Colombier 				close(fd);
352*63afb9a5SDavid du Colombier 				respond(r, "no more connections");
353*63afb9a5SDavid du Colombier 				return;
354*63afb9a5SDavid du Colombier 			}
355*63afb9a5SDavid du Colombier 			c->ctlfd = fd;
356*63afb9a5SDavid du Colombier 			c->poisoned = 0;
357*63afb9a5SDavid du Colombier 			filedup(r, c->ctlfile);
358*63afb9a5SDavid du Colombier 			sshlog(c, "new connection on fd %d", fd);
359*63afb9a5SDavid du Colombier 			break;
360*63afb9a5SDavid du Colombier 		case Connection:
361*63afb9a5SDavid du Colombier 			xconn = (qidpath >> Connshift) & Connmask;
362*63afb9a5SDavid du Colombier 			c = connections[xconn];
363*63afb9a5SDavid du Colombier 			if (c == nil) {
364*63afb9a5SDavid du Colombier 				respond(r, "bad connection");
365*63afb9a5SDavid du Colombier 				return;
366*63afb9a5SDavid du Colombier 			}
367*63afb9a5SDavid du Colombier 			sc = alloc_chan(c);
368*63afb9a5SDavid du Colombier 			if (sc == nil) {
369*63afb9a5SDavid du Colombier 				respond(r, "no more channels");
370*63afb9a5SDavid du Colombier 				return;
371*63afb9a5SDavid du Colombier 			}
372*63afb9a5SDavid du Colombier 			filedup(r, sc->ctl);
373*63afb9a5SDavid du Colombier 			break;
374*63afb9a5SDavid du Colombier 		default:
375*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "bad level %d", lev);
376*63afb9a5SDavid du Colombier 			readstr(r, buf);
377*63afb9a5SDavid du Colombier 			break;
378*63afb9a5SDavid du Colombier 		}
379*63afb9a5SDavid du Colombier 		respond(r, nil);
380*63afb9a5SDavid du Colombier 		break;
381*63afb9a5SDavid du Colombier 	}
382*63afb9a5SDavid du Colombier }
383*63afb9a5SDavid du Colombier 
384*63afb9a5SDavid du Colombier static void
385*63afb9a5SDavid du Colombier listerrexit(Req *r, Ioproc *io, Conn *cl)
386*63afb9a5SDavid du Colombier {
387*63afb9a5SDavid du Colombier 	r->aux = 0;
388*63afb9a5SDavid du Colombier 	responderror(r);
389*63afb9a5SDavid du Colombier 	closeioproc(io);
390*63afb9a5SDavid du Colombier 	shutdown(cl);
391*63afb9a5SDavid du Colombier 	threadexits(nil);
392*63afb9a5SDavid du Colombier }
393*63afb9a5SDavid du Colombier 
394*63afb9a5SDavid du Colombier void
395*63afb9a5SDavid du Colombier stlisconn(void *a)
396*63afb9a5SDavid du Colombier {
397*63afb9a5SDavid du Colombier 	int xconn, fd, n;
398*63afb9a5SDavid du Colombier 	uvlong qidpath;
399*63afb9a5SDavid du Colombier 	char *msg;
400*63afb9a5SDavid du Colombier 	char buf[Numbsz], path[NETPATHLEN];
401*63afb9a5SDavid du Colombier 	Conn *c, *cl;
402*63afb9a5SDavid du Colombier 	Ioproc *io;
403*63afb9a5SDavid du Colombier 	Req *r;
404*63afb9a5SDavid du Colombier 
405*63afb9a5SDavid du Colombier 	threadsetname("stlisconn");
406*63afb9a5SDavid du Colombier 	r = a;
407*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
408*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
409*63afb9a5SDavid du Colombier 
410*63afb9a5SDavid du Colombier 	cl = connections[xconn];
411*63afb9a5SDavid du Colombier 	if (cl == nil) {
412*63afb9a5SDavid du Colombier 		sshlog(cl, "bad connection");
413*63afb9a5SDavid du Colombier 		respond(r, "bad connection");
414*63afb9a5SDavid du Colombier 		threadexits("bad connection");
415*63afb9a5SDavid du Colombier 	}
416*63afb9a5SDavid du Colombier 	if (cl->poisoned) {
417*63afb9a5SDavid du Colombier 		sshdebug(cl, "stlisconn conn %d poisoned", xconn);
418*63afb9a5SDavid du Colombier 		r->aux = 0;
419*63afb9a5SDavid du Colombier 		respond(r, "top level listen conn poisoned");
420*63afb9a5SDavid du Colombier 		threadexits("top level listen conn poisoned");
421*63afb9a5SDavid du Colombier 	}
422*63afb9a5SDavid du Colombier 	if (cl->ctlfd < 0) {
423*63afb9a5SDavid du Colombier 		sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn);
424*63afb9a5SDavid du Colombier 		r->aux = 0;
425*63afb9a5SDavid du Colombier 		respond(r, "top level listen with closed fd");
426*63afb9a5SDavid du Colombier 		shutdown(cl);
427*63afb9a5SDavid du Colombier 		cl->poisoned = 1;	/* no more use until ctlfd is set */
428*63afb9a5SDavid du Colombier 		threadexits("top level listen with closed fd");
429*63afb9a5SDavid du Colombier 	}
430*63afb9a5SDavid du Colombier 
431*63afb9a5SDavid du Colombier 	io = ioproc();
432*63afb9a5SDavid du Colombier 
433*63afb9a5SDavid du Colombier 	/* read xconn's tcp conn's ctl file */
434*63afb9a5SDavid du Colombier 	seek(cl->ctlfd, 0, 0);
435*63afb9a5SDavid du Colombier 	n = ioread(io, cl->ctlfd, buf, sizeof buf - 1);
436*63afb9a5SDavid du Colombier 	if (n == 0) {
437*63afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd);
438*63afb9a5SDavid du Colombier 		listerrexit(r, io, cl);
439*63afb9a5SDavid du Colombier 	} else if (n < 0) {
440*63afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd);
441*63afb9a5SDavid du Colombier 		listerrexit(r, io, cl);
442*63afb9a5SDavid du Colombier 	}
443*63afb9a5SDavid du Colombier 	buf[n] = '\0';
444*63afb9a5SDavid du Colombier 
445*63afb9a5SDavid du Colombier 	cl->state = Listening;
446*63afb9a5SDavid du Colombier 	/* should use dial(2) instead of diddling /net/tcp */
447*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf);
448*63afb9a5SDavid du Colombier 	for(;;) {
449*63afb9a5SDavid du Colombier 		fd = ioopen(io, path, ORDWR);
450*63afb9a5SDavid du Colombier 		if (fd < 0)
451*63afb9a5SDavid du Colombier 			listerrexit(r, io, cl);
452*63afb9a5SDavid du Colombier 		c = alloc_conn();
453*63afb9a5SDavid du Colombier 		if (c)
454*63afb9a5SDavid du Colombier 			break;
455*63afb9a5SDavid du Colombier 		n = ioread(io, fd, buf, sizeof buf - 1);
456*63afb9a5SDavid du Colombier 		if (n <= 0)
457*63afb9a5SDavid du Colombier 			listerrexit(r, io, cl);
458*63afb9a5SDavid du Colombier 		buf[n] = '\0';
459*63afb9a5SDavid du Colombier 		msg = smprint("reject %s no available connections", buf);
460*63afb9a5SDavid du Colombier 		iowrite(io, fd, msg, strlen(msg));
461*63afb9a5SDavid du Colombier 		free(msg);
462*63afb9a5SDavid du Colombier 		close(fd);			/* surely ioclose? */
463*63afb9a5SDavid du Colombier 	}
464*63afb9a5SDavid du Colombier 	c->ctlfd = fd;
465*63afb9a5SDavid du Colombier 	if (c->ctlfd < 0) {
466*63afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn);
467*63afb9a5SDavid du Colombier 		threadexitsall("stlisconn c->ctlfd < 0");
468*63afb9a5SDavid du Colombier 	}
469*63afb9a5SDavid du Colombier 	c->poisoned = 0;
470*63afb9a5SDavid du Colombier 	c->stifle = 1;			/* defer server; was for coexistence */
471*63afb9a5SDavid du Colombier 	filedup(r, c->ctlfile);
472*63afb9a5SDavid du Colombier 	sshdebug(c, "responding to listen open");
473*63afb9a5SDavid du Colombier 	r->aux = 0;
474*63afb9a5SDavid du Colombier 	respond(r, nil);
475*63afb9a5SDavid du Colombier 	closeioproc(io);
476*63afb9a5SDavid du Colombier 	threadexits(nil);
477*63afb9a5SDavid du Colombier }
478*63afb9a5SDavid du Colombier 
479*63afb9a5SDavid du Colombier void
480*63afb9a5SDavid du Colombier stlischan(void *a)
481*63afb9a5SDavid du Colombier {
482*63afb9a5SDavid du Colombier 	Req *r;
483*63afb9a5SDavid du Colombier 	Packet *p2;
484*63afb9a5SDavid du Colombier 	Ioproc *io;
485*63afb9a5SDavid du Colombier 	Conn *c;
486*63afb9a5SDavid du Colombier 	SSHChan *sc;
487*63afb9a5SDavid du Colombier 	int i, n, xconn;
488*63afb9a5SDavid du Colombier 	uvlong qidpath;
489*63afb9a5SDavid du Colombier 
490*63afb9a5SDavid du Colombier 	threadsetname("stlischan");
491*63afb9a5SDavid du Colombier 	r = a;
492*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
493*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
494*63afb9a5SDavid du Colombier 	c = connections[xconn];
495*63afb9a5SDavid du Colombier 	if (c == nil) {
496*63afb9a5SDavid du Colombier 		respond(r, "bad channel");
497*63afb9a5SDavid du Colombier 		sshlog(c, "bad channel");
498*63afb9a5SDavid du Colombier 		threadexits(nil);
499*63afb9a5SDavid du Colombier 	}
500*63afb9a5SDavid du Colombier 	if (c->state == Closed || c->state == Closing)
501*63afb9a5SDavid du Colombier 		respexit(c, r, nil, "channel listen on closed connection");
502*63afb9a5SDavid du Colombier 	sc = c->chans[qidpath & Chanmask];
503*63afb9a5SDavid du Colombier 
504*63afb9a5SDavid du Colombier 	qlock(&c->l);
505*63afb9a5SDavid du Colombier 	sc->lreq = r;
506*63afb9a5SDavid du Colombier 	for (i = 0; i < c->nchan; ++i)
507*63afb9a5SDavid du Colombier 		if (c->chans[i] && c->chans[i]->state == Opening &&
508*63afb9a5SDavid du Colombier 		    c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0)
509*63afb9a5SDavid du Colombier 			break;
510*63afb9a5SDavid du Colombier 	if (i >= c->nchan) {
511*63afb9a5SDavid du Colombier 		sc->state = Listening;
512*63afb9a5SDavid du Colombier 		rsleep(&sc->r);
513*63afb9a5SDavid du Colombier 		i = sc->waker;
514*63afb9a5SDavid du Colombier 		if (i < 0) {
515*63afb9a5SDavid du Colombier 			qunlock(&c->l);
516*63afb9a5SDavid du Colombier 			r->aux = 0;
517*63afb9a5SDavid du Colombier 			responderror(r);
518*63afb9a5SDavid du Colombier 			threadexits(nil);
519*63afb9a5SDavid du Colombier 		}
520*63afb9a5SDavid du Colombier 	} else
521*63afb9a5SDavid du Colombier 		rwakeup(&c->chans[i]->r);
522*63afb9a5SDavid du Colombier 	qunlock(&c->l);
523*63afb9a5SDavid du Colombier 
524*63afb9a5SDavid du Colombier 	if (c->state == Closed || c->state == Closing || c->state == Eof)
525*63afb9a5SDavid du Colombier 		respexit(c, r, nil, "channel listen on closed connection");
526*63afb9a5SDavid du Colombier 	c->chans[i]->state = Established;
527*63afb9a5SDavid du Colombier 
528*63afb9a5SDavid du Colombier 	p2 = new_packet(c);
529*63afb9a5SDavid du Colombier 	c->chans[i]->rwindow = Maxpayload;
530*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
531*63afb9a5SDavid du Colombier 	hnputl(p2->payload + 1, c->chans[i]->otherid);
532*63afb9a5SDavid du Colombier 	hnputl(p2->payload + 5, c->chans[i]->id);
533*63afb9a5SDavid du Colombier 	hnputl(p2->payload + 9, Maxpayload);
534*63afb9a5SDavid du Colombier 	hnputl(p2->payload + 13, Maxrpcbuf);
535*63afb9a5SDavid du Colombier 	p2->rlength = 18;
536*63afb9a5SDavid du Colombier 	n = finish_packet(p2);
537*63afb9a5SDavid du Colombier 	filedup(r, c->chans[i]->ctl);
538*63afb9a5SDavid du Colombier 
539*63afb9a5SDavid du Colombier 	io = ioproc();
540*63afb9a5SDavid du Colombier 	n = iowrite(io, c->datafd, p2->nlength, n);
541*63afb9a5SDavid du Colombier 	closeioproc(io);
542*63afb9a5SDavid du Colombier 
543*63afb9a5SDavid du Colombier 	free(p2);
544*63afb9a5SDavid du Colombier 
545*63afb9a5SDavid du Colombier 	sshdebug(c, "responding to chan listen open");
546*63afb9a5SDavid du Colombier 	r->aux = 0;
547*63afb9a5SDavid du Colombier 	if (n < 0)
548*63afb9a5SDavid du Colombier 		responderror(r);
549*63afb9a5SDavid du Colombier 	else
550*63afb9a5SDavid du Colombier 		respond(r, nil);
551*63afb9a5SDavid du Colombier 	threadexits(nil);
552*63afb9a5SDavid du Colombier }
553*63afb9a5SDavid du Colombier 
554*63afb9a5SDavid du Colombier void
555*63afb9a5SDavid du Colombier getdata(Conn *c, SSHChan *sc, Req *r)
556*63afb9a5SDavid du Colombier {
557*63afb9a5SDavid du Colombier 	Packet *p;
558*63afb9a5SDavid du Colombier 	Plist *d;
559*63afb9a5SDavid du Colombier 	int n;
560*63afb9a5SDavid du Colombier 
561*63afb9a5SDavid du Colombier 	n = r->ifcall.count;
562*63afb9a5SDavid du Colombier 	if (sc->dataq->rem < n)
563*63afb9a5SDavid du Colombier 		n = sc->dataq->rem;
564*63afb9a5SDavid du Colombier 	if (n > Maxrpcbuf)
565*63afb9a5SDavid du Colombier 		n = Maxrpcbuf;
566*63afb9a5SDavid du Colombier 	r->ifcall.offset = 0;
567*63afb9a5SDavid du Colombier 
568*63afb9a5SDavid du Colombier 	readbuf(r, sc->dataq->st, n);
569*63afb9a5SDavid du Colombier 	sc->dataq->st += n;
570*63afb9a5SDavid du Colombier 	sc->dataq->rem -= n;
571*63afb9a5SDavid du Colombier 	sc->inrqueue -= n;
572*63afb9a5SDavid du Colombier 	if (sc->dataq->rem <= 0) {
573*63afb9a5SDavid du Colombier 		d = sc->dataq;
574*63afb9a5SDavid du Colombier 		sc->dataq = sc->dataq->next;
575*63afb9a5SDavid du Colombier 		if (d->pack->tlength > sc->rwindow)
576*63afb9a5SDavid du Colombier 			sc->rwindow = 0;
577*63afb9a5SDavid du Colombier 		else
578*63afb9a5SDavid du Colombier 			sc->rwindow -= d->pack->tlength;
579*63afb9a5SDavid du Colombier 		free(d->pack);
580*63afb9a5SDavid du Colombier 		free(d);
581*63afb9a5SDavid du Colombier 	}
582*63afb9a5SDavid du Colombier 	if (sc->rwindow < 16*1024) {		/* magic.  half-way, maybe? */
583*63afb9a5SDavid du Colombier 		sc->rwindow += Maxpayload;
584*63afb9a5SDavid du Colombier 		sshdebug(c, "increasing receive window to %lud, inq %lud\n",
585*63afb9a5SDavid du Colombier 			argv0, sc->rwindow, sc->inrqueue);
586*63afb9a5SDavid du Colombier 		p = new_packet(c);
587*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST);
588*63afb9a5SDavid du Colombier 		hnputl(p->payload+1, sc->otherid);
589*63afb9a5SDavid du Colombier 		hnputl(p->payload+5, Maxpayload);
590*63afb9a5SDavid du Colombier 		p->rlength += 8;
591*63afb9a5SDavid du Colombier 		n = finish_packet(p);
592*63afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
593*63afb9a5SDavid du Colombier 		free(p);
594*63afb9a5SDavid du Colombier 	}
595*63afb9a5SDavid du Colombier 	r->aux = 0;
596*63afb9a5SDavid du Colombier 	respond(r, nil);
597*63afb9a5SDavid du Colombier }
598*63afb9a5SDavid du Colombier 
599*63afb9a5SDavid du Colombier void
600*63afb9a5SDavid du Colombier stread(Req *r)
601*63afb9a5SDavid du Colombier {
602*63afb9a5SDavid du Colombier 	Conn *c;
603*63afb9a5SDavid du Colombier 	SSHChan *sc;
604*63afb9a5SDavid du Colombier 	int n, lev, cnum, xconn;
605*63afb9a5SDavid du Colombier 	uvlong qidpath;
606*63afb9a5SDavid du Colombier 	char buf[Arbbufsz], path[NETPATHLEN];
607*63afb9a5SDavid du Colombier 
608*63afb9a5SDavid du Colombier 	threadsetname("stread");
609*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
610*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
611*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
612*63afb9a5SDavid du Colombier 	c = connections[xconn];
613*63afb9a5SDavid du Colombier 	if (c == nil) {
614*63afb9a5SDavid du Colombier 		if (lev != Top || (qidpath & Qtypemask) != Qreqrem) {
615*63afb9a5SDavid du Colombier 			respond(r, "Invalid connection");
616*63afb9a5SDavid du Colombier 			return;
617*63afb9a5SDavid du Colombier 		}
618*63afb9a5SDavid du Colombier 		cnum = 0;
619*63afb9a5SDavid du Colombier 		sc = nil;
620*63afb9a5SDavid du Colombier 	} else {
621*63afb9a5SDavid du Colombier 		cnum = qidpath & Chanmask;
622*63afb9a5SDavid du Colombier 		sc = c->chans[cnum];
623*63afb9a5SDavid du Colombier 	}
624*63afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
625*63afb9a5SDavid du Colombier 	case Qctl:
626*63afb9a5SDavid du Colombier 	case Qlisten:
627*63afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
628*63afb9a5SDavid du Colombier 			respond(r, nil);
629*63afb9a5SDavid du Colombier 			break;
630*63afb9a5SDavid du Colombier 		}
631*63afb9a5SDavid du Colombier 		switch (lev) {
632*63afb9a5SDavid du Colombier 		case Top:
633*63afb9a5SDavid du Colombier 			readstr(r, st_names[c->state]);
634*63afb9a5SDavid du Colombier 			break;
635*63afb9a5SDavid du Colombier 		case Connection:
636*63afb9a5SDavid du Colombier 		case Subchannel:
637*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "%d", lev == Connection?
638*63afb9a5SDavid du Colombier 				xconn: cnum);
639*63afb9a5SDavid du Colombier 			readstr(r, buf);
640*63afb9a5SDavid du Colombier 			break;
641*63afb9a5SDavid du Colombier 		default:
642*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "stread error, level %d", lev);
643*63afb9a5SDavid du Colombier 			respond(r, buf);
644*63afb9a5SDavid du Colombier 			return;
645*63afb9a5SDavid du Colombier 		}
646*63afb9a5SDavid du Colombier 		respond(r, nil);
647*63afb9a5SDavid du Colombier 		break;
648*63afb9a5SDavid du Colombier 	case Qclone:
649*63afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
650*63afb9a5SDavid du Colombier 			respond(r, nil);
651*63afb9a5SDavid du Colombier 			break;
652*63afb9a5SDavid du Colombier 		}
653*63afb9a5SDavid du Colombier 		readstr(r, "Congratulations, you've achieved the impossible\n");
654*63afb9a5SDavid du Colombier 		respond(r, nil);
655*63afb9a5SDavid du Colombier 		break;
656*63afb9a5SDavid du Colombier 	case Qdata:
657*63afb9a5SDavid du Colombier 		if (lev == Top) {
658*63afb9a5SDavid du Colombier 			respond(r, nil);
659*63afb9a5SDavid du Colombier 			break;
660*63afb9a5SDavid du Colombier 		}
661*63afb9a5SDavid du Colombier 		if (lev == Connection) {
662*63afb9a5SDavid du Colombier 			if (0 && c->stifle) {	/* was for coexistence */
663*63afb9a5SDavid du Colombier 				c->stifle = 0;
664*63afb9a5SDavid du Colombier 				if (deferredinit(c) < 0) {
665*63afb9a5SDavid du Colombier 					respond(r, "deferredinit failed");
666*63afb9a5SDavid du Colombier 					break;
667*63afb9a5SDavid du Colombier 				}
668*63afb9a5SDavid du Colombier 			}
669*63afb9a5SDavid du Colombier 			if (c->cap)			/* auth capability? */
670*63afb9a5SDavid du Colombier 				readstr(r, c->cap);
671*63afb9a5SDavid du Colombier 			respond(r, nil);
672*63afb9a5SDavid du Colombier 			break;
673*63afb9a5SDavid du Colombier 		}
674*63afb9a5SDavid du Colombier 
675*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(readdata, r, Defstk);
676*63afb9a5SDavid du Colombier 		break;
677*63afb9a5SDavid du Colombier 	case Qlocal:
678*63afb9a5SDavid du Colombier 		if (lev == Connection)
679*63afb9a5SDavid du Colombier 			if (c->ctlfd < 0)
680*63afb9a5SDavid du Colombier 				readstr(r, "::!0\n");
681*63afb9a5SDavid du Colombier 			else {
682*63afb9a5SDavid du Colombier 				n = pread(c->ctlfd, buf, 10, 0); // magic 10
683*63afb9a5SDavid du Colombier 				buf[n >= 0? n: 0] = '\0';
684*63afb9a5SDavid du Colombier 				snprint(path, sizeof path, "%s/tcp/%s/local",
685*63afb9a5SDavid du Colombier 					mntpt, buf);
686*63afb9a5SDavid du Colombier 				readfile(path, buf, sizeof buf);
687*63afb9a5SDavid du Colombier 				readstr(r, buf);
688*63afb9a5SDavid du Colombier 			}
689*63afb9a5SDavid du Colombier 		respond(r, nil);
690*63afb9a5SDavid du Colombier 		break;
691*63afb9a5SDavid du Colombier 	case Qreqrem:
692*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(readreqrem, r, Defstk);
693*63afb9a5SDavid du Colombier 		break;
694*63afb9a5SDavid du Colombier 	case Qstatus:
695*63afb9a5SDavid du Colombier 		switch (lev) {
696*63afb9a5SDavid du Colombier 		case Top:
697*63afb9a5SDavid du Colombier 			readstr(r, "Impossible");
698*63afb9a5SDavid du Colombier 			break;
699*63afb9a5SDavid du Colombier 		case Connection:
700*63afb9a5SDavid du Colombier 			readstr(r, (uint)c->state > Closed?
701*63afb9a5SDavid du Colombier 				"Unknown": st_names[c->state]);
702*63afb9a5SDavid du Colombier 			break;
703*63afb9a5SDavid du Colombier 		case Subchannel:
704*63afb9a5SDavid du Colombier 			readstr(r, (uint)sc->state > Closed?
705*63afb9a5SDavid du Colombier 				"Unknown": st_names[sc->state]);
706*63afb9a5SDavid du Colombier 			break;
707*63afb9a5SDavid du Colombier 		}
708*63afb9a5SDavid du Colombier 		respond(r, nil);
709*63afb9a5SDavid du Colombier 		break;
710*63afb9a5SDavid du Colombier 	case Qtcp:
711*63afb9a5SDavid du Colombier 		/* connection number of underlying tcp connection */
712*63afb9a5SDavid du Colombier 		if (lev == Connection)
713*63afb9a5SDavid du Colombier 			if (c->ctlfd < 0)
714*63afb9a5SDavid du Colombier 				readstr(r, "-1\n");
715*63afb9a5SDavid du Colombier 			else {
716*63afb9a5SDavid du Colombier 				n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */
717*63afb9a5SDavid du Colombier 				buf[n >= 0? n: 0] = '\0';
718*63afb9a5SDavid du Colombier 				readstr(r, buf);
719*63afb9a5SDavid du Colombier 			}
720*63afb9a5SDavid du Colombier 		respond(r, nil);
721*63afb9a5SDavid du Colombier 		break;
722*63afb9a5SDavid du Colombier 	default:
723*63afb9a5SDavid du Colombier 		respond(r, nil);
724*63afb9a5SDavid du Colombier 		break;
725*63afb9a5SDavid du Colombier 	}
726*63afb9a5SDavid du Colombier }
727*63afb9a5SDavid du Colombier 
728*63afb9a5SDavid du Colombier void
729*63afb9a5SDavid du Colombier readreqrem(void *a)
730*63afb9a5SDavid du Colombier {
731*63afb9a5SDavid du Colombier 	Ioproc *io;
732*63afb9a5SDavid du Colombier 	Req *r;
733*63afb9a5SDavid du Colombier 	Conn *c;
734*63afb9a5SDavid du Colombier 	SSHChan *sc;
735*63afb9a5SDavid du Colombier 	int fd, n, lev, cnum, xconn;
736*63afb9a5SDavid du Colombier 	uvlong qidpath;
737*63afb9a5SDavid du Colombier 	char buf[Arbbufsz], path[NETPATHLEN];
738*63afb9a5SDavid du Colombier 
739*63afb9a5SDavid du Colombier 	threadsetname("readreqrem");
740*63afb9a5SDavid du Colombier 	r = a;
741*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
742*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
743*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
744*63afb9a5SDavid du Colombier 	c = connections[xconn];
745*63afb9a5SDavid du Colombier 	if (c == nil) {
746*63afb9a5SDavid du Colombier 		if (lev != Top) {
747*63afb9a5SDavid du Colombier 			respond(r, "Invalid connection");
748*63afb9a5SDavid du Colombier 			return;
749*63afb9a5SDavid du Colombier 		}
750*63afb9a5SDavid du Colombier 		sc = nil;
751*63afb9a5SDavid du Colombier 	} else {
752*63afb9a5SDavid du Colombier 		cnum = qidpath & Chanmask;
753*63afb9a5SDavid du Colombier 		sc = c->chans[cnum];
754*63afb9a5SDavid du Colombier 	}
755*63afb9a5SDavid du Colombier 	switch (lev) {
756*63afb9a5SDavid du Colombier 	case Top:
757*63afb9a5SDavid du Colombier 		if (r->ifcall.offset == 0 && keymbox.state != Empty) {
758*63afb9a5SDavid du Colombier 			r->aux = 0;
759*63afb9a5SDavid du Colombier 			respond(r, "Key file collision");	/* WTF? */
760*63afb9a5SDavid du Colombier 			break;
761*63afb9a5SDavid du Colombier 		}
762*63afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
763*63afb9a5SDavid du Colombier 			readstr(r, keymbox.msg);
764*63afb9a5SDavid du Colombier 			r->aux = 0;
765*63afb9a5SDavid du Colombier 			respond(r, nil);
766*63afb9a5SDavid du Colombier 			if (r->ifcall.offset + r->ifcall.count >=
767*63afb9a5SDavid du Colombier 			    strlen(keymbox.msg))
768*63afb9a5SDavid du Colombier 				keymbox.state = Empty;
769*63afb9a5SDavid du Colombier 			else
770*63afb9a5SDavid du Colombier 				keymbox.state = Allocated;
771*63afb9a5SDavid du Colombier 			break;
772*63afb9a5SDavid du Colombier 		}
773*63afb9a5SDavid du Colombier 		keymbox.state = Allocated;
774*63afb9a5SDavid du Colombier 		for(;;) {
775*63afb9a5SDavid du Colombier 			if (keymbox.msg == nil)
776*63afb9a5SDavid du Colombier 				if (recv(keymbox.mchan, nil) < 0) {
777*63afb9a5SDavid du Colombier 					r->aux = 0;
778*63afb9a5SDavid du Colombier 					responderror(r);
779*63afb9a5SDavid du Colombier 					keymbox.state = Empty;
780*63afb9a5SDavid du Colombier 					threadexits(nil);
781*63afb9a5SDavid du Colombier 				}
782*63afb9a5SDavid du Colombier 			if (keymbox.state == Empty)
783*63afb9a5SDavid du Colombier 				break;
784*63afb9a5SDavid du Colombier 			else if (keymbox.state == Allocated) {
785*63afb9a5SDavid du Colombier 				if (keymbox.msg) {
786*63afb9a5SDavid du Colombier 					readstr(r, keymbox.msg);
787*63afb9a5SDavid du Colombier 					if (r->ifcall.offset + r->ifcall.count
788*63afb9a5SDavid du Colombier 					    >= strlen(keymbox.msg)) {
789*63afb9a5SDavid du Colombier 						free(keymbox.msg);
790*63afb9a5SDavid du Colombier 						keymbox.msg = nil;
791*63afb9a5SDavid du Colombier 						keymbox.state = Empty;
792*63afb9a5SDavid du Colombier 					}
793*63afb9a5SDavid du Colombier 				}
794*63afb9a5SDavid du Colombier 				break;
795*63afb9a5SDavid du Colombier 			}
796*63afb9a5SDavid du Colombier 		}
797*63afb9a5SDavid du Colombier 		r->aux = 0;
798*63afb9a5SDavid du Colombier 		respond(r, nil);
799*63afb9a5SDavid du Colombier 		break;
800*63afb9a5SDavid du Colombier 	case Connection:
801*63afb9a5SDavid du Colombier 		if (c->ctlfd >= 0) {
802*63afb9a5SDavid du Colombier 			io = ioproc();
803*63afb9a5SDavid du Colombier 			seek(c->ctlfd, 0, 0);
804*63afb9a5SDavid du Colombier 			n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */
805*63afb9a5SDavid du Colombier 			if (n < 0) {
806*63afb9a5SDavid du Colombier 				r->aux = 0;
807*63afb9a5SDavid du Colombier 				responderror(r);
808*63afb9a5SDavid du Colombier 				closeioproc(io);
809*63afb9a5SDavid du Colombier 				break;
810*63afb9a5SDavid du Colombier 			}
811*63afb9a5SDavid du Colombier 			buf[n] = '\0';
812*63afb9a5SDavid du Colombier 			snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf);
813*63afb9a5SDavid du Colombier 			if ((fd = ioopen(io, path, OREAD)) < 0 ||
814*63afb9a5SDavid du Colombier 			    (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) {
815*63afb9a5SDavid du Colombier 				r->aux = 0;
816*63afb9a5SDavid du Colombier 				responderror(r);
817*63afb9a5SDavid du Colombier 				if (fd >= 0)
818*63afb9a5SDavid du Colombier 					ioclose(io, fd);
819*63afb9a5SDavid du Colombier 				closeioproc(io);
820*63afb9a5SDavid du Colombier 				break;
821*63afb9a5SDavid du Colombier 			}
822*63afb9a5SDavid du Colombier 			ioclose(io, fd);
823*63afb9a5SDavid du Colombier 			closeioproc(io);
824*63afb9a5SDavid du Colombier 			buf[n] = '\0';
825*63afb9a5SDavid du Colombier 			readstr(r, buf);
826*63afb9a5SDavid du Colombier 		} else
827*63afb9a5SDavid du Colombier 			readstr(r, "::!0\n");
828*63afb9a5SDavid du Colombier 		r->aux = 0;
829*63afb9a5SDavid du Colombier 		respond(r, nil);
830*63afb9a5SDavid du Colombier 		break;
831*63afb9a5SDavid du Colombier 	case Subchannel:
832*63afb9a5SDavid du Colombier 		if ((sc->state == Closed || sc->state == Closing ||
833*63afb9a5SDavid du Colombier 		    sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) {
834*63afb9a5SDavid du Colombier 			sshdebug(c, "sending EOF1 to channel request listener");
835*63afb9a5SDavid du Colombier 			r->aux = 0;
836*63afb9a5SDavid du Colombier 			respond(r, nil);
837*63afb9a5SDavid du Colombier 			break;
838*63afb9a5SDavid du Colombier 		}
839*63afb9a5SDavid du Colombier 		while (sc->reqq == nil) {
840*63afb9a5SDavid du Colombier 			if (recv(sc->reqchan, nil) < 0) {
841*63afb9a5SDavid du Colombier 				r->aux = 0;
842*63afb9a5SDavid du Colombier 				responderror(r);
843*63afb9a5SDavid du Colombier 				threadexits(nil);
844*63afb9a5SDavid du Colombier 			}
845*63afb9a5SDavid du Colombier 			if ((sc->state == Closed || sc->state == Closing ||
846*63afb9a5SDavid du Colombier 			    sc->state == Eof) && sc->reqq == nil &&
847*63afb9a5SDavid du Colombier 			    sc->dataq == nil) {
848*63afb9a5SDavid du Colombier 				sshdebug(c, "sending EOF2 to channel request "
849*63afb9a5SDavid du Colombier 					"listener");
850*63afb9a5SDavid du Colombier 				respexit(c, r, nil, nil);
851*63afb9a5SDavid du Colombier 			}
852*63afb9a5SDavid du Colombier 		}
853*63afb9a5SDavid du Colombier 		n = r->ifcall.count;
854*63afb9a5SDavid du Colombier 		if (sc->reqq->rem < n)
855*63afb9a5SDavid du Colombier 			n = sc->reqq->rem;
856*63afb9a5SDavid du Colombier 		if (n > Maxrpcbuf)
857*63afb9a5SDavid du Colombier 			n = Maxrpcbuf;
858*63afb9a5SDavid du Colombier 		r->ifcall.offset = 0;
859*63afb9a5SDavid du Colombier 		readbuf(r, sc->reqq->st, n);
860*63afb9a5SDavid du Colombier 		sc->reqq->st += n;
861*63afb9a5SDavid du Colombier 		sc->reqq->rem -= n;
862*63afb9a5SDavid du Colombier 		if (sc->reqq->rem <= 0) {
863*63afb9a5SDavid du Colombier 			Plist *d = sc->reqq;
864*63afb9a5SDavid du Colombier 			sc->reqq = sc->reqq->next;
865*63afb9a5SDavid du Colombier 			free(d->pack);
866*63afb9a5SDavid du Colombier 			free(d);
867*63afb9a5SDavid du Colombier 		}
868*63afb9a5SDavid du Colombier 		r->aux = 0;
869*63afb9a5SDavid du Colombier 		respond(r, nil);
870*63afb9a5SDavid du Colombier 		break;
871*63afb9a5SDavid du Colombier 	}
872*63afb9a5SDavid du Colombier 	threadexits(nil);
873*63afb9a5SDavid du Colombier }
874*63afb9a5SDavid du Colombier 
875*63afb9a5SDavid du Colombier void
876*63afb9a5SDavid du Colombier readdata(void *a)
877*63afb9a5SDavid du Colombier {
878*63afb9a5SDavid du Colombier 	Req *r;
879*63afb9a5SDavid du Colombier 	Conn *c;
880*63afb9a5SDavid du Colombier 	SSHChan *sc;
881*63afb9a5SDavid du Colombier 	int cnum, xconn;
882*63afb9a5SDavid du Colombier 	uvlong qidpath;
883*63afb9a5SDavid du Colombier 
884*63afb9a5SDavid du Colombier 	threadsetname("readdata");
885*63afb9a5SDavid du Colombier 	r = a;
886*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
887*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
888*63afb9a5SDavid du Colombier 	c = connections[xconn];
889*63afb9a5SDavid du Colombier 	if (c == nil) {
890*63afb9a5SDavid du Colombier 		respond(r, "bad connection");
891*63afb9a5SDavid du Colombier 		sshlog(c, "bad connection");
892*63afb9a5SDavid du Colombier 		threadexits(nil);
893*63afb9a5SDavid du Colombier 	}
894*63afb9a5SDavid du Colombier 	cnum = qidpath & Chanmask;
895*63afb9a5SDavid du Colombier 	sc = c->chans[cnum];
896*63afb9a5SDavid du Colombier 	if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing ||
897*63afb9a5SDavid du Colombier 	    sc->state == Eof)) {
898*63afb9a5SDavid du Colombier 		sshdebug(c, "sending EOF1 to channel listener");
899*63afb9a5SDavid du Colombier 		r->aux = 0;
900*63afb9a5SDavid du Colombier 		respond(r, nil);
901*63afb9a5SDavid du Colombier 		threadexits(nil);
902*63afb9a5SDavid du Colombier 	}
903*63afb9a5SDavid du Colombier 	if (sc->dataq != nil) {
904*63afb9a5SDavid du Colombier 		getdata(c, sc, r);
905*63afb9a5SDavid du Colombier 		threadexits(nil);
906*63afb9a5SDavid du Colombier 	}
907*63afb9a5SDavid du Colombier 	while (sc->dataq == nil) {
908*63afb9a5SDavid du Colombier 		if (recv(sc->inchan, nil) < 0) {
909*63afb9a5SDavid du Colombier 			sshdebug(c, "got interrupt/error in readdata %r");
910*63afb9a5SDavid du Colombier 			r->aux = 0;
911*63afb9a5SDavid du Colombier 			responderror(r);
912*63afb9a5SDavid du Colombier 			threadexits(nil);
913*63afb9a5SDavid du Colombier 		}
914*63afb9a5SDavid du Colombier 		if (sc->dataq == nil && (sc->state == Closed ||
915*63afb9a5SDavid du Colombier 		    sc->state == Closing || sc->state == Eof)) {
916*63afb9a5SDavid du Colombier 			sshdebug(c, "sending EOF2 to channel listener");
917*63afb9a5SDavid du Colombier 			r->aux = 0;
918*63afb9a5SDavid du Colombier 			respond(r, nil);
919*63afb9a5SDavid du Colombier 			threadexits(nil);
920*63afb9a5SDavid du Colombier 		}
921*63afb9a5SDavid du Colombier 	}
922*63afb9a5SDavid du Colombier 	getdata(c, sc, r);
923*63afb9a5SDavid du Colombier 	threadexits(nil);
924*63afb9a5SDavid du Colombier }
925*63afb9a5SDavid du Colombier 
926*63afb9a5SDavid du Colombier void
927*63afb9a5SDavid du Colombier stwrite(Req *r)
928*63afb9a5SDavid du Colombier {
929*63afb9a5SDavid du Colombier 	Conn *c;
930*63afb9a5SDavid du Colombier 	SSHChan *ch;
931*63afb9a5SDavid du Colombier 	int lev, xconn;
932*63afb9a5SDavid du Colombier 	uvlong qidpath;
933*63afb9a5SDavid du Colombier 
934*63afb9a5SDavid du Colombier 	threadsetname("stwrite");
935*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
936*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
937*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
938*63afb9a5SDavid du Colombier 	c = connections[xconn];
939*63afb9a5SDavid du Colombier 	if (c == nil) {
940*63afb9a5SDavid du Colombier 		respond(r, "invalid connection");
941*63afb9a5SDavid du Colombier 		return;
942*63afb9a5SDavid du Colombier 	}
943*63afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
944*63afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
945*63afb9a5SDavid du Colombier 	case Qclone:
946*63afb9a5SDavid du Colombier 	case Qctl:
947*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writectlproc, r, Defstk);
948*63afb9a5SDavid du Colombier 		break;
949*63afb9a5SDavid du Colombier 	case Qdata:
950*63afb9a5SDavid du Colombier 		r->ofcall.count = r->ifcall.count;
951*63afb9a5SDavid du Colombier 		if (lev == Top || lev == Connection ||
952*63afb9a5SDavid du Colombier 		    c->state == Closed || c->state == Closing ||
953*63afb9a5SDavid du Colombier 		    ch->state == Closed || ch->state == Closing) {
954*63afb9a5SDavid du Colombier 			respond(r, nil);
955*63afb9a5SDavid du Colombier 			break;
956*63afb9a5SDavid du Colombier 		}
957*63afb9a5SDavid du Colombier 		if (0 && c->stifle) {		/* was for coexistence */
958*63afb9a5SDavid du Colombier 			c->stifle = 0;
959*63afb9a5SDavid du Colombier 			if (deferredinit(c) < 0) {
960*63afb9a5SDavid du Colombier 				respond(r, "deferredinit failed");
961*63afb9a5SDavid du Colombier 				break;
962*63afb9a5SDavid du Colombier 			}
963*63afb9a5SDavid du Colombier 		}
964*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writedataproc, r, Defstk);
965*63afb9a5SDavid du Colombier 		break;
966*63afb9a5SDavid du Colombier 	case Qreqrem:
967*63afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writereqremproc, r, Defstk);
968*63afb9a5SDavid du Colombier 		break;
969*63afb9a5SDavid du Colombier 	default:
970*63afb9a5SDavid du Colombier 		respond(r, nil);
971*63afb9a5SDavid du Colombier 		break;
972*63afb9a5SDavid du Colombier 	}
973*63afb9a5SDavid du Colombier }
974*63afb9a5SDavid du Colombier 
975*63afb9a5SDavid du Colombier static int
976*63afb9a5SDavid du Colombier dialbyhand(Conn *c, int ntok, char *toks[])
977*63afb9a5SDavid du Colombier {
978*63afb9a5SDavid du Colombier 	/*
979*63afb9a5SDavid du Colombier 	 * this uses /net/tcp to connect directly.
980*63afb9a5SDavid du Colombier 	 * should use dial(2) instead of doing it by hand.
981*63afb9a5SDavid du Colombier 	 */
982*63afb9a5SDavid du Colombier 	sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: "");
983*63afb9a5SDavid du Colombier 	return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: "");
984*63afb9a5SDavid du Colombier }
985*63afb9a5SDavid du Colombier 
986*63afb9a5SDavid du Colombier static void
987*63afb9a5SDavid du Colombier userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[])
988*63afb9a5SDavid du Colombier {
989*63afb9a5SDavid du Colombier 	int n;
990*63afb9a5SDavid du Colombier 	char *attrs[5];
991*63afb9a5SDavid du Colombier 	Packet *p;
992*63afb9a5SDavid du Colombier 
993*63afb9a5SDavid du Colombier 	if (ntok < 3 || ntok > 4)
994*63afb9a5SDavid du Colombier 		respexit(c, r, buf, "bad connect command");
995*63afb9a5SDavid du Colombier 	if (!c->service)
996*63afb9a5SDavid du Colombier 		c->service = estrdup9p(toks[0]);
997*63afb9a5SDavid du Colombier 	if (c->user)
998*63afb9a5SDavid du Colombier 		free(c->user);
999*63afb9a5SDavid du Colombier 	c->user = estrdup9p(toks[2]);
1000*63afb9a5SDavid du Colombier 	sshdebug(c, "userauth for user %s", c->user);
1001*63afb9a5SDavid du Colombier 
1002*63afb9a5SDavid du Colombier 	if (ntok == 4 && strcmp(toks[1], "k") == 0) {
1003*63afb9a5SDavid du Colombier 		if (c->authkey) {
1004*63afb9a5SDavid du Colombier 			free(c->authkey);
1005*63afb9a5SDavid du Colombier 			c->authkey = nil;
1006*63afb9a5SDavid du Colombier 		}
1007*63afb9a5SDavid du Colombier 		if (c->password)
1008*63afb9a5SDavid du Colombier 			free(c->password);
1009*63afb9a5SDavid du Colombier 		c->password = estrdup9p(toks[3]);
1010*63afb9a5SDavid du Colombier 		sshdebug(c, "userauth got password");
1011*63afb9a5SDavid du Colombier 	} else {
1012*63afb9a5SDavid du Colombier 		if (c->password) {
1013*63afb9a5SDavid du Colombier 			free(c->password);
1014*63afb9a5SDavid du Colombier 			c->password = nil;
1015*63afb9a5SDavid du Colombier 		}
1016*63afb9a5SDavid du Colombier 		memset(attrs, 0, sizeof attrs);
1017*63afb9a5SDavid du Colombier 		attrs[0] = "proto=rsa";
1018*63afb9a5SDavid du Colombier 		attrs[1] = "!dk?";
1019*63afb9a5SDavid du Colombier 		attrs[2] = smprint("user=%s", c->user);
1020*63afb9a5SDavid du Colombier 		attrs[3] = smprint("sys=%s", c->remote);
1021*63afb9a5SDavid du Colombier 		if (c->authkey)
1022*63afb9a5SDavid du Colombier 			free(c->authkey);
1023*63afb9a5SDavid du Colombier 		sshdebug(c, "userauth trying rsa");
1024*63afb9a5SDavid du Colombier 		if (ntok == 3)
1025*63afb9a5SDavid du Colombier 			c->authkey = factlookup(4, 2, attrs);
1026*63afb9a5SDavid du Colombier 		else {
1027*63afb9a5SDavid du Colombier 			attrs[4] = toks[3];
1028*63afb9a5SDavid du Colombier 			c->authkey = factlookup(5, 2, attrs);
1029*63afb9a5SDavid du Colombier 		}
1030*63afb9a5SDavid du Colombier 		free(attrs[2]);
1031*63afb9a5SDavid du Colombier 		free(attrs[3]);
1032*63afb9a5SDavid du Colombier 	}
1033*63afb9a5SDavid du Colombier 
1034*63afb9a5SDavid du Colombier 	if (!c->password && !c->authkey)
1035*63afb9a5SDavid du Colombier 		respexit(c, r, buf, "no auth info");
1036*63afb9a5SDavid du Colombier 	else if (c->state != Authing) {
1037*63afb9a5SDavid du Colombier 		p = new_packet(c);
1038*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_SERVICE_REQUEST);
1039*63afb9a5SDavid du Colombier 		add_string(p, c->service);
1040*63afb9a5SDavid du Colombier 		n = finish_packet(p);
1041*63afb9a5SDavid du Colombier 		sshdebug(c, "sending msg svc req for %s", c->service);
1042*63afb9a5SDavid du Colombier 		if (writeio(c->dio, c->datafd, p->nlength, n) != n) {
1043*63afb9a5SDavid du Colombier 			sshdebug(c, "authing write failed: %r");
1044*63afb9a5SDavid du Colombier 			free(p);
1045*63afb9a5SDavid du Colombier 			r->aux = 0;
1046*63afb9a5SDavid du Colombier 			responderror(r);
1047*63afb9a5SDavid du Colombier 			free(buf);
1048*63afb9a5SDavid du Colombier 			threadexits(nil);
1049*63afb9a5SDavid du Colombier 		}
1050*63afb9a5SDavid du Colombier 		free(p);
1051*63afb9a5SDavid du Colombier 	} else
1052*63afb9a5SDavid du Colombier 		if (client_auth(c, c->dio) < 0)
1053*63afb9a5SDavid du Colombier 			respexit(c, r, buf, "ssh-userauth client auth failed");
1054*63afb9a5SDavid du Colombier 	qlock(&c->l);
1055*63afb9a5SDavid du Colombier 	if (c->state != Established) {
1056*63afb9a5SDavid du Colombier 		sshdebug(c, "sleeping for auth");
1057*63afb9a5SDavid du Colombier 		rsleep(&c->r);
1058*63afb9a5SDavid du Colombier 	}
1059*63afb9a5SDavid du Colombier 	qunlock(&c->l);
1060*63afb9a5SDavid du Colombier 	if (c->state != Established)
1061*63afb9a5SDavid du Colombier 		respexit(c, r, buf, "ssh-userath auth failed (not Established)");
1062*63afb9a5SDavid du Colombier }
1063*63afb9a5SDavid du Colombier 
1064*63afb9a5SDavid du Colombier void
1065*63afb9a5SDavid du Colombier writectlproc(void *a)
1066*63afb9a5SDavid du Colombier {
1067*63afb9a5SDavid du Colombier 	Req *r;
1068*63afb9a5SDavid du Colombier 	Packet *p;
1069*63afb9a5SDavid du Colombier 	Conn *c;
1070*63afb9a5SDavid du Colombier 	SSHChan *ch;
1071*63afb9a5SDavid du Colombier 	char *tcpconn2, *buf, *toks[4];
1072*63afb9a5SDavid du Colombier 	int n, ntok, lev, xconn;
1073*63afb9a5SDavid du Colombier 	uvlong qidpath;
1074*63afb9a5SDavid du Colombier 	char path[NETPATHLEN], tcpconn[Numbsz];
1075*63afb9a5SDavid du Colombier 
1076*63afb9a5SDavid du Colombier 	threadsetname("writectlproc");
1077*63afb9a5SDavid du Colombier 	r = a;
1078*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
1079*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
1080*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
1081*63afb9a5SDavid du Colombier 
1082*63afb9a5SDavid du Colombier 	c = connections[xconn];
1083*63afb9a5SDavid du Colombier 	if (c == nil) {
1084*63afb9a5SDavid du Colombier 		respond(r, "bad connection");
1085*63afb9a5SDavid du Colombier 		sshlog(c, "bad connection");
1086*63afb9a5SDavid du Colombier 		threadexits(nil);
1087*63afb9a5SDavid du Colombier 	}
1088*63afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
1089*63afb9a5SDavid du Colombier 
1090*63afb9a5SDavid du Colombier 	if (r->ifcall.count <= Numbsz)
1091*63afb9a5SDavid du Colombier 		buf = emalloc9p(Numbsz + 1);
1092*63afb9a5SDavid du Colombier 	else
1093*63afb9a5SDavid du Colombier 		buf = emalloc9p(r->ifcall.count + 1);
1094*63afb9a5SDavid du Colombier 	memmove(buf, r->ifcall.data, r->ifcall.count);
1095*63afb9a5SDavid du Colombier 	buf[r->ifcall.count] = '\0';
1096*63afb9a5SDavid du Colombier 
1097*63afb9a5SDavid du Colombier 	sshdebug(c, "level %d writectl: %s", lev, buf);
1098*63afb9a5SDavid du Colombier 	ntok = tokenize(buf, toks, nelem(toks));
1099*63afb9a5SDavid du Colombier 	switch (lev) {
1100*63afb9a5SDavid du Colombier 	case Connection:
1101*63afb9a5SDavid du Colombier 		if (strcmp(toks[0], "id") == 0) {	/* was for sshswitch */
1102*63afb9a5SDavid du Colombier 			if (ntok < 2)
1103*63afb9a5SDavid du Colombier 				respexit(c, r, buf, "bad id request");
1104*63afb9a5SDavid du Colombier 			strncpy(c->idstring, toks[1], sizeof c->idstring);
1105*63afb9a5SDavid du Colombier 			sshdebug(c, "id %s", toks[1]);
1106*63afb9a5SDavid du Colombier 			break;
1107*63afb9a5SDavid du Colombier 		}
1108*63afb9a5SDavid du Colombier 		if (strcmp(toks[0], "connect") == 0) {
1109*63afb9a5SDavid du Colombier 			if (ntok < 2)
1110*63afb9a5SDavid du Colombier 				respexit(c, r, buf, "bad connect request");
1111*63afb9a5SDavid du Colombier 			/*
1112*63afb9a5SDavid du Colombier 			 * should use dial(2) instead of doing it by hand.
1113*63afb9a5SDavid du Colombier 			 */
1114*63afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
1115*63afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
1116*63afb9a5SDavid du Colombier 			dialbyhand(c, ntok, toks);
1117*63afb9a5SDavid du Colombier 
1118*63afb9a5SDavid du Colombier 			c->role = Client;
1119*63afb9a5SDavid du Colombier 			/* Override the PKA list; we can take any in */
1120*63afb9a5SDavid du Colombier 			pkas[0] = &rsa_pka;
1121*63afb9a5SDavid du Colombier 			pkas[1] = &dss_pka;
1122*63afb9a5SDavid du Colombier 			pkas[2] = nil;
1123*63afb9a5SDavid du Colombier 			tcpconn2 = estrdup9p(tcpconn);
1124*63afb9a5SDavid du Colombier 
1125*63afb9a5SDavid du Colombier 			/* swap id strings, negotiate crypto */
1126*63afb9a5SDavid du Colombier 			if (dohandshake(c, tcpconn2) < 0) {
1127*63afb9a5SDavid du Colombier 				sshlog(c, "connect handshake failed: "
1128*63afb9a5SDavid du Colombier 					"tcp conn %s", tcpconn2);
1129*63afb9a5SDavid du Colombier 				free(tcpconn2);
1130*63afb9a5SDavid du Colombier 				respexit(c, r, buf, "connect handshake failed");
1131*63afb9a5SDavid du Colombier 			}
1132*63afb9a5SDavid du Colombier 			free(tcpconn2);
1133*63afb9a5SDavid du Colombier 			keymbox.state = Empty;
1134*63afb9a5SDavid du Colombier 			nbsendul(keymbox.mchan, 1);
1135*63afb9a5SDavid du Colombier 			break;
1136*63afb9a5SDavid du Colombier 		}
1137*63afb9a5SDavid du Colombier 
1138*63afb9a5SDavid du Colombier 		if (c->state == Closed || c->state == Closing)
1139*63afb9a5SDavid du Colombier 			respexit(c, r, buf, "connection closed");
1140*63afb9a5SDavid du Colombier 		if (strcmp(toks[0], "ssh-userauth") == 0)
1141*63afb9a5SDavid du Colombier 			userauth(c, r, buf, ntok, toks);
1142*63afb9a5SDavid du Colombier 		else if (strcmp(toks[0], "ssh-connection") == 0) {
1143*63afb9a5SDavid du Colombier 			/* your ad here */
1144*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "hangup") == 0) {
1145*63afb9a5SDavid du Colombier 			if (c->rpid >= 0)
1146*63afb9a5SDavid du Colombier 				threadint(c->rpid);
1147*63afb9a5SDavid du Colombier 			shutdown(c);
1148*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "announce") == 0) {
1149*63afb9a5SDavid du Colombier 			sshdebug(c, "got %s argument for announce", toks[1]);
1150*63afb9a5SDavid du Colombier 			write(c->ctlfd, r->ifcall.data, r->ifcall.count);
1151*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "accept") == 0) {
1152*63afb9a5SDavid du Colombier 			/* should use dial(2) instead of diddling /net/tcp */
1153*63afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
1154*63afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
1155*63afb9a5SDavid du Colombier 			fprint(c->ctlfd, "accept %s", tcpconn);
1156*63afb9a5SDavid du Colombier 
1157*63afb9a5SDavid du Colombier 			c->role = Server;
1158*63afb9a5SDavid du Colombier 			tcpconn2 = estrdup9p(tcpconn);
1159*63afb9a5SDavid du Colombier 			/* swap id strings, negotiate crypto */
1160*63afb9a5SDavid du Colombier 			if (dohandshake(c, tcpconn2) < 0) {
1161*63afb9a5SDavid du Colombier 				sshlog(c, "accept handshake failed: "
1162*63afb9a5SDavid du Colombier 					"tcp conn %s", tcpconn2);
1163*63afb9a5SDavid du Colombier 				free(tcpconn2);
1164*63afb9a5SDavid du Colombier 				shutdown(c);
1165*63afb9a5SDavid du Colombier 				respexit(c, r, buf, "accept handshake failed");
1166*63afb9a5SDavid du Colombier 			}
1167*63afb9a5SDavid du Colombier 			free(tcpconn2);
1168*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "reject") == 0) {
1169*63afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
1170*63afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
1171*63afb9a5SDavid du Colombier 
1172*63afb9a5SDavid du Colombier 			snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn);
1173*63afb9a5SDavid du Colombier 			c->datafd = open(path, ORDWR);
1174*63afb9a5SDavid du Colombier 
1175*63afb9a5SDavid du Colombier 			p = new_packet(c);
1176*63afb9a5SDavid du Colombier 			add_byte(p, SSH_MSG_DISCONNECT);
1177*63afb9a5SDavid du Colombier 			add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT);
1178*63afb9a5SDavid du Colombier 			add_string(p, toks[2]);
1179*63afb9a5SDavid du Colombier 			add_string(p, "EN");
1180*63afb9a5SDavid du Colombier 			n = finish_packet(p);
1181*63afb9a5SDavid du Colombier 			if (c->dio && c->datafd >= 0)
1182*63afb9a5SDavid du Colombier 				iowrite(c->dio, c->datafd, p->nlength, n);
1183*63afb9a5SDavid du Colombier 			free(p);
1184*63afb9a5SDavid du Colombier 			if (c->ctlfd >= 0)
1185*63afb9a5SDavid du Colombier 				fprint(c->ctlfd, "reject %s %s", buf, toks[2]);
1186*63afb9a5SDavid du Colombier 			if (c->rpid >= 0)
1187*63afb9a5SDavid du Colombier 				threadint(c->rpid);
1188*63afb9a5SDavid du Colombier 			shutdown(c);
1189*63afb9a5SDavid du Colombier 		}
1190*63afb9a5SDavid du Colombier 		break;
1191*63afb9a5SDavid du Colombier 	case Subchannel:
1192*63afb9a5SDavid du Colombier 		if (c->state == Closed || c->state == Closing)
1193*63afb9a5SDavid du Colombier 			respexit(c, r, buf, "channel closed");
1194*63afb9a5SDavid du Colombier 		if (strcmp(toks[0], "connect") == 0) {
1195*63afb9a5SDavid du Colombier 			p = new_packet(c);
1196*63afb9a5SDavid du Colombier 			add_byte(p, SSH_MSG_CHANNEL_OPEN);
1197*63afb9a5SDavid du Colombier 			sshdebug(c, "chan writectl: connect %s",
1198*63afb9a5SDavid du Colombier 				ntok > 1? toks[1]: "session");
1199*63afb9a5SDavid du Colombier 			add_string(p, ntok > 1? toks[1]: "session");
1200*63afb9a5SDavid du Colombier 			add_uint32(p, ch->id);
1201*63afb9a5SDavid du Colombier 			add_uint32(p, Maxpayload);
1202*63afb9a5SDavid du Colombier 			add_uint32(p, Maxrpcbuf);
1203*63afb9a5SDavid du Colombier 			/* more stuff if it's an x11 session */
1204*63afb9a5SDavid du Colombier 			n = finish_packet(p);
1205*63afb9a5SDavid du Colombier 			iowrite(c->dio, c->datafd, p->nlength, n);
1206*63afb9a5SDavid du Colombier 			free(p);
1207*63afb9a5SDavid du Colombier 			qlock(&c->l);
1208*63afb9a5SDavid du Colombier 			if (ch->otherid == -1)
1209*63afb9a5SDavid du Colombier 				rsleep(&ch->r);
1210*63afb9a5SDavid du Colombier 			qunlock(&c->l);
1211*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "global") == 0) {
1212*63afb9a5SDavid du Colombier 			/* your ad here */
1213*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "hangup") == 0) {
1214*63afb9a5SDavid du Colombier 			if (ch->state != Closed && ch->state != Closing) {
1215*63afb9a5SDavid du Colombier 				ch->state = Closing;
1216*63afb9a5SDavid du Colombier 				if (ch->otherid != -1) {
1217*63afb9a5SDavid du Colombier 					p = new_packet(c);
1218*63afb9a5SDavid du Colombier 					add_byte(p, SSH_MSG_CHANNEL_CLOSE);
1219*63afb9a5SDavid du Colombier 					add_uint32(p, ch->otherid);
1220*63afb9a5SDavid du Colombier 					n = finish_packet(p);
1221*63afb9a5SDavid du Colombier 					iowrite(c->dio, c->datafd, p->nlength, n);
1222*63afb9a5SDavid du Colombier 					free(p);
1223*63afb9a5SDavid du Colombier 				}
1224*63afb9a5SDavid du Colombier 				qlock(&c->l);
1225*63afb9a5SDavid du Colombier 				rwakeup(&ch->r);
1226*63afb9a5SDavid du Colombier 				qunlock(&c->l);
1227*63afb9a5SDavid du Colombier 				nbsendul(ch->inchan, 1);
1228*63afb9a5SDavid du Colombier 				nbsendul(ch->reqchan, 1);
1229*63afb9a5SDavid du Colombier 			}
1230*63afb9a5SDavid du Colombier 			for (n = 0; n < MAXCONN && (c->chans[n] == nil ||
1231*63afb9a5SDavid du Colombier 			    c->chans[n]->state == Empty ||
1232*63afb9a5SDavid du Colombier 			    c->chans[n]->state == Closing ||
1233*63afb9a5SDavid du Colombier 			    c->chans[n]->state == Closed); ++n)
1234*63afb9a5SDavid du Colombier 				;
1235*63afb9a5SDavid du Colombier 			if (n >= MAXCONN) {
1236*63afb9a5SDavid du Colombier 				if (c->rpid >= 0)
1237*63afb9a5SDavid du Colombier 					threadint(c->rpid);
1238*63afb9a5SDavid du Colombier 				shutdown(c);
1239*63afb9a5SDavid du Colombier 			}
1240*63afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "announce") == 0) {
1241*63afb9a5SDavid du Colombier 			sshdebug(c, "got argument `%s' for chan announce",
1242*63afb9a5SDavid du Colombier 				toks[1]);
1243*63afb9a5SDavid du Colombier 			free(ch->ann);
1244*63afb9a5SDavid du Colombier 			ch->ann = estrdup9p(toks[1]);
1245*63afb9a5SDavid du Colombier 		}
1246*63afb9a5SDavid du Colombier 		break;
1247*63afb9a5SDavid du Colombier 	}
1248*63afb9a5SDavid du Colombier 	r->ofcall.count = r->ifcall.count;
1249*63afb9a5SDavid du Colombier 	r->aux = 0;
1250*63afb9a5SDavid du Colombier 	respond(r, nil);
1251*63afb9a5SDavid du Colombier 	free(buf);
1252*63afb9a5SDavid du Colombier 	threadexits(nil);
1253*63afb9a5SDavid du Colombier }
1254*63afb9a5SDavid du Colombier 
1255*63afb9a5SDavid du Colombier void
1256*63afb9a5SDavid du Colombier writereqremproc(void *a)
1257*63afb9a5SDavid du Colombier {
1258*63afb9a5SDavid du Colombier 	Req *r;
1259*63afb9a5SDavid du Colombier 	Packet *p;
1260*63afb9a5SDavid du Colombier 	Conn *c;
1261*63afb9a5SDavid du Colombier 	SSHChan *ch;
1262*63afb9a5SDavid du Colombier 	char *cmd, *q, *buf, *toks[4];
1263*63afb9a5SDavid du Colombier 	int n, ntok, lev, xconn;
1264*63afb9a5SDavid du Colombier 	uvlong qidpath;
1265*63afb9a5SDavid du Colombier 
1266*63afb9a5SDavid du Colombier 	threadsetname("writereqremproc");
1267*63afb9a5SDavid du Colombier 	r = a;
1268*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
1269*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
1270*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
1271*63afb9a5SDavid du Colombier 	c = connections[xconn];
1272*63afb9a5SDavid du Colombier 	if (c == nil) {
1273*63afb9a5SDavid du Colombier 		respond(r, "Invalid connection");
1274*63afb9a5SDavid du Colombier 		threadexits(nil);
1275*63afb9a5SDavid du Colombier 	}
1276*63afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
1277*63afb9a5SDavid du Colombier 	if (r->ifcall.count <= 10)
1278*63afb9a5SDavid du Colombier 		buf = emalloc9p(10 + 1);
1279*63afb9a5SDavid du Colombier 	else
1280*63afb9a5SDavid du Colombier 		buf = emalloc9p(r->ifcall.count + 1);
1281*63afb9a5SDavid du Colombier 	memmove(buf, r->ifcall.data, r->ifcall.count);
1282*63afb9a5SDavid du Colombier 	buf[r->ifcall.count] = '\0';
1283*63afb9a5SDavid du Colombier 	sshdebug(c, "writereqrem: %s", buf);
1284*63afb9a5SDavid du Colombier 	ntok = tokenize(buf, toks, nelem(toks));
1285*63afb9a5SDavid du Colombier 
1286*63afb9a5SDavid du Colombier 	if (lev == Top) {
1287*63afb9a5SDavid du Colombier 		free(keymbox.msg);
1288*63afb9a5SDavid du Colombier 		keymbox.msg = buf;
1289*63afb9a5SDavid du Colombier 		nbsendul(keymbox.mchan, 1);
1290*63afb9a5SDavid du Colombier 		r->ofcall.count = r->ifcall.count;
1291*63afb9a5SDavid du Colombier 		respexit(c, r, nil, nil);
1292*63afb9a5SDavid du Colombier 	}
1293*63afb9a5SDavid du Colombier 
1294*63afb9a5SDavid du Colombier 	r->ofcall.count = r->ifcall.count;
1295*63afb9a5SDavid du Colombier 	if (c->state == Closed  || c->state == Closing ||
1296*63afb9a5SDavid du Colombier 	    ch->state == Closed || ch->state == Closing)
1297*63afb9a5SDavid du Colombier 		respexit(c, r, buf, nil);
1298*63afb9a5SDavid du Colombier 
1299*63afb9a5SDavid du Colombier 	p = new_packet(c);
1300*63afb9a5SDavid du Colombier 	if (strcmp(toks[0], "success") == 0) {
1301*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_SUCCESS);
1302*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1303*63afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "failure") == 0) {
1304*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_FAILURE);
1305*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1306*63afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "close") == 0) {
1307*63afb9a5SDavid du Colombier 		ch->state = Closing;
1308*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
1309*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1310*63afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "shell") == 0) {
1311*63afb9a5SDavid du Colombier 		ch->state = Established;
1312*63afb9a5SDavid du Colombier 		/*
1313*63afb9a5SDavid du Colombier 		 * Some servers *cough*OpenSSH*cough* don't seem to be able
1314*63afb9a5SDavid du Colombier 		 * to intelligently handle a shell with no pty.
1315*63afb9a5SDavid du Colombier 		 */
1316*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
1317*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1318*63afb9a5SDavid du Colombier 		add_string(p, "pty-req");
1319*63afb9a5SDavid du Colombier 		add_byte(p, 0);
1320*63afb9a5SDavid du Colombier 		if (ntok == 1)
1321*63afb9a5SDavid du Colombier 			add_string(p, "dumb");
1322*63afb9a5SDavid du Colombier 		else
1323*63afb9a5SDavid du Colombier 			add_string(p, toks[1]);
1324*63afb9a5SDavid du Colombier 		add_uint32(p, 0);
1325*63afb9a5SDavid du Colombier 		add_uint32(p, 0);
1326*63afb9a5SDavid du Colombier 		add_uint32(p, 0);
1327*63afb9a5SDavid du Colombier 		add_uint32(p, 0);
1328*63afb9a5SDavid du Colombier 		add_string(p, "");
1329*63afb9a5SDavid du Colombier 		n = finish_packet(p);
1330*63afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
1331*63afb9a5SDavid du Colombier 		init_packet(p);
1332*63afb9a5SDavid du Colombier 		p->c = c;
1333*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
1334*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1335*63afb9a5SDavid du Colombier 		add_string(p, "shell");
1336*63afb9a5SDavid du Colombier 		add_byte(p, 0);
1337*63afb9a5SDavid du Colombier 		sshdebug(c, "sending shell request: rlength=%lud twindow=%lud",
1338*63afb9a5SDavid du Colombier 			p->rlength, ch->twindow);
1339*63afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "exec") == 0) {
1340*63afb9a5SDavid du Colombier 		ch->state = Established;
1341*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
1342*63afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
1343*63afb9a5SDavid du Colombier 		add_string(p, "exec");
1344*63afb9a5SDavid du Colombier 		add_byte(p, 0);
1345*63afb9a5SDavid du Colombier 		cmd = emalloc9p(Bigbufsz);
1346*63afb9a5SDavid du Colombier 		q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]);
1347*63afb9a5SDavid du Colombier 		for (n = 2; n < ntok; ++n) {
1348*63afb9a5SDavid du Colombier 			q = seprint(q, cmd+Bigbufsz, " %s", toks[n]);
1349*63afb9a5SDavid du Colombier 			if (q == nil)
1350*63afb9a5SDavid du Colombier 				break;
1351*63afb9a5SDavid du Colombier 		}
1352*63afb9a5SDavid du Colombier 		add_string(p, cmd);
1353*63afb9a5SDavid du Colombier 		free(cmd);
1354*63afb9a5SDavid du Colombier 	} else
1355*63afb9a5SDavid du Colombier 		respexit(c, r, buf, "bad request command");
1356*63afb9a5SDavid du Colombier 	n = finish_packet(p);
1357*63afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p->nlength, n);
1358*63afb9a5SDavid du Colombier 	free(p);
1359*63afb9a5SDavid du Colombier 	respexit(c, r, buf, nil);
1360*63afb9a5SDavid du Colombier }
1361*63afb9a5SDavid du Colombier 
1362*63afb9a5SDavid du Colombier void
1363*63afb9a5SDavid du Colombier writedataproc(void *a)
1364*63afb9a5SDavid du Colombier {
1365*63afb9a5SDavid du Colombier 	Req *r;
1366*63afb9a5SDavid du Colombier 	Packet *p;
1367*63afb9a5SDavid du Colombier 	Conn *c;
1368*63afb9a5SDavid du Colombier 	SSHChan *ch;
1369*63afb9a5SDavid du Colombier 	int n, xconn;
1370*63afb9a5SDavid du Colombier 	uvlong qidpath;
1371*63afb9a5SDavid du Colombier 
1372*63afb9a5SDavid du Colombier 	threadsetname("writedataproc");
1373*63afb9a5SDavid du Colombier 	r = a;
1374*63afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
1375*63afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
1376*63afb9a5SDavid du Colombier 	c = connections[xconn];
1377*63afb9a5SDavid du Colombier 	if (c == nil) {
1378*63afb9a5SDavid du Colombier 		respond(r, "Invalid connection");
1379*63afb9a5SDavid du Colombier 		threadexits(nil);
1380*63afb9a5SDavid du Colombier 	}
1381*63afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
1382*63afb9a5SDavid du Colombier 
1383*63afb9a5SDavid du Colombier 	p = new_packet(c);
1384*63afb9a5SDavid du Colombier 	add_byte(p, SSH_MSG_CHANNEL_DATA);
1385*63afb9a5SDavid du Colombier 	hnputl(p->payload+1, ch->otherid);
1386*63afb9a5SDavid du Colombier 	p->rlength += 4;
1387*63afb9a5SDavid du Colombier 	add_block(p, r->ifcall.data, r->ifcall.count);
1388*63afb9a5SDavid du Colombier 	n = finish_packet(p);
1389*63afb9a5SDavid du Colombier 
1390*63afb9a5SDavid du Colombier 	if (ch->sent + p->rlength > ch->twindow) {
1391*63afb9a5SDavid du Colombier 		qlock(&ch->xmtlock);
1392*63afb9a5SDavid du Colombier 		while (ch->sent + p->rlength > ch->twindow)
1393*63afb9a5SDavid du Colombier 			rsleep(&ch->xmtrendez);
1394*63afb9a5SDavid du Colombier 		qunlock(&ch->xmtlock);
1395*63afb9a5SDavid du Colombier 	}
1396*63afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p->nlength, n);
1397*63afb9a5SDavid du Colombier 	respexit(c, r, p, nil);
1398*63afb9a5SDavid du Colombier }
1399*63afb9a5SDavid du Colombier 
1400*63afb9a5SDavid du Colombier /*
1401*63afb9a5SDavid du Colombier  * Although this is named stclunk, it's attached to the destroyfid
1402*63afb9a5SDavid du Colombier  * member of the Srv struct.  It turns out there's no member
1403*63afb9a5SDavid du Colombier  * called clunk.  But if there are no other references, a 9P Tclunk
1404*63afb9a5SDavid du Colombier  * will end up calling destroyfid.
1405*63afb9a5SDavid du Colombier  */
1406*63afb9a5SDavid du Colombier void
1407*63afb9a5SDavid du Colombier stclunk(Fid *f)
1408*63afb9a5SDavid du Colombier {
1409*63afb9a5SDavid du Colombier 	Packet *p;
1410*63afb9a5SDavid du Colombier 	Conn *c;
1411*63afb9a5SDavid du Colombier 	SSHChan *sc;
1412*63afb9a5SDavid du Colombier 	int n, lev, cnum, chnum;
1413*63afb9a5SDavid du Colombier 	uvlong qidpath;
1414*63afb9a5SDavid du Colombier 
1415*63afb9a5SDavid du Colombier 	threadsetname("stclunk");
1416*63afb9a5SDavid du Colombier 	if (f == nil || f->file == nil)
1417*63afb9a5SDavid du Colombier 		return;
1418*63afb9a5SDavid du Colombier 	qidpath = (uvlong)f->file->aux;
1419*63afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
1420*63afb9a5SDavid du Colombier 	cnum = (qidpath >> Connshift) & Connmask;
1421*63afb9a5SDavid du Colombier 	chnum = qidpath & Chanmask;
1422*63afb9a5SDavid du Colombier 	c = connections[cnum];
1423*63afb9a5SDavid du Colombier 	sshdebug(c, "got clunk on file: %#llux %d %d %d: %s",
1424*63afb9a5SDavid du Colombier 		qidpath, lev, cnum, chnum, f->file->name);
1425*63afb9a5SDavid du Colombier 	/* qidpath test implies conn 0, chan 0 */
1426*63afb9a5SDavid du Colombier 	if (lev == Top && qidpath == Qreqrem) {
1427*63afb9a5SDavid du Colombier 		if (keymbox.state != Empty) {
1428*63afb9a5SDavid du Colombier 			keymbox.state = Empty;
1429*63afb9a5SDavid du Colombier 			// nbsendul(keymbox.mchan, 1);
1430*63afb9a5SDavid du Colombier 		}
1431*63afb9a5SDavid du Colombier 		keymbox.msg = nil;
1432*63afb9a5SDavid du Colombier 		return;
1433*63afb9a5SDavid du Colombier 	}
1434*63afb9a5SDavid du Colombier 
1435*63afb9a5SDavid du Colombier 	if (c == nil)
1436*63afb9a5SDavid du Colombier 		return;
1437*63afb9a5SDavid du Colombier 	if (lev == Connection && (qidpath & Qtypemask) == Qctl &&
1438*63afb9a5SDavid du Colombier 	    (c->state == Opening || c->state == Negotiating ||
1439*63afb9a5SDavid du Colombier 	     c->state == Authing)) {
1440*63afb9a5SDavid du Colombier 		for (n = 0; n < MAXCONN && (!c->chans[n] ||
1441*63afb9a5SDavid du Colombier 		    c->chans[n]->state == Empty ||
1442*63afb9a5SDavid du Colombier 		    c->chans[n]->state == Closed ||
1443*63afb9a5SDavid du Colombier 		    c->chans[n]->state == Closing); ++n)
1444*63afb9a5SDavid du Colombier 			;
1445*63afb9a5SDavid du Colombier 		if (n >= MAXCONN) {
1446*63afb9a5SDavid du Colombier 			if (c->rpid >= 0)
1447*63afb9a5SDavid du Colombier 				threadint(c->rpid);
1448*63afb9a5SDavid du Colombier 			shutdown(c);
1449*63afb9a5SDavid du Colombier 		}
1450*63afb9a5SDavid du Colombier 		return;
1451*63afb9a5SDavid du Colombier 	}
1452*63afb9a5SDavid du Colombier 
1453*63afb9a5SDavid du Colombier 	sc = c->chans[chnum];
1454*63afb9a5SDavid du Colombier 	if (lev != Subchannel)
1455*63afb9a5SDavid du Colombier 		return;
1456*63afb9a5SDavid du Colombier 	if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) {
1457*63afb9a5SDavid du Colombier 		qlock(&c->l);
1458*63afb9a5SDavid du Colombier 		if (sc->state != Closed) {
1459*63afb9a5SDavid du Colombier 			sc->state = Closed;
1460*63afb9a5SDavid du Colombier 			chanclose(sc->inchan);
1461*63afb9a5SDavid du Colombier 			chanclose(sc->reqchan);
1462*63afb9a5SDavid du Colombier 		}
1463*63afb9a5SDavid du Colombier 		qunlock(&c->l);
1464*63afb9a5SDavid du Colombier 	} else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty &&
1465*63afb9a5SDavid du Colombier 	    sc->state != Closed && sc->state != Closing) {
1466*63afb9a5SDavid du Colombier 		if (f->file != sc->data && f->file != sc->request) {
1467*63afb9a5SDavid du Colombier 			sshlog(c, "great evil is upon us; destroying a fid "
1468*63afb9a5SDavid du Colombier 				"we didn't create");
1469*63afb9a5SDavid du Colombier 			return;
1470*63afb9a5SDavid du Colombier 		}
1471*63afb9a5SDavid du Colombier 
1472*63afb9a5SDavid du Colombier 		p = new_packet(c);
1473*63afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
1474*63afb9a5SDavid du Colombier 		hnputl(p->payload+1, sc->otherid);
1475*63afb9a5SDavid du Colombier 		p->rlength += 4;
1476*63afb9a5SDavid du Colombier 		n = finish_packet(p);
1477*63afb9a5SDavid du Colombier 		sc->state = Closing;
1478*63afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
1479*63afb9a5SDavid du Colombier 		free(p);
1480*63afb9a5SDavid du Colombier 
1481*63afb9a5SDavid du Colombier 		qlock(&c->l);
1482*63afb9a5SDavid du Colombier 		rwakeup(&sc->r);
1483*63afb9a5SDavid du Colombier 		qunlock(&c->l);
1484*63afb9a5SDavid du Colombier 		nbsendul(sc->inchan, 1);
1485*63afb9a5SDavid du Colombier 		nbsendul(sc->reqchan, 1);
1486*63afb9a5SDavid du Colombier 	}
1487*63afb9a5SDavid du Colombier 	for (n = 0; n < MAXCONN && (!c->chans[n] ||
1488*63afb9a5SDavid du Colombier 	    c->chans[n]->state == Empty || c->chans[n]->state == Closed ||
1489*63afb9a5SDavid du Colombier 	    c->chans[n]->state == Closing); ++n)
1490*63afb9a5SDavid du Colombier 		;
1491*63afb9a5SDavid du Colombier 	if (n >= MAXCONN) {
1492*63afb9a5SDavid du Colombier 		if (c->rpid >= 0)
1493*63afb9a5SDavid du Colombier 			threadint(c->rpid);
1494*63afb9a5SDavid du Colombier 		shutdown(c);
1495*63afb9a5SDavid du Colombier 	}
1496*63afb9a5SDavid du Colombier }
1497*63afb9a5SDavid du Colombier 
1498*63afb9a5SDavid du Colombier void
1499*63afb9a5SDavid du Colombier stflush(Req *r)
1500*63afb9a5SDavid du Colombier {
1501*63afb9a5SDavid du Colombier 	Req *or;
1502*63afb9a5SDavid du Colombier 	uvlong qidpath;
1503*63afb9a5SDavid du Colombier 
1504*63afb9a5SDavid du Colombier 	threadsetname("stflush");
1505*63afb9a5SDavid du Colombier 	or = r->oldreq;
1506*63afb9a5SDavid du Colombier 	qidpath = (uvlong)or->fid->file->aux;
1507*63afb9a5SDavid du Colombier 	sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p",
1508*63afb9a5SDavid du Colombier 		argv0, qidpath, qidpath >> Levshift,
1509*63afb9a5SDavid du Colombier 		(qidpath >> Connshift) & Connmask, qidpath & Chanmask,
1510*63afb9a5SDavid du Colombier 		or->fid->file->name, or->aux);
1511*63afb9a5SDavid du Colombier 	if (!or->aux)
1512*63afb9a5SDavid du Colombier 		respond(or, "interrupted");
1513*63afb9a5SDavid du Colombier 	else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten &&
1514*63afb9a5SDavid du Colombier 	    (qidpath >> Levshift) == Connection ||
1515*63afb9a5SDavid du Colombier 	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qdata &&
1516*63afb9a5SDavid du Colombier 	    (qidpath >> Levshift) == Subchannel ||
1517*63afb9a5SDavid du Colombier 	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qreqrem)
1518*63afb9a5SDavid du Colombier 		threadint((uintptr)or->aux);
1519*63afb9a5SDavid du Colombier 	else {
1520*63afb9a5SDavid du Colombier 		threadkill((uintptr)or->aux);
1521*63afb9a5SDavid du Colombier 		or->aux = 0;
1522*63afb9a5SDavid du Colombier 		respond(or, "interrupted");
1523*63afb9a5SDavid du Colombier 	}
1524*63afb9a5SDavid du Colombier 	respond(r, nil);
1525*63afb9a5SDavid du Colombier }
1526*63afb9a5SDavid du Colombier 
1527*63afb9a5SDavid du Colombier void
1528*63afb9a5SDavid du Colombier filedup(Req *r, File *src)
1529*63afb9a5SDavid du Colombier {
1530*63afb9a5SDavid du Colombier 	r->ofcall.qid = src->qid;
1531*63afb9a5SDavid du Colombier 	closefile(r->fid->file);
1532*63afb9a5SDavid du Colombier 	r->fid->file = src;
1533*63afb9a5SDavid du Colombier 	incref(src);
1534*63afb9a5SDavid du Colombier }
1535*63afb9a5SDavid du Colombier 
1536*63afb9a5SDavid du Colombier Conn *
1537*63afb9a5SDavid du Colombier alloc_conn(void)
1538*63afb9a5SDavid du Colombier {
1539*63afb9a5SDavid du Colombier 	int slevconn, i, s, firstnil;
1540*63afb9a5SDavid du Colombier 	char buf[Numbsz];
1541*63afb9a5SDavid du Colombier 	Conn *c;
1542*63afb9a5SDavid du Colombier 	static QLock aclock;
1543*63afb9a5SDavid du Colombier 
1544*63afb9a5SDavid du Colombier 	qlock(&aclock);
1545*63afb9a5SDavid du Colombier 	firstnil = -1;
1546*63afb9a5SDavid du Colombier 	for (i = 0; i < MAXCONN; ++i) {
1547*63afb9a5SDavid du Colombier 		if (connections[i] == nil) {
1548*63afb9a5SDavid du Colombier 			if (firstnil == -1)
1549*63afb9a5SDavid du Colombier 				firstnil = i;
1550*63afb9a5SDavid du Colombier 			continue;
1551*63afb9a5SDavid du Colombier 		}
1552*63afb9a5SDavid du Colombier 		s = connections[i]->state;
1553*63afb9a5SDavid du Colombier 		if (s == Empty || s == Closed)
1554*63afb9a5SDavid du Colombier 			break;
1555*63afb9a5SDavid du Colombier 	}
1556*63afb9a5SDavid du Colombier 	if (i >= MAXCONN) {
1557*63afb9a5SDavid du Colombier 		if (firstnil == -1) {		/* all slots in use? */
1558*63afb9a5SDavid du Colombier 			qunlock(&aclock);
1559*63afb9a5SDavid du Colombier 			return nil;
1560*63afb9a5SDavid du Colombier 		}
1561*63afb9a5SDavid du Colombier 		/* no reusable slots, allocate a new Conn */
1562*63afb9a5SDavid du Colombier 		connections[firstnil] = emalloc9p(sizeof(Conn));
1563*63afb9a5SDavid du Colombier 		memset(connections[firstnil], 0, sizeof(Conn));
1564*63afb9a5SDavid du Colombier 		i = firstnil;
1565*63afb9a5SDavid du Colombier 	}
1566*63afb9a5SDavid du Colombier 
1567*63afb9a5SDavid du Colombier 	c = connections[i];
1568*63afb9a5SDavid du Colombier 	memset(&c->r, '\0', sizeof(Rendez));
1569*63afb9a5SDavid du Colombier 	c->r.l = &c->l;
1570*63afb9a5SDavid du Colombier 	c->dio = ioproc();
1571*63afb9a5SDavid du Colombier 	c->rio = nil;
1572*63afb9a5SDavid du Colombier 	c->state = Allocated;
1573*63afb9a5SDavid du Colombier 	c->role = Server;
1574*63afb9a5SDavid du Colombier 	c->id = i;
1575*63afb9a5SDavid du Colombier 	c->stifle = c->poisoned = 0;
1576*63afb9a5SDavid du Colombier 	c->user = c->service = nil;
1577*63afb9a5SDavid du Colombier 	c->inseq = c->nchan = c->outseq = 0;
1578*63afb9a5SDavid du Colombier 	c->cscrypt = c->csmac = c->ctlfd = c->datafd = c->decrypt =
1579*63afb9a5SDavid du Colombier 		c->encrypt = c->inmac = c->ncscrypt = c->ncsmac =
1580*63afb9a5SDavid du Colombier 		c->nsccrypt = c->nscmac = c->outmac = c->rpid = c->sccrypt =
1581*63afb9a5SDavid du Colombier 		c->scmac = c->tcpconn = -1;
1582*63afb9a5SDavid du Colombier 	if (c->e) {
1583*63afb9a5SDavid du Colombier 		mpfree(c->e);
1584*63afb9a5SDavid du Colombier 		c->e = nil;
1585*63afb9a5SDavid du Colombier 	}
1586*63afb9a5SDavid du Colombier 	if (c->x) {
1587*63afb9a5SDavid du Colombier 		mpfree(c->x);
1588*63afb9a5SDavid du Colombier 		c->x = nil;
1589*63afb9a5SDavid du Colombier 	}
1590*63afb9a5SDavid du Colombier 
1591*63afb9a5SDavid du Colombier 	snprint(buf, sizeof buf, "%d", i);
1592*63afb9a5SDavid du Colombier 	if (c->dir == nil) {
1593*63afb9a5SDavid du Colombier 		slevconn = Connection << Levshift | i << Connshift;
1594*63afb9a5SDavid du Colombier 		c->dir = createfile(rootfile, buf, uid, 0555|DMDIR,
1595*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qroot));
1596*63afb9a5SDavid du Colombier 		c->clonefile = createfile(c->dir, "clone", uid, 0666,
1597*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qclone));
1598*63afb9a5SDavid du Colombier 		c->ctlfile = createfile(c->dir, "ctl", uid, 0666,
1599*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qctl));
1600*63afb9a5SDavid du Colombier 		c->datafile = createfile(c->dir, "data", uid, 0666,
1601*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qdata));
1602*63afb9a5SDavid du Colombier 		c->listenfile = createfile(c->dir, "listen", uid, 0666,
1603*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qlisten));
1604*63afb9a5SDavid du Colombier 		c->localfile = createfile(c->dir, "local", uid, 0444,
1605*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qlocal));
1606*63afb9a5SDavid du Colombier 		c->remotefile = createfile(c->dir, "remote", uid, 0444,
1607*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qreqrem));
1608*63afb9a5SDavid du Colombier 		c->statusfile = createfile(c->dir, "status", uid, 0444,
1609*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qstatus));
1610*63afb9a5SDavid du Colombier 		c->tcpfile = createfile(c->dir, "tcp", uid, 0444,
1611*63afb9a5SDavid du Colombier 			(void *)(slevconn | Qtcp));
1612*63afb9a5SDavid du Colombier 	}
1613*63afb9a5SDavid du Colombier //	c->skexinit = c->rkexinit = nil;
1614*63afb9a5SDavid du Colombier 	c->got_sessid = 0;
1615*63afb9a5SDavid du Colombier 	c->otherid = nil;
1616*63afb9a5SDavid du Colombier 	c->inik = c->outik = nil;
1617*63afb9a5SDavid du Colombier 	c->s2ccs = c->c2scs = c->enccs = c->deccs = nil;
1618*63afb9a5SDavid du Colombier 	qunlock(&aclock);
1619*63afb9a5SDavid du Colombier 	return c;
1620*63afb9a5SDavid du Colombier }
1621*63afb9a5SDavid du Colombier 
1622*63afb9a5SDavid du Colombier SSHChan *
1623*63afb9a5SDavid du Colombier alloc_chan(Conn *c)
1624*63afb9a5SDavid du Colombier {
1625*63afb9a5SDavid du Colombier 	int cnum, slcn;
1626*63afb9a5SDavid du Colombier 	char buf[Numbsz];
1627*63afb9a5SDavid du Colombier 	Plist *p, *next;
1628*63afb9a5SDavid du Colombier 	SSHChan *sc;
1629*63afb9a5SDavid du Colombier 
1630*63afb9a5SDavid du Colombier 	if (c->nchan >= MAXCONN)
1631*63afb9a5SDavid du Colombier 		return nil;
1632*63afb9a5SDavid du Colombier 	qlock(&c->l);
1633*63afb9a5SDavid du Colombier 	cnum = c->nchan;
1634*63afb9a5SDavid du Colombier 	if (c->chans[cnum] == nil) {
1635*63afb9a5SDavid du Colombier 		c->chans[cnum] = emalloc9p(sizeof(SSHChan));
1636*63afb9a5SDavid du Colombier 		memset(c->chans[cnum], 0, sizeof(SSHChan));
1637*63afb9a5SDavid du Colombier 	}
1638*63afb9a5SDavid du Colombier 	sc = c->chans[cnum];
1639*63afb9a5SDavid du Colombier 	snprint(buf, sizeof buf, "%d", cnum);
1640*63afb9a5SDavid du Colombier 	memset(&sc->r, '\0', sizeof(Rendez));
1641*63afb9a5SDavid du Colombier 	sc->r.l = &c->l;
1642*63afb9a5SDavid du Colombier 	sc->id = cnum;
1643*63afb9a5SDavid du Colombier 	sc->state = Empty;
1644*63afb9a5SDavid du Colombier 	sc->conn = c->id;
1645*63afb9a5SDavid du Colombier 	sc->otherid = sc->waker = -1;
1646*63afb9a5SDavid du Colombier 	sc->sent = sc->twindow = sc->rwindow = sc->inrqueue = 0;
1647*63afb9a5SDavid du Colombier 	sc->ann = nil;
1648*63afb9a5SDavid du Colombier 	sc->lreq = nil;
1649*63afb9a5SDavid du Colombier 
1650*63afb9a5SDavid du Colombier 	if (sc->dir == nil) {
1651*63afb9a5SDavid du Colombier 		slcn = Subchannel << Levshift | c->id << Connshift | cnum;
1652*63afb9a5SDavid du Colombier 		sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR,
1653*63afb9a5SDavid du Colombier 			(void *)(slcn | Qroot));
1654*63afb9a5SDavid du Colombier 		sc->ctl = createfile(sc->dir, "ctl", uid, 0666,
1655*63afb9a5SDavid du Colombier 			(void *)(slcn | Qctl));
1656*63afb9a5SDavid du Colombier 		sc->data = createfile(sc->dir, "data", uid, 0666,
1657*63afb9a5SDavid du Colombier 			(void *)(slcn | Qdata));
1658*63afb9a5SDavid du Colombier 		sc->listen = createfile(sc->dir, "listen", uid, 0666,
1659*63afb9a5SDavid du Colombier 			(void *)(slcn | Qlisten));
1660*63afb9a5SDavid du Colombier 		sc->request = createfile(sc->dir, "request", uid, 0666,
1661*63afb9a5SDavid du Colombier 			(void *)(slcn | Qreqrem));
1662*63afb9a5SDavid du Colombier 		sc->status = createfile(sc->dir, "status", uid, 0444,
1663*63afb9a5SDavid du Colombier 			(void *)(slcn | Qstatus));
1664*63afb9a5SDavid du Colombier 		sc->tcp = createfile(sc->dir, "tcp", uid, 0444,
1665*63afb9a5SDavid du Colombier 			(void *)(slcn | Qtcp));
1666*63afb9a5SDavid du Colombier 	}
1667*63afb9a5SDavid du Colombier 	c->nchan++;
1668*63afb9a5SDavid du Colombier 
1669*63afb9a5SDavid du Colombier 	for (; sc->reqq != nil; sc->reqq = next) {
1670*63afb9a5SDavid du Colombier 		p = sc->reqq;
1671*63afb9a5SDavid du Colombier 		next = p->next;
1672*63afb9a5SDavid du Colombier 		free(p->pack);
1673*63afb9a5SDavid du Colombier 		free(p);
1674*63afb9a5SDavid du Colombier 	}
1675*63afb9a5SDavid du Colombier 	sc->dataq = sc->datatl = sc->reqtl = nil;
1676*63afb9a5SDavid du Colombier 
1677*63afb9a5SDavid du Colombier 	if (sc->inchan)
1678*63afb9a5SDavid du Colombier 		chanfree(sc->inchan);
1679*63afb9a5SDavid du Colombier 	sc->inchan = chancreate(4, 0);
1680*63afb9a5SDavid du Colombier 
1681*63afb9a5SDavid du Colombier 	if (sc->reqchan)
1682*63afb9a5SDavid du Colombier 		chanfree(sc->reqchan);
1683*63afb9a5SDavid du Colombier 	sc->reqchan = chancreate(4, 0);
1684*63afb9a5SDavid du Colombier 
1685*63afb9a5SDavid du Colombier 	memset(&sc->xmtrendez, '\0', sizeof(Rendez));
1686*63afb9a5SDavid du Colombier 	sc->xmtrendez.l = &sc->xmtlock;
1687*63afb9a5SDavid du Colombier 	qunlock(&c->l);
1688*63afb9a5SDavid du Colombier 	return sc;
1689*63afb9a5SDavid du Colombier }
1690*63afb9a5SDavid du Colombier 
1691*63afb9a5SDavid du Colombier static int
1692*63afb9a5SDavid du Colombier readlineio(Conn *, Ioproc *io, int fd, char *buf, int size)
1693*63afb9a5SDavid du Colombier {
1694*63afb9a5SDavid du Colombier 	int n;
1695*63afb9a5SDavid du Colombier 	char *p;
1696*63afb9a5SDavid du Colombier 
1697*63afb9a5SDavid du Colombier 	for (p = buf; p < buf + size - 1; p++) {
1698*63afb9a5SDavid du Colombier 		n = ioread(io, fd, p, 1);
1699*63afb9a5SDavid du Colombier 		if (n != 1 || *p == '\n') {
1700*63afb9a5SDavid du Colombier 			*p = '\0';
1701*63afb9a5SDavid du Colombier 			break;
1702*63afb9a5SDavid du Colombier 		}
1703*63afb9a5SDavid du Colombier 	}
1704*63afb9a5SDavid du Colombier 	return p - buf;
1705*63afb9a5SDavid du Colombier }
1706*63afb9a5SDavid du Colombier 
1707*63afb9a5SDavid du Colombier static char *
1708*63afb9a5SDavid du Colombier readremote(Conn *c, Ioproc *io, char *tcpconn)
1709*63afb9a5SDavid du Colombier {
1710*63afb9a5SDavid du Colombier 	int n, remfd;
1711*63afb9a5SDavid du Colombier 	char *p, *remote;
1712*63afb9a5SDavid du Colombier 	char path[Arbbufsz], buf[NETPATHLEN];
1713*63afb9a5SDavid du Colombier 
1714*63afb9a5SDavid du Colombier 	remote = nil;
1715*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
1716*63afb9a5SDavid du Colombier 	remfd = ioopen(io, path, OREAD);
1717*63afb9a5SDavid du Colombier 	if (remfd < 0) {
1718*63afb9a5SDavid du Colombier 		sshlog(c, "readremote: can't open %s: %r", path);
1719*63afb9a5SDavid du Colombier 		return nil;
1720*63afb9a5SDavid du Colombier 	}
1721*63afb9a5SDavid du Colombier 	n = ioread(io, remfd, buf, sizeof buf - 1);
1722*63afb9a5SDavid du Colombier 	if (n > 0) {
1723*63afb9a5SDavid du Colombier 		buf[n] = 0;
1724*63afb9a5SDavid du Colombier 		p = strchr(buf, '!');
1725*63afb9a5SDavid du Colombier 		if (p)
1726*63afb9a5SDavid du Colombier 			*p = 0;
1727*63afb9a5SDavid du Colombier 		remote = estrdup9p(buf);
1728*63afb9a5SDavid du Colombier 	}
1729*63afb9a5SDavid du Colombier 	ioclose(io, remfd);
1730*63afb9a5SDavid du Colombier 	return remote;
1731*63afb9a5SDavid du Colombier }
1732*63afb9a5SDavid du Colombier 
1733*63afb9a5SDavid du Colombier static void
1734*63afb9a5SDavid du Colombier sendmyid(Conn *c, Ioproc *io)
1735*63afb9a5SDavid du Colombier {
1736*63afb9a5SDavid du Colombier 	char path[Arbbufsz];
1737*63afb9a5SDavid du Colombier 
1738*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s\r\n", MYID);
1739*63afb9a5SDavid du Colombier 	iowrite(io, c->datafd, path, strlen(path));
1740*63afb9a5SDavid du Colombier }
1741*63afb9a5SDavid du Colombier 
1742*63afb9a5SDavid du Colombier /* save and tidy up the remote id */
1743*63afb9a5SDavid du Colombier static void
1744*63afb9a5SDavid du Colombier stashremid(Conn *c, char *remid)
1745*63afb9a5SDavid du Colombier {
1746*63afb9a5SDavid du Colombier 	char *nl;
1747*63afb9a5SDavid du Colombier 
1748*63afb9a5SDavid du Colombier 	if (c->otherid)
1749*63afb9a5SDavid du Colombier 		free(c->otherid);
1750*63afb9a5SDavid du Colombier 	c->otherid = estrdup9p(remid);
1751*63afb9a5SDavid du Colombier 
1752*63afb9a5SDavid du Colombier 	nl = strchr(c->otherid, '\n');
1753*63afb9a5SDavid du Colombier 	if (nl)
1754*63afb9a5SDavid du Colombier 		*nl = '\0';
1755*63afb9a5SDavid du Colombier 	nl = strchr(c->otherid, '\r');
1756*63afb9a5SDavid du Colombier 	if (nl)
1757*63afb9a5SDavid du Colombier 		*nl = '\0';
1758*63afb9a5SDavid du Colombier }
1759*63afb9a5SDavid du Colombier 
1760*63afb9a5SDavid du Colombier static void
1761*63afb9a5SDavid du Colombier hangupconn(Conn *c)
1762*63afb9a5SDavid du Colombier {
1763*63afb9a5SDavid du Colombier 	hangup(c->ctlfd);
1764*63afb9a5SDavid du Colombier 	close(c->ctlfd);
1765*63afb9a5SDavid du Colombier 	close(c->datafd);
1766*63afb9a5SDavid du Colombier 	c->ctlfd = c->datafd = -1;
1767*63afb9a5SDavid du Colombier }
1768*63afb9a5SDavid du Colombier 
1769*63afb9a5SDavid du Colombier #ifdef COEXIST
1770*63afb9a5SDavid du Colombier static int
1771*63afb9a5SDavid du Colombier exchids(Conn *c, Ioproc *io, char *remid, int remsz)
1772*63afb9a5SDavid du Colombier {
1773*63afb9a5SDavid du Colombier 	int n;
1774*63afb9a5SDavid du Colombier 
1775*63afb9a5SDavid du Colombier 	/*
1776*63afb9a5SDavid du Colombier 	 * exchange versions.  server writes id, then reads;
1777*63afb9a5SDavid du Colombier 	 * client reads id then writes (in theory).
1778*63afb9a5SDavid du Colombier 	 */
1779*63afb9a5SDavid du Colombier 	if (c->role == Server) {
1780*63afb9a5SDavid du Colombier 		sendmyid(c, io);
1781*63afb9a5SDavid du Colombier 
1782*63afb9a5SDavid du Colombier 		n = readlineio(c, io, c->datafd, remid, remsz);
1783*63afb9a5SDavid du Colombier 		if (n < 5)		/* can't be a valid SSH id string */
1784*63afb9a5SDavid du Colombier 			return -1;
1785*63afb9a5SDavid du Colombier 		sshdebug(c, "dohandshake: server, got `%s', sent `%s'", remid,
1786*63afb9a5SDavid du Colombier 			MYID);
1787*63afb9a5SDavid du Colombier 	} else {
1788*63afb9a5SDavid du Colombier 		/* client: read server's id */
1789*63afb9a5SDavid du Colombier 		n = readlineio(c, io, c->datafd, remid, remsz);
1790*63afb9a5SDavid du Colombier 		if (n < 5)		/* can't be a valid SSH id string */
1791*63afb9a5SDavid du Colombier 			return -1;
1792*63afb9a5SDavid du Colombier 
1793*63afb9a5SDavid du Colombier 		sendmyid(c, io);
1794*63afb9a5SDavid du Colombier 		sshdebug(c, "dohandshake: client, got `%s' sent `%s'", remid, MYID);
1795*63afb9a5SDavid du Colombier 		if (remid[0] == '\0') {
1796*63afb9a5SDavid du Colombier 			sshlog(c, "dohandshake: client, empty remote id string;"
1797*63afb9a5SDavid du Colombier 				" out of sync");
1798*63afb9a5SDavid du Colombier 			return -1;
1799*63afb9a5SDavid du Colombier 		}
1800*63afb9a5SDavid du Colombier 	}
1801*63afb9a5SDavid du Colombier 	sshdebug(c, "remote id string `%s'", remid);
1802*63afb9a5SDavid du Colombier 	return 0;
1803*63afb9a5SDavid du Colombier }
1804*63afb9a5SDavid du Colombier 
1805*63afb9a5SDavid du Colombier /*
1806*63afb9a5SDavid du Colombier  * negotiate the protocols.
1807*63afb9a5SDavid du Colombier  * We don't do the full negotiation here, because we also have
1808*63afb9a5SDavid du Colombier  * to handle a re-negotiation request from the other end.
1809*63afb9a5SDavid du Colombier  * So we just kick it off and let the receiver process take it from there.
1810*63afb9a5SDavid du Colombier  */
1811*63afb9a5SDavid du Colombier static int
1812*63afb9a5SDavid du Colombier negotiate(Conn *c)
1813*63afb9a5SDavid du Colombier {
1814*63afb9a5SDavid du Colombier 	send_kexinit(c);
1815*63afb9a5SDavid du Colombier 
1816*63afb9a5SDavid du Colombier 	qlock(&c->l);
1817*63afb9a5SDavid du Colombier 	if ((c->role == Client && c->state != Negotiating) ||
1818*63afb9a5SDavid du Colombier 	    (c->role == Server && c->state != Established)) {
1819*63afb9a5SDavid du Colombier 		sshdebug(c, "awaiting establishment");
1820*63afb9a5SDavid du Colombier 		rsleep(&c->r);
1821*63afb9a5SDavid du Colombier 	}
1822*63afb9a5SDavid du Colombier 	qunlock(&c->l);
1823*63afb9a5SDavid du Colombier 
1824*63afb9a5SDavid du Colombier 	if (c->role == Server && c->state != Established ||
1825*63afb9a5SDavid du Colombier 	    c->role == Client && c->state != Negotiating) {
1826*63afb9a5SDavid du Colombier 		sshdebug(c, "failed to establish");
1827*63afb9a5SDavid du Colombier 		return -1;
1828*63afb9a5SDavid du Colombier 	}
1829*63afb9a5SDavid du Colombier 	sshdebug(c, "established; crypto now on");
1830*63afb9a5SDavid du Colombier 	return 0;
1831*63afb9a5SDavid du Colombier }
1832*63afb9a5SDavid du Colombier 
1833*63afb9a5SDavid du Colombier /* this was deferred when trying to make coexistence with v1 work */
1834*63afb9a5SDavid du Colombier static int
1835*63afb9a5SDavid du Colombier deferredinit(Conn *c)
1836*63afb9a5SDavid du Colombier {
1837*63afb9a5SDavid du Colombier 	char remid[Arbbufsz];
1838*63afb9a5SDavid du Colombier 	Ioproc *io;
1839*63afb9a5SDavid du Colombier 
1840*63afb9a5SDavid du Colombier 	io = ioproc();
1841*63afb9a5SDavid du Colombier 	/*
1842*63afb9a5SDavid du Colombier 	 * don't bother checking the remote's id string.
1843*63afb9a5SDavid du Colombier 	 * as a client, we can cope with v1 if we don't verify the host key.
1844*63afb9a5SDavid du Colombier 	 */
1845*63afb9a5SDavid du Colombier 	if (exchids(c, io, remid, sizeof remid) < 0 ||
1846*63afb9a5SDavid du Colombier 	    0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 &&
1847*63afb9a5SDavid du Colombier 	    strncmp(remid, "SSH-1.99", 8) != 0) {
1848*63afb9a5SDavid du Colombier 		/* not a protocol version we know; give up */
1849*63afb9a5SDavid du Colombier 		closeioproc(io);
1850*63afb9a5SDavid du Colombier 		hangupconn(c);
1851*63afb9a5SDavid du Colombier 		return -1;
1852*63afb9a5SDavid du Colombier 	}
1853*63afb9a5SDavid du Colombier 	closeioproc(io);
1854*63afb9a5SDavid du Colombier 	stashremid(c, remid);
1855*63afb9a5SDavid du Colombier 
1856*63afb9a5SDavid du Colombier 	c->state = Initting;
1857*63afb9a5SDavid du Colombier 
1858*63afb9a5SDavid du Colombier 	/* start the reader thread */
1859*63afb9a5SDavid du Colombier 	if (c->rpid < 0)
1860*63afb9a5SDavid du Colombier 		c->rpid = threadcreate(reader, c, Defstk);
1861*63afb9a5SDavid du Colombier 
1862*63afb9a5SDavid du Colombier 	return negotiate(c);
1863*63afb9a5SDavid du Colombier }
1864*63afb9a5SDavid du Colombier 
1865*63afb9a5SDavid du Colombier int
1866*63afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
1867*63afb9a5SDavid du Colombier {
1868*63afb9a5SDavid du Colombier 	int tcpdfd;
1869*63afb9a5SDavid du Colombier 	char *remote;
1870*63afb9a5SDavid du Colombier 	char path[Arbbufsz];
1871*63afb9a5SDavid du Colombier 	Ioproc *io;
1872*63afb9a5SDavid du Colombier 
1873*63afb9a5SDavid du Colombier 	io = ioproc();
1874*63afb9a5SDavid du Colombier 
1875*63afb9a5SDavid du Colombier 	/* read tcp conn's remote address into c->remote */
1876*63afb9a5SDavid du Colombier 	remote = readremote(c, io, tcpconn);
1877*63afb9a5SDavid du Colombier 	if (remote) {
1878*63afb9a5SDavid du Colombier 		free(c->remote);
1879*63afb9a5SDavid du Colombier 		c->remote = remote;
1880*63afb9a5SDavid du Colombier 	}
1881*63afb9a5SDavid du Colombier 
1882*63afb9a5SDavid du Colombier 	/* open tcp conn's data file */
1883*63afb9a5SDavid du Colombier 	c->tcpconn = atoi(tcpconn);
1884*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
1885*63afb9a5SDavid du Colombier 	tcpdfd = ioopen(io, path, ORDWR);
1886*63afb9a5SDavid du Colombier 	closeioproc(io);
1887*63afb9a5SDavid du Colombier 	if (tcpdfd < 0) {
1888*63afb9a5SDavid du Colombier 		sshlog(c, "dohandshake: can't open %s: %r", path);
1889*63afb9a5SDavid du Colombier 		return -1;
1890*63afb9a5SDavid du Colombier 	}
1891*63afb9a5SDavid du Colombier 	c->datafd = tcpdfd;		/* underlying tcp data descriptor */
1892*63afb9a5SDavid du Colombier 
1893*63afb9a5SDavid du Colombier 	return deferredinit(c);
1894*63afb9a5SDavid du Colombier }
1895*63afb9a5SDavid du Colombier #endif					/* COEXIST */
1896*63afb9a5SDavid du Colombier 
1897*63afb9a5SDavid du Colombier int
1898*63afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
1899*63afb9a5SDavid du Colombier {
1900*63afb9a5SDavid du Colombier 	int fd, n;
1901*63afb9a5SDavid du Colombier 	char *p, *othid;
1902*63afb9a5SDavid du Colombier 	char path[Arbbufsz], buf[NETPATHLEN];
1903*63afb9a5SDavid du Colombier 	Ioproc *io;
1904*63afb9a5SDavid du Colombier 
1905*63afb9a5SDavid du Colombier 	io = ioproc();
1906*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
1907*63afb9a5SDavid du Colombier 	fd = ioopen(io, path, OREAD);
1908*63afb9a5SDavid du Colombier 	n = ioread(io, fd, buf, sizeof buf - 1);
1909*63afb9a5SDavid du Colombier 	if (n > 0) {
1910*63afb9a5SDavid du Colombier 		buf[n] = 0;
1911*63afb9a5SDavid du Colombier 		p = strchr(buf, '!');
1912*63afb9a5SDavid du Colombier 		if (p)
1913*63afb9a5SDavid du Colombier 			*p = 0;
1914*63afb9a5SDavid du Colombier 		free(c->remote);
1915*63afb9a5SDavid du Colombier 		c->remote = estrdup9p(buf);
1916*63afb9a5SDavid du Colombier 	}
1917*63afb9a5SDavid du Colombier 	ioclose(io, fd);
1918*63afb9a5SDavid du Colombier 
1919*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
1920*63afb9a5SDavid du Colombier 	fd = ioopen(io, path, ORDWR);
1921*63afb9a5SDavid du Colombier 	if (fd < 0) {
1922*63afb9a5SDavid du Colombier 		closeioproc(io);
1923*63afb9a5SDavid du Colombier 		return -1;
1924*63afb9a5SDavid du Colombier 	}
1925*63afb9a5SDavid du Colombier 	c->datafd = fd;
1926*63afb9a5SDavid du Colombier 
1927*63afb9a5SDavid du Colombier 	/* exchange versions--we're only doing SSH2, unfortunately */
1928*63afb9a5SDavid du Colombier 
1929*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s\r\n", MYID);
1930*63afb9a5SDavid du Colombier 	if (c->idstring && c->idstring[0])
1931*63afb9a5SDavid du Colombier 		strncpy(path, c->idstring, sizeof path);
1932*63afb9a5SDavid du Colombier 	else {
1933*63afb9a5SDavid du Colombier 		iowrite(io, fd, path, strlen(path));
1934*63afb9a5SDavid du Colombier 		p = path;
1935*63afb9a5SDavid du Colombier 		n = 0;
1936*63afb9a5SDavid du Colombier 		do {
1937*63afb9a5SDavid du Colombier 			if (ioread(io, fd, p, 1) < 0) {
1938*63afb9a5SDavid du Colombier 				fprint(2, "%s: short read in ID exchange: %r\n",
1939*63afb9a5SDavid du Colombier 					argv0);
1940*63afb9a5SDavid du Colombier 				break;
1941*63afb9a5SDavid du Colombier 			}
1942*63afb9a5SDavid du Colombier 			++n;
1943*63afb9a5SDavid du Colombier 		} while (*p++ != '\n');
1944*63afb9a5SDavid du Colombier 		if (n < 5) {		/* can't be a valid SSH id string */
1945*63afb9a5SDavid du Colombier 			close(fd);
1946*63afb9a5SDavid du Colombier 			goto err;
1947*63afb9a5SDavid du Colombier 		}
1948*63afb9a5SDavid du Colombier 		*p = 0;
1949*63afb9a5SDavid du Colombier 	}
1950*63afb9a5SDavid du Colombier 	sshdebug(c, "id string `%s'", path);
1951*63afb9a5SDavid du Colombier 	if (c->idstring[0] == '\0' &&
1952*63afb9a5SDavid du Colombier 	    strncmp(path, "SSH-2", 5) != 0 &&
1953*63afb9a5SDavid du Colombier 	    strncmp(path, "SSH-1.99", 8) != 0) {
1954*63afb9a5SDavid du Colombier 		/* not a protocol version we know; give up */
1955*63afb9a5SDavid du Colombier 		ioclose(io, fd);
1956*63afb9a5SDavid du Colombier 		goto err;
1957*63afb9a5SDavid du Colombier 	}
1958*63afb9a5SDavid du Colombier 	closeioproc(io);
1959*63afb9a5SDavid du Colombier 
1960*63afb9a5SDavid du Colombier 	if (c->otherid)
1961*63afb9a5SDavid du Colombier 		free(c->otherid);
1962*63afb9a5SDavid du Colombier 	c->otherid = othid = estrdup9p(path);
1963*63afb9a5SDavid du Colombier 	for (n = strlen(othid) - 1; othid[n] == '\r' || othid[n] == '\n'; --n)
1964*63afb9a5SDavid du Colombier 		othid[n] = '\0';
1965*63afb9a5SDavid du Colombier 	c->state = Initting;
1966*63afb9a5SDavid du Colombier 
1967*63afb9a5SDavid du Colombier 	/* start the reader thread */
1968*63afb9a5SDavid du Colombier 	if (c->rpid < 0)
1969*63afb9a5SDavid du Colombier 		c->rpid = threadcreate(reader, c, Defstk);
1970*63afb9a5SDavid du Colombier 
1971*63afb9a5SDavid du Colombier 	/*
1972*63afb9a5SDavid du Colombier 	 * negotiate the protocols
1973*63afb9a5SDavid du Colombier 	 * We don't do the full negotiation here, because we also have
1974*63afb9a5SDavid du Colombier 	 * to handle a re-negotiation request from the other end.  So
1975*63afb9a5SDavid du Colombier 	 * we just kick it off and let the receiver process take it from there.
1976*63afb9a5SDavid du Colombier 	 */
1977*63afb9a5SDavid du Colombier 
1978*63afb9a5SDavid du Colombier 	send_kexinit(c);
1979*63afb9a5SDavid du Colombier 
1980*63afb9a5SDavid du Colombier 	qlock(&c->l);
1981*63afb9a5SDavid du Colombier 	if ((c->role == Client && c->state != Negotiating) ||
1982*63afb9a5SDavid du Colombier 	    (c->role == Server && c->state != Established))
1983*63afb9a5SDavid du Colombier 		rsleep(&c->r);
1984*63afb9a5SDavid du Colombier 	qunlock(&c->l);
1985*63afb9a5SDavid du Colombier 	if (c->role == Server && c->state != Established ||
1986*63afb9a5SDavid du Colombier 	    c->role == Client && c->state != Negotiating)
1987*63afb9a5SDavid du Colombier 		return -1;
1988*63afb9a5SDavid du Colombier 	return 0;
1989*63afb9a5SDavid du Colombier err:
1990*63afb9a5SDavid du Colombier 	/* should use hangup in dial(2) instead of diddling /net/tcp */
1991*63afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/ctl", mntpt, tcpconn);
1992*63afb9a5SDavid du Colombier 	fd = ioopen(io, path, OWRITE);
1993*63afb9a5SDavid du Colombier 	iowrite(io, fd, "hangup", 6);
1994*63afb9a5SDavid du Colombier 	ioclose(io, fd);
1995*63afb9a5SDavid du Colombier 	closeioproc(io);
1996*63afb9a5SDavid du Colombier 	return -1;
1997*63afb9a5SDavid du Colombier }
1998*63afb9a5SDavid du Colombier 
1999*63afb9a5SDavid du Colombier void
2000*63afb9a5SDavid du Colombier send_kexinit(Conn *c)
2001*63afb9a5SDavid du Colombier {
2002*63afb9a5SDavid du Colombier 	Packet *ptmp;
2003*63afb9a5SDavid du Colombier 	char *buf, *p, *e;
2004*63afb9a5SDavid du Colombier 	int i, msglen;
2005*63afb9a5SDavid du Colombier 
2006*63afb9a5SDavid du Colombier 	sshdebug(c, "initializing kexinit packet");
2007*63afb9a5SDavid du Colombier 	if (c->skexinit != nil)
2008*63afb9a5SDavid du Colombier 		free(c->skexinit);
2009*63afb9a5SDavid du Colombier 	c->skexinit = new_packet(c);
2010*63afb9a5SDavid du Colombier 
2011*63afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
2012*63afb9a5SDavid du Colombier 	buf[0] = (uchar)SSH_MSG_KEXINIT;
2013*63afb9a5SDavid du Colombier 
2014*63afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 1);
2015*63afb9a5SDavid du Colombier 	for (i = 0; i < 16; ++i)
2016*63afb9a5SDavid du Colombier 		buf[i] = fastrand();
2017*63afb9a5SDavid du Colombier 
2018*63afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 16);		/* cookie */
2019*63afb9a5SDavid du Colombier 	e = buf + Bigbufsz - 1;
2020*63afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", kexes[0]->name);
2021*63afb9a5SDavid du Colombier 	for (i = 1; i < nelem(kexes); ++i)
2022*63afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", kexes[i]->name);
2023*63afb9a5SDavid du Colombier 	sshdebug(c, "sent KEX algs: %s", buf);
2024*63afb9a5SDavid du Colombier 
2025*63afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* Key exchange */
2026*63afb9a5SDavid du Colombier 	if (pkas[0] == nil)
2027*63afb9a5SDavid du Colombier 		add_string(c->skexinit, "");
2028*63afb9a5SDavid du Colombier 	else{
2029*63afb9a5SDavid du Colombier 		p = seprint(buf, e, "%s", pkas[0]->name);
2030*63afb9a5SDavid du Colombier 		for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i)
2031*63afb9a5SDavid du Colombier 			p = seprint(p, e, ",%s", pkas[i]->name);
2032*63afb9a5SDavid du Colombier 		sshdebug(c, "sent host key algs: %s", buf);
2033*63afb9a5SDavid du Colombier 		add_string(c->skexinit, buf);		/* server's key algs */
2034*63afb9a5SDavid du Colombier 	}
2035*63afb9a5SDavid du Colombier 
2036*63afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", cryptos[0]->name);
2037*63afb9a5SDavid du Colombier 	for (i = 1; i < nelem(cryptos); ++i)
2038*63afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", cryptos[i]->name);
2039*63afb9a5SDavid du Colombier 	sshdebug(c, "sent crypto algs: %s", buf);
2040*63afb9a5SDavid du Colombier 
2041*63afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* c->s crypto */
2042*63afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* s->c crypto */
2043*63afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", macnames[0]);
2044*63afb9a5SDavid du Colombier 	for (i = 1; i < nelem(macnames); ++i)
2045*63afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", macnames[i]);
2046*63afb9a5SDavid du Colombier 	sshdebug(c, "sent MAC algs: %s", buf);
2047*63afb9a5SDavid du Colombier 
2048*63afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* c->s mac */
2049*63afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* s->c mac */
2050*63afb9a5SDavid du Colombier 	add_string(c->skexinit, "none");	/* c->s compression */
2051*63afb9a5SDavid du Colombier 	add_string(c->skexinit, "none");	/* s->c compression */
2052*63afb9a5SDavid du Colombier 	add_string(c->skexinit, "");		/* c->s languages */
2053*63afb9a5SDavid du Colombier 	add_string(c->skexinit, "");		/* s->c languages */
2054*63afb9a5SDavid du Colombier 	memset(buf, 0, 5);
2055*63afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 5);
2056*63afb9a5SDavid du Colombier 
2057*63afb9a5SDavid du Colombier 	ptmp = new_packet(c);
2058*63afb9a5SDavid du Colombier 	memmove(ptmp, c->skexinit, sizeof(Packet));
2059*63afb9a5SDavid du Colombier 	msglen = finish_packet(ptmp);
2060*63afb9a5SDavid du Colombier 
2061*63afb9a5SDavid du Colombier 	if (c->dio && c->datafd >= 0)
2062*63afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, ptmp->nlength, msglen);
2063*63afb9a5SDavid du Colombier 	free(ptmp);
2064*63afb9a5SDavid du Colombier 	free(buf);
2065*63afb9a5SDavid du Colombier }
2066*63afb9a5SDavid du Colombier 
2067*63afb9a5SDavid du Colombier static void
2068*63afb9a5SDavid du Colombier establish(Conn *c)
2069*63afb9a5SDavid du Colombier {
2070*63afb9a5SDavid du Colombier 	qlock(&c->l);
2071*63afb9a5SDavid du Colombier 	c->state = Established;
2072*63afb9a5SDavid du Colombier 	rwakeup(&c->r);
2073*63afb9a5SDavid du Colombier 	qunlock(&c->l);
2074*63afb9a5SDavid du Colombier }
2075*63afb9a5SDavid du Colombier 
2076*63afb9a5SDavid du Colombier static int
2077*63afb9a5SDavid du Colombier negotiating(Conn *c, Packet *p, Packet *p2, char *buf, int size)
2078*63afb9a5SDavid du Colombier {
2079*63afb9a5SDavid du Colombier 	int i, n;
2080*63afb9a5SDavid du Colombier 
2081*63afb9a5SDavid du Colombier 	USED(size);
2082*63afb9a5SDavid du Colombier 	switch (p->payload[0]) {
2083*63afb9a5SDavid du Colombier 	case SSH_MSG_DISCONNECT:
2084*63afb9a5SDavid du Colombier 		if (debug) {
2085*63afb9a5SDavid du Colombier 			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
2086*63afb9a5SDavid du Colombier 			sshdebug(c, "got disconnect: %s", buf);
2087*63afb9a5SDavid du Colombier 		}
2088*63afb9a5SDavid du Colombier 		return -1;
2089*63afb9a5SDavid du Colombier 	case SSH_MSG_NEWKEYS:
2090*63afb9a5SDavid du Colombier 		/*
2091*63afb9a5SDavid du Colombier 		 * If we're just updating, go straight to
2092*63afb9a5SDavid du Colombier 		 * established, otherwise wait for auth'n.
2093*63afb9a5SDavid du Colombier 		 */
2094*63afb9a5SDavid du Colombier 		i = c->encrypt;
2095*63afb9a5SDavid du Colombier 		memmove(c->c2siv, c->nc2siv, SHA1dlen*2);
2096*63afb9a5SDavid du Colombier 		memmove(c->s2civ, c->ns2civ, SHA1dlen*2);
2097*63afb9a5SDavid du Colombier 		memmove(c->c2sek, c->nc2sek, SHA1dlen*2);
2098*63afb9a5SDavid du Colombier 		memmove(c->s2cek, c->ns2cek, SHA1dlen*2);
2099*63afb9a5SDavid du Colombier 		memmove(c->c2sik, c->nc2sik, SHA1dlen*2);
2100*63afb9a5SDavid du Colombier 		memmove(c->s2cik, c->ns2cik, SHA1dlen*2);
2101*63afb9a5SDavid du Colombier 		c->cscrypt = c->ncscrypt;
2102*63afb9a5SDavid du Colombier 		c->sccrypt = c->nsccrypt;
2103*63afb9a5SDavid du Colombier 		c->csmac = c->ncsmac;
2104*63afb9a5SDavid du Colombier 		c->scmac = c->nscmac;
2105*63afb9a5SDavid du Colombier 		c->c2scs = cryptos[c->cscrypt]->init(c, 0);
2106*63afb9a5SDavid du Colombier 		c->s2ccs = cryptos[c->sccrypt]->init(c, 1);
2107*63afb9a5SDavid du Colombier 		if (c->role == Server) {
2108*63afb9a5SDavid du Colombier 			c->encrypt = c->sccrypt;
2109*63afb9a5SDavid du Colombier 			c->decrypt = c->cscrypt;
2110*63afb9a5SDavid du Colombier 			c->outmac = c->scmac;
2111*63afb9a5SDavid du Colombier 			c->inmac = c->csmac;
2112*63afb9a5SDavid du Colombier 			c->enccs = c->s2ccs;
2113*63afb9a5SDavid du Colombier 			c->deccs = c->c2scs;
2114*63afb9a5SDavid du Colombier 			c->outik = c->s2cik;
2115*63afb9a5SDavid du Colombier 			c->inik = c->c2sik;
2116*63afb9a5SDavid du Colombier 		} else{
2117*63afb9a5SDavid du Colombier 			c->encrypt = c->cscrypt;
2118*63afb9a5SDavid du Colombier 			c->decrypt = c->sccrypt;
2119*63afb9a5SDavid du Colombier 			c->outmac = c->csmac;
2120*63afb9a5SDavid du Colombier 			c->inmac = c->scmac;
2121*63afb9a5SDavid du Colombier 			c->enccs = c->c2scs;
2122*63afb9a5SDavid du Colombier 			c->deccs = c->s2ccs;
2123*63afb9a5SDavid du Colombier 			c->outik = c->c2sik;
2124*63afb9a5SDavid du Colombier 			c->inik = c->s2cik;
2125*63afb9a5SDavid du Colombier 		}
2126*63afb9a5SDavid du Colombier 		sshdebug(c, "using %s for encryption and %s for decryption",
2127*63afb9a5SDavid du Colombier 			cryptos[c->encrypt]->name, cryptos[c->decrypt]->name);
2128*63afb9a5SDavid du Colombier 		qlock(&c->l);
2129*63afb9a5SDavid du Colombier 		if (i != -1)
2130*63afb9a5SDavid du Colombier 			c->state = Established;
2131*63afb9a5SDavid du Colombier 		if (c->role == Client)
2132*63afb9a5SDavid du Colombier 			rwakeup(&c->r);
2133*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2134*63afb9a5SDavid du Colombier 		break;
2135*63afb9a5SDavid du Colombier 	case SSH_MSG_KEXDH_INIT:
2136*63afb9a5SDavid du Colombier 		kexes[c->kexalg]->serverkex(c, p);
2137*63afb9a5SDavid du Colombier 		break;
2138*63afb9a5SDavid du Colombier 	case SSH_MSG_KEXDH_REPLY:
2139*63afb9a5SDavid du Colombier 		init_packet(p2);
2140*63afb9a5SDavid du Colombier 		p2->c = c;
2141*63afb9a5SDavid du Colombier 		if (kexes[c->kexalg]->clientkex2(c, p) < 0) {
2142*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_DISCONNECT);
2143*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED);
2144*63afb9a5SDavid du Colombier 			add_string(p2, "Key exchange failure");
2145*63afb9a5SDavid du Colombier 			add_string(p2, "");
2146*63afb9a5SDavid du Colombier 			n = finish_packet(p2);
2147*63afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
2148*63afb9a5SDavid du Colombier 			shutdown(c);
2149*63afb9a5SDavid du Colombier 			free(p);
2150*63afb9a5SDavid du Colombier 			free(p2);
2151*63afb9a5SDavid du Colombier 			closeioproc(c->rio);
2152*63afb9a5SDavid du Colombier 			c->rio = nil;
2153*63afb9a5SDavid du Colombier 			c->rpid = -1;
2154*63afb9a5SDavid du Colombier 
2155*63afb9a5SDavid du Colombier 			qlock(&c->l);
2156*63afb9a5SDavid du Colombier 			rwakeup(&c->r);
2157*63afb9a5SDavid du Colombier 			qunlock(&c->l);
2158*63afb9a5SDavid du Colombier 
2159*63afb9a5SDavid du Colombier 			sshlog(c, "key exchange failure");
2160*63afb9a5SDavid du Colombier 			threadexits(nil);
2161*63afb9a5SDavid du Colombier 		}
2162*63afb9a5SDavid du Colombier 		add_byte(p2, SSH_MSG_NEWKEYS);
2163*63afb9a5SDavid du Colombier 		n = finish_packet(p2);
2164*63afb9a5SDavid du Colombier 		iowrite(c->rio, c->datafd, p2->nlength, n);
2165*63afb9a5SDavid du Colombier 		qlock(&c->l);
2166*63afb9a5SDavid du Colombier 		rwakeup(&c->r);
2167*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2168*63afb9a5SDavid du Colombier 		break;
2169*63afb9a5SDavid du Colombier 	case SSH_MSG_SERVICE_REQUEST:
2170*63afb9a5SDavid du Colombier 		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
2171*63afb9a5SDavid du Colombier 		sshdebug(c, "got service request: %s", buf);
2172*63afb9a5SDavid du Colombier 		if (strcmp(buf, "ssh-userauth") == 0 ||
2173*63afb9a5SDavid du Colombier 		    strcmp(buf, "ssh-connection") == 0) {
2174*63afb9a5SDavid du Colombier 			init_packet(p2);
2175*63afb9a5SDavid du Colombier 			p2->c = c;
2176*63afb9a5SDavid du Colombier 			sshdebug(c, "connection");
2177*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_SERVICE_ACCEPT);
2178*63afb9a5SDavid du Colombier 			add_string(p2, buf);
2179*63afb9a5SDavid du Colombier 			n = finish_packet(p2);
2180*63afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
2181*63afb9a5SDavid du Colombier 			c->state = Authing;
2182*63afb9a5SDavid du Colombier 		} else{
2183*63afb9a5SDavid du Colombier 			init_packet(p2);
2184*63afb9a5SDavid du Colombier 			p2->c = c;
2185*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_DISCONNECT);
2186*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE);
2187*63afb9a5SDavid du Colombier 			add_string(p2, "Unknown service type");
2188*63afb9a5SDavid du Colombier 			add_string(p2, "");
2189*63afb9a5SDavid du Colombier 			n = finish_packet(p2);
2190*63afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
2191*63afb9a5SDavid du Colombier 			return -1;
2192*63afb9a5SDavid du Colombier 		}
2193*63afb9a5SDavid du Colombier 		break;
2194*63afb9a5SDavid du Colombier 	case SSH_MSG_SERVICE_ACCEPT:
2195*63afb9a5SDavid du Colombier 		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
2196*63afb9a5SDavid du Colombier 		if (c->service && strcmp(c->service, "ssh-userauth") == 0) {
2197*63afb9a5SDavid du Colombier 			free(c->service);
2198*63afb9a5SDavid du Colombier 			c->service = estrdup9p("ssh-connection");
2199*63afb9a5SDavid du Colombier 		}
2200*63afb9a5SDavid du Colombier 		sshdebug(c, "got service accept: %s: responding with %s %s",
2201*63afb9a5SDavid du Colombier 			buf, c->user, c->service);
2202*63afb9a5SDavid du Colombier 		n = client_auth(c, c->rio);
2203*63afb9a5SDavid du Colombier 		c->state = Authing;
2204*63afb9a5SDavid du Colombier 		if (n < 0) {
2205*63afb9a5SDavid du Colombier 			qlock(&c->l);
2206*63afb9a5SDavid du Colombier 			rwakeup(&c->r);
2207*63afb9a5SDavid du Colombier 			qunlock(&c->l);
2208*63afb9a5SDavid du Colombier 		}
2209*63afb9a5SDavid du Colombier 		break;
2210*63afb9a5SDavid du Colombier 	}
2211*63afb9a5SDavid du Colombier 	return 0;
2212*63afb9a5SDavid du Colombier }
2213*63afb9a5SDavid du Colombier 
2214*63afb9a5SDavid du Colombier static void
2215*63afb9a5SDavid du Colombier nochans(Conn *c, Packet *p, Packet *p2)
2216*63afb9a5SDavid du Colombier {
2217*63afb9a5SDavid du Colombier 	int n;
2218*63afb9a5SDavid du Colombier 
2219*63afb9a5SDavid du Colombier 	init_packet(p2);
2220*63afb9a5SDavid du Colombier 	p2->c = c;
2221*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE);
2222*63afb9a5SDavid du Colombier 	add_block(p2, p->payload + 5, 4);
2223*63afb9a5SDavid du Colombier 	hnputl(p2->payload + p2->rlength - 1, 4);
2224*63afb9a5SDavid du Colombier 	p2->rlength += 4;
2225*63afb9a5SDavid du Colombier 	add_string(p2, "No available channels");
2226*63afb9a5SDavid du Colombier 	add_string(p2, "EN");
2227*63afb9a5SDavid du Colombier 	n = finish_packet(p2);
2228*63afb9a5SDavid du Colombier 	iowrite(c->rio, c->datafd, p2->nlength, n);
2229*63afb9a5SDavid du Colombier }
2230*63afb9a5SDavid du Colombier 
2231*63afb9a5SDavid du Colombier static int
2232*63afb9a5SDavid du Colombier established(Conn *c, Packet *p, Packet *p2, char *buf, int size)
2233*63afb9a5SDavid du Colombier {
2234*63afb9a5SDavid du Colombier 	int i, n, cnum;
2235*63afb9a5SDavid du Colombier 	uchar *q;
2236*63afb9a5SDavid du Colombier 	Plist *pl;
2237*63afb9a5SDavid du Colombier 	SSHChan *ch;
2238*63afb9a5SDavid du Colombier 
2239*63afb9a5SDavid du Colombier 	USED(size);
2240*63afb9a5SDavid du Colombier 	if (debug > 1) {
2241*63afb9a5SDavid du Colombier 		sshdebug(c, "in Established state, got:");
2242*63afb9a5SDavid du Colombier 		dump_packet(p);
2243*63afb9a5SDavid du Colombier 	}
2244*63afb9a5SDavid du Colombier 	switch (p->payload[0]) {
2245*63afb9a5SDavid du Colombier 	case SSH_MSG_DISCONNECT:
2246*63afb9a5SDavid du Colombier 		if (debug) {
2247*63afb9a5SDavid du Colombier 			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
2248*63afb9a5SDavid du Colombier 			sshdebug(c, "got disconnect: %s", buf);
2249*63afb9a5SDavid du Colombier 		}
2250*63afb9a5SDavid du Colombier 		return -1;
2251*63afb9a5SDavid du Colombier 	case SSH_MSG_IGNORE:
2252*63afb9a5SDavid du Colombier 	case SSH_MSG_UNIMPLEMENTED:
2253*63afb9a5SDavid du Colombier 		break;
2254*63afb9a5SDavid du Colombier 	case SSH_MSG_DEBUG:
2255*63afb9a5SDavid du Colombier 		if (debug || p->payload[1]) {
2256*63afb9a5SDavid du Colombier 			get_string(p, p->payload + 2, buf, Arbbufsz, nil);
2257*63afb9a5SDavid du Colombier 			sshdebug(c, "got debug message: %s", buf);
2258*63afb9a5SDavid du Colombier 		}
2259*63afb9a5SDavid du Colombier 		break;
2260*63afb9a5SDavid du Colombier 	case SSH_MSG_KEXINIT:
2261*63afb9a5SDavid du Colombier 		send_kexinit(c);
2262*63afb9a5SDavid du Colombier 		if (c->rkexinit)
2263*63afb9a5SDavid du Colombier 			free(c->rkexinit);
2264*63afb9a5SDavid du Colombier 		c->rkexinit = new_packet(c);
2265*63afb9a5SDavid du Colombier 		memmove(c->rkexinit, p, sizeof(Packet));
2266*63afb9a5SDavid du Colombier 		if (validatekex(c, p) < 0) {
2267*63afb9a5SDavid du Colombier 			sshdebug(c, "kex crypto algorithm mismatch (Established)");
2268*63afb9a5SDavid du Colombier 			return -1;
2269*63afb9a5SDavid du Colombier 		}
2270*63afb9a5SDavid du Colombier 		sshdebug(c, "using %s Kex algorithm and %s PKA",
2271*63afb9a5SDavid du Colombier 			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
2272*63afb9a5SDavid du Colombier 		c->state = Negotiating;
2273*63afb9a5SDavid du Colombier 		break;
2274*63afb9a5SDavid du Colombier 	case SSH_MSG_GLOBAL_REQUEST:
2275*63afb9a5SDavid du Colombier 	case SSH_MSG_REQUEST_SUCCESS:
2276*63afb9a5SDavid du Colombier 	case SSH_MSG_REQUEST_FAILURE:
2277*63afb9a5SDavid du Colombier 		break;
2278*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN:
2279*63afb9a5SDavid du Colombier 		q = get_string(p, p->payload + 1, buf, Arbbufsz, nil);
2280*63afb9a5SDavid du Colombier 		sshdebug(c, "searching for a listener for channel type %s", buf);
2281*63afb9a5SDavid du Colombier 		ch = alloc_chan(c);
2282*63afb9a5SDavid du Colombier 		if (ch == nil) {
2283*63afb9a5SDavid du Colombier 			nochans(c, p, p2);
2284*63afb9a5SDavid du Colombier 			break;
2285*63afb9a5SDavid du Colombier 		}
2286*63afb9a5SDavid du Colombier 
2287*63afb9a5SDavid du Colombier 		sshdebug(c, "alloced channel %d for listener", ch->id);
2288*63afb9a5SDavid du Colombier 		qlock(&c->l);
2289*63afb9a5SDavid du Colombier 		ch->otherid = nhgetl(q);
2290*63afb9a5SDavid du Colombier 		ch->twindow = nhgetl(q+4);
2291*63afb9a5SDavid du Colombier 		sshdebug(c, "got lock in channel open");
2292*63afb9a5SDavid du Colombier 		for (i = 0; i < c->nchan; ++i)
2293*63afb9a5SDavid du Colombier 			if (c->chans[i] && c->chans[i]->state == Listening &&
2294*63afb9a5SDavid du Colombier 			    c->chans[i]->ann &&
2295*63afb9a5SDavid du Colombier 			    strcmp(c->chans[i]->ann, buf) == 0)
2296*63afb9a5SDavid du Colombier 				break;
2297*63afb9a5SDavid du Colombier 		if (i >= c->nchan) {
2298*63afb9a5SDavid du Colombier 			sshdebug(c, "no listener: sleeping");
2299*63afb9a5SDavid du Colombier 			ch->state = Opening;
2300*63afb9a5SDavid du Colombier 			if (ch->ann)
2301*63afb9a5SDavid du Colombier 				free(ch->ann);
2302*63afb9a5SDavid du Colombier 			ch->ann = estrdup9p(buf);
2303*63afb9a5SDavid du Colombier 			sshdebug(c, "waiting for someone to announce %s", ch->ann);
2304*63afb9a5SDavid du Colombier 			rsleep(&ch->r);
2305*63afb9a5SDavid du Colombier 		} else{
2306*63afb9a5SDavid du Colombier 			sshdebug(c, "found listener on channel %d", ch->id);
2307*63afb9a5SDavid du Colombier 			c->chans[i]->waker = ch->id;
2308*63afb9a5SDavid du Colombier 			rwakeup(&c->chans[i]->r);
2309*63afb9a5SDavid du Colombier 		}
2310*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2311*63afb9a5SDavid du Colombier 		break;
2312*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
2313*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2314*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2315*63afb9a5SDavid du Colombier 		qlock(&c->l);
2316*63afb9a5SDavid du Colombier 		ch->otherid = nhgetl(p->payload+5);
2317*63afb9a5SDavid du Colombier 		ch->twindow = nhgetl(p->payload+9);
2318*63afb9a5SDavid du Colombier 		rwakeup(&ch->r);
2319*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2320*63afb9a5SDavid du Colombier 		break;
2321*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN_FAILURE:
2322*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2323*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2324*63afb9a5SDavid du Colombier 		qlock(&c->l);
2325*63afb9a5SDavid du Colombier 		rwakeup(&ch->r);
2326*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2327*63afb9a5SDavid du Colombier 		return -1;
2328*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_WINDOW_ADJUST:
2329*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2330*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2331*63afb9a5SDavid du Colombier 		ch->twindow += nhgetl(p->payload + 5);
2332*63afb9a5SDavid du Colombier 		sshdebug(c, "new twindow for channel: %d: %lud", cnum, ch->twindow);
2333*63afb9a5SDavid du Colombier 		qlock(&ch->xmtlock);
2334*63afb9a5SDavid du Colombier 		rwakeup(&ch->xmtrendez);
2335*63afb9a5SDavid du Colombier 		qunlock(&ch->xmtlock);
2336*63afb9a5SDavid du Colombier 		break;
2337*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_DATA:
2338*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_EXTENDED_DATA:
2339*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2340*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2341*63afb9a5SDavid du Colombier 		pl = emalloc9p(sizeof(Plist));
2342*63afb9a5SDavid du Colombier 		pl->pack = emalloc9p(sizeof(Packet));
2343*63afb9a5SDavid du Colombier 		memmove(pl->pack, p, sizeof(Packet));
2344*63afb9a5SDavid du Colombier 		if (p->payload[0] == SSH_MSG_CHANNEL_DATA) {
2345*63afb9a5SDavid du Colombier 			pl->rem = nhgetl(p->payload + 5);
2346*63afb9a5SDavid du Colombier 			pl->st = pl->pack->payload + 9;
2347*63afb9a5SDavid du Colombier 		} else {
2348*63afb9a5SDavid du Colombier 			pl->rem = nhgetl(p->payload + 9);
2349*63afb9a5SDavid du Colombier 			pl->st = pl->pack->payload + 13;
2350*63afb9a5SDavid du Colombier 		}
2351*63afb9a5SDavid du Colombier 		pl->next = nil;
2352*63afb9a5SDavid du Colombier 		if (ch->dataq == nil)
2353*63afb9a5SDavid du Colombier 			ch->dataq = pl;
2354*63afb9a5SDavid du Colombier 		else
2355*63afb9a5SDavid du Colombier 			ch->datatl->next = pl;
2356*63afb9a5SDavid du Colombier 		ch->datatl = pl;
2357*63afb9a5SDavid du Colombier 		ch->inrqueue += pl->rem;
2358*63afb9a5SDavid du Colombier 		nbsendul(ch->inchan, 1);
2359*63afb9a5SDavid du Colombier 		break;
2360*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_EOF:
2361*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2362*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2363*63afb9a5SDavid du Colombier 		if (ch->state != Closed && ch->state != Closing) {
2364*63afb9a5SDavid du Colombier 			ch->state = Eof;
2365*63afb9a5SDavid du Colombier 			nbsendul(ch->inchan, 1);
2366*63afb9a5SDavid du Colombier 			nbsendul(ch->reqchan, 1);
2367*63afb9a5SDavid du Colombier 		}
2368*63afb9a5SDavid du Colombier 		break;
2369*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_CLOSE:
2370*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2371*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2372*63afb9a5SDavid du Colombier 		if (ch->state != Closed && ch->state != Closing) {
2373*63afb9a5SDavid du Colombier 			init_packet(p2);
2374*63afb9a5SDavid du Colombier 			p2->c = c;
2375*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_CHANNEL_CLOSE);
2376*63afb9a5SDavid du Colombier 			hnputl(p2->payload + 1, ch->otherid);
2377*63afb9a5SDavid du Colombier 			p2->rlength += 4;
2378*63afb9a5SDavid du Colombier 			n = finish_packet(p2);
2379*63afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
2380*63afb9a5SDavid du Colombier 		}
2381*63afb9a5SDavid du Colombier 		qlock(&c->l);
2382*63afb9a5SDavid du Colombier 		if (ch->state != Closed) {
2383*63afb9a5SDavid du Colombier 			ch->state = Closed;
2384*63afb9a5SDavid du Colombier 			rwakeup(&ch->r);
2385*63afb9a5SDavid du Colombier 			nbsendul(ch->inchan, 1);
2386*63afb9a5SDavid du Colombier 			nbsendul(ch->reqchan, 1);
2387*63afb9a5SDavid du Colombier 			chanclose(ch->inchan);
2388*63afb9a5SDavid du Colombier 			chanclose(ch->reqchan);
2389*63afb9a5SDavid du Colombier 		}
2390*63afb9a5SDavid du Colombier 		qunlock(&c->l);
2391*63afb9a5SDavid du Colombier 		for (i = 0; i < MAXCONN && (!c->chans[i] ||
2392*63afb9a5SDavid du Colombier 		    c->chans[i]->state == Empty || c->chans[i]->state == Closed);
2393*63afb9a5SDavid du Colombier 		    ++i)
2394*63afb9a5SDavid du Colombier 			;
2395*63afb9a5SDavid du Colombier 		if (i >= MAXCONN)
2396*63afb9a5SDavid du Colombier 			return -1;
2397*63afb9a5SDavid du Colombier 		break;
2398*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_REQUEST:
2399*63afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
2400*63afb9a5SDavid du Colombier 		ch = c->chans[cnum];
2401*63afb9a5SDavid du Colombier 		sshdebug(c, "queueing channel request for channel: %d", cnum);
2402*63afb9a5SDavid du Colombier 		q = get_string(p, p->payload+5, buf, Arbbufsz, nil);
2403*63afb9a5SDavid du Colombier 		pl = emalloc9p(sizeof(Plist));
2404*63afb9a5SDavid du Colombier 		pl->pack = emalloc9p(sizeof(Packet));
2405*63afb9a5SDavid du Colombier 		n = snprint((char *)pl->pack->payload,
2406*63afb9a5SDavid du Colombier 			Maxpayload, "%s %c", buf, *q? 't': 'f');
2407*63afb9a5SDavid du Colombier 		sshdebug(c, "request message begins: %s",
2408*63afb9a5SDavid du Colombier 			(char *)pl->pack->payload);
2409*63afb9a5SDavid du Colombier 		memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2)));
2410*63afb9a5SDavid du Colombier 		pl->rem = p->rlength - 11 + 2;
2411*63afb9a5SDavid du Colombier 		pl->st = pl->pack->payload;
2412*63afb9a5SDavid du Colombier 		pl->next = nil;
2413*63afb9a5SDavid du Colombier 		if (ch->reqq == nil)
2414*63afb9a5SDavid du Colombier 			ch->reqq = pl;
2415*63afb9a5SDavid du Colombier 		else
2416*63afb9a5SDavid du Colombier 			ch->reqtl->next = pl;
2417*63afb9a5SDavid du Colombier 		ch->reqtl = pl;
2418*63afb9a5SDavid du Colombier 		nbsendul(ch->reqchan, 1);
2419*63afb9a5SDavid du Colombier 		break;
2420*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_SUCCESS:
2421*63afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_FAILURE:
2422*63afb9a5SDavid du Colombier 	default:
2423*63afb9a5SDavid du Colombier 		break;
2424*63afb9a5SDavid du Colombier 	}
2425*63afb9a5SDavid du Colombier 	return 0;
2426*63afb9a5SDavid du Colombier }
2427*63afb9a5SDavid du Colombier 
2428*63afb9a5SDavid du Colombier static void
2429*63afb9a5SDavid du Colombier bail(Conn *c, Packet *p, Packet *p2, char *sts)
2430*63afb9a5SDavid du Colombier {
2431*63afb9a5SDavid du Colombier 	shutdown(c);
2432*63afb9a5SDavid du Colombier 	free(p);
2433*63afb9a5SDavid du Colombier 	free(p2);
2434*63afb9a5SDavid du Colombier 	if (c->rio) {
2435*63afb9a5SDavid du Colombier 		closeioproc(c->rio);
2436*63afb9a5SDavid du Colombier 		c->rio = nil;
2437*63afb9a5SDavid du Colombier 	}
2438*63afb9a5SDavid du Colombier 	c->rpid = -1;
2439*63afb9a5SDavid du Colombier 	threadexits(sts);
2440*63afb9a5SDavid du Colombier }
2441*63afb9a5SDavid du Colombier 
2442*63afb9a5SDavid du Colombier static void
2443*63afb9a5SDavid du Colombier reader0(Conn *c, Packet *p, Packet *p2)
2444*63afb9a5SDavid du Colombier {
2445*63afb9a5SDavid du Colombier 	int i, n, nl, np, nm, nb;
2446*63afb9a5SDavid du Colombier 	char buf[Arbbufsz];
2447*63afb9a5SDavid du Colombier 
2448*63afb9a5SDavid du Colombier 	nm = 0;
2449*63afb9a5SDavid du Colombier 	nb = 4;
2450*63afb9a5SDavid du Colombier 	if (c->decrypt != -1)
2451*63afb9a5SDavid du Colombier 		nb = cryptos[c->decrypt]->blklen;
2452*63afb9a5SDavid du Colombier 	sshdebug(c, "calling read for connection %d, state %d, nb %d, dc %d",
2453*63afb9a5SDavid du Colombier 		c->id, c->state, nb, c->decrypt);
2454*63afb9a5SDavid du Colombier 	if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) {
2455*63afb9a5SDavid du Colombier 		sshdebug(c, "reader for connection %d exiting, got %d: %r",
2456*63afb9a5SDavid du Colombier 			c->id, nl);
2457*63afb9a5SDavid du Colombier 		bail(c, p, p2, "reader exiting");
2458*63afb9a5SDavid du Colombier 	}
2459*63afb9a5SDavid du Colombier 	if (c->decrypt != -1)
2460*63afb9a5SDavid du Colombier 		cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb);
2461*63afb9a5SDavid du Colombier 	p->rlength = nhgetl(p->nlength);
2462*63afb9a5SDavid du Colombier 	sshdebug(c, "got message length: %ld", p->rlength);
2463*63afb9a5SDavid du Colombier 	if (p->rlength > Maxpktpay) {
2464*63afb9a5SDavid du Colombier 		sshdebug(c, "absurd packet length: %ld, unrecoverable decrypt failure",
2465*63afb9a5SDavid du Colombier 			p->rlength);
2466*63afb9a5SDavid du Colombier 		bail(c, p, p2, "absurd packet length");
2467*63afb9a5SDavid du Colombier 	}
2468*63afb9a5SDavid du Colombier 	np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb);
2469*63afb9a5SDavid du Colombier 	if (c->inmac != -1)
2470*63afb9a5SDavid du Colombier 		nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4,
2471*63afb9a5SDavid du Colombier 			SHA1dlen);		/* SHA1dlen was magic 20 */
2472*63afb9a5SDavid du Colombier 	n = nl + np + nm;
2473*63afb9a5SDavid du Colombier 	if (debug) {
2474*63afb9a5SDavid du Colombier 		sshdebug(c, "got message of %d bytes %d padding", n, p->pad_len);
2475*63afb9a5SDavid du Colombier 		if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) {
2476*63afb9a5SDavid du Colombier 			i = nhgetl(p->payload+1);
2477*63afb9a5SDavid du Colombier 			if (c->chans[i])
2478*63afb9a5SDavid du Colombier 				sshdebug(c, " for channel %d win %lud",
2479*63afb9a5SDavid du Colombier 					i, c->chans[i]->rwindow);
2480*63afb9a5SDavid du Colombier 			else
2481*63afb9a5SDavid du Colombier 				sshdebug(c, " for invalid channel %d", i);
2482*63afb9a5SDavid du Colombier 		}
2483*63afb9a5SDavid du Colombier 		sshdebug(c, " first byte: %d", p->payload[0]);
2484*63afb9a5SDavid du Colombier 	}
2485*63afb9a5SDavid du Colombier 	/* SHA1dlen was magic 20 */
2486*63afb9a5SDavid du Colombier 	if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) {
2487*63afb9a5SDavid du Colombier 		sshdebug(c, "got EOF/error on connection read: %d %d %r", np, nm);
2488*63afb9a5SDavid du Colombier 		bail(c, p, p2, "error or eof");
2489*63afb9a5SDavid du Colombier 	}
2490*63afb9a5SDavid du Colombier 	p->tlength = n;
2491*63afb9a5SDavid du Colombier 	p->rlength = n - 4;
2492*63afb9a5SDavid du Colombier 	if (undo_packet(p) < 0) {
2493*63afb9a5SDavid du Colombier 		sshdebug(c, "bad packet in connection %d: exiting", c->id);
2494*63afb9a5SDavid du Colombier 		bail(c, p, p2, "bad packet");
2495*63afb9a5SDavid du Colombier 	}
2496*63afb9a5SDavid du Colombier 
2497*63afb9a5SDavid du Colombier 	if (c->state == Initting) {
2498*63afb9a5SDavid du Colombier 		if (p->payload[0] != SSH_MSG_KEXINIT) {
2499*63afb9a5SDavid du Colombier 			sshdebug(c, "missing KEX init packet: %d", p->payload[0]);
2500*63afb9a5SDavid du Colombier 			bail(c, p, p2, "bad kex");
2501*63afb9a5SDavid du Colombier 		}
2502*63afb9a5SDavid du Colombier 		if (c->rkexinit)
2503*63afb9a5SDavid du Colombier 			free(c->rkexinit);
2504*63afb9a5SDavid du Colombier 		c->rkexinit = new_packet(c);
2505*63afb9a5SDavid du Colombier 		memmove(c->rkexinit, p, sizeof(Packet));
2506*63afb9a5SDavid du Colombier 		if (validatekex(c, p) < 0) {
2507*63afb9a5SDavid du Colombier 			sshdebug(c, "kex crypto algorithm mismatch (Initting)");
2508*63afb9a5SDavid du Colombier 			bail(c, p, p2, "bad kex");
2509*63afb9a5SDavid du Colombier 		}
2510*63afb9a5SDavid du Colombier 		sshdebug(c, "using %s Kex algorithm and %s PKA",
2511*63afb9a5SDavid du Colombier 			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
2512*63afb9a5SDavid du Colombier 		if (c->role == Client)
2513*63afb9a5SDavid du Colombier 			kexes[c->kexalg]->clientkex1(c, p);
2514*63afb9a5SDavid du Colombier 		c->state = Negotiating;
2515*63afb9a5SDavid du Colombier 	} else if (c->state == Negotiating) {
2516*63afb9a5SDavid du Colombier 		if (negotiating(c, p, p2, buf, sizeof buf) < 0)
2517*63afb9a5SDavid du Colombier 			bail(c, p, p2, "negotiating");
2518*63afb9a5SDavid du Colombier 	} else if (c->state == Authing) {
2519*63afb9a5SDavid du Colombier 		switch (p->payload[0]) {
2520*63afb9a5SDavid du Colombier 		case SSH_MSG_DISCONNECT:
2521*63afb9a5SDavid du Colombier 			if (debug) {
2522*63afb9a5SDavid du Colombier 				get_string(p, p->payload + 5, buf, Arbbufsz, nil);
2523*63afb9a5SDavid du Colombier 				sshdebug(c, "got disconnect: %s", buf);
2524*63afb9a5SDavid du Colombier 			}
2525*63afb9a5SDavid du Colombier 			bail(c, p, p2, "msg disconnect");
2526*63afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_REQUEST:
2527*63afb9a5SDavid du Colombier 			switch (auth_req(p, c)) {
2528*63afb9a5SDavid du Colombier 			case 0:
2529*63afb9a5SDavid du Colombier 				establish(c);
2530*63afb9a5SDavid du Colombier 				break;
2531*63afb9a5SDavid du Colombier 			case -1:
2532*63afb9a5SDavid du Colombier 				break;
2533*63afb9a5SDavid du Colombier 			case -2:
2534*63afb9a5SDavid du Colombier 				bail(c, p, p2, "in userauth request");
2535*63afb9a5SDavid du Colombier 			}
2536*63afb9a5SDavid du Colombier 			break;
2537*63afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_FAILURE:
2538*63afb9a5SDavid du Colombier 			qlock(&c->l);
2539*63afb9a5SDavid du Colombier 			rwakeup(&c->r);
2540*63afb9a5SDavid du Colombier 			qunlock(&c->l);
2541*63afb9a5SDavid du Colombier 			break;
2542*63afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_SUCCESS:
2543*63afb9a5SDavid du Colombier 			establish(c);
2544*63afb9a5SDavid du Colombier 			break;
2545*63afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_BANNER:
2546*63afb9a5SDavid du Colombier 			break;
2547*63afb9a5SDavid du Colombier 		}
2548*63afb9a5SDavid du Colombier 	} else if (c->state == Established) {
2549*63afb9a5SDavid du Colombier 		if (established(c, p, p2, buf, sizeof buf) < 0)
2550*63afb9a5SDavid du Colombier 			bail(c, p, p2, "from established state");
2551*63afb9a5SDavid du Colombier 	} else {
2552*63afb9a5SDavid du Colombier 		sshdebug(c, "connection %d in bad state, reader exiting", c->id);
2553*63afb9a5SDavid du Colombier 		bail(c, p, p2, "bad conn state");
2554*63afb9a5SDavid du Colombier 	}
2555*63afb9a5SDavid du Colombier }
2556*63afb9a5SDavid du Colombier 
2557*63afb9a5SDavid du Colombier void
2558*63afb9a5SDavid du Colombier reader(void *a)
2559*63afb9a5SDavid du Colombier {
2560*63afb9a5SDavid du Colombier 	Conn *c;
2561*63afb9a5SDavid du Colombier 	Packet *p, *p2;
2562*63afb9a5SDavid du Colombier 
2563*63afb9a5SDavid du Colombier 	threadsetname("reader");
2564*63afb9a5SDavid du Colombier 	c = a;
2565*63afb9a5SDavid du Colombier 	c->rpid = threadid();
2566*63afb9a5SDavid du Colombier 	sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid);
2567*63afb9a5SDavid du Colombier 	threadsetname("reader");
2568*63afb9a5SDavid du Colombier 	p = new_packet(c);
2569*63afb9a5SDavid du Colombier 	p2 = new_packet(c);
2570*63afb9a5SDavid du Colombier 	c->rio = ioproc();
2571*63afb9a5SDavid du Colombier 	for(;;)
2572*63afb9a5SDavid du Colombier 		reader0(c, p, p2);
2573*63afb9a5SDavid du Colombier }
2574*63afb9a5SDavid du Colombier 
2575*63afb9a5SDavid du Colombier int
2576*63afb9a5SDavid du Colombier validatekex(Conn *c, Packet *p)
2577*63afb9a5SDavid du Colombier {
2578*63afb9a5SDavid du Colombier 	if (c->role == Server)
2579*63afb9a5SDavid du Colombier 		return validatekexs(p);
2580*63afb9a5SDavid du Colombier 	else
2581*63afb9a5SDavid du Colombier 		return validatekexc(p);
2582*63afb9a5SDavid du Colombier }
2583*63afb9a5SDavid du Colombier 
2584*63afb9a5SDavid du Colombier int
2585*63afb9a5SDavid du Colombier validatekexs(Packet *p)
2586*63afb9a5SDavid du Colombier {
2587*63afb9a5SDavid du Colombier 	uchar *q;
2588*63afb9a5SDavid du Colombier 	char *toks[Maxtoks];
2589*63afb9a5SDavid du Colombier 	int i, j, n;
2590*63afb9a5SDavid du Colombier 	char *buf;
2591*63afb9a5SDavid du Colombier 
2592*63afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
2593*63afb9a5SDavid du Colombier 	q = p->payload + 17;
2594*63afb9a5SDavid du Colombier 
2595*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2596*63afb9a5SDavid du Colombier 	sshdebug(nil, "received KEX algs: %s", buf);
2597*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2598*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2599*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(kexes); ++j)
2600*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], kexes[j]->name) == 0)
2601*63afb9a5SDavid du Colombier 				goto foundk;
2602*63afb9a5SDavid du Colombier 	sshdebug(nil, "kex algs not in kexes");
2603*63afb9a5SDavid du Colombier 	free(buf);
2604*63afb9a5SDavid du Colombier 	return -1;
2605*63afb9a5SDavid du Colombier foundk:
2606*63afb9a5SDavid du Colombier 	p->c->kexalg = j;
2607*63afb9a5SDavid du Colombier 
2608*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2609*63afb9a5SDavid du Colombier 	sshdebug(nil, "received host key algs: %s", buf);
2610*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2611*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2612*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
2613*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], pkas[j]->name) == 0)
2614*63afb9a5SDavid du Colombier 				goto foundpka;
2615*63afb9a5SDavid du Colombier 	sshdebug(nil, "host key algs not in pkas");
2616*63afb9a5SDavid du Colombier 	free(buf);
2617*63afb9a5SDavid du Colombier 	return -1;
2618*63afb9a5SDavid du Colombier foundpka:
2619*63afb9a5SDavid du Colombier 	p->c->pkalg = j;
2620*63afb9a5SDavid du Colombier 
2621*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2622*63afb9a5SDavid du Colombier 	sshdebug(nil, "received C2S crypto algs: %s", buf);
2623*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2624*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2625*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(cryptos); ++j)
2626*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
2627*63afb9a5SDavid du Colombier 				goto foundc1;
2628*63afb9a5SDavid du Colombier 	sshdebug(nil, "c2s crypto algs not in cryptos");
2629*63afb9a5SDavid du Colombier 	free(buf);
2630*63afb9a5SDavid du Colombier 	return -1;
2631*63afb9a5SDavid du Colombier foundc1:
2632*63afb9a5SDavid du Colombier 	p->c->ncscrypt = j;
2633*63afb9a5SDavid du Colombier 
2634*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2635*63afb9a5SDavid du Colombier 	sshdebug(nil, "received S2C crypto algs: %s", buf);
2636*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2637*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2638*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(cryptos); ++j)
2639*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
2640*63afb9a5SDavid du Colombier 				goto foundc2;
2641*63afb9a5SDavid du Colombier 	sshdebug(nil, "s2c crypto algs not in cryptos");
2642*63afb9a5SDavid du Colombier 	free(buf);
2643*63afb9a5SDavid du Colombier 	return -1;
2644*63afb9a5SDavid du Colombier foundc2:
2645*63afb9a5SDavid du Colombier 	p->c->nsccrypt = j;
2646*63afb9a5SDavid du Colombier 
2647*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2648*63afb9a5SDavid du Colombier 	sshdebug(nil, "received C2S MAC algs: %s", buf);
2649*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2650*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2651*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(macnames); ++j)
2652*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
2653*63afb9a5SDavid du Colombier 				goto foundm1;
2654*63afb9a5SDavid du Colombier 	sshdebug(nil, "c2s mac algs not in cryptos");
2655*63afb9a5SDavid du Colombier 	free(buf);
2656*63afb9a5SDavid du Colombier 	return -1;
2657*63afb9a5SDavid du Colombier foundm1:
2658*63afb9a5SDavid du Colombier 	p->c->ncsmac = j;
2659*63afb9a5SDavid du Colombier 
2660*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2661*63afb9a5SDavid du Colombier 	sshdebug(nil, "received S2C MAC algs: %s", buf);
2662*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2663*63afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
2664*63afb9a5SDavid du Colombier 		for (j = 0; j < nelem(macnames); ++j)
2665*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
2666*63afb9a5SDavid du Colombier 				goto foundm2;
2667*63afb9a5SDavid du Colombier 	sshdebug(nil, "s2c mac algs not in cryptos");
2668*63afb9a5SDavid du Colombier 	free(buf);
2669*63afb9a5SDavid du Colombier 	return -1;
2670*63afb9a5SDavid du Colombier foundm2:
2671*63afb9a5SDavid du Colombier 	p->c->nscmac = j;
2672*63afb9a5SDavid du Colombier 
2673*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2674*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2675*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2676*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2677*63afb9a5SDavid du Colombier 	free(buf);
2678*63afb9a5SDavid du Colombier 	if (*q)
2679*63afb9a5SDavid du Colombier 		return 1;
2680*63afb9a5SDavid du Colombier 	return 0;
2681*63afb9a5SDavid du Colombier }
2682*63afb9a5SDavid du Colombier 
2683*63afb9a5SDavid du Colombier int
2684*63afb9a5SDavid du Colombier validatekexc(Packet *p)
2685*63afb9a5SDavid du Colombier {
2686*63afb9a5SDavid du Colombier 	uchar *q;
2687*63afb9a5SDavid du Colombier 	char *toks[Maxtoks];
2688*63afb9a5SDavid du Colombier 	int i, j, n;
2689*63afb9a5SDavid du Colombier 	char *buf;
2690*63afb9a5SDavid du Colombier 
2691*63afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
2692*63afb9a5SDavid du Colombier 	q = p->payload + 17;
2693*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2694*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2695*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(kexes); ++j)
2696*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2697*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], kexes[j]->name) == 0)
2698*63afb9a5SDavid du Colombier 				goto foundk;
2699*63afb9a5SDavid du Colombier 	free(buf);
2700*63afb9a5SDavid du Colombier 	return -1;
2701*63afb9a5SDavid du Colombier foundk:
2702*63afb9a5SDavid du Colombier 	p->c->kexalg = j;
2703*63afb9a5SDavid du Colombier 
2704*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2705*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2706*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
2707*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2708*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], pkas[j]->name) == 0)
2709*63afb9a5SDavid du Colombier 				goto foundpka;
2710*63afb9a5SDavid du Colombier 	free(buf);
2711*63afb9a5SDavid du Colombier 	return -1;
2712*63afb9a5SDavid du Colombier foundpka:
2713*63afb9a5SDavid du Colombier 	p->c->pkalg = j;
2714*63afb9a5SDavid du Colombier 
2715*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2716*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2717*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(cryptos); ++j)
2718*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2719*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
2720*63afb9a5SDavid du Colombier 				goto foundc1;
2721*63afb9a5SDavid du Colombier 	free(buf);
2722*63afb9a5SDavid du Colombier 	return -1;
2723*63afb9a5SDavid du Colombier foundc1:
2724*63afb9a5SDavid du Colombier 	p->c->ncscrypt = j;
2725*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2726*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2727*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(cryptos); ++j)
2728*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2729*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
2730*63afb9a5SDavid du Colombier 				goto foundc2;
2731*63afb9a5SDavid du Colombier 	free(buf);
2732*63afb9a5SDavid du Colombier 	return -1;
2733*63afb9a5SDavid du Colombier foundc2:
2734*63afb9a5SDavid du Colombier 	p->c->nsccrypt = j;
2735*63afb9a5SDavid du Colombier 
2736*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2737*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2738*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(macnames); ++j)
2739*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2740*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
2741*63afb9a5SDavid du Colombier 				goto foundm1;
2742*63afb9a5SDavid du Colombier 	free(buf);
2743*63afb9a5SDavid du Colombier 	return -1;
2744*63afb9a5SDavid du Colombier foundm1:
2745*63afb9a5SDavid du Colombier 	p->c->ncsmac = j;
2746*63afb9a5SDavid du Colombier 
2747*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2748*63afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
2749*63afb9a5SDavid du Colombier 	for (j = 0; j < nelem(macnames); ++j)
2750*63afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
2751*63afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
2752*63afb9a5SDavid du Colombier 				goto foundm2;
2753*63afb9a5SDavid du Colombier 	free(buf);
2754*63afb9a5SDavid du Colombier 	return -1;
2755*63afb9a5SDavid du Colombier foundm2:
2756*63afb9a5SDavid du Colombier 	p->c->nscmac = j;
2757*63afb9a5SDavid du Colombier 
2758*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2759*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2760*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2761*63afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
2762*63afb9a5SDavid du Colombier 	free(buf);
2763*63afb9a5SDavid du Colombier 	return *q != 0;
2764*63afb9a5SDavid du Colombier }
2765*63afb9a5SDavid du Colombier 
2766*63afb9a5SDavid du Colombier int
2767*63afb9a5SDavid du Colombier memrandom(void *p, int n)
2768*63afb9a5SDavid du Colombier {
2769*63afb9a5SDavid du Colombier 	uchar *cp;
2770*63afb9a5SDavid du Colombier 
2771*63afb9a5SDavid du Colombier 	for (cp = (uchar*)p; n > 0; n--)
2772*63afb9a5SDavid du Colombier 		*cp++ = fastrand();
2773*63afb9a5SDavid du Colombier 	return 0;
2774*63afb9a5SDavid du Colombier }
2775*63afb9a5SDavid du Colombier 
2776*63afb9a5SDavid du Colombier /*
2777*63afb9a5SDavid du Colombier  *  create a change uid capability
2778*63afb9a5SDavid du Colombier  */
2779*63afb9a5SDavid du Colombier char*
2780*63afb9a5SDavid du Colombier mkcap(char *from, char *to)
2781*63afb9a5SDavid du Colombier {
2782*63afb9a5SDavid du Colombier 	int fd, fromtosz;
2783*63afb9a5SDavid du Colombier 	char *cap, *key;
2784*63afb9a5SDavid du Colombier 	uchar rand[SHA1dlen], hash[SHA1dlen];
2785*63afb9a5SDavid du Colombier 
2786*63afb9a5SDavid du Colombier 	fd = open("#¤/caphash", OWRITE);
2787*63afb9a5SDavid du Colombier 	if (fd < 0)
2788*63afb9a5SDavid du Colombier 		sshlog(nil, "can't open #¤/caphash: %r");
2789*63afb9a5SDavid du Colombier 
2790*63afb9a5SDavid du Colombier 	/* create the capability */
2791*63afb9a5SDavid du Colombier 	fromtosz = strlen(from) + 1 + strlen(to) + 1;
2792*63afb9a5SDavid du Colombier 	cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1);
2793*63afb9a5SDavid du Colombier 	snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to);
2794*63afb9a5SDavid du Colombier 	memrandom(rand, sizeof(rand));
2795*63afb9a5SDavid du Colombier 	key = cap + fromtosz;
2796*63afb9a5SDavid du Colombier 	enc64(key, sizeof(rand)*3, rand, sizeof(rand));
2797*63afb9a5SDavid du Colombier 
2798*63afb9a5SDavid du Colombier 	/* hash the capability */
2799*63afb9a5SDavid du Colombier 	hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
2800*63afb9a5SDavid du Colombier 
2801*63afb9a5SDavid du Colombier 	/* give the kernel the hash */
2802*63afb9a5SDavid du Colombier 	key[-1] = '@';
2803*63afb9a5SDavid du Colombier 	sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash);
2804*63afb9a5SDavid du Colombier 	if (write(fd, hash, SHA1dlen) != SHA1dlen) {
2805*63afb9a5SDavid du Colombier 		close(fd);
2806*63afb9a5SDavid du Colombier 		free(cap);
2807*63afb9a5SDavid du Colombier 		return nil;
2808*63afb9a5SDavid du Colombier 	}
2809*63afb9a5SDavid du Colombier 	close(fd);
2810*63afb9a5SDavid du Colombier 	return cap;
2811*63afb9a5SDavid du Colombier }
2812*63afb9a5SDavid du Colombier 
2813*63afb9a5SDavid du Colombier /*
2814*63afb9a5SDavid du Colombier  * ask keyfs (assumes we are on an auth server)
2815*63afb9a5SDavid du Colombier  */
2816*63afb9a5SDavid du Colombier static AuthInfo *
2817*63afb9a5SDavid du Colombier keyfsauth(char *me, char *user, char *pw, char *key1, char *key2)
2818*63afb9a5SDavid du Colombier {
2819*63afb9a5SDavid du Colombier 	int fd;
2820*63afb9a5SDavid du Colombier 	char path[Arbpathlen];
2821*63afb9a5SDavid du Colombier 	AuthInfo *ai;
2822*63afb9a5SDavid du Colombier 
2823*63afb9a5SDavid du Colombier 	if (passtokey(key1, pw) == 0)
2824*63afb9a5SDavid du Colombier 		return nil;
2825*63afb9a5SDavid du Colombier 
2826*63afb9a5SDavid du Colombier 	snprint(path, Arbpathlen, "/mnt/keys/%s/key", user);
2827*63afb9a5SDavid du Colombier 	if ((fd = open(path, OREAD)) < 0) {
2828*63afb9a5SDavid du Colombier 		werrstr("Invalid user %s", user);
2829*63afb9a5SDavid du Colombier 		return nil;
2830*63afb9a5SDavid du Colombier 	}
2831*63afb9a5SDavid du Colombier 	if (read(fd, key2, DESKEYLEN) != DESKEYLEN) {
2832*63afb9a5SDavid du Colombier 		close(fd);
2833*63afb9a5SDavid du Colombier 		werrstr("Password mismatch 1");
2834*63afb9a5SDavid du Colombier 		return nil;
2835*63afb9a5SDavid du Colombier 	}
2836*63afb9a5SDavid du Colombier 	close(fd);
2837*63afb9a5SDavid du Colombier 
2838*63afb9a5SDavid du Colombier 	if (memcmp(key1, key2, DESKEYLEN) != 0) {
2839*63afb9a5SDavid du Colombier 		werrstr("Password mismatch 2");
2840*63afb9a5SDavid du Colombier 		return nil;
2841*63afb9a5SDavid du Colombier 	}
2842*63afb9a5SDavid du Colombier 
2843*63afb9a5SDavid du Colombier 	ai = emalloc9p(sizeof(AuthInfo));
2844*63afb9a5SDavid du Colombier 	ai->cuid = estrdup9p(user);
2845*63afb9a5SDavid du Colombier 	ai->suid = estrdup9p(me);
2846*63afb9a5SDavid du Colombier 	ai->cap = mkcap(me, user);
2847*63afb9a5SDavid du Colombier 	ai->nsecret = 0;
2848*63afb9a5SDavid du Colombier 	ai->secret = (uchar *)estrdup9p("");
2849*63afb9a5SDavid du Colombier 	return ai;
2850*63afb9a5SDavid du Colombier }
2851*63afb9a5SDavid du Colombier 
2852*63afb9a5SDavid du Colombier static void
2853*63afb9a5SDavid du Colombier userauthfailed(Packet *p2)
2854*63afb9a5SDavid du Colombier {
2855*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_FAILURE);
2856*63afb9a5SDavid du Colombier 	add_string(p2, "password,publickey");
2857*63afb9a5SDavid du Colombier 	add_byte(p2, 0);
2858*63afb9a5SDavid du Colombier }
2859*63afb9a5SDavid du Colombier 
2860*63afb9a5SDavid du Colombier static int
2861*63afb9a5SDavid du Colombier authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q,
2862*63afb9a5SDavid du Colombier 	char *alg, char *blob, char *sig, char *service, char *me)
2863*63afb9a5SDavid du Colombier {
2864*63afb9a5SDavid du Colombier 	int n, thisway, nblob, nsig;
2865*63afb9a5SDavid du Colombier 	char method[32];
2866*63afb9a5SDavid du Colombier 
2867*63afb9a5SDavid du Colombier 	sshdebug(c, "auth_req publickey for user %s", user);
2868*63afb9a5SDavid du Colombier 	thisway = *q == '\0';
2869*63afb9a5SDavid du Colombier 	q = get_string(p, q+1, alg, Arbpathlen, nil);
2870*63afb9a5SDavid du Colombier 	q = get_string(p, q, blob, Blobsz, &nblob);
2871*63afb9a5SDavid du Colombier 	if (thisway) {
2872*63afb9a5SDavid du Colombier 		/*
2873*63afb9a5SDavid du Colombier 		 * Should really check to see if this user can
2874*63afb9a5SDavid du Colombier 		 * be authed this way.
2875*63afb9a5SDavid du Colombier 		 */
2876*63afb9a5SDavid du Colombier 		for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
2877*63afb9a5SDavid du Colombier 		    strcmp(pkas[n]->name, alg) != 0; ++n)
2878*63afb9a5SDavid du Colombier 			;
2879*63afb9a5SDavid du Colombier 		if (n >= nelem(pkas) || pkas[n] == nil) {
2880*63afb9a5SDavid du Colombier 			userauthfailed(p2);
2881*63afb9a5SDavid du Colombier 			return -1;
2882*63afb9a5SDavid du Colombier 		}
2883*63afb9a5SDavid du Colombier 		add_byte(p2, SSH_MSG_USERAUTH_PK_OK);
2884*63afb9a5SDavid du Colombier 		add_string(p2, alg);
2885*63afb9a5SDavid du Colombier 		add_block(p2, blob, nblob);
2886*63afb9a5SDavid du Colombier 		return 0;
2887*63afb9a5SDavid du Colombier 	}
2888*63afb9a5SDavid du Colombier 
2889*63afb9a5SDavid du Colombier 	get_string(p, q, sig, Blobsz, &nsig);
2890*63afb9a5SDavid du Colombier 	for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
2891*63afb9a5SDavid du Colombier 	    strcmp(pkas[n]->name, alg) != 0; ++n)
2892*63afb9a5SDavid du Colombier 		;
2893*63afb9a5SDavid du Colombier 	if (n >= nelem(pkas) || pkas[n] == nil) {
2894*63afb9a5SDavid du Colombier 		userauthfailed(p2);
2895*63afb9a5SDavid du Colombier 		return -1;
2896*63afb9a5SDavid du Colombier 	}
2897*63afb9a5SDavid du Colombier 
2898*63afb9a5SDavid du Colombier 	add_block(p2, c->sessid, SHA1dlen);
2899*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
2900*63afb9a5SDavid du Colombier 	add_string(p2, user);
2901*63afb9a5SDavid du Colombier 	add_string(p2, service);
2902*63afb9a5SDavid du Colombier 	add_string(p2, method);
2903*63afb9a5SDavid du Colombier 	add_byte(p2, 1);
2904*63afb9a5SDavid du Colombier 	add_string(p2, alg);
2905*63afb9a5SDavid du Colombier 	add_block(p2, blob, nblob);
2906*63afb9a5SDavid du Colombier 	if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig)
2907*63afb9a5SDavid du Colombier 	    == 0) {
2908*63afb9a5SDavid du Colombier 		init_packet(p2);
2909*63afb9a5SDavid du Colombier 		p2->c = c;
2910*63afb9a5SDavid du Colombier 		sshlog(c, "public key login failed");
2911*63afb9a5SDavid du Colombier 		userauthfailed(p2);
2912*63afb9a5SDavid du Colombier 		return -1;
2913*63afb9a5SDavid du Colombier 	}
2914*63afb9a5SDavid du Colombier 	free(c->cap);
2915*63afb9a5SDavid du Colombier 	c->cap = mkcap(me, user);
2916*63afb9a5SDavid du Colombier 	init_packet(p2);
2917*63afb9a5SDavid du Colombier 	p2->c = c;
2918*63afb9a5SDavid du Colombier 	sshlog(c, "logged in by public key");
2919*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
2920*63afb9a5SDavid du Colombier 	return 0;
2921*63afb9a5SDavid du Colombier }
2922*63afb9a5SDavid du Colombier 
2923*63afb9a5SDavid du Colombier int
2924*63afb9a5SDavid du Colombier auth_req(Packet *p, Conn *c)
2925*63afb9a5SDavid du Colombier {
2926*63afb9a5SDavid du Colombier 	int n, ret;
2927*63afb9a5SDavid du Colombier 	char *alg, *blob, *sig, *service, *me, *user, *pw, *path;
2928*63afb9a5SDavid du Colombier 	char key1[DESKEYLEN], key2[DESKEYLEN], method[32];
2929*63afb9a5SDavid du Colombier 	uchar *q;
2930*63afb9a5SDavid du Colombier 	AuthInfo *ai;
2931*63afb9a5SDavid du Colombier 	Packet *p2;
2932*63afb9a5SDavid du Colombier 
2933*63afb9a5SDavid du Colombier 	service = emalloc9p(Arbpathlen);
2934*63afb9a5SDavid du Colombier 	me = emalloc9p(Arbpathlen);
2935*63afb9a5SDavid du Colombier 	user = emalloc9p(Arbpathlen);
2936*63afb9a5SDavid du Colombier 	pw = emalloc9p(Arbpathlen);
2937*63afb9a5SDavid du Colombier 	alg = emalloc9p(Arbpathlen);
2938*63afb9a5SDavid du Colombier 	path = emalloc9p(Arbpathlen);
2939*63afb9a5SDavid du Colombier 	blob = emalloc9p(Blobsz);
2940*63afb9a5SDavid du Colombier 	sig = emalloc9p(Blobsz);
2941*63afb9a5SDavid du Colombier 	ret = -1;				/* failure is default */
2942*63afb9a5SDavid du Colombier 
2943*63afb9a5SDavid du Colombier 	q = get_string(p, p->payload + 1, user, Arbpathlen, nil);
2944*63afb9a5SDavid du Colombier 	free(c->user);
2945*63afb9a5SDavid du Colombier 	c->user = estrdup9p(user);
2946*63afb9a5SDavid du Colombier 	q = get_string(p, q, service, Arbpathlen, nil);
2947*63afb9a5SDavid du Colombier 	q = get_string(p, q, method, sizeof method, nil);
2948*63afb9a5SDavid du Colombier 	sshdebug(c, "got userauth request: %s %s %s", user, service, method);
2949*63afb9a5SDavid du Colombier 
2950*63afb9a5SDavid du Colombier 	readfile("/dev/user", me, Arbpathlen);
2951*63afb9a5SDavid du Colombier 
2952*63afb9a5SDavid du Colombier 	p2 = new_packet(c);
2953*63afb9a5SDavid du Colombier 	if (strcmp(method, "publickey") == 0)
2954*63afb9a5SDavid du Colombier 		ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me);
2955*63afb9a5SDavid du Colombier 	else if (strcmp(method, "password") == 0) {
2956*63afb9a5SDavid du Colombier 		get_string(p, q + 1, pw, Arbpathlen, nil);
2957*63afb9a5SDavid du Colombier 		// sshdebug(c, "%s", pw);	/* bad idea to log passwords */
2958*63afb9a5SDavid du Colombier 		sshdebug(c, "auth_req password");
2959*63afb9a5SDavid du Colombier 		if (kflag)
2960*63afb9a5SDavid du Colombier 			ai = keyfsauth(me, user, pw, key1, key2);
2961*63afb9a5SDavid du Colombier 		else
2962*63afb9a5SDavid du Colombier 			ai = auth_userpasswd(user, pw);
2963*63afb9a5SDavid du Colombier 		if (ai == nil) {
2964*63afb9a5SDavid du Colombier 			sshlog(c, "login failed: %r");
2965*63afb9a5SDavid du Colombier 			userauthfailed(p2);
2966*63afb9a5SDavid du Colombier 		} else {
2967*63afb9a5SDavid du Colombier 			sshdebug(c, "auth successful: cuid %s suid %s cap %s",
2968*63afb9a5SDavid du Colombier 				ai->cuid, ai->suid, ai->cap);
2969*63afb9a5SDavid du Colombier 			free(c->cap);
2970*63afb9a5SDavid du Colombier 			if (strcmp(user, me) == 0)
2971*63afb9a5SDavid du Colombier 				c->cap = estrdup9p("n/a");
2972*63afb9a5SDavid du Colombier 			else
2973*63afb9a5SDavid du Colombier 				c->cap = estrdup9p(ai->cap);
2974*63afb9a5SDavid du Colombier 			sshlog(c, "logged in by password");
2975*63afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
2976*63afb9a5SDavid du Colombier 			auth_freeAI(ai);
2977*63afb9a5SDavid du Colombier 			ret = 0;
2978*63afb9a5SDavid du Colombier 		}
2979*63afb9a5SDavid du Colombier 	} else
2980*63afb9a5SDavid du Colombier 		userauthfailed(p2);
2981*63afb9a5SDavid du Colombier 
2982*63afb9a5SDavid du Colombier 	n = finish_packet(p2);
2983*63afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p2->nlength, n);
2984*63afb9a5SDavid du Colombier 
2985*63afb9a5SDavid du Colombier 	free(service);
2986*63afb9a5SDavid du Colombier 	free(me);
2987*63afb9a5SDavid du Colombier 	free(user);
2988*63afb9a5SDavid du Colombier 	free(pw);
2989*63afb9a5SDavid du Colombier 	free(alg);
2990*63afb9a5SDavid du Colombier 	free(blob);
2991*63afb9a5SDavid du Colombier 	free(sig);
2992*63afb9a5SDavid du Colombier 	free(path);
2993*63afb9a5SDavid du Colombier 	free(p2);
2994*63afb9a5SDavid du Colombier 	return ret;
2995*63afb9a5SDavid du Colombier }
2996*63afb9a5SDavid du Colombier 
2997*63afb9a5SDavid du Colombier int
2998*63afb9a5SDavid du Colombier client_auth(Conn *c, Ioproc *io)
2999*63afb9a5SDavid du Colombier {
3000*63afb9a5SDavid du Colombier 	Packet *p2, *p3, *p4;
3001*63afb9a5SDavid du Colombier 	char *r, *s;
3002*63afb9a5SDavid du Colombier 	mpint *ek, *nk;
3003*63afb9a5SDavid du Colombier 	int i, n;
3004*63afb9a5SDavid du Colombier 
3005*63afb9a5SDavid du Colombier 	sshdebug(c, "client_auth");
3006*63afb9a5SDavid du Colombier 	if (!c->password && !c->authkey)
3007*63afb9a5SDavid du Colombier 		return -1;
3008*63afb9a5SDavid du Colombier 
3009*63afb9a5SDavid du Colombier 	p2 = new_packet(c);
3010*63afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
3011*63afb9a5SDavid du Colombier 	add_string(p2, c->user);
3012*63afb9a5SDavid du Colombier 	add_string(p2, c->service);
3013*63afb9a5SDavid du Colombier 	if (c->password) {
3014*63afb9a5SDavid du Colombier 		add_string(p2, "password");
3015*63afb9a5SDavid du Colombier 		add_byte(p2, 0);
3016*63afb9a5SDavid du Colombier 		add_string(p2, c->password);
3017*63afb9a5SDavid du Colombier 		sshdebug(c, "client_auth using password for svc %s", c->service);
3018*63afb9a5SDavid du Colombier 	} else {
3019*63afb9a5SDavid du Colombier 		sshdebug(c, "client_auth trying rsa public key");
3020*63afb9a5SDavid du Colombier 		add_string(p2, "publickey");
3021*63afb9a5SDavid du Colombier 		add_byte(p2, 1);
3022*63afb9a5SDavid du Colombier 		add_string(p2, "ssh-rsa");
3023*63afb9a5SDavid du Colombier 
3024*63afb9a5SDavid du Colombier 		r = strstr(c->authkey, " ek=");
3025*63afb9a5SDavid du Colombier 		s = strstr(c->authkey, " n=");
3026*63afb9a5SDavid du Colombier 		if (!r || !s) {
3027*63afb9a5SDavid du Colombier 			shutdown(c);
3028*63afb9a5SDavid du Colombier 			free(p2);
3029*63afb9a5SDavid du Colombier 			sshdebug(c, "client_auth no rsa key");
3030*63afb9a5SDavid du Colombier 			return -1;
3031*63afb9a5SDavid du Colombier 		}
3032*63afb9a5SDavid du Colombier 		ek = strtomp(r+4, nil, 16, nil);
3033*63afb9a5SDavid du Colombier 		nk = strtomp(s+3, nil, 16, nil);
3034*63afb9a5SDavid du Colombier 
3035*63afb9a5SDavid du Colombier 		p3 = new_packet(c);
3036*63afb9a5SDavid du Colombier 		add_string(p3, "ssh-rsa");
3037*63afb9a5SDavid du Colombier 		add_mp(p3, ek);
3038*63afb9a5SDavid du Colombier 		add_mp(p3, nk);
3039*63afb9a5SDavid du Colombier 		add_block(p2, p3->payload, p3->rlength-1);
3040*63afb9a5SDavid du Colombier 
3041*63afb9a5SDavid du Colombier 		p4 = new_packet(c);
3042*63afb9a5SDavid du Colombier 		add_block(p4, c->sessid, SHA1dlen);
3043*63afb9a5SDavid du Colombier 		add_byte(p4, SSH_MSG_USERAUTH_REQUEST);
3044*63afb9a5SDavid du Colombier 		add_string(p4, c->user);
3045*63afb9a5SDavid du Colombier 		add_string(p4, c->service);
3046*63afb9a5SDavid du Colombier 		add_string(p4, "publickey");
3047*63afb9a5SDavid du Colombier 		add_byte(p4, 1);
3048*63afb9a5SDavid du Colombier 		add_string(p4, "ssh-rsa");
3049*63afb9a5SDavid du Colombier 		add_block(p4, p3->payload, p3->rlength-1);
3050*63afb9a5SDavid du Colombier 		mpfree(ek);
3051*63afb9a5SDavid du Colombier 		mpfree(nk);
3052*63afb9a5SDavid du Colombier 		free(p3);
3053*63afb9a5SDavid du Colombier 
3054*63afb9a5SDavid du Colombier 		for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0;
3055*63afb9a5SDavid du Colombier 		    ++i)
3056*63afb9a5SDavid du Colombier 			;
3057*63afb9a5SDavid du Colombier 		sshdebug(c, "client_auth rsa signing alg %d: %r",  i);
3058*63afb9a5SDavid du Colombier 		if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) {
3059*63afb9a5SDavid du Colombier 			sshdebug(c, "client_auth rsa signing failed: %r");
3060*63afb9a5SDavid du Colombier 			free(p4);
3061*63afb9a5SDavid du Colombier 			free(p2);
3062*63afb9a5SDavid du Colombier 			return -1;
3063*63afb9a5SDavid du Colombier 		}
3064*63afb9a5SDavid du Colombier 		add_block(p2, p3->payload, p3->rlength-1);
3065*63afb9a5SDavid du Colombier 		free(p3);
3066*63afb9a5SDavid du Colombier 		free(p4);
3067*63afb9a5SDavid du Colombier 	}
3068*63afb9a5SDavid du Colombier 
3069*63afb9a5SDavid du Colombier 	n = finish_packet(p2);
3070*63afb9a5SDavid du Colombier 	if (writeio(io, c->datafd, p2->nlength, n) != n)
3071*63afb9a5SDavid du Colombier 		sshdebug(c, "client_auth write failed: %r");
3072*63afb9a5SDavid du Colombier 	free(p2);
3073*63afb9a5SDavid du Colombier 	return 0;
3074*63afb9a5SDavid du Colombier }
3075*63afb9a5SDavid du Colombier 
3076*63afb9a5SDavid du Colombier /* should use auth_getkey or something similar */
3077*63afb9a5SDavid du Colombier char *
3078*63afb9a5SDavid du Colombier factlookup(int nattr, int nreq, char *attrs[])
3079*63afb9a5SDavid du Colombier {
3080*63afb9a5SDavid du Colombier 	Biobuf *bp;
3081*63afb9a5SDavid du Colombier 	char *buf, *toks[Maxtoks], *res, *q;
3082*63afb9a5SDavid du Colombier 	int ntok, nmatch, maxmatch;
3083*63afb9a5SDavid du Colombier 	int i, j;
3084*63afb9a5SDavid du Colombier 
3085*63afb9a5SDavid du Colombier 	res = nil;
3086*63afb9a5SDavid du Colombier 	bp = Bopen("/mnt/factotum/ctl", OREAD);
3087*63afb9a5SDavid du Colombier 	if (bp == nil)
3088*63afb9a5SDavid du Colombier 		return nil;
3089*63afb9a5SDavid du Colombier 	maxmatch = 0;
3090*63afb9a5SDavid du Colombier 	while (buf = Brdstr(bp, '\n', 1)) {
3091*63afb9a5SDavid du Colombier 		q = estrdup9p(buf);
3092*63afb9a5SDavid du Colombier 		ntok = gettokens(buf, toks, nelem(toks), " ");
3093*63afb9a5SDavid du Colombier 		nmatch = 0;
3094*63afb9a5SDavid du Colombier 		for (i = 0; i < nattr; ++i) {
3095*63afb9a5SDavid du Colombier 			for (j = 0; j < ntok; ++j)
3096*63afb9a5SDavid du Colombier 				if (strcmp(attrs[i], toks[j]) == 0) {
3097*63afb9a5SDavid du Colombier 					++nmatch;
3098*63afb9a5SDavid du Colombier 					break;
3099*63afb9a5SDavid du Colombier 				}
3100*63afb9a5SDavid du Colombier 			if (i < nreq && j >= ntok)
3101*63afb9a5SDavid du Colombier 				break;
3102*63afb9a5SDavid du Colombier 		}
3103*63afb9a5SDavid du Colombier 		if (i >= nattr && nmatch > maxmatch) {
3104*63afb9a5SDavid du Colombier 			free(res);
3105*63afb9a5SDavid du Colombier 			res = q;
3106*63afb9a5SDavid du Colombier 			maxmatch = nmatch;
3107*63afb9a5SDavid du Colombier 		} else
3108*63afb9a5SDavid du Colombier 			free(q);
3109*63afb9a5SDavid du Colombier 		free(buf);
3110*63afb9a5SDavid du Colombier 	}
3111*63afb9a5SDavid du Colombier 	Bterm(bp);
3112*63afb9a5SDavid du Colombier 	return res;
3113*63afb9a5SDavid du Colombier }
3114*63afb9a5SDavid du Colombier 
3115*63afb9a5SDavid du Colombier void
3116*63afb9a5SDavid du Colombier shutdown(Conn *c)
3117*63afb9a5SDavid du Colombier {
3118*63afb9a5SDavid du Colombier 	Plist *p;
3119*63afb9a5SDavid du Colombier 	SSHChan *sc;
3120*63afb9a5SDavid du Colombier 	int i, ostate;
3121*63afb9a5SDavid du Colombier 
3122*63afb9a5SDavid du Colombier 	sshdebug(c, "shutting down connection %d", c->id);
3123*63afb9a5SDavid du Colombier 	ostate = c->state;
3124*63afb9a5SDavid du Colombier 	if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 &&
3125*63afb9a5SDavid du Colombier 	    c->datafile->ref <= 2 && c->listenfile->ref <= 2 &&
3126*63afb9a5SDavid du Colombier 	    c->localfile->ref <= 2 && c->remotefile->ref <= 2 &&
3127*63afb9a5SDavid du Colombier 	    c->statusfile->ref <= 2)
3128*63afb9a5SDavid du Colombier 		c->state = Closed;
3129*63afb9a5SDavid du Colombier 	else {
3130*63afb9a5SDavid du Colombier 		if (c->state != Closed)
3131*63afb9a5SDavid du Colombier 			c->state = Closing;
3132*63afb9a5SDavid du Colombier 		sshdebug(c, "clone %ld ctl %ld data %ld listen %ld "
3133*63afb9a5SDavid du Colombier 			"local %ld remote %ld status %ld",
3134*63afb9a5SDavid du Colombier 			c->clonefile->ref, c->ctlfile->ref, c->datafile->ref,
3135*63afb9a5SDavid du Colombier 			c->listenfile->ref, c->localfile->ref, c->remotefile->ref,
3136*63afb9a5SDavid du Colombier 			c->statusfile->ref);
3137*63afb9a5SDavid du Colombier 	}
3138*63afb9a5SDavid du Colombier 	if (ostate == Closed || ostate == Closing) {
3139*63afb9a5SDavid du Colombier 		c->state = Closed;
3140*63afb9a5SDavid du Colombier 		return;
3141*63afb9a5SDavid du Colombier 	}
3142*63afb9a5SDavid du Colombier 	if (c->role == Server && c->remote)
3143*63afb9a5SDavid du Colombier 		sshlog(c, "closing connection");
3144*63afb9a5SDavid du Colombier 	hangupconn(c);
3145*63afb9a5SDavid du Colombier 	if (c->dio) {
3146*63afb9a5SDavid du Colombier 		closeioproc(c->dio);
3147*63afb9a5SDavid du Colombier 		c->dio = nil;
3148*63afb9a5SDavid du Colombier 	}
3149*63afb9a5SDavid du Colombier 
3150*63afb9a5SDavid du Colombier 	c->decrypt = -1;
3151*63afb9a5SDavid du Colombier 	c->inmac = -1;
3152*63afb9a5SDavid du Colombier 	c->nchan = 0;
3153*63afb9a5SDavid du Colombier 	free(c->otherid);
3154*63afb9a5SDavid du Colombier 	free(c->s2ccs);
3155*63afb9a5SDavid du Colombier 	c->s2ccs = nil;
3156*63afb9a5SDavid du Colombier 	free(c->c2scs);
3157*63afb9a5SDavid du Colombier 	c->c2scs = nil;
3158*63afb9a5SDavid du Colombier 	free(c->remote);
3159*63afb9a5SDavid du Colombier 	c->remote = nil;
3160*63afb9a5SDavid du Colombier 	if (c->x) {
3161*63afb9a5SDavid du Colombier 		mpfree(c->x);
3162*63afb9a5SDavid du Colombier 		c->x = nil;
3163*63afb9a5SDavid du Colombier 	}
3164*63afb9a5SDavid du Colombier 	if (c->e) {
3165*63afb9a5SDavid du Colombier 		mpfree(c->e);
3166*63afb9a5SDavid du Colombier 		c->e = nil;
3167*63afb9a5SDavid du Colombier 	}
3168*63afb9a5SDavid du Colombier 	free(c->user);
3169*63afb9a5SDavid du Colombier 	c->user = nil;
3170*63afb9a5SDavid du Colombier 	free(c->service);
3171*63afb9a5SDavid du Colombier 	c->service = nil;
3172*63afb9a5SDavid du Colombier 	c->otherid = nil;
3173*63afb9a5SDavid du Colombier 	qlock(&c->l);
3174*63afb9a5SDavid du Colombier 	rwakeupall(&c->r);
3175*63afb9a5SDavid du Colombier 	qunlock(&c->l);
3176*63afb9a5SDavid du Colombier 	for (i = 0; i < MAXCONN; ++i) {
3177*63afb9a5SDavid du Colombier 		sc = c->chans[i];
3178*63afb9a5SDavid du Colombier 		if (sc == nil)
3179*63afb9a5SDavid du Colombier 			continue;
3180*63afb9a5SDavid du Colombier 		free(sc->ann);
3181*63afb9a5SDavid du Colombier 		sc->ann = nil;
3182*63afb9a5SDavid du Colombier 		if (sc->state != Empty && sc->state != Closed) {
3183*63afb9a5SDavid du Colombier 			sc->state = Closed;
3184*63afb9a5SDavid du Colombier 			sc->lreq = nil;
3185*63afb9a5SDavid du Colombier 			while (sc->dataq != nil) {
3186*63afb9a5SDavid du Colombier 				p = sc->dataq;
3187*63afb9a5SDavid du Colombier 				sc->dataq = p->next;
3188*63afb9a5SDavid du Colombier 				free(p->pack);
3189*63afb9a5SDavid du Colombier 				free(p);
3190*63afb9a5SDavid du Colombier 			}
3191*63afb9a5SDavid du Colombier 			while (sc->reqq != nil) {
3192*63afb9a5SDavid du Colombier 				p = sc->reqq;
3193*63afb9a5SDavid du Colombier 				sc->reqq = p->next;
3194*63afb9a5SDavid du Colombier 				free(p->pack);
3195*63afb9a5SDavid du Colombier 				free(p);
3196*63afb9a5SDavid du Colombier 			}
3197*63afb9a5SDavid du Colombier 			qlock(&c->l);
3198*63afb9a5SDavid du Colombier 			rwakeupall(&sc->r);
3199*63afb9a5SDavid du Colombier 			nbsendul(sc->inchan, 1);
3200*63afb9a5SDavid du Colombier 			nbsendul(sc->reqchan, 1);
3201*63afb9a5SDavid du Colombier 			chanclose(sc->inchan);
3202*63afb9a5SDavid du Colombier 			chanclose(sc->reqchan);
3203*63afb9a5SDavid du Colombier 			qunlock(&c->l);
3204*63afb9a5SDavid du Colombier 		}
3205*63afb9a5SDavid du Colombier 	}
3206*63afb9a5SDavid du Colombier 	qlock(&availlck);
3207*63afb9a5SDavid du Colombier 	rwakeup(&availrend);
3208*63afb9a5SDavid du Colombier 	qunlock(&availlck);
3209*63afb9a5SDavid du Colombier 	sshdebug(c, "done processing shutdown of connection %d", c->id);
3210*63afb9a5SDavid du Colombier }
3211