163afb9a5SDavid du Colombier /*
263afb9a5SDavid du Colombier * /net/ssh
363afb9a5SDavid du Colombier */
463afb9a5SDavid du Colombier #include <u.h>
563afb9a5SDavid du Colombier #include <libc.h>
663afb9a5SDavid du Colombier #include <fcall.h>
763afb9a5SDavid du Colombier #include <thread.h>
863afb9a5SDavid du Colombier #include <9p.h>
963afb9a5SDavid du Colombier #include <mp.h>
1063afb9a5SDavid du Colombier #include <auth.h>
1163afb9a5SDavid du Colombier #include <authsrv.h>
1263afb9a5SDavid du Colombier #include <libsec.h>
1363afb9a5SDavid du Colombier #include <ip.h>
1463afb9a5SDavid du Colombier #include "netssh.h"
1563afb9a5SDavid du Colombier
1663afb9a5SDavid du Colombier extern int nokeyverify;
1763afb9a5SDavid du Colombier
1863afb9a5SDavid du Colombier void stclunk(Fid *);
1963afb9a5SDavid du Colombier void stend(Srv *);
2063afb9a5SDavid du Colombier void stflush(Req *);
2163afb9a5SDavid du Colombier void stopen(Req *);
2263afb9a5SDavid du Colombier void stread(Req *);
2363afb9a5SDavid du Colombier void stwrite(Req *);
2463afb9a5SDavid du Colombier
2563afb9a5SDavid du Colombier Srv netsshsrv = {
2663afb9a5SDavid du Colombier .open = stopen,
2763afb9a5SDavid du Colombier .read = stread,
2863afb9a5SDavid du Colombier .write = stwrite,
2963afb9a5SDavid du Colombier .flush = stflush,
3063afb9a5SDavid du Colombier .destroyfid = stclunk,
3163afb9a5SDavid du Colombier .end = stend,
3263afb9a5SDavid du Colombier };
3363afb9a5SDavid du Colombier
3463afb9a5SDavid du Colombier Cipher *cryptos[] = {
3563afb9a5SDavid du Colombier &cipheraes128,
3663afb9a5SDavid du Colombier &cipheraes192,
3763afb9a5SDavid du Colombier &cipheraes256,
3863afb9a5SDavid du Colombier // &cipherblowfish,
3963afb9a5SDavid du Colombier &cipher3des,
4063afb9a5SDavid du Colombier &cipherrc4,
4163afb9a5SDavid du Colombier };
4263afb9a5SDavid du Colombier
4363afb9a5SDavid du Colombier Kex *kexes[] = {
4463afb9a5SDavid du Colombier &dh1sha1,
4563afb9a5SDavid du Colombier &dh14sha1,
4663afb9a5SDavid du Colombier };
4763afb9a5SDavid du Colombier
4863afb9a5SDavid du Colombier PKA *pkas[3];
4963afb9a5SDavid du Colombier
5063afb9a5SDavid du Colombier char *macnames[] = {
5163afb9a5SDavid du Colombier "hmac-sha1",
5263afb9a5SDavid du Colombier };
5363afb9a5SDavid du Colombier
5463afb9a5SDavid du Colombier char *st_names[] = {
5563afb9a5SDavid du Colombier [Empty] "Empty",
5663afb9a5SDavid du Colombier [Allocated] "Allocated",
5763afb9a5SDavid du Colombier [Initting] "Initting",
5863afb9a5SDavid du Colombier [Listening] "Listening",
5963afb9a5SDavid du Colombier [Opening] "Opening",
6063afb9a5SDavid du Colombier [Negotiating] "Negotiating",
6163afb9a5SDavid du Colombier [Authing] "Authing",
6263afb9a5SDavid du Colombier [Established] "Established",
6363afb9a5SDavid du Colombier [Eof] "Eof",
6463afb9a5SDavid du Colombier [Closing] "Closing",
6563afb9a5SDavid du Colombier [Closed] "Closed",
6663afb9a5SDavid du Colombier };
6763afb9a5SDavid du Colombier
6863afb9a5SDavid du Colombier int debug;
6963afb9a5SDavid du Colombier int kflag;
7063afb9a5SDavid du Colombier char *mntpt = "/net";
7163afb9a5SDavid du Colombier char uid[32];
7263afb9a5SDavid du Colombier Conn *connections[MAXCONN];
7363afb9a5SDavid du Colombier File *rootfile, *clonefile, *ctlfile, *keysfile;
7463afb9a5SDavid du Colombier Ioproc *io9p;
7563afb9a5SDavid du Colombier MBox keymbox;
7663afb9a5SDavid du Colombier QLock availlck;
7763afb9a5SDavid du Colombier Rendez availrend;
7863afb9a5SDavid du Colombier
7963afb9a5SDavid du Colombier SSHChan *alloc_chan(Conn *);
8063afb9a5SDavid du Colombier Conn *alloc_conn(void);
8163afb9a5SDavid du Colombier int auth_req(Packet *, Conn *);
8263afb9a5SDavid du Colombier int client_auth(Conn *, Ioproc *);
8363afb9a5SDavid du Colombier int dohandshake(Conn *, char *);
8463afb9a5SDavid du Colombier char *factlookup(int, int, char *[]);
8563afb9a5SDavid du Colombier void filedup(Req *, File *);
8663afb9a5SDavid du Colombier void readdata(void *);
8763afb9a5SDavid du Colombier void reader(void *);
8863afb9a5SDavid du Colombier void readreqrem(void *);
8963afb9a5SDavid du Colombier void send_kexinit(Conn *);
9063afb9a5SDavid du Colombier void server(char *, char *);
9163afb9a5SDavid du Colombier void shutdown(Conn *);
9263afb9a5SDavid du Colombier void stlisconn(void *);
9363afb9a5SDavid du Colombier void stlischan(void *);
9463afb9a5SDavid du Colombier int validatekex(Conn *, Packet *);
9563afb9a5SDavid du Colombier int validatekexc(Packet *);
9663afb9a5SDavid du Colombier int validatekexs(Packet *);
9763afb9a5SDavid du Colombier void writectlproc(void *);
9863afb9a5SDavid du Colombier void writedataproc(void *);
9963afb9a5SDavid du Colombier void writereqremproc(void *);
10063afb9a5SDavid du Colombier
10163afb9a5SDavid du Colombier static int deferredinit(Conn *c);
10263afb9a5SDavid du Colombier
10363afb9a5SDavid du Colombier static void
sshlogint(Conn * c,char * file,char * p)10463afb9a5SDavid du Colombier sshlogint(Conn *c, char *file, char *p)
10563afb9a5SDavid du Colombier {
10663afb9a5SDavid du Colombier char *role, *id;
10763afb9a5SDavid du Colombier
10863afb9a5SDavid du Colombier if (c == nil)
10963afb9a5SDavid du Colombier role = "";
11063afb9a5SDavid du Colombier else if (c->role == Server)
11163afb9a5SDavid du Colombier role = "server ";
11263afb9a5SDavid du Colombier else
11363afb9a5SDavid du Colombier role = "client ";
11463afb9a5SDavid du Colombier if (c == nil)
11563afb9a5SDavid du Colombier id = strdup("");
11663afb9a5SDavid du Colombier else if (c->user || c->remote)
11763afb9a5SDavid du Colombier id = smprint("user %s@%s id %d ", c->user, c->remote, c->id);
11863afb9a5SDavid du Colombier else
11963afb9a5SDavid du Colombier id = smprint("id %d ", c->id);
12063afb9a5SDavid du Colombier
12163afb9a5SDavid du Colombier syslog(0, file, "%s: %s%s%s", argv0, role, id, p);
12263afb9a5SDavid du Colombier free(id);
12363afb9a5SDavid du Colombier }
12463afb9a5SDavid du Colombier
12563afb9a5SDavid du Colombier void
sshlog(Conn * c,char * fmt,...)12663afb9a5SDavid du Colombier sshlog(Conn *c, char *fmt, ...)
12763afb9a5SDavid du Colombier {
12863afb9a5SDavid du Colombier va_list args;
12963afb9a5SDavid du Colombier char *p;
13063afb9a5SDavid du Colombier
13163afb9a5SDavid du Colombier /* do this first in case fmt contains "%r" */
13263afb9a5SDavid du Colombier va_start(args, fmt);
13363afb9a5SDavid du Colombier p = vsmprint(fmt, args);
13463afb9a5SDavid du Colombier va_end(args);
13563afb9a5SDavid du Colombier
13663afb9a5SDavid du Colombier sshlogint(c, "ssh", p);
13763afb9a5SDavid du Colombier sshlogint(c, "sshdebug", p); /* log in both places */
13863afb9a5SDavid du Colombier free(p);
13963afb9a5SDavid du Colombier }
14063afb9a5SDavid du Colombier
14163afb9a5SDavid du Colombier void
sshdebug(Conn * c,char * fmt,...)14263afb9a5SDavid du Colombier sshdebug(Conn *c, char *fmt, ...)
14363afb9a5SDavid du Colombier {
14463afb9a5SDavid du Colombier va_list args;
14563afb9a5SDavid du Colombier char *p;
14663afb9a5SDavid du Colombier
14763afb9a5SDavid du Colombier if (!debug)
14863afb9a5SDavid du Colombier return;
14963afb9a5SDavid du Colombier
15063afb9a5SDavid du Colombier /* do this first in case fmt contains "%r" */
15163afb9a5SDavid du Colombier va_start(args, fmt);
15263afb9a5SDavid du Colombier p = vsmprint(fmt, args);
15363afb9a5SDavid du Colombier va_end(args);
15463afb9a5SDavid du Colombier
15563afb9a5SDavid du Colombier sshlogint(c, "sshdebug", p);
15663afb9a5SDavid du Colombier free(p);
15763afb9a5SDavid du Colombier }
15863afb9a5SDavid du Colombier
15963afb9a5SDavid du Colombier void
usage(void)16063afb9a5SDavid du Colombier usage(void)
16163afb9a5SDavid du Colombier {
16263afb9a5SDavid du Colombier fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0);
16363afb9a5SDavid du Colombier exits("usage");
16463afb9a5SDavid du Colombier }
16563afb9a5SDavid du Colombier
16663afb9a5SDavid du Colombier void
threadmain(int argc,char * argv[])16763afb9a5SDavid du Colombier threadmain(int argc, char *argv[])
16863afb9a5SDavid du Colombier {
16963afb9a5SDavid du Colombier char *p, *srvpt = nil;
17063afb9a5SDavid du Colombier
171fccc105cSDavid du Colombier quotefmtinstall();
17263afb9a5SDavid du Colombier threadsetname("main");
173d7e44d0dSDavid du Colombier nokeyverify = 1; /* temporary until verification is fixed */
17463afb9a5SDavid du Colombier ARGBEGIN {
17563afb9a5SDavid du Colombier case '9':
17663afb9a5SDavid du Colombier chatty9p = 1;
17763afb9a5SDavid du Colombier break;
17863afb9a5SDavid du Colombier case 'd':
17963afb9a5SDavid du Colombier debug++;
18063afb9a5SDavid du Colombier break;
18163afb9a5SDavid du Colombier case 'k':
18263afb9a5SDavid du Colombier kflag = 1;
18363afb9a5SDavid du Colombier break;
18463afb9a5SDavid du Colombier case 'm':
18563afb9a5SDavid du Colombier mntpt = EARGF(usage());
18663afb9a5SDavid du Colombier break;
18763afb9a5SDavid du Colombier case 's':
18863afb9a5SDavid du Colombier srvpt = EARGF(usage());
18963afb9a5SDavid du Colombier break;
19063afb9a5SDavid du Colombier case 'v':
19163afb9a5SDavid du Colombier nokeyverify = 1;
19263afb9a5SDavid du Colombier break;
193d7e44d0dSDavid du Colombier case 'V':
194d7e44d0dSDavid du Colombier nokeyverify = 0;
195d7e44d0dSDavid du Colombier break;
19663afb9a5SDavid du Colombier default:
19763afb9a5SDavid du Colombier usage();
19863afb9a5SDavid du Colombier break;
19963afb9a5SDavid du Colombier } ARGEND;
20063afb9a5SDavid du Colombier
20163afb9a5SDavid du Colombier p = getenv("nosshkeyverify");
202d7e44d0dSDavid du Colombier if (p && p[0] != '\0')
20363afb9a5SDavid du Colombier nokeyverify = 1;
20463afb9a5SDavid du Colombier free(p);
20563afb9a5SDavid du Colombier
20663afb9a5SDavid du Colombier if (readfile("/dev/user", uid, sizeof uid) <= 0)
20763afb9a5SDavid du Colombier strcpy(uid, "none");
20863afb9a5SDavid du Colombier
20963afb9a5SDavid du Colombier keymbox.mchan = chancreate(4, 0);
21063afb9a5SDavid du Colombier availrend.l = &availlck;
21163afb9a5SDavid du Colombier dh_init(pkas);
21263afb9a5SDavid du Colombier
21363afb9a5SDavid du Colombier /* become a daemon */
21463afb9a5SDavid du Colombier if (rfork(RFNOTEG) < 0)
21563afb9a5SDavid du Colombier fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0);
21663afb9a5SDavid du Colombier server(mntpt, srvpt);
217e5a0fbb7SDavid du Colombier threadexits(nil);
21863afb9a5SDavid du Colombier }
21963afb9a5SDavid du Colombier
22063afb9a5SDavid du Colombier int
readio(Ioproc * io,int fd,void * buf,int n)22163afb9a5SDavid du Colombier readio(Ioproc *io, int fd, void *buf, int n)
22263afb9a5SDavid du Colombier {
22363afb9a5SDavid du Colombier if (io)
22463afb9a5SDavid du Colombier return ioread(io, fd, buf, n);
22563afb9a5SDavid du Colombier else
22663afb9a5SDavid du Colombier return read(fd, buf, n);
22763afb9a5SDavid du Colombier }
22863afb9a5SDavid du Colombier
22963afb9a5SDavid du Colombier int
writeio(Ioproc * io,int fd,void * buf,int n)23063afb9a5SDavid du Colombier writeio(Ioproc *io, int fd, void *buf, int n)
23163afb9a5SDavid du Colombier {
23263afb9a5SDavid du Colombier if (io)
23363afb9a5SDavid du Colombier return iowrite(io, fd, buf, n);
23463afb9a5SDavid du Colombier else
23563afb9a5SDavid du Colombier return write(fd, buf, n);
23663afb9a5SDavid du Colombier }
23763afb9a5SDavid du Colombier
23863afb9a5SDavid du Colombier int
read9pmsg(int fd,void * abuf,uint n)23963afb9a5SDavid du Colombier read9pmsg(int fd, void *abuf, uint n)
24063afb9a5SDavid du Colombier {
24163afb9a5SDavid du Colombier int m, len;
24263afb9a5SDavid du Colombier uchar *buf;
24363afb9a5SDavid du Colombier
24463afb9a5SDavid du Colombier if (io9p == nil)
24563afb9a5SDavid du Colombier io9p = ioproc();
24663afb9a5SDavid du Colombier
24763afb9a5SDavid du Colombier buf = abuf;
24863afb9a5SDavid du Colombier
24963afb9a5SDavid du Colombier /* read count */
25063afb9a5SDavid du Colombier m = ioreadn(io9p, fd, buf, BIT32SZ);
25163afb9a5SDavid du Colombier if(m != BIT32SZ){
25263afb9a5SDavid du Colombier if(m < 0)
25363afb9a5SDavid du Colombier return -1;
25463afb9a5SDavid du Colombier return 0;
25563afb9a5SDavid du Colombier }
25663afb9a5SDavid du Colombier
25763afb9a5SDavid du Colombier len = GBIT32(buf);
25863afb9a5SDavid du Colombier if(len <= BIT32SZ || len > n){
25963afb9a5SDavid du Colombier werrstr("bad length in 9P2000 message header");
26063afb9a5SDavid du Colombier return -1;
26163afb9a5SDavid du Colombier }
26263afb9a5SDavid du Colombier len -= BIT32SZ;
26363afb9a5SDavid du Colombier m = ioreadn(io9p, fd, buf+BIT32SZ, len);
26463afb9a5SDavid du Colombier if(m < len)
26563afb9a5SDavid du Colombier return 0;
26663afb9a5SDavid du Colombier return BIT32SZ+m;
26763afb9a5SDavid du Colombier }
26863afb9a5SDavid du Colombier
26963afb9a5SDavid du Colombier void
stend(Srv *)27063afb9a5SDavid du Colombier stend(Srv *)
27163afb9a5SDavid du Colombier {
27263afb9a5SDavid du Colombier closeioproc(io9p);
27363afb9a5SDavid du Colombier threadkillgrp(threadgetgrp());
27463afb9a5SDavid du Colombier }
27563afb9a5SDavid du Colombier
27663afb9a5SDavid du Colombier void
server(char * mntpt,char * srvpt)27763afb9a5SDavid du Colombier server(char *mntpt, char *srvpt)
27863afb9a5SDavid du Colombier {
27963afb9a5SDavid du Colombier Dir d;
28063afb9a5SDavid du Colombier char *p;
28163afb9a5SDavid du Colombier int fd;
28263afb9a5SDavid du Colombier
28363afb9a5SDavid du Colombier netsshsrv.tree = alloctree(uid, uid, 0777, nil);
28463afb9a5SDavid du Colombier rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,
28563afb9a5SDavid du Colombier (void*)Qroot);
28663afb9a5SDavid du Colombier clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);
28763afb9a5SDavid du Colombier ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);
28863afb9a5SDavid du Colombier keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem);
28963afb9a5SDavid du Colombier
290e5a0fbb7SDavid du Colombier /*
291e5a0fbb7SDavid du Colombier * needs to be MBEFORE in case there are previous, now defunct,
292e5a0fbb7SDavid du Colombier * netssh processes mounted in mntpt.
293e5a0fbb7SDavid du Colombier */
294e5a0fbb7SDavid du Colombier threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE);
29563afb9a5SDavid du Colombier
29663afb9a5SDavid du Colombier p = esmprint("%s/cs", mntpt);
29763afb9a5SDavid du Colombier fd = open(p, OWRITE);
29863afb9a5SDavid du Colombier free(p);
29963afb9a5SDavid du Colombier if (fd >= 0) {
30063afb9a5SDavid du Colombier fprint(fd, "add ssh");
30163afb9a5SDavid du Colombier close(fd);
30263afb9a5SDavid du Colombier }
30363afb9a5SDavid du Colombier if (srvpt) {
30463afb9a5SDavid du Colombier nulldir(&d);
30563afb9a5SDavid du Colombier d.mode = 0666;
30663afb9a5SDavid du Colombier p = esmprint("/srv/%s", srvpt);
30763afb9a5SDavid du Colombier dirwstat(p, &d);
30863afb9a5SDavid du Colombier free(p);
30963afb9a5SDavid du Colombier }
31063afb9a5SDavid du Colombier sshdebug(nil, "server started for %s", getuser());
31163afb9a5SDavid du Colombier }
31263afb9a5SDavid du Colombier
31363afb9a5SDavid du Colombier static void
respexit(Conn * c,Req * r,void * freeme,char * msg)31463afb9a5SDavid du Colombier respexit(Conn *c, Req *r, void *freeme, char *msg)
31563afb9a5SDavid du Colombier {
31663afb9a5SDavid du Colombier if (msg)
31763afb9a5SDavid du Colombier sshdebug(c, "%s", msg);
31863afb9a5SDavid du Colombier r->aux = 0;
31963afb9a5SDavid du Colombier respond(r, msg);
32063afb9a5SDavid du Colombier free(freeme);
32163afb9a5SDavid du Colombier threadexits(nil); /* maybe use msg here */
32263afb9a5SDavid du Colombier }
32363afb9a5SDavid du Colombier
32463afb9a5SDavid du Colombier void
stopen(Req * r)32563afb9a5SDavid du Colombier stopen(Req *r)
32663afb9a5SDavid du Colombier {
32763afb9a5SDavid du Colombier int lev, xconn, fd;
32863afb9a5SDavid du Colombier uvlong qidpath;
32963afb9a5SDavid du Colombier char *p;
33063afb9a5SDavid du Colombier char buf[32];
33163afb9a5SDavid du Colombier Conn *c;
33263afb9a5SDavid du Colombier SSHChan *sc;
33363afb9a5SDavid du Colombier
33463afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
33563afb9a5SDavid du Colombier lev = qidpath >> Levshift;
33663afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) {
33763afb9a5SDavid du Colombier default:
33863afb9a5SDavid du Colombier respond(r, nil);
33963afb9a5SDavid du Colombier break;
34063afb9a5SDavid du Colombier case Qlisten:
34163afb9a5SDavid du Colombier r->aux = (void *)threadcreate((lev == Connection?
34263afb9a5SDavid du Colombier stlisconn: stlischan), r, Defstk);
34363afb9a5SDavid du Colombier break;
34463afb9a5SDavid du Colombier case Qclone:
34563afb9a5SDavid du Colombier switch (lev) {
34663afb9a5SDavid du Colombier case Top:
34763afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */
34863afb9a5SDavid du Colombier p = esmprint("%s/tcp/clone", mntpt);
34963afb9a5SDavid du Colombier fd = open(p, ORDWR);
35063afb9a5SDavid du Colombier if (fd < 0) {
35163afb9a5SDavid du Colombier sshdebug(nil, "stopen: open %s failed: %r", p);
35263afb9a5SDavid du Colombier free(p);
35363afb9a5SDavid du Colombier responderror(r);
35463afb9a5SDavid du Colombier return;
35563afb9a5SDavid du Colombier }
35663afb9a5SDavid du Colombier free(p);
35763afb9a5SDavid du Colombier
35863afb9a5SDavid du Colombier c = alloc_conn();
35963afb9a5SDavid du Colombier if (c == nil) {
36063afb9a5SDavid du Colombier close(fd);
36163afb9a5SDavid du Colombier respond(r, "no more connections");
36263afb9a5SDavid du Colombier return;
36363afb9a5SDavid du Colombier }
36463afb9a5SDavid du Colombier c->ctlfd = fd;
36563afb9a5SDavid du Colombier c->poisoned = 0;
36663afb9a5SDavid du Colombier filedup(r, c->ctlfile);
36763afb9a5SDavid du Colombier sshlog(c, "new connection on fd %d", fd);
36863afb9a5SDavid du Colombier break;
36963afb9a5SDavid du Colombier case Connection:
37063afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
37163afb9a5SDavid du Colombier c = connections[xconn];
37263afb9a5SDavid du Colombier if (c == nil) {
37363afb9a5SDavid du Colombier respond(r, "bad connection");
37463afb9a5SDavid du Colombier return;
37563afb9a5SDavid du Colombier }
37663afb9a5SDavid du Colombier sc = alloc_chan(c);
37763afb9a5SDavid du Colombier if (sc == nil) {
37863afb9a5SDavid du Colombier respond(r, "no more channels");
37963afb9a5SDavid du Colombier return;
38063afb9a5SDavid du Colombier }
38163afb9a5SDavid du Colombier filedup(r, sc->ctl);
38263afb9a5SDavid du Colombier break;
38363afb9a5SDavid du Colombier default:
38463afb9a5SDavid du Colombier snprint(buf, sizeof buf, "bad level %d", lev);
38563afb9a5SDavid du Colombier readstr(r, buf);
38663afb9a5SDavid du Colombier break;
38763afb9a5SDavid du Colombier }
38863afb9a5SDavid du Colombier respond(r, nil);
38963afb9a5SDavid du Colombier break;
39063afb9a5SDavid du Colombier }
39163afb9a5SDavid du Colombier }
39263afb9a5SDavid du Colombier
39363afb9a5SDavid du Colombier static void
listerrexit(Req * r,Ioproc * io,Conn * cl)39463afb9a5SDavid du Colombier listerrexit(Req *r, Ioproc *io, Conn *cl)
39563afb9a5SDavid du Colombier {
39663afb9a5SDavid du Colombier r->aux = 0;
39763afb9a5SDavid du Colombier responderror(r);
39863afb9a5SDavid du Colombier closeioproc(io);
39963afb9a5SDavid du Colombier shutdown(cl);
40063afb9a5SDavid du Colombier threadexits(nil);
40163afb9a5SDavid du Colombier }
40263afb9a5SDavid du Colombier
40363afb9a5SDavid du Colombier void
stlisconn(void * a)40463afb9a5SDavid du Colombier stlisconn(void *a)
40563afb9a5SDavid du Colombier {
40663afb9a5SDavid du Colombier int xconn, fd, n;
40763afb9a5SDavid du Colombier uvlong qidpath;
40863afb9a5SDavid du Colombier char *msg;
40963afb9a5SDavid du Colombier char buf[Numbsz], path[NETPATHLEN];
41063afb9a5SDavid du Colombier Conn *c, *cl;
41163afb9a5SDavid du Colombier Ioproc *io;
41263afb9a5SDavid du Colombier Req *r;
41363afb9a5SDavid du Colombier
41463afb9a5SDavid du Colombier threadsetname("stlisconn");
41563afb9a5SDavid du Colombier r = a;
41663afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
41763afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
41863afb9a5SDavid du Colombier
41963afb9a5SDavid du Colombier cl = connections[xconn];
42063afb9a5SDavid du Colombier if (cl == nil) {
42163afb9a5SDavid du Colombier sshlog(cl, "bad connection");
42263afb9a5SDavid du Colombier respond(r, "bad connection");
42363afb9a5SDavid du Colombier threadexits("bad connection");
42463afb9a5SDavid du Colombier }
42563afb9a5SDavid du Colombier if (cl->poisoned) {
42663afb9a5SDavid du Colombier sshdebug(cl, "stlisconn conn %d poisoned", xconn);
42763afb9a5SDavid du Colombier r->aux = 0;
42863afb9a5SDavid du Colombier respond(r, "top level listen conn poisoned");
42963afb9a5SDavid du Colombier threadexits("top level listen conn poisoned");
43063afb9a5SDavid du Colombier }
43163afb9a5SDavid du Colombier if (cl->ctlfd < 0) {
43263afb9a5SDavid du Colombier sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn);
43363afb9a5SDavid du Colombier r->aux = 0;
43463afb9a5SDavid du Colombier respond(r, "top level listen with closed fd");
43563afb9a5SDavid du Colombier shutdown(cl);
43663afb9a5SDavid du Colombier cl->poisoned = 1; /* no more use until ctlfd is set */
43763afb9a5SDavid du Colombier threadexits("top level listen with closed fd");
43863afb9a5SDavid du Colombier }
43963afb9a5SDavid du Colombier
44063afb9a5SDavid du Colombier io = ioproc();
44163afb9a5SDavid du Colombier
44263afb9a5SDavid du Colombier /* read xconn's tcp conn's ctl file */
44363afb9a5SDavid du Colombier seek(cl->ctlfd, 0, 0);
44463afb9a5SDavid du Colombier n = ioread(io, cl->ctlfd, buf, sizeof buf - 1);
44563afb9a5SDavid du Colombier if (n == 0) {
44663afb9a5SDavid du Colombier sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd);
44763afb9a5SDavid du Colombier listerrexit(r, io, cl);
44863afb9a5SDavid du Colombier } else if (n < 0) {
44963afb9a5SDavid du Colombier sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd);
45063afb9a5SDavid du Colombier listerrexit(r, io, cl);
45163afb9a5SDavid du Colombier }
45263afb9a5SDavid du Colombier buf[n] = '\0';
45363afb9a5SDavid du Colombier
45463afb9a5SDavid du Colombier cl->state = Listening;
45563afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */
45663afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf);
45763afb9a5SDavid du Colombier for(;;) {
45863afb9a5SDavid du Colombier fd = ioopen(io, path, ORDWR);
45963afb9a5SDavid du Colombier if (fd < 0)
46063afb9a5SDavid du Colombier listerrexit(r, io, cl);
46163afb9a5SDavid du Colombier c = alloc_conn();
46263afb9a5SDavid du Colombier if (c)
46363afb9a5SDavid du Colombier break;
46463afb9a5SDavid du Colombier n = ioread(io, fd, buf, sizeof buf - 1);
46563afb9a5SDavid du Colombier if (n <= 0)
46663afb9a5SDavid du Colombier listerrexit(r, io, cl);
46763afb9a5SDavid du Colombier buf[n] = '\0';
46863afb9a5SDavid du Colombier msg = smprint("reject %s no available connections", buf);
46963afb9a5SDavid du Colombier iowrite(io, fd, msg, strlen(msg));
47063afb9a5SDavid du Colombier free(msg);
47163afb9a5SDavid du Colombier close(fd); /* surely ioclose? */
47263afb9a5SDavid du Colombier }
47363afb9a5SDavid du Colombier c->ctlfd = fd;
47463afb9a5SDavid du Colombier if (c->ctlfd < 0) {
47563afb9a5SDavid du Colombier sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn);
47663afb9a5SDavid du Colombier threadexitsall("stlisconn c->ctlfd < 0");
47763afb9a5SDavid du Colombier }
47863afb9a5SDavid du Colombier c->poisoned = 0;
47963afb9a5SDavid du Colombier c->stifle = 1; /* defer server; was for coexistence */
48063afb9a5SDavid du Colombier filedup(r, c->ctlfile);
48163afb9a5SDavid du Colombier sshdebug(c, "responding to listen open");
48263afb9a5SDavid du Colombier r->aux = 0;
48363afb9a5SDavid du Colombier respond(r, nil);
48463afb9a5SDavid du Colombier closeioproc(io);
48563afb9a5SDavid du Colombier threadexits(nil);
48663afb9a5SDavid du Colombier }
48763afb9a5SDavid du Colombier
48863afb9a5SDavid du Colombier void
stlischan(void * a)48963afb9a5SDavid du Colombier stlischan(void *a)
49063afb9a5SDavid du Colombier {
49163afb9a5SDavid du Colombier Req *r;
49263afb9a5SDavid du Colombier Packet *p2;
49363afb9a5SDavid du Colombier Ioproc *io;
49463afb9a5SDavid du Colombier Conn *c;
49563afb9a5SDavid du Colombier SSHChan *sc;
49663afb9a5SDavid du Colombier int i, n, xconn;
49763afb9a5SDavid du Colombier uvlong qidpath;
49863afb9a5SDavid du Colombier
49963afb9a5SDavid du Colombier threadsetname("stlischan");
50063afb9a5SDavid du Colombier r = a;
50163afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
50263afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
50363afb9a5SDavid du Colombier c = connections[xconn];
50463afb9a5SDavid du Colombier if (c == nil) {
50563afb9a5SDavid du Colombier respond(r, "bad channel");
50663afb9a5SDavid du Colombier sshlog(c, "bad channel");
50763afb9a5SDavid du Colombier threadexits(nil);
50863afb9a5SDavid du Colombier }
50963afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing)
51063afb9a5SDavid du Colombier respexit(c, r, nil, "channel listen on closed connection");
51163afb9a5SDavid du Colombier sc = c->chans[qidpath & Chanmask];
51263afb9a5SDavid du Colombier
51363afb9a5SDavid du Colombier qlock(&c->l);
51463afb9a5SDavid du Colombier sc->lreq = r;
51563afb9a5SDavid du Colombier for (i = 0; i < c->nchan; ++i)
51663afb9a5SDavid du Colombier if (c->chans[i] && c->chans[i]->state == Opening &&
51763afb9a5SDavid du Colombier c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0)
51863afb9a5SDavid du Colombier break;
51963afb9a5SDavid du Colombier if (i >= c->nchan) {
52063afb9a5SDavid du Colombier sc->state = Listening;
52163afb9a5SDavid du Colombier rsleep(&sc->r);
52263afb9a5SDavid du Colombier i = sc->waker;
52363afb9a5SDavid du Colombier if (i < 0) {
52463afb9a5SDavid du Colombier qunlock(&c->l);
52563afb9a5SDavid du Colombier r->aux = 0;
52663afb9a5SDavid du Colombier responderror(r);
52763afb9a5SDavid du Colombier threadexits(nil);
52863afb9a5SDavid du Colombier }
52963afb9a5SDavid du Colombier } else
53063afb9a5SDavid du Colombier rwakeup(&c->chans[i]->r);
53163afb9a5SDavid du Colombier qunlock(&c->l);
53263afb9a5SDavid du Colombier
53363afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing || c->state == Eof)
53463afb9a5SDavid du Colombier respexit(c, r, nil, "channel listen on closed connection");
53563afb9a5SDavid du Colombier c->chans[i]->state = Established;
53663afb9a5SDavid du Colombier
53763afb9a5SDavid du Colombier p2 = new_packet(c);
53863afb9a5SDavid du Colombier c->chans[i]->rwindow = Maxpayload;
53963afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
54063afb9a5SDavid du Colombier hnputl(p2->payload + 1, c->chans[i]->otherid);
54163afb9a5SDavid du Colombier hnputl(p2->payload + 5, c->chans[i]->id);
54263afb9a5SDavid du Colombier hnputl(p2->payload + 9, Maxpayload);
54363afb9a5SDavid du Colombier hnputl(p2->payload + 13, Maxrpcbuf);
54463afb9a5SDavid du Colombier p2->rlength = 18;
54563afb9a5SDavid du Colombier n = finish_packet(p2);
54663afb9a5SDavid du Colombier filedup(r, c->chans[i]->ctl);
54763afb9a5SDavid du Colombier
54863afb9a5SDavid du Colombier io = ioproc();
54963afb9a5SDavid du Colombier n = iowrite(io, c->datafd, p2->nlength, n);
55063afb9a5SDavid du Colombier closeioproc(io);
55163afb9a5SDavid du Colombier
55263afb9a5SDavid du Colombier free(p2);
55363afb9a5SDavid du Colombier
55463afb9a5SDavid du Colombier sshdebug(c, "responding to chan listen open");
55563afb9a5SDavid du Colombier r->aux = 0;
55663afb9a5SDavid du Colombier if (n < 0)
55763afb9a5SDavid du Colombier responderror(r);
55863afb9a5SDavid du Colombier else
55963afb9a5SDavid du Colombier respond(r, nil);
56063afb9a5SDavid du Colombier threadexits(nil);
56163afb9a5SDavid du Colombier }
56263afb9a5SDavid du Colombier
56363afb9a5SDavid du Colombier void
getdata(Conn * c,SSHChan * sc,Req * r)56463afb9a5SDavid du Colombier getdata(Conn *c, SSHChan *sc, Req *r)
56563afb9a5SDavid du Colombier {
56663afb9a5SDavid du Colombier Packet *p;
56763afb9a5SDavid du Colombier Plist *d;
56863afb9a5SDavid du Colombier int n;
56963afb9a5SDavid du Colombier
57063afb9a5SDavid du Colombier n = r->ifcall.count;
57163afb9a5SDavid du Colombier if (sc->dataq->rem < n)
57263afb9a5SDavid du Colombier n = sc->dataq->rem;
57363afb9a5SDavid du Colombier if (n > Maxrpcbuf)
57463afb9a5SDavid du Colombier n = Maxrpcbuf;
57563afb9a5SDavid du Colombier r->ifcall.offset = 0;
57663afb9a5SDavid du Colombier
57763afb9a5SDavid du Colombier readbuf(r, sc->dataq->st, n);
57863afb9a5SDavid du Colombier sc->dataq->st += n;
57963afb9a5SDavid du Colombier sc->dataq->rem -= n;
58063afb9a5SDavid du Colombier sc->inrqueue -= n;
58163afb9a5SDavid du Colombier if (sc->dataq->rem <= 0) {
58263afb9a5SDavid du Colombier d = sc->dataq;
58363afb9a5SDavid du Colombier sc->dataq = sc->dataq->next;
58463afb9a5SDavid du Colombier if (d->pack->tlength > sc->rwindow)
58563afb9a5SDavid du Colombier sc->rwindow = 0;
58663afb9a5SDavid du Colombier else
58763afb9a5SDavid du Colombier sc->rwindow -= d->pack->tlength;
58863afb9a5SDavid du Colombier free(d->pack);
58963afb9a5SDavid du Colombier free(d);
59063afb9a5SDavid du Colombier }
59163afb9a5SDavid du Colombier if (sc->rwindow < 16*1024) { /* magic. half-way, maybe? */
59263afb9a5SDavid du Colombier sc->rwindow += Maxpayload;
59363afb9a5SDavid du Colombier sshdebug(c, "increasing receive window to %lud, inq %lud\n",
59463afb9a5SDavid du Colombier argv0, sc->rwindow, sc->inrqueue);
59563afb9a5SDavid du Colombier p = new_packet(c);
59663afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST);
59763afb9a5SDavid du Colombier hnputl(p->payload+1, sc->otherid);
59863afb9a5SDavid du Colombier hnputl(p->payload+5, Maxpayload);
59963afb9a5SDavid du Colombier p->rlength += 8;
60063afb9a5SDavid du Colombier n = finish_packet(p);
60163afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
60263afb9a5SDavid du Colombier free(p);
60363afb9a5SDavid du Colombier }
60463afb9a5SDavid du Colombier r->aux = 0;
60563afb9a5SDavid du Colombier respond(r, nil);
60663afb9a5SDavid du Colombier }
60763afb9a5SDavid du Colombier
60863afb9a5SDavid du Colombier void
stread(Req * r)60963afb9a5SDavid du Colombier stread(Req *r)
61063afb9a5SDavid du Colombier {
61163afb9a5SDavid du Colombier Conn *c;
61263afb9a5SDavid du Colombier SSHChan *sc;
61363afb9a5SDavid du Colombier int n, lev, cnum, xconn;
61463afb9a5SDavid du Colombier uvlong qidpath;
61563afb9a5SDavid du Colombier char buf[Arbbufsz], path[NETPATHLEN];
61663afb9a5SDavid du Colombier
61763afb9a5SDavid du Colombier threadsetname("stread");
61863afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
61963afb9a5SDavid du Colombier lev = qidpath >> Levshift;
62063afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
62163afb9a5SDavid du Colombier c = connections[xconn];
62263afb9a5SDavid du Colombier if (c == nil) {
62363afb9a5SDavid du Colombier if (lev != Top || (qidpath & Qtypemask) != Qreqrem) {
62463afb9a5SDavid du Colombier respond(r, "Invalid connection");
62563afb9a5SDavid du Colombier return;
62663afb9a5SDavid du Colombier }
62763afb9a5SDavid du Colombier cnum = 0;
62863afb9a5SDavid du Colombier sc = nil;
62963afb9a5SDavid du Colombier } else {
63063afb9a5SDavid du Colombier cnum = qidpath & Chanmask;
63163afb9a5SDavid du Colombier sc = c->chans[cnum];
63263afb9a5SDavid du Colombier }
63363afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) {
63463afb9a5SDavid du Colombier case Qctl:
63563afb9a5SDavid du Colombier case Qlisten:
63663afb9a5SDavid du Colombier if (r->ifcall.offset != 0) {
63763afb9a5SDavid du Colombier respond(r, nil);
63863afb9a5SDavid du Colombier break;
63963afb9a5SDavid du Colombier }
64063afb9a5SDavid du Colombier switch (lev) {
64163afb9a5SDavid du Colombier case Top:
64263afb9a5SDavid du Colombier readstr(r, st_names[c->state]);
64363afb9a5SDavid du Colombier break;
64463afb9a5SDavid du Colombier case Connection:
64563afb9a5SDavid du Colombier case Subchannel:
64663afb9a5SDavid du Colombier snprint(buf, sizeof buf, "%d", lev == Connection?
64763afb9a5SDavid du Colombier xconn: cnum);
64863afb9a5SDavid du Colombier readstr(r, buf);
64963afb9a5SDavid du Colombier break;
65063afb9a5SDavid du Colombier default:
65163afb9a5SDavid du Colombier snprint(buf, sizeof buf, "stread error, level %d", lev);
65263afb9a5SDavid du Colombier respond(r, buf);
65363afb9a5SDavid du Colombier return;
65463afb9a5SDavid du Colombier }
65563afb9a5SDavid du Colombier respond(r, nil);
65663afb9a5SDavid du Colombier break;
65763afb9a5SDavid du Colombier case Qclone:
65863afb9a5SDavid du Colombier if (r->ifcall.offset != 0) {
65963afb9a5SDavid du Colombier respond(r, nil);
66063afb9a5SDavid du Colombier break;
66163afb9a5SDavid du Colombier }
66263afb9a5SDavid du Colombier readstr(r, "Congratulations, you've achieved the impossible\n");
66363afb9a5SDavid du Colombier respond(r, nil);
66463afb9a5SDavid du Colombier break;
66563afb9a5SDavid du Colombier case Qdata:
66663afb9a5SDavid du Colombier if (lev == Top) {
66763afb9a5SDavid du Colombier respond(r, nil);
66863afb9a5SDavid du Colombier break;
66963afb9a5SDavid du Colombier }
67063afb9a5SDavid du Colombier if (lev == Connection) {
67163afb9a5SDavid du Colombier if (0 && c->stifle) { /* was for coexistence */
67263afb9a5SDavid du Colombier c->stifle = 0;
67363afb9a5SDavid du Colombier if (deferredinit(c) < 0) {
67463afb9a5SDavid du Colombier respond(r, "deferredinit failed");
67563afb9a5SDavid du Colombier break;
67663afb9a5SDavid du Colombier }
67763afb9a5SDavid du Colombier }
67863afb9a5SDavid du Colombier if (c->cap) /* auth capability? */
67963afb9a5SDavid du Colombier readstr(r, c->cap);
68063afb9a5SDavid du Colombier respond(r, nil);
68163afb9a5SDavid du Colombier break;
68263afb9a5SDavid du Colombier }
68363afb9a5SDavid du Colombier
68463afb9a5SDavid du Colombier r->aux = (void *)threadcreate(readdata, r, Defstk);
68563afb9a5SDavid du Colombier break;
68663afb9a5SDavid du Colombier case Qlocal:
68763afb9a5SDavid du Colombier if (lev == Connection)
68863afb9a5SDavid du Colombier if (c->ctlfd < 0)
68963afb9a5SDavid du Colombier readstr(r, "::!0\n");
69063afb9a5SDavid du Colombier else {
69163afb9a5SDavid du Colombier n = pread(c->ctlfd, buf, 10, 0); // magic 10
69263afb9a5SDavid du Colombier buf[n >= 0? n: 0] = '\0';
69363afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/local",
69463afb9a5SDavid du Colombier mntpt, buf);
69563afb9a5SDavid du Colombier readfile(path, buf, sizeof buf);
69663afb9a5SDavid du Colombier readstr(r, buf);
69763afb9a5SDavid du Colombier }
69863afb9a5SDavid du Colombier respond(r, nil);
69963afb9a5SDavid du Colombier break;
70063afb9a5SDavid du Colombier case Qreqrem:
70163afb9a5SDavid du Colombier r->aux = (void *)threadcreate(readreqrem, r, Defstk);
70263afb9a5SDavid du Colombier break;
70363afb9a5SDavid du Colombier case Qstatus:
70463afb9a5SDavid du Colombier switch (lev) {
70563afb9a5SDavid du Colombier case Top:
70663afb9a5SDavid du Colombier readstr(r, "Impossible");
70763afb9a5SDavid du Colombier break;
70863afb9a5SDavid du Colombier case Connection:
70963afb9a5SDavid du Colombier readstr(r, (uint)c->state > Closed?
71063afb9a5SDavid du Colombier "Unknown": st_names[c->state]);
71163afb9a5SDavid du Colombier break;
71263afb9a5SDavid du Colombier case Subchannel:
71363afb9a5SDavid du Colombier readstr(r, (uint)sc->state > Closed?
71463afb9a5SDavid du Colombier "Unknown": st_names[sc->state]);
71563afb9a5SDavid du Colombier break;
71663afb9a5SDavid du Colombier }
71763afb9a5SDavid du Colombier respond(r, nil);
71863afb9a5SDavid du Colombier break;
71963afb9a5SDavid du Colombier case Qtcp:
72063afb9a5SDavid du Colombier /* connection number of underlying tcp connection */
72163afb9a5SDavid du Colombier if (lev == Connection)
72263afb9a5SDavid du Colombier if (c->ctlfd < 0)
72363afb9a5SDavid du Colombier readstr(r, "-1\n");
72463afb9a5SDavid du Colombier else {
72563afb9a5SDavid du Colombier n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */
72663afb9a5SDavid du Colombier buf[n >= 0? n: 0] = '\0';
72763afb9a5SDavid du Colombier readstr(r, buf);
72863afb9a5SDavid du Colombier }
72963afb9a5SDavid du Colombier respond(r, nil);
73063afb9a5SDavid du Colombier break;
73163afb9a5SDavid du Colombier default:
73263afb9a5SDavid du Colombier respond(r, nil);
73363afb9a5SDavid du Colombier break;
73463afb9a5SDavid du Colombier }
73563afb9a5SDavid du Colombier }
73663afb9a5SDavid du Colombier
73763afb9a5SDavid du Colombier void
readreqrem(void * a)73863afb9a5SDavid du Colombier readreqrem(void *a)
73963afb9a5SDavid du Colombier {
74063afb9a5SDavid du Colombier Ioproc *io;
74163afb9a5SDavid du Colombier Req *r;
74263afb9a5SDavid du Colombier Conn *c;
74363afb9a5SDavid du Colombier SSHChan *sc;
74463afb9a5SDavid du Colombier int fd, n, lev, cnum, xconn;
74563afb9a5SDavid du Colombier uvlong qidpath;
74663afb9a5SDavid du Colombier char buf[Arbbufsz], path[NETPATHLEN];
74763afb9a5SDavid du Colombier
74863afb9a5SDavid du Colombier threadsetname("readreqrem");
74963afb9a5SDavid du Colombier r = a;
75063afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
75163afb9a5SDavid du Colombier lev = qidpath >> Levshift;
75263afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
75363afb9a5SDavid du Colombier c = connections[xconn];
75463afb9a5SDavid du Colombier if (c == nil) {
75563afb9a5SDavid du Colombier if (lev != Top) {
75663afb9a5SDavid du Colombier respond(r, "Invalid connection");
75763afb9a5SDavid du Colombier return;
75863afb9a5SDavid du Colombier }
75963afb9a5SDavid du Colombier sc = nil;
76063afb9a5SDavid du Colombier } else {
76163afb9a5SDavid du Colombier cnum = qidpath & Chanmask;
76263afb9a5SDavid du Colombier sc = c->chans[cnum];
76363afb9a5SDavid du Colombier }
76463afb9a5SDavid du Colombier switch (lev) {
76563afb9a5SDavid du Colombier case Top:
76663afb9a5SDavid du Colombier if (r->ifcall.offset == 0 && keymbox.state != Empty) {
76763afb9a5SDavid du Colombier r->aux = 0;
76863afb9a5SDavid du Colombier respond(r, "Key file collision"); /* WTF? */
76963afb9a5SDavid du Colombier break;
77063afb9a5SDavid du Colombier }
77163afb9a5SDavid du Colombier if (r->ifcall.offset != 0) {
77263afb9a5SDavid du Colombier readstr(r, keymbox.msg);
77363afb9a5SDavid du Colombier r->aux = 0;
77463afb9a5SDavid du Colombier respond(r, nil);
77563afb9a5SDavid du Colombier if (r->ifcall.offset + r->ifcall.count >=
77663afb9a5SDavid du Colombier strlen(keymbox.msg))
77763afb9a5SDavid du Colombier keymbox.state = Empty;
77863afb9a5SDavid du Colombier else
77963afb9a5SDavid du Colombier keymbox.state = Allocated;
78063afb9a5SDavid du Colombier break;
78163afb9a5SDavid du Colombier }
78263afb9a5SDavid du Colombier keymbox.state = Allocated;
78363afb9a5SDavid du Colombier for(;;) {
78463afb9a5SDavid du Colombier if (keymbox.msg == nil)
78563afb9a5SDavid du Colombier if (recv(keymbox.mchan, nil) < 0) {
78663afb9a5SDavid du Colombier r->aux = 0;
78763afb9a5SDavid du Colombier responderror(r);
78863afb9a5SDavid du Colombier keymbox.state = Empty;
78963afb9a5SDavid du Colombier threadexits(nil);
79063afb9a5SDavid du Colombier }
79163afb9a5SDavid du Colombier if (keymbox.state == Empty)
79263afb9a5SDavid du Colombier break;
79363afb9a5SDavid du Colombier else if (keymbox.state == Allocated) {
79463afb9a5SDavid du Colombier if (keymbox.msg) {
79563afb9a5SDavid du Colombier readstr(r, keymbox.msg);
79663afb9a5SDavid du Colombier if (r->ifcall.offset + r->ifcall.count
79763afb9a5SDavid du Colombier >= strlen(keymbox.msg)) {
79863afb9a5SDavid du Colombier free(keymbox.msg);
79963afb9a5SDavid du Colombier keymbox.msg = nil;
80063afb9a5SDavid du Colombier keymbox.state = Empty;
80163afb9a5SDavid du Colombier }
80263afb9a5SDavid du Colombier }
80363afb9a5SDavid du Colombier break;
80463afb9a5SDavid du Colombier }
80563afb9a5SDavid du Colombier }
80663afb9a5SDavid du Colombier r->aux = 0;
80763afb9a5SDavid du Colombier respond(r, nil);
80863afb9a5SDavid du Colombier break;
80963afb9a5SDavid du Colombier case Connection:
81063afb9a5SDavid du Colombier if (c->ctlfd >= 0) {
81163afb9a5SDavid du Colombier io = ioproc();
81263afb9a5SDavid du Colombier seek(c->ctlfd, 0, 0);
81363afb9a5SDavid du Colombier n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */
81463afb9a5SDavid du Colombier if (n < 0) {
81563afb9a5SDavid du Colombier r->aux = 0;
81663afb9a5SDavid du Colombier responderror(r);
81763afb9a5SDavid du Colombier closeioproc(io);
81863afb9a5SDavid du Colombier break;
81963afb9a5SDavid du Colombier }
82063afb9a5SDavid du Colombier buf[n] = '\0';
82163afb9a5SDavid du Colombier snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf);
82263afb9a5SDavid du Colombier if ((fd = ioopen(io, path, OREAD)) < 0 ||
82363afb9a5SDavid du Colombier (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) {
82463afb9a5SDavid du Colombier r->aux = 0;
82563afb9a5SDavid du Colombier responderror(r);
82663afb9a5SDavid du Colombier if (fd >= 0)
82763afb9a5SDavid du Colombier ioclose(io, fd);
82863afb9a5SDavid du Colombier closeioproc(io);
82963afb9a5SDavid du Colombier break;
83063afb9a5SDavid du Colombier }
83163afb9a5SDavid du Colombier ioclose(io, fd);
83263afb9a5SDavid du Colombier closeioproc(io);
83363afb9a5SDavid du Colombier buf[n] = '\0';
83463afb9a5SDavid du Colombier readstr(r, buf);
83563afb9a5SDavid du Colombier } else
83663afb9a5SDavid du Colombier readstr(r, "::!0\n");
83763afb9a5SDavid du Colombier r->aux = 0;
83863afb9a5SDavid du Colombier respond(r, nil);
83963afb9a5SDavid du Colombier break;
84063afb9a5SDavid du Colombier case Subchannel:
84163afb9a5SDavid du Colombier if ((sc->state == Closed || sc->state == Closing ||
84263afb9a5SDavid du Colombier sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) {
84363afb9a5SDavid du Colombier sshdebug(c, "sending EOF1 to channel request listener");
84463afb9a5SDavid du Colombier r->aux = 0;
84563afb9a5SDavid du Colombier respond(r, nil);
84663afb9a5SDavid du Colombier break;
84763afb9a5SDavid du Colombier }
84863afb9a5SDavid du Colombier while (sc->reqq == nil) {
84963afb9a5SDavid du Colombier if (recv(sc->reqchan, nil) < 0) {
85063afb9a5SDavid du Colombier r->aux = 0;
85163afb9a5SDavid du Colombier responderror(r);
85263afb9a5SDavid du Colombier threadexits(nil);
85363afb9a5SDavid du Colombier }
85463afb9a5SDavid du Colombier if ((sc->state == Closed || sc->state == Closing ||
85563afb9a5SDavid du Colombier sc->state == Eof) && sc->reqq == nil &&
85663afb9a5SDavid du Colombier sc->dataq == nil) {
85763afb9a5SDavid du Colombier sshdebug(c, "sending EOF2 to channel request "
85863afb9a5SDavid du Colombier "listener");
85963afb9a5SDavid du Colombier respexit(c, r, nil, nil);
86063afb9a5SDavid du Colombier }
86163afb9a5SDavid du Colombier }
86263afb9a5SDavid du Colombier n = r->ifcall.count;
86363afb9a5SDavid du Colombier if (sc->reqq->rem < n)
86463afb9a5SDavid du Colombier n = sc->reqq->rem;
86563afb9a5SDavid du Colombier if (n > Maxrpcbuf)
86663afb9a5SDavid du Colombier n = Maxrpcbuf;
86763afb9a5SDavid du Colombier r->ifcall.offset = 0;
86863afb9a5SDavid du Colombier readbuf(r, sc->reqq->st, n);
86963afb9a5SDavid du Colombier sc->reqq->st += n;
87063afb9a5SDavid du Colombier sc->reqq->rem -= n;
87163afb9a5SDavid du Colombier if (sc->reqq->rem <= 0) {
87263afb9a5SDavid du Colombier Plist *d = sc->reqq;
87363afb9a5SDavid du Colombier sc->reqq = sc->reqq->next;
87463afb9a5SDavid du Colombier free(d->pack);
87563afb9a5SDavid du Colombier free(d);
87663afb9a5SDavid du Colombier }
87763afb9a5SDavid du Colombier r->aux = 0;
87863afb9a5SDavid du Colombier respond(r, nil);
87963afb9a5SDavid du Colombier break;
88063afb9a5SDavid du Colombier }
88163afb9a5SDavid du Colombier threadexits(nil);
88263afb9a5SDavid du Colombier }
88363afb9a5SDavid du Colombier
88463afb9a5SDavid du Colombier void
readdata(void * a)88563afb9a5SDavid du Colombier readdata(void *a)
88663afb9a5SDavid du Colombier {
88763afb9a5SDavid du Colombier Req *r;
88863afb9a5SDavid du Colombier Conn *c;
88963afb9a5SDavid du Colombier SSHChan *sc;
89063afb9a5SDavid du Colombier int cnum, xconn;
89163afb9a5SDavid du Colombier uvlong qidpath;
89263afb9a5SDavid du Colombier
89363afb9a5SDavid du Colombier threadsetname("readdata");
89463afb9a5SDavid du Colombier r = a;
89563afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
89663afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
89763afb9a5SDavid du Colombier c = connections[xconn];
89863afb9a5SDavid du Colombier if (c == nil) {
89963afb9a5SDavid du Colombier respond(r, "bad connection");
90063afb9a5SDavid du Colombier sshlog(c, "bad connection");
90163afb9a5SDavid du Colombier threadexits(nil);
90263afb9a5SDavid du Colombier }
90363afb9a5SDavid du Colombier cnum = qidpath & Chanmask;
90463afb9a5SDavid du Colombier sc = c->chans[cnum];
90563afb9a5SDavid du Colombier if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing ||
90663afb9a5SDavid du Colombier sc->state == Eof)) {
90763afb9a5SDavid du Colombier sshdebug(c, "sending EOF1 to channel listener");
90863afb9a5SDavid du Colombier r->aux = 0;
90963afb9a5SDavid du Colombier respond(r, nil);
91063afb9a5SDavid du Colombier threadexits(nil);
91163afb9a5SDavid du Colombier }
91263afb9a5SDavid du Colombier if (sc->dataq != nil) {
91363afb9a5SDavid du Colombier getdata(c, sc, r);
91463afb9a5SDavid du Colombier threadexits(nil);
91563afb9a5SDavid du Colombier }
91663afb9a5SDavid du Colombier while (sc->dataq == nil) {
91763afb9a5SDavid du Colombier if (recv(sc->inchan, nil) < 0) {
91863afb9a5SDavid du Colombier sshdebug(c, "got interrupt/error in readdata %r");
91963afb9a5SDavid du Colombier r->aux = 0;
92063afb9a5SDavid du Colombier responderror(r);
92163afb9a5SDavid du Colombier threadexits(nil);
92263afb9a5SDavid du Colombier }
92363afb9a5SDavid du Colombier if (sc->dataq == nil && (sc->state == Closed ||
92463afb9a5SDavid du Colombier sc->state == Closing || sc->state == Eof)) {
92563afb9a5SDavid du Colombier sshdebug(c, "sending EOF2 to channel listener");
92663afb9a5SDavid du Colombier r->aux = 0;
92763afb9a5SDavid du Colombier respond(r, nil);
92863afb9a5SDavid du Colombier threadexits(nil);
92963afb9a5SDavid du Colombier }
93063afb9a5SDavid du Colombier }
93163afb9a5SDavid du Colombier getdata(c, sc, r);
93263afb9a5SDavid du Colombier threadexits(nil);
93363afb9a5SDavid du Colombier }
93463afb9a5SDavid du Colombier
93563afb9a5SDavid du Colombier void
stwrite(Req * r)93663afb9a5SDavid du Colombier stwrite(Req *r)
93763afb9a5SDavid du Colombier {
93863afb9a5SDavid du Colombier Conn *c;
93963afb9a5SDavid du Colombier SSHChan *ch;
94063afb9a5SDavid du Colombier int lev, xconn;
94163afb9a5SDavid du Colombier uvlong qidpath;
94263afb9a5SDavid du Colombier
94363afb9a5SDavid du Colombier threadsetname("stwrite");
94463afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
94563afb9a5SDavid du Colombier lev = qidpath >> Levshift;
94663afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
94763afb9a5SDavid du Colombier c = connections[xconn];
94863afb9a5SDavid du Colombier if (c == nil) {
94963afb9a5SDavid du Colombier respond(r, "invalid connection");
95063afb9a5SDavid du Colombier return;
95163afb9a5SDavid du Colombier }
95263afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask];
95363afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) {
95463afb9a5SDavid du Colombier case Qclone:
95563afb9a5SDavid du Colombier case Qctl:
95663afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writectlproc, r, Defstk);
95763afb9a5SDavid du Colombier break;
95863afb9a5SDavid du Colombier case Qdata:
95963afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count;
96063afb9a5SDavid du Colombier if (lev == Top || lev == Connection ||
96163afb9a5SDavid du Colombier c->state == Closed || c->state == Closing ||
96263afb9a5SDavid du Colombier ch->state == Closed || ch->state == Closing) {
96363afb9a5SDavid du Colombier respond(r, nil);
96463afb9a5SDavid du Colombier break;
96563afb9a5SDavid du Colombier }
96663afb9a5SDavid du Colombier if (0 && c->stifle) { /* was for coexistence */
96763afb9a5SDavid du Colombier c->stifle = 0;
96863afb9a5SDavid du Colombier if (deferredinit(c) < 0) {
96963afb9a5SDavid du Colombier respond(r, "deferredinit failed");
97063afb9a5SDavid du Colombier break;
97163afb9a5SDavid du Colombier }
97263afb9a5SDavid du Colombier }
97363afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writedataproc, r, Defstk);
97463afb9a5SDavid du Colombier break;
97563afb9a5SDavid du Colombier case Qreqrem:
97663afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writereqremproc, r, Defstk);
97763afb9a5SDavid du Colombier break;
97863afb9a5SDavid du Colombier default:
97963afb9a5SDavid du Colombier respond(r, nil);
98063afb9a5SDavid du Colombier break;
98163afb9a5SDavid du Colombier }
98263afb9a5SDavid du Colombier }
98363afb9a5SDavid du Colombier
98463afb9a5SDavid du Colombier static int
dialbyhand(Conn * c,int ntok,char * toks[])98563afb9a5SDavid du Colombier dialbyhand(Conn *c, int ntok, char *toks[])
98663afb9a5SDavid du Colombier {
98763afb9a5SDavid du Colombier /*
98863afb9a5SDavid du Colombier * this uses /net/tcp to connect directly.
98963afb9a5SDavid du Colombier * should use dial(2) instead of doing it by hand.
99063afb9a5SDavid du Colombier */
99163afb9a5SDavid du Colombier sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: "");
99263afb9a5SDavid du Colombier return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: "");
99363afb9a5SDavid du Colombier }
99463afb9a5SDavid du Colombier
99563afb9a5SDavid du Colombier static void
userauth(Conn * c,Req * r,char * buf,int ntok,char * toks[])99663afb9a5SDavid du Colombier userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[])
99763afb9a5SDavid du Colombier {
99863afb9a5SDavid du Colombier int n;
99963afb9a5SDavid du Colombier char *attrs[5];
100063afb9a5SDavid du Colombier Packet *p;
100163afb9a5SDavid du Colombier
100263afb9a5SDavid du Colombier if (ntok < 3 || ntok > 4)
100363afb9a5SDavid du Colombier respexit(c, r, buf, "bad connect command");
100463afb9a5SDavid du Colombier if (!c->service)
100563afb9a5SDavid du Colombier c->service = estrdup9p(toks[0]);
100663afb9a5SDavid du Colombier if (c->user)
100763afb9a5SDavid du Colombier free(c->user);
100863afb9a5SDavid du Colombier c->user = estrdup9p(toks[2]);
100963afb9a5SDavid du Colombier sshdebug(c, "userauth for user %s", c->user);
101063afb9a5SDavid du Colombier
101163afb9a5SDavid du Colombier if (ntok == 4 && strcmp(toks[1], "k") == 0) {
101263afb9a5SDavid du Colombier if (c->authkey) {
101363afb9a5SDavid du Colombier free(c->authkey);
101463afb9a5SDavid du Colombier c->authkey = nil;
101563afb9a5SDavid du Colombier }
101663afb9a5SDavid du Colombier if (c->password)
101763afb9a5SDavid du Colombier free(c->password);
101863afb9a5SDavid du Colombier c->password = estrdup9p(toks[3]);
101963afb9a5SDavid du Colombier sshdebug(c, "userauth got password");
102063afb9a5SDavid du Colombier } else {
102163afb9a5SDavid du Colombier if (c->password) {
102263afb9a5SDavid du Colombier free(c->password);
102363afb9a5SDavid du Colombier c->password = nil;
102463afb9a5SDavid du Colombier }
102563afb9a5SDavid du Colombier memset(attrs, 0, sizeof attrs);
102663afb9a5SDavid du Colombier attrs[0] = "proto=rsa";
102763afb9a5SDavid du Colombier attrs[1] = "!dk?";
102863afb9a5SDavid du Colombier attrs[2] = smprint("user=%s", c->user);
102963afb9a5SDavid du Colombier attrs[3] = smprint("sys=%s", c->remote);
103063afb9a5SDavid du Colombier if (c->authkey)
103163afb9a5SDavid du Colombier free(c->authkey);
103263afb9a5SDavid du Colombier sshdebug(c, "userauth trying rsa");
103363afb9a5SDavid du Colombier if (ntok == 3)
103463afb9a5SDavid du Colombier c->authkey = factlookup(4, 2, attrs);
103563afb9a5SDavid du Colombier else {
103663afb9a5SDavid du Colombier attrs[4] = toks[3];
103763afb9a5SDavid du Colombier c->authkey = factlookup(5, 2, attrs);
103863afb9a5SDavid du Colombier }
103963afb9a5SDavid du Colombier free(attrs[2]);
104063afb9a5SDavid du Colombier free(attrs[3]);
104163afb9a5SDavid du Colombier }
104263afb9a5SDavid du Colombier
104363afb9a5SDavid du Colombier if (!c->password && !c->authkey)
104463afb9a5SDavid du Colombier respexit(c, r, buf, "no auth info");
104563afb9a5SDavid du Colombier else if (c->state != Authing) {
104663afb9a5SDavid du Colombier p = new_packet(c);
104763afb9a5SDavid du Colombier add_byte(p, SSH_MSG_SERVICE_REQUEST);
104863afb9a5SDavid du Colombier add_string(p, c->service);
104963afb9a5SDavid du Colombier n = finish_packet(p);
105063afb9a5SDavid du Colombier sshdebug(c, "sending msg svc req for %s", c->service);
105163afb9a5SDavid du Colombier if (writeio(c->dio, c->datafd, p->nlength, n) != n) {
105263afb9a5SDavid du Colombier sshdebug(c, "authing write failed: %r");
105363afb9a5SDavid du Colombier free(p);
105463afb9a5SDavid du Colombier r->aux = 0;
105563afb9a5SDavid du Colombier responderror(r);
105663afb9a5SDavid du Colombier free(buf);
105763afb9a5SDavid du Colombier threadexits(nil);
105863afb9a5SDavid du Colombier }
105963afb9a5SDavid du Colombier free(p);
106063afb9a5SDavid du Colombier } else
106163afb9a5SDavid du Colombier if (client_auth(c, c->dio) < 0)
106263afb9a5SDavid du Colombier respexit(c, r, buf, "ssh-userauth client auth failed");
106363afb9a5SDavid du Colombier qlock(&c->l);
106463afb9a5SDavid du Colombier if (c->state != Established) {
106563afb9a5SDavid du Colombier sshdebug(c, "sleeping for auth");
106663afb9a5SDavid du Colombier rsleep(&c->r);
106763afb9a5SDavid du Colombier }
106863afb9a5SDavid du Colombier qunlock(&c->l);
106963afb9a5SDavid du Colombier if (c->state != Established)
107063afb9a5SDavid du Colombier respexit(c, r, buf, "ssh-userath auth failed (not Established)");
107163afb9a5SDavid du Colombier }
107263afb9a5SDavid du Colombier
107363afb9a5SDavid du Colombier void
writectlproc(void * a)107463afb9a5SDavid du Colombier writectlproc(void *a)
107563afb9a5SDavid du Colombier {
107663afb9a5SDavid du Colombier Req *r;
107763afb9a5SDavid du Colombier Packet *p;
107863afb9a5SDavid du Colombier Conn *c;
107963afb9a5SDavid du Colombier SSHChan *ch;
108063afb9a5SDavid du Colombier char *tcpconn2, *buf, *toks[4];
108163afb9a5SDavid du Colombier int n, ntok, lev, xconn;
108263afb9a5SDavid du Colombier uvlong qidpath;
108363afb9a5SDavid du Colombier char path[NETPATHLEN], tcpconn[Numbsz];
108463afb9a5SDavid du Colombier
108563afb9a5SDavid du Colombier threadsetname("writectlproc");
108663afb9a5SDavid du Colombier r = a;
108763afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
108863afb9a5SDavid du Colombier lev = qidpath >> Levshift;
108963afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
109063afb9a5SDavid du Colombier
109163afb9a5SDavid du Colombier c = connections[xconn];
109263afb9a5SDavid du Colombier if (c == nil) {
109363afb9a5SDavid du Colombier respond(r, "bad connection");
109463afb9a5SDavid du Colombier sshlog(c, "bad connection");
109563afb9a5SDavid du Colombier threadexits(nil);
109663afb9a5SDavid du Colombier }
109763afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask];
109863afb9a5SDavid du Colombier
109963afb9a5SDavid du Colombier if (r->ifcall.count <= Numbsz)
110063afb9a5SDavid du Colombier buf = emalloc9p(Numbsz + 1);
110163afb9a5SDavid du Colombier else
110263afb9a5SDavid du Colombier buf = emalloc9p(r->ifcall.count + 1);
110363afb9a5SDavid du Colombier memmove(buf, r->ifcall.data, r->ifcall.count);
110463afb9a5SDavid du Colombier buf[r->ifcall.count] = '\0';
110563afb9a5SDavid du Colombier
110663afb9a5SDavid du Colombier sshdebug(c, "level %d writectl: %s", lev, buf);
110763afb9a5SDavid du Colombier ntok = tokenize(buf, toks, nelem(toks));
110863afb9a5SDavid du Colombier switch (lev) {
110963afb9a5SDavid du Colombier case Connection:
111063afb9a5SDavid du Colombier if (strcmp(toks[0], "id") == 0) { /* was for sshswitch */
111163afb9a5SDavid du Colombier if (ntok < 2)
111263afb9a5SDavid du Colombier respexit(c, r, buf, "bad id request");
111363afb9a5SDavid du Colombier strncpy(c->idstring, toks[1], sizeof c->idstring);
111463afb9a5SDavid du Colombier sshdebug(c, "id %s", toks[1]);
111563afb9a5SDavid du Colombier break;
111663afb9a5SDavid du Colombier }
111763afb9a5SDavid du Colombier if (strcmp(toks[0], "connect") == 0) {
111863afb9a5SDavid du Colombier if (ntok < 2)
111963afb9a5SDavid du Colombier respexit(c, r, buf, "bad connect request");
112063afb9a5SDavid du Colombier /*
112163afb9a5SDavid du Colombier * should use dial(2) instead of doing it by hand.
112263afb9a5SDavid du Colombier */
112363afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn));
112463afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
112563afb9a5SDavid du Colombier dialbyhand(c, ntok, toks);
112663afb9a5SDavid du Colombier
112763afb9a5SDavid du Colombier c->role = Client;
112863afb9a5SDavid du Colombier /* Override the PKA list; we can take any in */
112963afb9a5SDavid du Colombier pkas[0] = &rsa_pka;
113063afb9a5SDavid du Colombier pkas[1] = &dss_pka;
113163afb9a5SDavid du Colombier pkas[2] = nil;
113263afb9a5SDavid du Colombier tcpconn2 = estrdup9p(tcpconn);
113363afb9a5SDavid du Colombier
113463afb9a5SDavid du Colombier /* swap id strings, negotiate crypto */
113563afb9a5SDavid du Colombier if (dohandshake(c, tcpconn2) < 0) {
113663afb9a5SDavid du Colombier sshlog(c, "connect handshake failed: "
113763afb9a5SDavid du Colombier "tcp conn %s", tcpconn2);
113863afb9a5SDavid du Colombier free(tcpconn2);
113963afb9a5SDavid du Colombier respexit(c, r, buf, "connect handshake failed");
114063afb9a5SDavid du Colombier }
114163afb9a5SDavid du Colombier free(tcpconn2);
114263afb9a5SDavid du Colombier keymbox.state = Empty;
114363afb9a5SDavid du Colombier nbsendul(keymbox.mchan, 1);
114463afb9a5SDavid du Colombier break;
114563afb9a5SDavid du Colombier }
114663afb9a5SDavid du Colombier
114763afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing)
114863afb9a5SDavid du Colombier respexit(c, r, buf, "connection closed");
114963afb9a5SDavid du Colombier if (strcmp(toks[0], "ssh-userauth") == 0)
115063afb9a5SDavid du Colombier userauth(c, r, buf, ntok, toks);
115163afb9a5SDavid du Colombier else if (strcmp(toks[0], "ssh-connection") == 0) {
115263afb9a5SDavid du Colombier /* your ad here */
115363afb9a5SDavid du Colombier } else if (strcmp(toks[0], "hangup") == 0) {
115463afb9a5SDavid du Colombier if (c->rpid >= 0)
115563afb9a5SDavid du Colombier threadint(c->rpid);
115663afb9a5SDavid du Colombier shutdown(c);
115763afb9a5SDavid du Colombier } else if (strcmp(toks[0], "announce") == 0) {
115863afb9a5SDavid du Colombier sshdebug(c, "got %s argument for announce", toks[1]);
115963afb9a5SDavid du Colombier write(c->ctlfd, r->ifcall.data, r->ifcall.count);
116063afb9a5SDavid du Colombier } else if (strcmp(toks[0], "accept") == 0) {
116163afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */
116263afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn));
116363afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
116463afb9a5SDavid du Colombier fprint(c->ctlfd, "accept %s", tcpconn);
116563afb9a5SDavid du Colombier
116663afb9a5SDavid du Colombier c->role = Server;
116763afb9a5SDavid du Colombier tcpconn2 = estrdup9p(tcpconn);
116863afb9a5SDavid du Colombier /* swap id strings, negotiate crypto */
116963afb9a5SDavid du Colombier if (dohandshake(c, tcpconn2) < 0) {
117063afb9a5SDavid du Colombier sshlog(c, "accept handshake failed: "
117163afb9a5SDavid du Colombier "tcp conn %s", tcpconn2);
117263afb9a5SDavid du Colombier free(tcpconn2);
117363afb9a5SDavid du Colombier shutdown(c);
117463afb9a5SDavid du Colombier respexit(c, r, buf, "accept handshake failed");
117563afb9a5SDavid du Colombier }
117663afb9a5SDavid du Colombier free(tcpconn2);
117763afb9a5SDavid du Colombier } else if (strcmp(toks[0], "reject") == 0) {
117863afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn));
117963afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
118063afb9a5SDavid du Colombier
118163afb9a5SDavid du Colombier snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn);
118263afb9a5SDavid du Colombier c->datafd = open(path, ORDWR);
118363afb9a5SDavid du Colombier
118463afb9a5SDavid du Colombier p = new_packet(c);
118563afb9a5SDavid du Colombier add_byte(p, SSH_MSG_DISCONNECT);
118663afb9a5SDavid du Colombier add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT);
118763afb9a5SDavid du Colombier add_string(p, toks[2]);
118863afb9a5SDavid du Colombier add_string(p, "EN");
118963afb9a5SDavid du Colombier n = finish_packet(p);
119063afb9a5SDavid du Colombier if (c->dio && c->datafd >= 0)
119163afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
119263afb9a5SDavid du Colombier free(p);
119363afb9a5SDavid du Colombier if (c->ctlfd >= 0)
119463afb9a5SDavid du Colombier fprint(c->ctlfd, "reject %s %s", buf, toks[2]);
119563afb9a5SDavid du Colombier if (c->rpid >= 0)
119663afb9a5SDavid du Colombier threadint(c->rpid);
119763afb9a5SDavid du Colombier shutdown(c);
119863afb9a5SDavid du Colombier }
119963afb9a5SDavid du Colombier break;
120063afb9a5SDavid du Colombier case Subchannel:
120163afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing)
120263afb9a5SDavid du Colombier respexit(c, r, buf, "channel closed");
120363afb9a5SDavid du Colombier if (strcmp(toks[0], "connect") == 0) {
120463afb9a5SDavid du Colombier p = new_packet(c);
120563afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_OPEN);
120663afb9a5SDavid du Colombier sshdebug(c, "chan writectl: connect %s",
120763afb9a5SDavid du Colombier ntok > 1? toks[1]: "session");
120863afb9a5SDavid du Colombier add_string(p, ntok > 1? toks[1]: "session");
120963afb9a5SDavid du Colombier add_uint32(p, ch->id);
121063afb9a5SDavid du Colombier add_uint32(p, Maxpayload);
121163afb9a5SDavid du Colombier add_uint32(p, Maxrpcbuf);
121263afb9a5SDavid du Colombier /* more stuff if it's an x11 session */
121363afb9a5SDavid du Colombier n = finish_packet(p);
121463afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
121563afb9a5SDavid du Colombier free(p);
121663afb9a5SDavid du Colombier qlock(&c->l);
121763afb9a5SDavid du Colombier if (ch->otherid == -1)
121863afb9a5SDavid du Colombier rsleep(&ch->r);
121963afb9a5SDavid du Colombier qunlock(&c->l);
122063afb9a5SDavid du Colombier } else if (strcmp(toks[0], "global") == 0) {
122163afb9a5SDavid du Colombier /* your ad here */
122263afb9a5SDavid du Colombier } else if (strcmp(toks[0], "hangup") == 0) {
122363afb9a5SDavid du Colombier if (ch->state != Closed && ch->state != Closing) {
122463afb9a5SDavid du Colombier ch->state = Closing;
122563afb9a5SDavid du Colombier if (ch->otherid != -1) {
122663afb9a5SDavid du Colombier p = new_packet(c);
122763afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE);
122863afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
122963afb9a5SDavid du Colombier n = finish_packet(p);
123063afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
123163afb9a5SDavid du Colombier free(p);
123263afb9a5SDavid du Colombier }
123363afb9a5SDavid du Colombier qlock(&c->l);
123463afb9a5SDavid du Colombier rwakeup(&ch->r);
123563afb9a5SDavid du Colombier qunlock(&c->l);
123663afb9a5SDavid du Colombier nbsendul(ch->inchan, 1);
123763afb9a5SDavid du Colombier nbsendul(ch->reqchan, 1);
123863afb9a5SDavid du Colombier }
123963afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (c->chans[n] == nil ||
124063afb9a5SDavid du Colombier c->chans[n]->state == Empty ||
124163afb9a5SDavid du Colombier c->chans[n]->state == Closing ||
124263afb9a5SDavid du Colombier c->chans[n]->state == Closed); ++n)
124363afb9a5SDavid du Colombier ;
124463afb9a5SDavid du Colombier if (n >= MAXCONN) {
124563afb9a5SDavid du Colombier if (c->rpid >= 0)
124663afb9a5SDavid du Colombier threadint(c->rpid);
124763afb9a5SDavid du Colombier shutdown(c);
124863afb9a5SDavid du Colombier }
124963afb9a5SDavid du Colombier } else if (strcmp(toks[0], "announce") == 0) {
125063afb9a5SDavid du Colombier sshdebug(c, "got argument `%s' for chan announce",
125163afb9a5SDavid du Colombier toks[1]);
125263afb9a5SDavid du Colombier free(ch->ann);
125363afb9a5SDavid du Colombier ch->ann = estrdup9p(toks[1]);
125463afb9a5SDavid du Colombier }
125563afb9a5SDavid du Colombier break;
125663afb9a5SDavid du Colombier }
125763afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count;
125863afb9a5SDavid du Colombier r->aux = 0;
125963afb9a5SDavid du Colombier respond(r, nil);
126063afb9a5SDavid du Colombier free(buf);
126163afb9a5SDavid du Colombier threadexits(nil);
126263afb9a5SDavid du Colombier }
126363afb9a5SDavid du Colombier
126463afb9a5SDavid du Colombier void
writereqremproc(void * a)126563afb9a5SDavid du Colombier writereqremproc(void *a)
126663afb9a5SDavid du Colombier {
126763afb9a5SDavid du Colombier Req *r;
126863afb9a5SDavid du Colombier Packet *p;
126963afb9a5SDavid du Colombier Conn *c;
127063afb9a5SDavid du Colombier SSHChan *ch;
127163afb9a5SDavid du Colombier char *cmd, *q, *buf, *toks[4];
127263afb9a5SDavid du Colombier int n, ntok, lev, xconn;
127363afb9a5SDavid du Colombier uvlong qidpath;
127463afb9a5SDavid du Colombier
127563afb9a5SDavid du Colombier threadsetname("writereqremproc");
127663afb9a5SDavid du Colombier r = a;
127763afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
127863afb9a5SDavid du Colombier lev = qidpath >> Levshift;
127963afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
128063afb9a5SDavid du Colombier c = connections[xconn];
128163afb9a5SDavid du Colombier if (c == nil) {
128263afb9a5SDavid du Colombier respond(r, "Invalid connection");
128363afb9a5SDavid du Colombier threadexits(nil);
128463afb9a5SDavid du Colombier }
128563afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask];
128663afb9a5SDavid du Colombier if (r->ifcall.count <= 10)
128763afb9a5SDavid du Colombier buf = emalloc9p(10 + 1);
128863afb9a5SDavid du Colombier else
128963afb9a5SDavid du Colombier buf = emalloc9p(r->ifcall.count + 1);
129063afb9a5SDavid du Colombier memmove(buf, r->ifcall.data, r->ifcall.count);
129163afb9a5SDavid du Colombier buf[r->ifcall.count] = '\0';
129263afb9a5SDavid du Colombier sshdebug(c, "writereqrem: %s", buf);
129363afb9a5SDavid du Colombier ntok = tokenize(buf, toks, nelem(toks));
129463afb9a5SDavid du Colombier
129563afb9a5SDavid du Colombier if (lev == Top) {
129663afb9a5SDavid du Colombier free(keymbox.msg);
129763afb9a5SDavid du Colombier keymbox.msg = buf;
129863afb9a5SDavid du Colombier nbsendul(keymbox.mchan, 1);
129963afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count;
130063afb9a5SDavid du Colombier respexit(c, r, nil, nil);
130163afb9a5SDavid du Colombier }
130263afb9a5SDavid du Colombier
130363afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count;
130463afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing ||
130563afb9a5SDavid du Colombier ch->state == Closed || ch->state == Closing)
130663afb9a5SDavid du Colombier respexit(c, r, buf, nil);
130763afb9a5SDavid du Colombier
130863afb9a5SDavid du Colombier p = new_packet(c);
130963afb9a5SDavid du Colombier if (strcmp(toks[0], "success") == 0) {
131063afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_SUCCESS);
131163afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
131263afb9a5SDavid du Colombier } else if (strcmp(toks[0], "failure") == 0) {
131363afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_FAILURE);
131463afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
131563afb9a5SDavid du Colombier } else if (strcmp(toks[0], "close") == 0) {
131663afb9a5SDavid du Colombier ch->state = Closing;
131763afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE);
131863afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
131963afb9a5SDavid du Colombier } else if (strcmp(toks[0], "shell") == 0) {
132063afb9a5SDavid du Colombier ch->state = Established;
132163afb9a5SDavid du Colombier /*
132263afb9a5SDavid du Colombier * Some servers *cough*OpenSSH*cough* don't seem to be able
132363afb9a5SDavid du Colombier * to intelligently handle a shell with no pty.
132463afb9a5SDavid du Colombier */
132563afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST);
132663afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
132763afb9a5SDavid du Colombier add_string(p, "pty-req");
132863afb9a5SDavid du Colombier add_byte(p, 0);
132963afb9a5SDavid du Colombier if (ntok == 1)
133063afb9a5SDavid du Colombier add_string(p, "dumb");
133163afb9a5SDavid du Colombier else
133263afb9a5SDavid du Colombier add_string(p, toks[1]);
133363afb9a5SDavid du Colombier add_uint32(p, 0);
133463afb9a5SDavid du Colombier add_uint32(p, 0);
133563afb9a5SDavid du Colombier add_uint32(p, 0);
133663afb9a5SDavid du Colombier add_uint32(p, 0);
133763afb9a5SDavid du Colombier add_string(p, "");
133863afb9a5SDavid du Colombier n = finish_packet(p);
133963afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
134063afb9a5SDavid du Colombier init_packet(p);
134163afb9a5SDavid du Colombier p->c = c;
134263afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST);
134363afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
134463afb9a5SDavid du Colombier add_string(p, "shell");
134563afb9a5SDavid du Colombier add_byte(p, 0);
134663afb9a5SDavid du Colombier sshdebug(c, "sending shell request: rlength=%lud twindow=%lud",
134763afb9a5SDavid du Colombier p->rlength, ch->twindow);
134863afb9a5SDavid du Colombier } else if (strcmp(toks[0], "exec") == 0) {
134963afb9a5SDavid du Colombier ch->state = Established;
135063afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST);
135163afb9a5SDavid du Colombier add_uint32(p, ch->otherid);
135263afb9a5SDavid du Colombier add_string(p, "exec");
135363afb9a5SDavid du Colombier add_byte(p, 0);
1354d7e44d0dSDavid du Colombier
135563afb9a5SDavid du Colombier cmd = emalloc9p(Bigbufsz);
135663afb9a5SDavid du Colombier q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]);
135763afb9a5SDavid du Colombier for (n = 2; n < ntok; ++n) {
1358d7e44d0dSDavid du Colombier q = seprint(q, cmd+Bigbufsz, " %q", toks[n]);
135963afb9a5SDavid du Colombier if (q == nil)
136063afb9a5SDavid du Colombier break;
136163afb9a5SDavid du Colombier }
136263afb9a5SDavid du Colombier add_string(p, cmd);
136363afb9a5SDavid du Colombier free(cmd);
136463afb9a5SDavid du Colombier } else
136563afb9a5SDavid du Colombier respexit(c, r, buf, "bad request command");
136663afb9a5SDavid du Colombier n = finish_packet(p);
136763afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
136863afb9a5SDavid du Colombier free(p);
136963afb9a5SDavid du Colombier respexit(c, r, buf, nil);
137063afb9a5SDavid du Colombier }
137163afb9a5SDavid du Colombier
137263afb9a5SDavid du Colombier void
writedataproc(void * a)137363afb9a5SDavid du Colombier writedataproc(void *a)
137463afb9a5SDavid du Colombier {
137563afb9a5SDavid du Colombier Req *r;
137663afb9a5SDavid du Colombier Packet *p;
137763afb9a5SDavid du Colombier Conn *c;
137863afb9a5SDavid du Colombier SSHChan *ch;
137963afb9a5SDavid du Colombier int n, xconn;
138063afb9a5SDavid du Colombier uvlong qidpath;
138163afb9a5SDavid du Colombier
138263afb9a5SDavid du Colombier threadsetname("writedataproc");
138363afb9a5SDavid du Colombier r = a;
138463afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux;
138563afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask;
138663afb9a5SDavid du Colombier c = connections[xconn];
138763afb9a5SDavid du Colombier if (c == nil) {
138863afb9a5SDavid du Colombier respond(r, "Invalid connection");
138963afb9a5SDavid du Colombier threadexits(nil);
139063afb9a5SDavid du Colombier }
139163afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask];
139263afb9a5SDavid du Colombier
139363afb9a5SDavid du Colombier p = new_packet(c);
139463afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_DATA);
139563afb9a5SDavid du Colombier hnputl(p->payload+1, ch->otherid);
139663afb9a5SDavid du Colombier p->rlength += 4;
139763afb9a5SDavid du Colombier add_block(p, r->ifcall.data, r->ifcall.count);
139863afb9a5SDavid du Colombier n = finish_packet(p);
139963afb9a5SDavid du Colombier
140063afb9a5SDavid du Colombier if (ch->sent + p->rlength > ch->twindow) {
140163afb9a5SDavid du Colombier qlock(&ch->xmtlock);
140263afb9a5SDavid du Colombier while (ch->sent + p->rlength > ch->twindow)
140363afb9a5SDavid du Colombier rsleep(&ch->xmtrendez);
140463afb9a5SDavid du Colombier qunlock(&ch->xmtlock);
140563afb9a5SDavid du Colombier }
140663afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
140763afb9a5SDavid du Colombier respexit(c, r, p, nil);
140863afb9a5SDavid du Colombier }
140963afb9a5SDavid du Colombier
141063afb9a5SDavid du Colombier /*
141163afb9a5SDavid du Colombier * Although this is named stclunk, it's attached to the destroyfid
141263afb9a5SDavid du Colombier * member of the Srv struct. It turns out there's no member
141363afb9a5SDavid du Colombier * called clunk. But if there are no other references, a 9P Tclunk
141463afb9a5SDavid du Colombier * will end up calling destroyfid.
141563afb9a5SDavid du Colombier */
141663afb9a5SDavid du Colombier void
stclunk(Fid * f)141763afb9a5SDavid du Colombier stclunk(Fid *f)
141863afb9a5SDavid du Colombier {
141963afb9a5SDavid du Colombier Packet *p;
142063afb9a5SDavid du Colombier Conn *c;
142163afb9a5SDavid du Colombier SSHChan *sc;
142263afb9a5SDavid du Colombier int n, lev, cnum, chnum;
142363afb9a5SDavid du Colombier uvlong qidpath;
142463afb9a5SDavid du Colombier
142563afb9a5SDavid du Colombier threadsetname("stclunk");
142663afb9a5SDavid du Colombier if (f == nil || f->file == nil)
142763afb9a5SDavid du Colombier return;
142863afb9a5SDavid du Colombier qidpath = (uvlong)f->file->aux;
142963afb9a5SDavid du Colombier lev = qidpath >> Levshift;
143063afb9a5SDavid du Colombier cnum = (qidpath >> Connshift) & Connmask;
143163afb9a5SDavid du Colombier chnum = qidpath & Chanmask;
143263afb9a5SDavid du Colombier c = connections[cnum];
143363afb9a5SDavid du Colombier sshdebug(c, "got clunk on file: %#llux %d %d %d: %s",
143463afb9a5SDavid du Colombier qidpath, lev, cnum, chnum, f->file->name);
143563afb9a5SDavid du Colombier /* qidpath test implies conn 0, chan 0 */
143663afb9a5SDavid du Colombier if (lev == Top && qidpath == Qreqrem) {
143763afb9a5SDavid du Colombier if (keymbox.state != Empty) {
143863afb9a5SDavid du Colombier keymbox.state = Empty;
143963afb9a5SDavid du Colombier // nbsendul(keymbox.mchan, 1);
144063afb9a5SDavid du Colombier }
144163afb9a5SDavid du Colombier keymbox.msg = nil;
144263afb9a5SDavid du Colombier return;
144363afb9a5SDavid du Colombier }
144463afb9a5SDavid du Colombier
144563afb9a5SDavid du Colombier if (c == nil)
144663afb9a5SDavid du Colombier return;
144763afb9a5SDavid du Colombier if (lev == Connection && (qidpath & Qtypemask) == Qctl &&
144863afb9a5SDavid du Colombier (c->state == Opening || c->state == Negotiating ||
144963afb9a5SDavid du Colombier c->state == Authing)) {
145063afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (!c->chans[n] ||
145163afb9a5SDavid du Colombier c->chans[n]->state == Empty ||
145263afb9a5SDavid du Colombier c->chans[n]->state == Closed ||
145363afb9a5SDavid du Colombier c->chans[n]->state == Closing); ++n)
145463afb9a5SDavid du Colombier ;
145563afb9a5SDavid du Colombier if (n >= MAXCONN) {
145663afb9a5SDavid du Colombier if (c->rpid >= 0)
145763afb9a5SDavid du Colombier threadint(c->rpid);
145863afb9a5SDavid du Colombier shutdown(c);
145963afb9a5SDavid du Colombier }
146063afb9a5SDavid du Colombier return;
146163afb9a5SDavid du Colombier }
146263afb9a5SDavid du Colombier
146363afb9a5SDavid du Colombier sc = c->chans[chnum];
146463afb9a5SDavid du Colombier if (lev != Subchannel)
146563afb9a5SDavid du Colombier return;
146663afb9a5SDavid du Colombier if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) {
146763afb9a5SDavid du Colombier qlock(&c->l);
146863afb9a5SDavid du Colombier if (sc->state != Closed) {
146963afb9a5SDavid du Colombier sc->state = Closed;
147063afb9a5SDavid du Colombier chanclose(sc->inchan);
147163afb9a5SDavid du Colombier chanclose(sc->reqchan);
147263afb9a5SDavid du Colombier }
147363afb9a5SDavid du Colombier qunlock(&c->l);
147463afb9a5SDavid du Colombier } else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty &&
147563afb9a5SDavid du Colombier sc->state != Closed && sc->state != Closing) {
147663afb9a5SDavid du Colombier if (f->file != sc->data && f->file != sc->request) {
147763afb9a5SDavid du Colombier sshlog(c, "great evil is upon us; destroying a fid "
147863afb9a5SDavid du Colombier "we didn't create");
147963afb9a5SDavid du Colombier return;
148063afb9a5SDavid du Colombier }
148163afb9a5SDavid du Colombier
148263afb9a5SDavid du Colombier p = new_packet(c);
148363afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE);
148463afb9a5SDavid du Colombier hnputl(p->payload+1, sc->otherid);
148563afb9a5SDavid du Colombier p->rlength += 4;
148663afb9a5SDavid du Colombier n = finish_packet(p);
148763afb9a5SDavid du Colombier sc->state = Closing;
148863afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n);
148963afb9a5SDavid du Colombier free(p);
149063afb9a5SDavid du Colombier
149163afb9a5SDavid du Colombier qlock(&c->l);
149263afb9a5SDavid du Colombier rwakeup(&sc->r);
149363afb9a5SDavid du Colombier qunlock(&c->l);
149463afb9a5SDavid du Colombier nbsendul(sc->inchan, 1);
149563afb9a5SDavid du Colombier nbsendul(sc->reqchan, 1);
149663afb9a5SDavid du Colombier }
149763afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (!c->chans[n] ||
149863afb9a5SDavid du Colombier c->chans[n]->state == Empty || c->chans[n]->state == Closed ||
149963afb9a5SDavid du Colombier c->chans[n]->state == Closing); ++n)
150063afb9a5SDavid du Colombier ;
150163afb9a5SDavid du Colombier if (n >= MAXCONN) {
150263afb9a5SDavid du Colombier if (c->rpid >= 0)
150363afb9a5SDavid du Colombier threadint(c->rpid);
150463afb9a5SDavid du Colombier shutdown(c);
150563afb9a5SDavid du Colombier }
150663afb9a5SDavid du Colombier }
150763afb9a5SDavid du Colombier
150863afb9a5SDavid du Colombier void
stflush(Req * r)150963afb9a5SDavid du Colombier stflush(Req *r)
151063afb9a5SDavid du Colombier {
151163afb9a5SDavid du Colombier Req *or;
151263afb9a5SDavid du Colombier uvlong qidpath;
151363afb9a5SDavid du Colombier
151463afb9a5SDavid du Colombier threadsetname("stflush");
151563afb9a5SDavid du Colombier or = r->oldreq;
151663afb9a5SDavid du Colombier qidpath = (uvlong)or->fid->file->aux;
151763afb9a5SDavid du Colombier sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p",
151863afb9a5SDavid du Colombier argv0, qidpath, qidpath >> Levshift,
151963afb9a5SDavid du Colombier (qidpath >> Connshift) & Connmask, qidpath & Chanmask,
152063afb9a5SDavid du Colombier or->fid->file->name, or->aux);
152163afb9a5SDavid du Colombier if (!or->aux)
152263afb9a5SDavid du Colombier respond(or, "interrupted");
1523eac5056eSDavid du Colombier else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten ||
152463afb9a5SDavid du Colombier or->ifcall.type == Tread && (qidpath & Qtypemask) == Qdata &&
152563afb9a5SDavid du Colombier (qidpath >> Levshift) == Subchannel ||
152663afb9a5SDavid du Colombier or->ifcall.type == Tread && (qidpath & Qtypemask) == Qreqrem)
152763afb9a5SDavid du Colombier threadint((uintptr)or->aux);
152863afb9a5SDavid du Colombier else {
152963afb9a5SDavid du Colombier threadkill((uintptr)or->aux);
153063afb9a5SDavid du Colombier or->aux = 0;
153163afb9a5SDavid du Colombier respond(or, "interrupted");
153263afb9a5SDavid du Colombier }
153363afb9a5SDavid du Colombier respond(r, nil);
153463afb9a5SDavid du Colombier }
153563afb9a5SDavid du Colombier
153663afb9a5SDavid du Colombier void
filedup(Req * r,File * src)153763afb9a5SDavid du Colombier filedup(Req *r, File *src)
153863afb9a5SDavid du Colombier {
153963afb9a5SDavid du Colombier r->ofcall.qid = src->qid;
154063afb9a5SDavid du Colombier closefile(r->fid->file);
154163afb9a5SDavid du Colombier r->fid->file = src;
154263afb9a5SDavid du Colombier incref(src);
154363afb9a5SDavid du Colombier }
154463afb9a5SDavid du Colombier
154563afb9a5SDavid du Colombier Conn *
alloc_conn(void)154663afb9a5SDavid du Colombier alloc_conn(void)
154763afb9a5SDavid du Colombier {
154863afb9a5SDavid du Colombier int slevconn, i, s, firstnil;
154963afb9a5SDavid du Colombier char buf[Numbsz];
155063afb9a5SDavid du Colombier Conn *c;
155163afb9a5SDavid du Colombier static QLock aclock;
155263afb9a5SDavid du Colombier
155363afb9a5SDavid du Colombier qlock(&aclock);
155463afb9a5SDavid du Colombier firstnil = -1;
155563afb9a5SDavid du Colombier for (i = 0; i < MAXCONN; ++i) {
155663afb9a5SDavid du Colombier if (connections[i] == nil) {
155763afb9a5SDavid du Colombier if (firstnil == -1)
155863afb9a5SDavid du Colombier firstnil = i;
155963afb9a5SDavid du Colombier continue;
156063afb9a5SDavid du Colombier }
156163afb9a5SDavid du Colombier s = connections[i]->state;
156263afb9a5SDavid du Colombier if (s == Empty || s == Closed)
156363afb9a5SDavid du Colombier break;
156463afb9a5SDavid du Colombier }
156563afb9a5SDavid du Colombier if (i >= MAXCONN) {
156663afb9a5SDavid du Colombier if (firstnil == -1) { /* all slots in use? */
156763afb9a5SDavid du Colombier qunlock(&aclock);
156863afb9a5SDavid du Colombier return nil;
156963afb9a5SDavid du Colombier }
157063afb9a5SDavid du Colombier /* no reusable slots, allocate a new Conn */
157163afb9a5SDavid du Colombier connections[firstnil] = emalloc9p(sizeof(Conn));
157263afb9a5SDavid du Colombier memset(connections[firstnil], 0, sizeof(Conn));
157363afb9a5SDavid du Colombier i = firstnil;
157463afb9a5SDavid du Colombier }
157563afb9a5SDavid du Colombier
157663afb9a5SDavid du Colombier c = connections[i];
157763afb9a5SDavid du Colombier memset(&c->r, '\0', sizeof(Rendez));
157863afb9a5SDavid du Colombier c->r.l = &c->l;
157963afb9a5SDavid du Colombier c->dio = ioproc();
158063afb9a5SDavid du Colombier c->rio = nil;
158163afb9a5SDavid du Colombier c->state = Allocated;
158263afb9a5SDavid du Colombier c->role = Server;
158363afb9a5SDavid du Colombier c->id = i;
158463afb9a5SDavid du Colombier c->stifle = c->poisoned = 0;
158563afb9a5SDavid du Colombier c->user = c->service = nil;
158663afb9a5SDavid du Colombier c->inseq = c->nchan = c->outseq = 0;
158763afb9a5SDavid du Colombier c->cscrypt = c->csmac = c->ctlfd = c->datafd = c->decrypt =
158863afb9a5SDavid du Colombier c->encrypt = c->inmac = c->ncscrypt = c->ncsmac =
158963afb9a5SDavid du Colombier c->nsccrypt = c->nscmac = c->outmac = c->rpid = c->sccrypt =
159063afb9a5SDavid du Colombier c->scmac = c->tcpconn = -1;
159163afb9a5SDavid du Colombier if (c->e) {
159263afb9a5SDavid du Colombier mpfree(c->e);
159363afb9a5SDavid du Colombier c->e = nil;
159463afb9a5SDavid du Colombier }
159563afb9a5SDavid du Colombier if (c->x) {
159663afb9a5SDavid du Colombier mpfree(c->x);
159763afb9a5SDavid du Colombier c->x = nil;
159863afb9a5SDavid du Colombier }
159963afb9a5SDavid du Colombier
160063afb9a5SDavid du Colombier snprint(buf, sizeof buf, "%d", i);
160163afb9a5SDavid du Colombier if (c->dir == nil) {
160263afb9a5SDavid du Colombier slevconn = Connection << Levshift | i << Connshift;
160363afb9a5SDavid du Colombier c->dir = createfile(rootfile, buf, uid, 0555|DMDIR,
160463afb9a5SDavid du Colombier (void *)(slevconn | Qroot));
160563afb9a5SDavid du Colombier c->clonefile = createfile(c->dir, "clone", uid, 0666,
160663afb9a5SDavid du Colombier (void *)(slevconn | Qclone));
160763afb9a5SDavid du Colombier c->ctlfile = createfile(c->dir, "ctl", uid, 0666,
160863afb9a5SDavid du Colombier (void *)(slevconn | Qctl));
160963afb9a5SDavid du Colombier c->datafile = createfile(c->dir, "data", uid, 0666,
161063afb9a5SDavid du Colombier (void *)(slevconn | Qdata));
161163afb9a5SDavid du Colombier c->listenfile = createfile(c->dir, "listen", uid, 0666,
161263afb9a5SDavid du Colombier (void *)(slevconn | Qlisten));
161363afb9a5SDavid du Colombier c->localfile = createfile(c->dir, "local", uid, 0444,
161463afb9a5SDavid du Colombier (void *)(slevconn | Qlocal));
161563afb9a5SDavid du Colombier c->remotefile = createfile(c->dir, "remote", uid, 0444,
161663afb9a5SDavid du Colombier (void *)(slevconn | Qreqrem));
161763afb9a5SDavid du Colombier c->statusfile = createfile(c->dir, "status", uid, 0444,
161863afb9a5SDavid du Colombier (void *)(slevconn | Qstatus));
161963afb9a5SDavid du Colombier c->tcpfile = createfile(c->dir, "tcp", uid, 0444,
162063afb9a5SDavid du Colombier (void *)(slevconn | Qtcp));
162163afb9a5SDavid du Colombier }
162263afb9a5SDavid du Colombier // c->skexinit = c->rkexinit = nil;
162363afb9a5SDavid du Colombier c->got_sessid = 0;
162463afb9a5SDavid du Colombier c->otherid = nil;
162563afb9a5SDavid du Colombier c->inik = c->outik = nil;
162663afb9a5SDavid du Colombier c->s2ccs = c->c2scs = c->enccs = c->deccs = nil;
162763afb9a5SDavid du Colombier qunlock(&aclock);
162863afb9a5SDavid du Colombier return c;
162963afb9a5SDavid du Colombier }
163063afb9a5SDavid du Colombier
163163afb9a5SDavid du Colombier SSHChan *
alloc_chan(Conn * c)163263afb9a5SDavid du Colombier alloc_chan(Conn *c)
163363afb9a5SDavid du Colombier {
163463afb9a5SDavid du Colombier int cnum, slcn;
163563afb9a5SDavid du Colombier char buf[Numbsz];
163663afb9a5SDavid du Colombier Plist *p, *next;
163763afb9a5SDavid du Colombier SSHChan *sc;
163863afb9a5SDavid du Colombier
163963afb9a5SDavid du Colombier if (c->nchan >= MAXCONN)
164063afb9a5SDavid du Colombier return nil;
164163afb9a5SDavid du Colombier qlock(&c->l);
164263afb9a5SDavid du Colombier cnum = c->nchan;
164363afb9a5SDavid du Colombier if (c->chans[cnum] == nil) {
164463afb9a5SDavid du Colombier c->chans[cnum] = emalloc9p(sizeof(SSHChan));
164563afb9a5SDavid du Colombier memset(c->chans[cnum], 0, sizeof(SSHChan));
164663afb9a5SDavid du Colombier }
164763afb9a5SDavid du Colombier sc = c->chans[cnum];
164863afb9a5SDavid du Colombier snprint(buf, sizeof buf, "%d", cnum);
164963afb9a5SDavid du Colombier memset(&sc->r, '\0', sizeof(Rendez));
165063afb9a5SDavid du Colombier sc->r.l = &c->l;
165163afb9a5SDavid du Colombier sc->id = cnum;
165263afb9a5SDavid du Colombier sc->state = Empty;
165363afb9a5SDavid du Colombier sc->conn = c->id;
165463afb9a5SDavid du Colombier sc->otherid = sc->waker = -1;
165563afb9a5SDavid du Colombier sc->sent = sc->twindow = sc->rwindow = sc->inrqueue = 0;
165663afb9a5SDavid du Colombier sc->ann = nil;
165763afb9a5SDavid du Colombier sc->lreq = nil;
165863afb9a5SDavid du Colombier
165963afb9a5SDavid du Colombier if (sc->dir == nil) {
166063afb9a5SDavid du Colombier slcn = Subchannel << Levshift | c->id << Connshift | cnum;
166163afb9a5SDavid du Colombier sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR,
166263afb9a5SDavid du Colombier (void *)(slcn | Qroot));
166363afb9a5SDavid du Colombier sc->ctl = createfile(sc->dir, "ctl", uid, 0666,
166463afb9a5SDavid du Colombier (void *)(slcn | Qctl));
166563afb9a5SDavid du Colombier sc->data = createfile(sc->dir, "data", uid, 0666,
166663afb9a5SDavid du Colombier (void *)(slcn | Qdata));
166763afb9a5SDavid du Colombier sc->listen = createfile(sc->dir, "listen", uid, 0666,
166863afb9a5SDavid du Colombier (void *)(slcn | Qlisten));
166963afb9a5SDavid du Colombier sc->request = createfile(sc->dir, "request", uid, 0666,
167063afb9a5SDavid du Colombier (void *)(slcn | Qreqrem));
167163afb9a5SDavid du Colombier sc->status = createfile(sc->dir, "status", uid, 0444,
167263afb9a5SDavid du Colombier (void *)(slcn | Qstatus));
167363afb9a5SDavid du Colombier sc->tcp = createfile(sc->dir, "tcp", uid, 0444,
167463afb9a5SDavid du Colombier (void *)(slcn | Qtcp));
167563afb9a5SDavid du Colombier }
167663afb9a5SDavid du Colombier c->nchan++;
167763afb9a5SDavid du Colombier
167863afb9a5SDavid du Colombier for (; sc->reqq != nil; sc->reqq = next) {
167963afb9a5SDavid du Colombier p = sc->reqq;
168063afb9a5SDavid du Colombier next = p->next;
168163afb9a5SDavid du Colombier free(p->pack);
168263afb9a5SDavid du Colombier free(p);
168363afb9a5SDavid du Colombier }
168463afb9a5SDavid du Colombier sc->dataq = sc->datatl = sc->reqtl = nil;
168563afb9a5SDavid du Colombier
168663afb9a5SDavid du Colombier if (sc->inchan)
168763afb9a5SDavid du Colombier chanfree(sc->inchan);
168863afb9a5SDavid du Colombier sc->inchan = chancreate(4, 0);
168963afb9a5SDavid du Colombier
169063afb9a5SDavid du Colombier if (sc->reqchan)
169163afb9a5SDavid du Colombier chanfree(sc->reqchan);
169263afb9a5SDavid du Colombier sc->reqchan = chancreate(4, 0);
169363afb9a5SDavid du Colombier
169463afb9a5SDavid du Colombier memset(&sc->xmtrendez, '\0', sizeof(Rendez));
169563afb9a5SDavid du Colombier sc->xmtrendez.l = &sc->xmtlock;
169663afb9a5SDavid du Colombier qunlock(&c->l);
169763afb9a5SDavid du Colombier return sc;
169863afb9a5SDavid du Colombier }
169963afb9a5SDavid du Colombier
170063afb9a5SDavid du Colombier static int
readlineio(Conn *,Ioproc * io,int fd,char * buf,int size)170163afb9a5SDavid du Colombier readlineio(Conn *, Ioproc *io, int fd, char *buf, int size)
170263afb9a5SDavid du Colombier {
170363afb9a5SDavid du Colombier int n;
170463afb9a5SDavid du Colombier char *p;
170563afb9a5SDavid du Colombier
170663afb9a5SDavid du Colombier for (p = buf; p < buf + size - 1; p++) {
170763afb9a5SDavid du Colombier n = ioread(io, fd, p, 1);
170863afb9a5SDavid du Colombier if (n != 1 || *p == '\n') {
170963afb9a5SDavid du Colombier *p = '\0';
171063afb9a5SDavid du Colombier break;
171163afb9a5SDavid du Colombier }
171263afb9a5SDavid du Colombier }
171363afb9a5SDavid du Colombier return p - buf;
171463afb9a5SDavid du Colombier }
171563afb9a5SDavid du Colombier
171663afb9a5SDavid du Colombier static char *
readremote(Conn * c,Ioproc * io,char * tcpconn)171763afb9a5SDavid du Colombier readremote(Conn *c, Ioproc *io, char *tcpconn)
171863afb9a5SDavid du Colombier {
171963afb9a5SDavid du Colombier int n, remfd;
172063afb9a5SDavid du Colombier char *p, *remote;
172163afb9a5SDavid du Colombier char path[Arbbufsz], buf[NETPATHLEN];
172263afb9a5SDavid du Colombier
172363afb9a5SDavid du Colombier remote = nil;
172463afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
172563afb9a5SDavid du Colombier remfd = ioopen(io, path, OREAD);
172663afb9a5SDavid du Colombier if (remfd < 0) {
172763afb9a5SDavid du Colombier sshlog(c, "readremote: can't open %s: %r", path);
172863afb9a5SDavid du Colombier return nil;
172963afb9a5SDavid du Colombier }
173063afb9a5SDavid du Colombier n = ioread(io, remfd, buf, sizeof buf - 1);
173163afb9a5SDavid du Colombier if (n > 0) {
173263afb9a5SDavid du Colombier buf[n] = 0;
173363afb9a5SDavid du Colombier p = strchr(buf, '!');
173463afb9a5SDavid du Colombier if (p)
173563afb9a5SDavid du Colombier *p = 0;
173663afb9a5SDavid du Colombier remote = estrdup9p(buf);
173763afb9a5SDavid du Colombier }
173863afb9a5SDavid du Colombier ioclose(io, remfd);
173963afb9a5SDavid du Colombier return remote;
174063afb9a5SDavid du Colombier }
174163afb9a5SDavid du Colombier
174263afb9a5SDavid du Colombier static void
sendmyid(Conn * c,Ioproc * io)174363afb9a5SDavid du Colombier sendmyid(Conn *c, Ioproc *io)
174463afb9a5SDavid du Colombier {
174563afb9a5SDavid du Colombier char path[Arbbufsz];
174663afb9a5SDavid du Colombier
174763afb9a5SDavid du Colombier snprint(path, sizeof path, "%s\r\n", MYID);
174863afb9a5SDavid du Colombier iowrite(io, c->datafd, path, strlen(path));
174963afb9a5SDavid du Colombier }
175063afb9a5SDavid du Colombier
175163afb9a5SDavid du Colombier /* save and tidy up the remote id */
175263afb9a5SDavid du Colombier static void
stashremid(Conn * c,char * remid)175363afb9a5SDavid du Colombier stashremid(Conn *c, char *remid)
175463afb9a5SDavid du Colombier {
175563afb9a5SDavid du Colombier char *nl;
175663afb9a5SDavid du Colombier
175763afb9a5SDavid du Colombier if (c->otherid)
175863afb9a5SDavid du Colombier free(c->otherid);
175963afb9a5SDavid du Colombier c->otherid = estrdup9p(remid);
176063afb9a5SDavid du Colombier
176163afb9a5SDavid du Colombier nl = strchr(c->otherid, '\n');
176263afb9a5SDavid du Colombier if (nl)
176363afb9a5SDavid du Colombier *nl = '\0';
176463afb9a5SDavid du Colombier nl = strchr(c->otherid, '\r');
176563afb9a5SDavid du Colombier if (nl)
176663afb9a5SDavid du Colombier *nl = '\0';
176763afb9a5SDavid du Colombier }
176863afb9a5SDavid du Colombier
176963afb9a5SDavid du Colombier static void
hangupconn(Conn * c)177063afb9a5SDavid du Colombier hangupconn(Conn *c)
177163afb9a5SDavid du Colombier {
177263afb9a5SDavid du Colombier hangup(c->ctlfd);
177363afb9a5SDavid du Colombier close(c->ctlfd);
177463afb9a5SDavid du Colombier close(c->datafd);
177563afb9a5SDavid du Colombier c->ctlfd = c->datafd = -1;
177663afb9a5SDavid du Colombier }
177763afb9a5SDavid du Colombier
177863afb9a5SDavid du Colombier #ifdef COEXIST
177963afb9a5SDavid du Colombier static int
exchids(Conn * c,Ioproc * io,char * remid,int remsz)178063afb9a5SDavid du Colombier exchids(Conn *c, Ioproc *io, char *remid, int remsz)
178163afb9a5SDavid du Colombier {
178263afb9a5SDavid du Colombier int n;
178363afb9a5SDavid du Colombier
178463afb9a5SDavid du Colombier /*
178563afb9a5SDavid du Colombier * exchange versions. server writes id, then reads;
178663afb9a5SDavid du Colombier * client reads id then writes (in theory).
178763afb9a5SDavid du Colombier */
178863afb9a5SDavid du Colombier if (c->role == Server) {
178963afb9a5SDavid du Colombier sendmyid(c, io);
179063afb9a5SDavid du Colombier
179163afb9a5SDavid du Colombier n = readlineio(c, io, c->datafd, remid, remsz);
179263afb9a5SDavid du Colombier if (n < 5) /* can't be a valid SSH id string */
179363afb9a5SDavid du Colombier return -1;
179463afb9a5SDavid du Colombier sshdebug(c, "dohandshake: server, got `%s', sent `%s'", remid,
179563afb9a5SDavid du Colombier MYID);
179663afb9a5SDavid du Colombier } else {
179763afb9a5SDavid du Colombier /* client: read server's id */
179863afb9a5SDavid du Colombier n = readlineio(c, io, c->datafd, remid, remsz);
179963afb9a5SDavid du Colombier if (n < 5) /* can't be a valid SSH id string */
180063afb9a5SDavid du Colombier return -1;
180163afb9a5SDavid du Colombier
180263afb9a5SDavid du Colombier sendmyid(c, io);
180363afb9a5SDavid du Colombier sshdebug(c, "dohandshake: client, got `%s' sent `%s'", remid, MYID);
180463afb9a5SDavid du Colombier if (remid[0] == '\0') {
180563afb9a5SDavid du Colombier sshlog(c, "dohandshake: client, empty remote id string;"
180663afb9a5SDavid du Colombier " out of sync");
180763afb9a5SDavid du Colombier return -1;
180863afb9a5SDavid du Colombier }
180963afb9a5SDavid du Colombier }
181063afb9a5SDavid du Colombier sshdebug(c, "remote id string `%s'", remid);
181163afb9a5SDavid du Colombier return 0;
181263afb9a5SDavid du Colombier }
181363afb9a5SDavid du Colombier
181463afb9a5SDavid du Colombier /*
181563afb9a5SDavid du Colombier * negotiate the protocols.
181663afb9a5SDavid du Colombier * We don't do the full negotiation here, because we also have
181763afb9a5SDavid du Colombier * to handle a re-negotiation request from the other end.
181863afb9a5SDavid du Colombier * So we just kick it off and let the receiver process take it from there.
181963afb9a5SDavid du Colombier */
182063afb9a5SDavid du Colombier static int
negotiate(Conn * c)182163afb9a5SDavid du Colombier negotiate(Conn *c)
182263afb9a5SDavid du Colombier {
182363afb9a5SDavid du Colombier send_kexinit(c);
182463afb9a5SDavid du Colombier
182563afb9a5SDavid du Colombier qlock(&c->l);
182663afb9a5SDavid du Colombier if ((c->role == Client && c->state != Negotiating) ||
182763afb9a5SDavid du Colombier (c->role == Server && c->state != Established)) {
182863afb9a5SDavid du Colombier sshdebug(c, "awaiting establishment");
182963afb9a5SDavid du Colombier rsleep(&c->r);
183063afb9a5SDavid du Colombier }
183163afb9a5SDavid du Colombier qunlock(&c->l);
183263afb9a5SDavid du Colombier
183363afb9a5SDavid du Colombier if (c->role == Server && c->state != Established ||
183463afb9a5SDavid du Colombier c->role == Client && c->state != Negotiating) {
183563afb9a5SDavid du Colombier sshdebug(c, "failed to establish");
183663afb9a5SDavid du Colombier return -1;
183763afb9a5SDavid du Colombier }
183863afb9a5SDavid du Colombier sshdebug(c, "established; crypto now on");
183963afb9a5SDavid du Colombier return 0;
184063afb9a5SDavid du Colombier }
184163afb9a5SDavid du Colombier
184263afb9a5SDavid du Colombier /* this was deferred when trying to make coexistence with v1 work */
184363afb9a5SDavid du Colombier static int
deferredinit(Conn * c)184463afb9a5SDavid du Colombier deferredinit(Conn *c)
184563afb9a5SDavid du Colombier {
184663afb9a5SDavid du Colombier char remid[Arbbufsz];
184763afb9a5SDavid du Colombier Ioproc *io;
184863afb9a5SDavid du Colombier
184963afb9a5SDavid du Colombier io = ioproc();
185063afb9a5SDavid du Colombier /*
185163afb9a5SDavid du Colombier * don't bother checking the remote's id string.
185263afb9a5SDavid du Colombier * as a client, we can cope with v1 if we don't verify the host key.
185363afb9a5SDavid du Colombier */
185463afb9a5SDavid du Colombier if (exchids(c, io, remid, sizeof remid) < 0 ||
185563afb9a5SDavid du Colombier 0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 &&
185663afb9a5SDavid du Colombier strncmp(remid, "SSH-1.99", 8) != 0) {
185763afb9a5SDavid du Colombier /* not a protocol version we know; give up */
185863afb9a5SDavid du Colombier closeioproc(io);
185963afb9a5SDavid du Colombier hangupconn(c);
186063afb9a5SDavid du Colombier return -1;
186163afb9a5SDavid du Colombier }
186263afb9a5SDavid du Colombier closeioproc(io);
186363afb9a5SDavid du Colombier stashremid(c, remid);
186463afb9a5SDavid du Colombier
186563afb9a5SDavid du Colombier c->state = Initting;
186663afb9a5SDavid du Colombier
186763afb9a5SDavid du Colombier /* start the reader thread */
186863afb9a5SDavid du Colombier if (c->rpid < 0)
186963afb9a5SDavid du Colombier c->rpid = threadcreate(reader, c, Defstk);
187063afb9a5SDavid du Colombier
187163afb9a5SDavid du Colombier return negotiate(c);
187263afb9a5SDavid du Colombier }
187363afb9a5SDavid du Colombier
187463afb9a5SDavid du Colombier int
dohandshake(Conn * c,char * tcpconn)187563afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
187663afb9a5SDavid du Colombier {
187763afb9a5SDavid du Colombier int tcpdfd;
187863afb9a5SDavid du Colombier char *remote;
187963afb9a5SDavid du Colombier char path[Arbbufsz];
188063afb9a5SDavid du Colombier Ioproc *io;
188163afb9a5SDavid du Colombier
188263afb9a5SDavid du Colombier io = ioproc();
188363afb9a5SDavid du Colombier
188463afb9a5SDavid du Colombier /* read tcp conn's remote address into c->remote */
188563afb9a5SDavid du Colombier remote = readremote(c, io, tcpconn);
188663afb9a5SDavid du Colombier if (remote) {
188763afb9a5SDavid du Colombier free(c->remote);
188863afb9a5SDavid du Colombier c->remote = remote;
188963afb9a5SDavid du Colombier }
189063afb9a5SDavid du Colombier
189163afb9a5SDavid du Colombier /* open tcp conn's data file */
189263afb9a5SDavid du Colombier c->tcpconn = atoi(tcpconn);
189363afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
189463afb9a5SDavid du Colombier tcpdfd = ioopen(io, path, ORDWR);
189563afb9a5SDavid du Colombier closeioproc(io);
189663afb9a5SDavid du Colombier if (tcpdfd < 0) {
189763afb9a5SDavid du Colombier sshlog(c, "dohandshake: can't open %s: %r", path);
189863afb9a5SDavid du Colombier return -1;
189963afb9a5SDavid du Colombier }
190063afb9a5SDavid du Colombier c->datafd = tcpdfd; /* underlying tcp data descriptor */
190163afb9a5SDavid du Colombier
190263afb9a5SDavid du Colombier return deferredinit(c);
190363afb9a5SDavid du Colombier }
190463afb9a5SDavid du Colombier #endif /* COEXIST */
190563afb9a5SDavid du Colombier
190663afb9a5SDavid du Colombier int
dohandshake(Conn * c,char * tcpconn)190763afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
190863afb9a5SDavid du Colombier {
190963afb9a5SDavid du Colombier int fd, n;
191063afb9a5SDavid du Colombier char *p, *othid;
191163afb9a5SDavid du Colombier char path[Arbbufsz], buf[NETPATHLEN];
191263afb9a5SDavid du Colombier Ioproc *io;
191363afb9a5SDavid du Colombier
191463afb9a5SDavid du Colombier io = ioproc();
191563afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
191663afb9a5SDavid du Colombier fd = ioopen(io, path, OREAD);
191763afb9a5SDavid du Colombier n = ioread(io, fd, buf, sizeof buf - 1);
191863afb9a5SDavid du Colombier if (n > 0) {
191963afb9a5SDavid du Colombier buf[n] = 0;
192063afb9a5SDavid du Colombier p = strchr(buf, '!');
192163afb9a5SDavid du Colombier if (p)
192263afb9a5SDavid du Colombier *p = 0;
192363afb9a5SDavid du Colombier free(c->remote);
192463afb9a5SDavid du Colombier c->remote = estrdup9p(buf);
192563afb9a5SDavid du Colombier }
192663afb9a5SDavid du Colombier ioclose(io, fd);
192763afb9a5SDavid du Colombier
192863afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
192963afb9a5SDavid du Colombier fd = ioopen(io, path, ORDWR);
193063afb9a5SDavid du Colombier if (fd < 0) {
193163afb9a5SDavid du Colombier closeioproc(io);
193263afb9a5SDavid du Colombier return -1;
193363afb9a5SDavid du Colombier }
193463afb9a5SDavid du Colombier c->datafd = fd;
193563afb9a5SDavid du Colombier
193663afb9a5SDavid du Colombier /* exchange versions--we're only doing SSH2, unfortunately */
193763afb9a5SDavid du Colombier
193863afb9a5SDavid du Colombier snprint(path, sizeof path, "%s\r\n", MYID);
193963afb9a5SDavid du Colombier if (c->idstring && c->idstring[0])
194063afb9a5SDavid du Colombier strncpy(path, c->idstring, sizeof path);
194163afb9a5SDavid du Colombier else {
194263afb9a5SDavid du Colombier iowrite(io, fd, path, strlen(path));
194363afb9a5SDavid du Colombier p = path;
194463afb9a5SDavid du Colombier n = 0;
194563afb9a5SDavid du Colombier do {
194663afb9a5SDavid du Colombier if (ioread(io, fd, p, 1) < 0) {
194763afb9a5SDavid du Colombier fprint(2, "%s: short read in ID exchange: %r\n",
194863afb9a5SDavid du Colombier argv0);
194963afb9a5SDavid du Colombier break;
195063afb9a5SDavid du Colombier }
195163afb9a5SDavid du Colombier ++n;
195263afb9a5SDavid du Colombier } while (*p++ != '\n');
195363afb9a5SDavid du Colombier if (n < 5) { /* can't be a valid SSH id string */
195463afb9a5SDavid du Colombier close(fd);
195563afb9a5SDavid du Colombier goto err;
195663afb9a5SDavid du Colombier }
195763afb9a5SDavid du Colombier *p = 0;
195863afb9a5SDavid du Colombier }
195963afb9a5SDavid du Colombier sshdebug(c, "id string `%s'", path);
196063afb9a5SDavid du Colombier if (c->idstring[0] == '\0' &&
196163afb9a5SDavid du Colombier strncmp(path, "SSH-2", 5) != 0 &&
196263afb9a5SDavid du Colombier strncmp(path, "SSH-1.99", 8) != 0) {
196363afb9a5SDavid du Colombier /* not a protocol version we know; give up */
196463afb9a5SDavid du Colombier ioclose(io, fd);
196563afb9a5SDavid du Colombier goto err;
196663afb9a5SDavid du Colombier }
196763afb9a5SDavid du Colombier closeioproc(io);
196863afb9a5SDavid du Colombier
196963afb9a5SDavid du Colombier if (c->otherid)
197063afb9a5SDavid du Colombier free(c->otherid);
197163afb9a5SDavid du Colombier c->otherid = othid = estrdup9p(path);
197263afb9a5SDavid du Colombier for (n = strlen(othid) - 1; othid[n] == '\r' || othid[n] == '\n'; --n)
197363afb9a5SDavid du Colombier othid[n] = '\0';
197463afb9a5SDavid du Colombier c->state = Initting;
197563afb9a5SDavid du Colombier
197663afb9a5SDavid du Colombier /* start the reader thread */
197763afb9a5SDavid du Colombier if (c->rpid < 0)
197863afb9a5SDavid du Colombier c->rpid = threadcreate(reader, c, Defstk);
197963afb9a5SDavid du Colombier
198063afb9a5SDavid du Colombier /*
198163afb9a5SDavid du Colombier * negotiate the protocols
198263afb9a5SDavid du Colombier * We don't do the full negotiation here, because we also have
198363afb9a5SDavid du Colombier * to handle a re-negotiation request from the other end. So
198463afb9a5SDavid du Colombier * we just kick it off and let the receiver process take it from there.
198563afb9a5SDavid du Colombier */
198663afb9a5SDavid du Colombier
198763afb9a5SDavid du Colombier send_kexinit(c);
198863afb9a5SDavid du Colombier
198963afb9a5SDavid du Colombier qlock(&c->l);
199063afb9a5SDavid du Colombier if ((c->role == Client && c->state != Negotiating) ||
199163afb9a5SDavid du Colombier (c->role == Server && c->state != Established))
199263afb9a5SDavid du Colombier rsleep(&c->r);
199363afb9a5SDavid du Colombier qunlock(&c->l);
199463afb9a5SDavid du Colombier if (c->role == Server && c->state != Established ||
199563afb9a5SDavid du Colombier c->role == Client && c->state != Negotiating)
199663afb9a5SDavid du Colombier return -1;
199763afb9a5SDavid du Colombier return 0;
199863afb9a5SDavid du Colombier err:
199963afb9a5SDavid du Colombier /* should use hangup in dial(2) instead of diddling /net/tcp */
200063afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/ctl", mntpt, tcpconn);
200163afb9a5SDavid du Colombier fd = ioopen(io, path, OWRITE);
200263afb9a5SDavid du Colombier iowrite(io, fd, "hangup", 6);
200363afb9a5SDavid du Colombier ioclose(io, fd);
200463afb9a5SDavid du Colombier closeioproc(io);
200563afb9a5SDavid du Colombier return -1;
200663afb9a5SDavid du Colombier }
200763afb9a5SDavid du Colombier
200863afb9a5SDavid du Colombier void
send_kexinit(Conn * c)200963afb9a5SDavid du Colombier send_kexinit(Conn *c)
201063afb9a5SDavid du Colombier {
201163afb9a5SDavid du Colombier Packet *ptmp;
201263afb9a5SDavid du Colombier char *buf, *p, *e;
201363afb9a5SDavid du Colombier int i, msglen;
201463afb9a5SDavid du Colombier
201563afb9a5SDavid du Colombier sshdebug(c, "initializing kexinit packet");
201663afb9a5SDavid du Colombier if (c->skexinit != nil)
201763afb9a5SDavid du Colombier free(c->skexinit);
201863afb9a5SDavid du Colombier c->skexinit = new_packet(c);
201963afb9a5SDavid du Colombier
202063afb9a5SDavid du Colombier buf = emalloc9p(Bigbufsz);
202163afb9a5SDavid du Colombier buf[0] = (uchar)SSH_MSG_KEXINIT;
202263afb9a5SDavid du Colombier
202363afb9a5SDavid du Colombier add_packet(c->skexinit, buf, 1);
202463afb9a5SDavid du Colombier for (i = 0; i < 16; ++i)
202563afb9a5SDavid du Colombier buf[i] = fastrand();
202663afb9a5SDavid du Colombier
202763afb9a5SDavid du Colombier add_packet(c->skexinit, buf, 16); /* cookie */
202863afb9a5SDavid du Colombier e = buf + Bigbufsz - 1;
202963afb9a5SDavid du Colombier p = seprint(buf, e, "%s", kexes[0]->name);
203063afb9a5SDavid du Colombier for (i = 1; i < nelem(kexes); ++i)
203163afb9a5SDavid du Colombier p = seprint(p, e, ",%s", kexes[i]->name);
203263afb9a5SDavid du Colombier sshdebug(c, "sent KEX algs: %s", buf);
203363afb9a5SDavid du Colombier
203463afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* Key exchange */
203563afb9a5SDavid du Colombier if (pkas[0] == nil)
203663afb9a5SDavid du Colombier add_string(c->skexinit, "");
203763afb9a5SDavid du Colombier else{
203863afb9a5SDavid du Colombier p = seprint(buf, e, "%s", pkas[0]->name);
203963afb9a5SDavid du Colombier for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i)
204063afb9a5SDavid du Colombier p = seprint(p, e, ",%s", pkas[i]->name);
204163afb9a5SDavid du Colombier sshdebug(c, "sent host key algs: %s", buf);
204263afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* server's key algs */
204363afb9a5SDavid du Colombier }
204463afb9a5SDavid du Colombier
204563afb9a5SDavid du Colombier p = seprint(buf, e, "%s", cryptos[0]->name);
204663afb9a5SDavid du Colombier for (i = 1; i < nelem(cryptos); ++i)
204763afb9a5SDavid du Colombier p = seprint(p, e, ",%s", cryptos[i]->name);
204863afb9a5SDavid du Colombier sshdebug(c, "sent crypto algs: %s", buf);
204963afb9a5SDavid du Colombier
205063afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* c->s crypto */
205163afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* s->c crypto */
205263afb9a5SDavid du Colombier p = seprint(buf, e, "%s", macnames[0]);
205363afb9a5SDavid du Colombier for (i = 1; i < nelem(macnames); ++i)
205463afb9a5SDavid du Colombier p = seprint(p, e, ",%s", macnames[i]);
205563afb9a5SDavid du Colombier sshdebug(c, "sent MAC algs: %s", buf);
205663afb9a5SDavid du Colombier
205763afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* c->s mac */
205863afb9a5SDavid du Colombier add_string(c->skexinit, buf); /* s->c mac */
205963afb9a5SDavid du Colombier add_string(c->skexinit, "none"); /* c->s compression */
206063afb9a5SDavid du Colombier add_string(c->skexinit, "none"); /* s->c compression */
206163afb9a5SDavid du Colombier add_string(c->skexinit, ""); /* c->s languages */
206263afb9a5SDavid du Colombier add_string(c->skexinit, ""); /* s->c languages */
206363afb9a5SDavid du Colombier memset(buf, 0, 5);
206463afb9a5SDavid du Colombier add_packet(c->skexinit, buf, 5);
206563afb9a5SDavid du Colombier
206663afb9a5SDavid du Colombier ptmp = new_packet(c);
206763afb9a5SDavid du Colombier memmove(ptmp, c->skexinit, sizeof(Packet));
206863afb9a5SDavid du Colombier msglen = finish_packet(ptmp);
206963afb9a5SDavid du Colombier
207063afb9a5SDavid du Colombier if (c->dio && c->datafd >= 0)
207163afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, ptmp->nlength, msglen);
207263afb9a5SDavid du Colombier free(ptmp);
207363afb9a5SDavid du Colombier free(buf);
207463afb9a5SDavid du Colombier }
207563afb9a5SDavid du Colombier
207663afb9a5SDavid du Colombier static void
establish(Conn * c)207763afb9a5SDavid du Colombier establish(Conn *c)
207863afb9a5SDavid du Colombier {
207963afb9a5SDavid du Colombier qlock(&c->l);
208063afb9a5SDavid du Colombier c->state = Established;
208163afb9a5SDavid du Colombier rwakeup(&c->r);
208263afb9a5SDavid du Colombier qunlock(&c->l);
208363afb9a5SDavid du Colombier }
208463afb9a5SDavid du Colombier
208563afb9a5SDavid du Colombier static int
negotiating(Conn * c,Packet * p,Packet * p2,char * buf,int size)208663afb9a5SDavid du Colombier negotiating(Conn *c, Packet *p, Packet *p2, char *buf, int size)
208763afb9a5SDavid du Colombier {
208863afb9a5SDavid du Colombier int i, n;
208963afb9a5SDavid du Colombier
209063afb9a5SDavid du Colombier USED(size);
209163afb9a5SDavid du Colombier switch (p->payload[0]) {
209263afb9a5SDavid du Colombier case SSH_MSG_DISCONNECT:
209363afb9a5SDavid du Colombier if (debug) {
209463afb9a5SDavid du Colombier get_string(p, p->payload + 5, buf, Arbbufsz, nil);
209563afb9a5SDavid du Colombier sshdebug(c, "got disconnect: %s", buf);
209663afb9a5SDavid du Colombier }
209763afb9a5SDavid du Colombier return -1;
209863afb9a5SDavid du Colombier case SSH_MSG_NEWKEYS:
209963afb9a5SDavid du Colombier /*
210063afb9a5SDavid du Colombier * If we're just updating, go straight to
210163afb9a5SDavid du Colombier * established, otherwise wait for auth'n.
210263afb9a5SDavid du Colombier */
210363afb9a5SDavid du Colombier i = c->encrypt;
210463afb9a5SDavid du Colombier memmove(c->c2siv, c->nc2siv, SHA1dlen*2);
210563afb9a5SDavid du Colombier memmove(c->s2civ, c->ns2civ, SHA1dlen*2);
210663afb9a5SDavid du Colombier memmove(c->c2sek, c->nc2sek, SHA1dlen*2);
210763afb9a5SDavid du Colombier memmove(c->s2cek, c->ns2cek, SHA1dlen*2);
210863afb9a5SDavid du Colombier memmove(c->c2sik, c->nc2sik, SHA1dlen*2);
210963afb9a5SDavid du Colombier memmove(c->s2cik, c->ns2cik, SHA1dlen*2);
211063afb9a5SDavid du Colombier c->cscrypt = c->ncscrypt;
211163afb9a5SDavid du Colombier c->sccrypt = c->nsccrypt;
211263afb9a5SDavid du Colombier c->csmac = c->ncsmac;
211363afb9a5SDavid du Colombier c->scmac = c->nscmac;
211463afb9a5SDavid du Colombier c->c2scs = cryptos[c->cscrypt]->init(c, 0);
211563afb9a5SDavid du Colombier c->s2ccs = cryptos[c->sccrypt]->init(c, 1);
211663afb9a5SDavid du Colombier if (c->role == Server) {
211763afb9a5SDavid du Colombier c->encrypt = c->sccrypt;
211863afb9a5SDavid du Colombier c->decrypt = c->cscrypt;
211963afb9a5SDavid du Colombier c->outmac = c->scmac;
212063afb9a5SDavid du Colombier c->inmac = c->csmac;
212163afb9a5SDavid du Colombier c->enccs = c->s2ccs;
212263afb9a5SDavid du Colombier c->deccs = c->c2scs;
212363afb9a5SDavid du Colombier c->outik = c->s2cik;
212463afb9a5SDavid du Colombier c->inik = c->c2sik;
212563afb9a5SDavid du Colombier } else{
212663afb9a5SDavid du Colombier c->encrypt = c->cscrypt;
212763afb9a5SDavid du Colombier c->decrypt = c->sccrypt;
212863afb9a5SDavid du Colombier c->outmac = c->csmac;
212963afb9a5SDavid du Colombier c->inmac = c->scmac;
213063afb9a5SDavid du Colombier c->enccs = c->c2scs;
213163afb9a5SDavid du Colombier c->deccs = c->s2ccs;
213263afb9a5SDavid du Colombier c->outik = c->c2sik;
213363afb9a5SDavid du Colombier c->inik = c->s2cik;
213463afb9a5SDavid du Colombier }
213563afb9a5SDavid du Colombier sshdebug(c, "using %s for encryption and %s for decryption",
213663afb9a5SDavid du Colombier cryptos[c->encrypt]->name, cryptos[c->decrypt]->name);
213763afb9a5SDavid du Colombier qlock(&c->l);
213863afb9a5SDavid du Colombier if (i != -1)
213963afb9a5SDavid du Colombier c->state = Established;
214063afb9a5SDavid du Colombier if (c->role == Client)
214163afb9a5SDavid du Colombier rwakeup(&c->r);
214263afb9a5SDavid du Colombier qunlock(&c->l);
214363afb9a5SDavid du Colombier break;
214463afb9a5SDavid du Colombier case SSH_MSG_KEXDH_INIT:
214563afb9a5SDavid du Colombier kexes[c->kexalg]->serverkex(c, p);
214663afb9a5SDavid du Colombier break;
214763afb9a5SDavid du Colombier case SSH_MSG_KEXDH_REPLY:
214863afb9a5SDavid du Colombier init_packet(p2);
214963afb9a5SDavid du Colombier p2->c = c;
215063afb9a5SDavid du Colombier if (kexes[c->kexalg]->clientkex2(c, p) < 0) {
215163afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_DISCONNECT);
215263afb9a5SDavid du Colombier add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED);
215363afb9a5SDavid du Colombier add_string(p2, "Key exchange failure");
215463afb9a5SDavid du Colombier add_string(p2, "");
215563afb9a5SDavid du Colombier n = finish_packet(p2);
215663afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
215763afb9a5SDavid du Colombier shutdown(c);
215863afb9a5SDavid du Colombier free(p);
215963afb9a5SDavid du Colombier free(p2);
216063afb9a5SDavid du Colombier closeioproc(c->rio);
216163afb9a5SDavid du Colombier c->rio = nil;
216263afb9a5SDavid du Colombier c->rpid = -1;
216363afb9a5SDavid du Colombier
216463afb9a5SDavid du Colombier qlock(&c->l);
216563afb9a5SDavid du Colombier rwakeup(&c->r);
216663afb9a5SDavid du Colombier qunlock(&c->l);
216763afb9a5SDavid du Colombier
216863afb9a5SDavid du Colombier sshlog(c, "key exchange failure");
216963afb9a5SDavid du Colombier threadexits(nil);
217063afb9a5SDavid du Colombier }
217163afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_NEWKEYS);
217263afb9a5SDavid du Colombier n = finish_packet(p2);
217363afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
217463afb9a5SDavid du Colombier qlock(&c->l);
217563afb9a5SDavid du Colombier rwakeup(&c->r);
217663afb9a5SDavid du Colombier qunlock(&c->l);
217763afb9a5SDavid du Colombier break;
217863afb9a5SDavid du Colombier case SSH_MSG_SERVICE_REQUEST:
217963afb9a5SDavid du Colombier get_string(p, p->payload + 1, buf, Arbbufsz, nil);
218063afb9a5SDavid du Colombier sshdebug(c, "got service request: %s", buf);
218163afb9a5SDavid du Colombier if (strcmp(buf, "ssh-userauth") == 0 ||
218263afb9a5SDavid du Colombier strcmp(buf, "ssh-connection") == 0) {
218363afb9a5SDavid du Colombier init_packet(p2);
218463afb9a5SDavid du Colombier p2->c = c;
218563afb9a5SDavid du Colombier sshdebug(c, "connection");
218663afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_SERVICE_ACCEPT);
218763afb9a5SDavid du Colombier add_string(p2, buf);
218863afb9a5SDavid du Colombier n = finish_packet(p2);
218963afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
219063afb9a5SDavid du Colombier c->state = Authing;
219163afb9a5SDavid du Colombier } else{
219263afb9a5SDavid du Colombier init_packet(p2);
219363afb9a5SDavid du Colombier p2->c = c;
219463afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_DISCONNECT);
219563afb9a5SDavid du Colombier add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE);
219663afb9a5SDavid du Colombier add_string(p2, "Unknown service type");
219763afb9a5SDavid du Colombier add_string(p2, "");
219863afb9a5SDavid du Colombier n = finish_packet(p2);
219963afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
220063afb9a5SDavid du Colombier return -1;
220163afb9a5SDavid du Colombier }
220263afb9a5SDavid du Colombier break;
220363afb9a5SDavid du Colombier case SSH_MSG_SERVICE_ACCEPT:
220463afb9a5SDavid du Colombier get_string(p, p->payload + 1, buf, Arbbufsz, nil);
220563afb9a5SDavid du Colombier if (c->service && strcmp(c->service, "ssh-userauth") == 0) {
220663afb9a5SDavid du Colombier free(c->service);
220763afb9a5SDavid du Colombier c->service = estrdup9p("ssh-connection");
220863afb9a5SDavid du Colombier }
220963afb9a5SDavid du Colombier sshdebug(c, "got service accept: %s: responding with %s %s",
221063afb9a5SDavid du Colombier buf, c->user, c->service);
221163afb9a5SDavid du Colombier n = client_auth(c, c->rio);
221263afb9a5SDavid du Colombier c->state = Authing;
221363afb9a5SDavid du Colombier if (n < 0) {
221463afb9a5SDavid du Colombier qlock(&c->l);
221563afb9a5SDavid du Colombier rwakeup(&c->r);
221663afb9a5SDavid du Colombier qunlock(&c->l);
221763afb9a5SDavid du Colombier }
221863afb9a5SDavid du Colombier break;
221963afb9a5SDavid du Colombier }
222063afb9a5SDavid du Colombier return 0;
222163afb9a5SDavid du Colombier }
222263afb9a5SDavid du Colombier
222363afb9a5SDavid du Colombier static void
nochans(Conn * c,Packet * p,Packet * p2)222463afb9a5SDavid du Colombier nochans(Conn *c, Packet *p, Packet *p2)
222563afb9a5SDavid du Colombier {
222663afb9a5SDavid du Colombier int n;
222763afb9a5SDavid du Colombier
222863afb9a5SDavid du Colombier init_packet(p2);
222963afb9a5SDavid du Colombier p2->c = c;
223063afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE);
223163afb9a5SDavid du Colombier add_block(p2, p->payload + 5, 4);
223263afb9a5SDavid du Colombier hnputl(p2->payload + p2->rlength - 1, 4);
223363afb9a5SDavid du Colombier p2->rlength += 4;
223463afb9a5SDavid du Colombier add_string(p2, "No available channels");
223563afb9a5SDavid du Colombier add_string(p2, "EN");
223663afb9a5SDavid du Colombier n = finish_packet(p2);
223763afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
223863afb9a5SDavid du Colombier }
223963afb9a5SDavid du Colombier
224063afb9a5SDavid du Colombier static int
established(Conn * c,Packet * p,Packet * p2,char * buf,int size)224163afb9a5SDavid du Colombier established(Conn *c, Packet *p, Packet *p2, char *buf, int size)
224263afb9a5SDavid du Colombier {
224363afb9a5SDavid du Colombier int i, n, cnum;
224463afb9a5SDavid du Colombier uchar *q;
224563afb9a5SDavid du Colombier Plist *pl;
224663afb9a5SDavid du Colombier SSHChan *ch;
224763afb9a5SDavid du Colombier
224863afb9a5SDavid du Colombier USED(size);
224963afb9a5SDavid du Colombier if (debug > 1) {
225063afb9a5SDavid du Colombier sshdebug(c, "in Established state, got:");
225163afb9a5SDavid du Colombier dump_packet(p);
225263afb9a5SDavid du Colombier }
225363afb9a5SDavid du Colombier switch (p->payload[0]) {
225463afb9a5SDavid du Colombier case SSH_MSG_DISCONNECT:
225563afb9a5SDavid du Colombier if (debug) {
225663afb9a5SDavid du Colombier get_string(p, p->payload + 5, buf, Arbbufsz, nil);
225763afb9a5SDavid du Colombier sshdebug(c, "got disconnect: %s", buf);
225863afb9a5SDavid du Colombier }
225963afb9a5SDavid du Colombier return -1;
226063afb9a5SDavid du Colombier case SSH_MSG_IGNORE:
226163afb9a5SDavid du Colombier case SSH_MSG_UNIMPLEMENTED:
226263afb9a5SDavid du Colombier break;
226363afb9a5SDavid du Colombier case SSH_MSG_DEBUG:
226463afb9a5SDavid du Colombier if (debug || p->payload[1]) {
226563afb9a5SDavid du Colombier get_string(p, p->payload + 2, buf, Arbbufsz, nil);
226663afb9a5SDavid du Colombier sshdebug(c, "got debug message: %s", buf);
226763afb9a5SDavid du Colombier }
226863afb9a5SDavid du Colombier break;
226963afb9a5SDavid du Colombier case SSH_MSG_KEXINIT:
227063afb9a5SDavid du Colombier send_kexinit(c);
227163afb9a5SDavid du Colombier if (c->rkexinit)
227263afb9a5SDavid du Colombier free(c->rkexinit);
227363afb9a5SDavid du Colombier c->rkexinit = new_packet(c);
227463afb9a5SDavid du Colombier memmove(c->rkexinit, p, sizeof(Packet));
227563afb9a5SDavid du Colombier if (validatekex(c, p) < 0) {
227663afb9a5SDavid du Colombier sshdebug(c, "kex crypto algorithm mismatch (Established)");
227763afb9a5SDavid du Colombier return -1;
227863afb9a5SDavid du Colombier }
227963afb9a5SDavid du Colombier sshdebug(c, "using %s Kex algorithm and %s PKA",
228063afb9a5SDavid du Colombier kexes[c->kexalg]->name, pkas[c->pkalg]->name);
228163afb9a5SDavid du Colombier c->state = Negotiating;
228263afb9a5SDavid du Colombier break;
228363afb9a5SDavid du Colombier case SSH_MSG_GLOBAL_REQUEST:
228463afb9a5SDavid du Colombier case SSH_MSG_REQUEST_SUCCESS:
228563afb9a5SDavid du Colombier case SSH_MSG_REQUEST_FAILURE:
228663afb9a5SDavid du Colombier break;
228763afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_OPEN:
228863afb9a5SDavid du Colombier q = get_string(p, p->payload + 1, buf, Arbbufsz, nil);
228963afb9a5SDavid du Colombier sshdebug(c, "searching for a listener for channel type %s", buf);
229063afb9a5SDavid du Colombier ch = alloc_chan(c);
229163afb9a5SDavid du Colombier if (ch == nil) {
229263afb9a5SDavid du Colombier nochans(c, p, p2);
229363afb9a5SDavid du Colombier break;
229463afb9a5SDavid du Colombier }
229563afb9a5SDavid du Colombier
229663afb9a5SDavid du Colombier sshdebug(c, "alloced channel %d for listener", ch->id);
229763afb9a5SDavid du Colombier qlock(&c->l);
229863afb9a5SDavid du Colombier ch->otherid = nhgetl(q);
229963afb9a5SDavid du Colombier ch->twindow = nhgetl(q+4);
230063afb9a5SDavid du Colombier sshdebug(c, "got lock in channel open");
230163afb9a5SDavid du Colombier for (i = 0; i < c->nchan; ++i)
230263afb9a5SDavid du Colombier if (c->chans[i] && c->chans[i]->state == Listening &&
230363afb9a5SDavid du Colombier c->chans[i]->ann &&
230463afb9a5SDavid du Colombier strcmp(c->chans[i]->ann, buf) == 0)
230563afb9a5SDavid du Colombier break;
230663afb9a5SDavid du Colombier if (i >= c->nchan) {
230763afb9a5SDavid du Colombier sshdebug(c, "no listener: sleeping");
230863afb9a5SDavid du Colombier ch->state = Opening;
230963afb9a5SDavid du Colombier if (ch->ann)
231063afb9a5SDavid du Colombier free(ch->ann);
231163afb9a5SDavid du Colombier ch->ann = estrdup9p(buf);
231263afb9a5SDavid du Colombier sshdebug(c, "waiting for someone to announce %s", ch->ann);
231363afb9a5SDavid du Colombier rsleep(&ch->r);
231463afb9a5SDavid du Colombier } else{
231563afb9a5SDavid du Colombier sshdebug(c, "found listener on channel %d", ch->id);
231663afb9a5SDavid du Colombier c->chans[i]->waker = ch->id;
231763afb9a5SDavid du Colombier rwakeup(&c->chans[i]->r);
231863afb9a5SDavid du Colombier }
231963afb9a5SDavid du Colombier qunlock(&c->l);
232063afb9a5SDavid du Colombier break;
232163afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
232263afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
232363afb9a5SDavid du Colombier ch = c->chans[cnum];
232463afb9a5SDavid du Colombier qlock(&c->l);
232563afb9a5SDavid du Colombier ch->otherid = nhgetl(p->payload+5);
232663afb9a5SDavid du Colombier ch->twindow = nhgetl(p->payload+9);
232763afb9a5SDavid du Colombier rwakeup(&ch->r);
232863afb9a5SDavid du Colombier qunlock(&c->l);
232963afb9a5SDavid du Colombier break;
233063afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_OPEN_FAILURE:
233163afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
233263afb9a5SDavid du Colombier ch = c->chans[cnum];
233363afb9a5SDavid du Colombier qlock(&c->l);
233463afb9a5SDavid du Colombier rwakeup(&ch->r);
233563afb9a5SDavid du Colombier qunlock(&c->l);
233663afb9a5SDavid du Colombier return -1;
233763afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_WINDOW_ADJUST:
233863afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
233963afb9a5SDavid du Colombier ch = c->chans[cnum];
234063afb9a5SDavid du Colombier ch->twindow += nhgetl(p->payload + 5);
234163afb9a5SDavid du Colombier sshdebug(c, "new twindow for channel: %d: %lud", cnum, ch->twindow);
234263afb9a5SDavid du Colombier qlock(&ch->xmtlock);
234363afb9a5SDavid du Colombier rwakeup(&ch->xmtrendez);
234463afb9a5SDavid du Colombier qunlock(&ch->xmtlock);
234563afb9a5SDavid du Colombier break;
234663afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_DATA:
234763afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_EXTENDED_DATA:
234863afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
234963afb9a5SDavid du Colombier ch = c->chans[cnum];
235063afb9a5SDavid du Colombier pl = emalloc9p(sizeof(Plist));
235163afb9a5SDavid du Colombier pl->pack = emalloc9p(sizeof(Packet));
235263afb9a5SDavid du Colombier memmove(pl->pack, p, sizeof(Packet));
235363afb9a5SDavid du Colombier if (p->payload[0] == SSH_MSG_CHANNEL_DATA) {
235463afb9a5SDavid du Colombier pl->rem = nhgetl(p->payload + 5);
235563afb9a5SDavid du Colombier pl->st = pl->pack->payload + 9;
235663afb9a5SDavid du Colombier } else {
235763afb9a5SDavid du Colombier pl->rem = nhgetl(p->payload + 9);
235863afb9a5SDavid du Colombier pl->st = pl->pack->payload + 13;
235963afb9a5SDavid du Colombier }
236063afb9a5SDavid du Colombier pl->next = nil;
236163afb9a5SDavid du Colombier if (ch->dataq == nil)
236263afb9a5SDavid du Colombier ch->dataq = pl;
236363afb9a5SDavid du Colombier else
236463afb9a5SDavid du Colombier ch->datatl->next = pl;
236563afb9a5SDavid du Colombier ch->datatl = pl;
236663afb9a5SDavid du Colombier ch->inrqueue += pl->rem;
236763afb9a5SDavid du Colombier nbsendul(ch->inchan, 1);
236863afb9a5SDavid du Colombier break;
236963afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_EOF:
237063afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
237163afb9a5SDavid du Colombier ch = c->chans[cnum];
237263afb9a5SDavid du Colombier if (ch->state != Closed && ch->state != Closing) {
237363afb9a5SDavid du Colombier ch->state = Eof;
237463afb9a5SDavid du Colombier nbsendul(ch->inchan, 1);
237563afb9a5SDavid du Colombier nbsendul(ch->reqchan, 1);
237663afb9a5SDavid du Colombier }
237763afb9a5SDavid du Colombier break;
237863afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_CLOSE:
237963afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
238063afb9a5SDavid du Colombier ch = c->chans[cnum];
238163afb9a5SDavid du Colombier if (ch->state != Closed && ch->state != Closing) {
238263afb9a5SDavid du Colombier init_packet(p2);
238363afb9a5SDavid du Colombier p2->c = c;
238463afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_CHANNEL_CLOSE);
238563afb9a5SDavid du Colombier hnputl(p2->payload + 1, ch->otherid);
238663afb9a5SDavid du Colombier p2->rlength += 4;
238763afb9a5SDavid du Colombier n = finish_packet(p2);
238863afb9a5SDavid du Colombier iowrite(c->rio, c->datafd, p2->nlength, n);
238963afb9a5SDavid du Colombier }
239063afb9a5SDavid du Colombier qlock(&c->l);
239163afb9a5SDavid du Colombier if (ch->state != Closed) {
239263afb9a5SDavid du Colombier ch->state = Closed;
239363afb9a5SDavid du Colombier rwakeup(&ch->r);
239463afb9a5SDavid du Colombier nbsendul(ch->inchan, 1);
239563afb9a5SDavid du Colombier nbsendul(ch->reqchan, 1);
239663afb9a5SDavid du Colombier chanclose(ch->inchan);
239763afb9a5SDavid du Colombier chanclose(ch->reqchan);
239863afb9a5SDavid du Colombier }
239963afb9a5SDavid du Colombier qunlock(&c->l);
240063afb9a5SDavid du Colombier for (i = 0; i < MAXCONN && (!c->chans[i] ||
240163afb9a5SDavid du Colombier c->chans[i]->state == Empty || c->chans[i]->state == Closed);
240263afb9a5SDavid du Colombier ++i)
240363afb9a5SDavid du Colombier ;
240463afb9a5SDavid du Colombier if (i >= MAXCONN)
240563afb9a5SDavid du Colombier return -1;
240663afb9a5SDavid du Colombier break;
240763afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_REQUEST:
240863afb9a5SDavid du Colombier cnum = nhgetl(p->payload + 1);
240963afb9a5SDavid du Colombier ch = c->chans[cnum];
241063afb9a5SDavid du Colombier sshdebug(c, "queueing channel request for channel: %d", cnum);
241163afb9a5SDavid du Colombier q = get_string(p, p->payload+5, buf, Arbbufsz, nil);
241263afb9a5SDavid du Colombier pl = emalloc9p(sizeof(Plist));
241363afb9a5SDavid du Colombier pl->pack = emalloc9p(sizeof(Packet));
241463afb9a5SDavid du Colombier n = snprint((char *)pl->pack->payload,
241563afb9a5SDavid du Colombier Maxpayload, "%s %c", buf, *q? 't': 'f');
241663afb9a5SDavid du Colombier sshdebug(c, "request message begins: %s",
241763afb9a5SDavid du Colombier (char *)pl->pack->payload);
2418d7e44d0dSDavid du Colombier memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + n-2));
241963afb9a5SDavid du Colombier pl->rem = p->rlength - 11 + 2;
242063afb9a5SDavid du Colombier pl->st = pl->pack->payload;
242163afb9a5SDavid du Colombier pl->next = nil;
242263afb9a5SDavid du Colombier if (ch->reqq == nil)
242363afb9a5SDavid du Colombier ch->reqq = pl;
242463afb9a5SDavid du Colombier else
242563afb9a5SDavid du Colombier ch->reqtl->next = pl;
242663afb9a5SDavid du Colombier ch->reqtl = pl;
242763afb9a5SDavid du Colombier nbsendul(ch->reqchan, 1);
242863afb9a5SDavid du Colombier break;
242963afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_SUCCESS:
243063afb9a5SDavid du Colombier case SSH_MSG_CHANNEL_FAILURE:
243163afb9a5SDavid du Colombier default:
243263afb9a5SDavid du Colombier break;
243363afb9a5SDavid du Colombier }
243463afb9a5SDavid du Colombier return 0;
243563afb9a5SDavid du Colombier }
243663afb9a5SDavid du Colombier
243763afb9a5SDavid du Colombier static void
bail(Conn * c,Packet * p,Packet * p2,char * sts)243863afb9a5SDavid du Colombier bail(Conn *c, Packet *p, Packet *p2, char *sts)
243963afb9a5SDavid du Colombier {
244063afb9a5SDavid du Colombier shutdown(c);
244163afb9a5SDavid du Colombier free(p);
244263afb9a5SDavid du Colombier free(p2);
244363afb9a5SDavid du Colombier if (c->rio) {
244463afb9a5SDavid du Colombier closeioproc(c->rio);
244563afb9a5SDavid du Colombier c->rio = nil;
244663afb9a5SDavid du Colombier }
244763afb9a5SDavid du Colombier c->rpid = -1;
244863afb9a5SDavid du Colombier threadexits(sts);
244963afb9a5SDavid du Colombier }
245063afb9a5SDavid du Colombier
245163afb9a5SDavid du Colombier static void
reader0(Conn * c,Packet * p,Packet * p2)245263afb9a5SDavid du Colombier reader0(Conn *c, Packet *p, Packet *p2)
245363afb9a5SDavid du Colombier {
245463afb9a5SDavid du Colombier int i, n, nl, np, nm, nb;
245563afb9a5SDavid du Colombier char buf[Arbbufsz];
245663afb9a5SDavid du Colombier
245763afb9a5SDavid du Colombier nm = 0;
245863afb9a5SDavid du Colombier nb = 4;
245963afb9a5SDavid du Colombier if (c->decrypt != -1)
246063afb9a5SDavid du Colombier nb = cryptos[c->decrypt]->blklen;
246163afb9a5SDavid du Colombier sshdebug(c, "calling read for connection %d, state %d, nb %d, dc %d",
246263afb9a5SDavid du Colombier c->id, c->state, nb, c->decrypt);
246363afb9a5SDavid du Colombier if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) {
246463afb9a5SDavid du Colombier sshdebug(c, "reader for connection %d exiting, got %d: %r",
246563afb9a5SDavid du Colombier c->id, nl);
246663afb9a5SDavid du Colombier bail(c, p, p2, "reader exiting");
246763afb9a5SDavid du Colombier }
246863afb9a5SDavid du Colombier if (c->decrypt != -1)
246963afb9a5SDavid du Colombier cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb);
247063afb9a5SDavid du Colombier p->rlength = nhgetl(p->nlength);
247163afb9a5SDavid du Colombier sshdebug(c, "got message length: %ld", p->rlength);
247263afb9a5SDavid du Colombier if (p->rlength > Maxpktpay) {
247363afb9a5SDavid du Colombier sshdebug(c, "absurd packet length: %ld, unrecoverable decrypt failure",
247463afb9a5SDavid du Colombier p->rlength);
247563afb9a5SDavid du Colombier bail(c, p, p2, "absurd packet length");
247663afb9a5SDavid du Colombier }
247763afb9a5SDavid du Colombier np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb);
247863afb9a5SDavid du Colombier if (c->inmac != -1)
247963afb9a5SDavid du Colombier nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4,
248063afb9a5SDavid du Colombier SHA1dlen); /* SHA1dlen was magic 20 */
248163afb9a5SDavid du Colombier n = nl + np + nm;
248263afb9a5SDavid du Colombier if (debug) {
248363afb9a5SDavid du Colombier sshdebug(c, "got message of %d bytes %d padding", n, p->pad_len);
248463afb9a5SDavid du Colombier if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) {
248563afb9a5SDavid du Colombier i = nhgetl(p->payload+1);
248663afb9a5SDavid du Colombier if (c->chans[i])
248763afb9a5SDavid du Colombier sshdebug(c, " for channel %d win %lud",
248863afb9a5SDavid du Colombier i, c->chans[i]->rwindow);
248963afb9a5SDavid du Colombier else
249063afb9a5SDavid du Colombier sshdebug(c, " for invalid channel %d", i);
249163afb9a5SDavid du Colombier }
249263afb9a5SDavid du Colombier sshdebug(c, " first byte: %d", p->payload[0]);
249363afb9a5SDavid du Colombier }
249463afb9a5SDavid du Colombier /* SHA1dlen was magic 20 */
249563afb9a5SDavid du Colombier if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) {
249663afb9a5SDavid du Colombier sshdebug(c, "got EOF/error on connection read: %d %d %r", np, nm);
249763afb9a5SDavid du Colombier bail(c, p, p2, "error or eof");
249863afb9a5SDavid du Colombier }
249963afb9a5SDavid du Colombier p->tlength = n;
250063afb9a5SDavid du Colombier p->rlength = n - 4;
250163afb9a5SDavid du Colombier if (undo_packet(p) < 0) {
250263afb9a5SDavid du Colombier sshdebug(c, "bad packet in connection %d: exiting", c->id);
250363afb9a5SDavid du Colombier bail(c, p, p2, "bad packet");
250463afb9a5SDavid du Colombier }
250563afb9a5SDavid du Colombier
250663afb9a5SDavid du Colombier if (c->state == Initting) {
250763afb9a5SDavid du Colombier if (p->payload[0] != SSH_MSG_KEXINIT) {
250863afb9a5SDavid du Colombier sshdebug(c, "missing KEX init packet: %d", p->payload[0]);
250963afb9a5SDavid du Colombier bail(c, p, p2, "bad kex");
251063afb9a5SDavid du Colombier }
251163afb9a5SDavid du Colombier if (c->rkexinit)
251263afb9a5SDavid du Colombier free(c->rkexinit);
251363afb9a5SDavid du Colombier c->rkexinit = new_packet(c);
251463afb9a5SDavid du Colombier memmove(c->rkexinit, p, sizeof(Packet));
251563afb9a5SDavid du Colombier if (validatekex(c, p) < 0) {
251663afb9a5SDavid du Colombier sshdebug(c, "kex crypto algorithm mismatch (Initting)");
251763afb9a5SDavid du Colombier bail(c, p, p2, "bad kex");
251863afb9a5SDavid du Colombier }
251963afb9a5SDavid du Colombier sshdebug(c, "using %s Kex algorithm and %s PKA",
252063afb9a5SDavid du Colombier kexes[c->kexalg]->name, pkas[c->pkalg]->name);
252163afb9a5SDavid du Colombier if (c->role == Client)
252263afb9a5SDavid du Colombier kexes[c->kexalg]->clientkex1(c, p);
252363afb9a5SDavid du Colombier c->state = Negotiating;
252463afb9a5SDavid du Colombier } else if (c->state == Negotiating) {
252563afb9a5SDavid du Colombier if (negotiating(c, p, p2, buf, sizeof buf) < 0)
252663afb9a5SDavid du Colombier bail(c, p, p2, "negotiating");
252763afb9a5SDavid du Colombier } else if (c->state == Authing) {
252863afb9a5SDavid du Colombier switch (p->payload[0]) {
252963afb9a5SDavid du Colombier case SSH_MSG_DISCONNECT:
253063afb9a5SDavid du Colombier if (debug) {
253163afb9a5SDavid du Colombier get_string(p, p->payload + 5, buf, Arbbufsz, nil);
253263afb9a5SDavid du Colombier sshdebug(c, "got disconnect: %s", buf);
253363afb9a5SDavid du Colombier }
253463afb9a5SDavid du Colombier bail(c, p, p2, "msg disconnect");
253563afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_REQUEST:
253663afb9a5SDavid du Colombier switch (auth_req(p, c)) {
2537*48207d97SDavid du Colombier case 0: /* success */
253863afb9a5SDavid du Colombier establish(c);
253963afb9a5SDavid du Colombier break;
2540*48207d97SDavid du Colombier case 1: /* ok to try again */
2541*48207d97SDavid du Colombier case -1: /* failure */
254263afb9a5SDavid du Colombier break;
2543*48207d97SDavid du Colombier case -2: /* can't happen, now at least */
254463afb9a5SDavid du Colombier bail(c, p, p2, "in userauth request");
254563afb9a5SDavid du Colombier }
254663afb9a5SDavid du Colombier break;
254763afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_FAILURE:
254863afb9a5SDavid du Colombier qlock(&c->l);
254963afb9a5SDavid du Colombier rwakeup(&c->r);
255063afb9a5SDavid du Colombier qunlock(&c->l);
255163afb9a5SDavid du Colombier break;
255263afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_SUCCESS:
255363afb9a5SDavid du Colombier establish(c);
255463afb9a5SDavid du Colombier break;
255563afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_BANNER:
255663afb9a5SDavid du Colombier break;
255763afb9a5SDavid du Colombier }
255863afb9a5SDavid du Colombier } else if (c->state == Established) {
255963afb9a5SDavid du Colombier if (established(c, p, p2, buf, sizeof buf) < 0)
256063afb9a5SDavid du Colombier bail(c, p, p2, "from established state");
256163afb9a5SDavid du Colombier } else {
256263afb9a5SDavid du Colombier sshdebug(c, "connection %d in bad state, reader exiting", c->id);
256363afb9a5SDavid du Colombier bail(c, p, p2, "bad conn state");
256463afb9a5SDavid du Colombier }
256563afb9a5SDavid du Colombier }
256663afb9a5SDavid du Colombier
256763afb9a5SDavid du Colombier void
reader(void * a)256863afb9a5SDavid du Colombier reader(void *a)
256963afb9a5SDavid du Colombier {
257063afb9a5SDavid du Colombier Conn *c;
257163afb9a5SDavid du Colombier Packet *p, *p2;
257263afb9a5SDavid du Colombier
257363afb9a5SDavid du Colombier threadsetname("reader");
257463afb9a5SDavid du Colombier c = a;
257563afb9a5SDavid du Colombier c->rpid = threadid();
257663afb9a5SDavid du Colombier sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid);
257763afb9a5SDavid du Colombier threadsetname("reader");
257863afb9a5SDavid du Colombier p = new_packet(c);
257963afb9a5SDavid du Colombier p2 = new_packet(c);
258063afb9a5SDavid du Colombier c->rio = ioproc();
258163afb9a5SDavid du Colombier for(;;)
258263afb9a5SDavid du Colombier reader0(c, p, p2);
258363afb9a5SDavid du Colombier }
258463afb9a5SDavid du Colombier
258563afb9a5SDavid du Colombier int
validatekex(Conn * c,Packet * p)258663afb9a5SDavid du Colombier validatekex(Conn *c, Packet *p)
258763afb9a5SDavid du Colombier {
258863afb9a5SDavid du Colombier if (c->role == Server)
258963afb9a5SDavid du Colombier return validatekexs(p);
259063afb9a5SDavid du Colombier else
259163afb9a5SDavid du Colombier return validatekexc(p);
259263afb9a5SDavid du Colombier }
259363afb9a5SDavid du Colombier
259463afb9a5SDavid du Colombier int
validatekexs(Packet * p)259563afb9a5SDavid du Colombier validatekexs(Packet *p)
259663afb9a5SDavid du Colombier {
259763afb9a5SDavid du Colombier uchar *q;
259863afb9a5SDavid du Colombier char *toks[Maxtoks];
259963afb9a5SDavid du Colombier int i, j, n;
260063afb9a5SDavid du Colombier char *buf;
260163afb9a5SDavid du Colombier
260263afb9a5SDavid du Colombier buf = emalloc9p(Bigbufsz);
260363afb9a5SDavid du Colombier q = p->payload + 17;
260463afb9a5SDavid du Colombier
260563afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
260663afb9a5SDavid du Colombier sshdebug(nil, "received KEX algs: %s", buf);
260763afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
260863afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
260963afb9a5SDavid du Colombier for (j = 0; j < nelem(kexes); ++j)
261063afb9a5SDavid du Colombier if (strcmp(toks[i], kexes[j]->name) == 0)
261163afb9a5SDavid du Colombier goto foundk;
261263afb9a5SDavid du Colombier sshdebug(nil, "kex algs not in kexes");
261363afb9a5SDavid du Colombier free(buf);
261463afb9a5SDavid du Colombier return -1;
261563afb9a5SDavid du Colombier foundk:
261663afb9a5SDavid du Colombier p->c->kexalg = j;
261763afb9a5SDavid du Colombier
261863afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
261963afb9a5SDavid du Colombier sshdebug(nil, "received host key algs: %s", buf);
262063afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
262163afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
262263afb9a5SDavid du Colombier for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
262363afb9a5SDavid du Colombier if (strcmp(toks[i], pkas[j]->name) == 0)
262463afb9a5SDavid du Colombier goto foundpka;
262563afb9a5SDavid du Colombier sshdebug(nil, "host key algs not in pkas");
262663afb9a5SDavid du Colombier free(buf);
262763afb9a5SDavid du Colombier return -1;
262863afb9a5SDavid du Colombier foundpka:
262963afb9a5SDavid du Colombier p->c->pkalg = j;
263063afb9a5SDavid du Colombier
263163afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
263263afb9a5SDavid du Colombier sshdebug(nil, "received C2S crypto algs: %s", buf);
263363afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
263463afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
263563afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j)
263663afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0)
263763afb9a5SDavid du Colombier goto foundc1;
263863afb9a5SDavid du Colombier sshdebug(nil, "c2s crypto algs not in cryptos");
263963afb9a5SDavid du Colombier free(buf);
264063afb9a5SDavid du Colombier return -1;
264163afb9a5SDavid du Colombier foundc1:
264263afb9a5SDavid du Colombier p->c->ncscrypt = j;
264363afb9a5SDavid du Colombier
264463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
264563afb9a5SDavid du Colombier sshdebug(nil, "received S2C crypto algs: %s", buf);
264663afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
264763afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
264863afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j)
264963afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0)
265063afb9a5SDavid du Colombier goto foundc2;
265163afb9a5SDavid du Colombier sshdebug(nil, "s2c crypto algs not in cryptos");
265263afb9a5SDavid du Colombier free(buf);
265363afb9a5SDavid du Colombier return -1;
265463afb9a5SDavid du Colombier foundc2:
265563afb9a5SDavid du Colombier p->c->nsccrypt = j;
265663afb9a5SDavid du Colombier
265763afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
265863afb9a5SDavid du Colombier sshdebug(nil, "received C2S MAC algs: %s", buf);
265963afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
266063afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
266163afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j)
266263afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0)
266363afb9a5SDavid du Colombier goto foundm1;
266463afb9a5SDavid du Colombier sshdebug(nil, "c2s mac algs not in cryptos");
266563afb9a5SDavid du Colombier free(buf);
266663afb9a5SDavid du Colombier return -1;
266763afb9a5SDavid du Colombier foundm1:
266863afb9a5SDavid du Colombier p->c->ncsmac = j;
266963afb9a5SDavid du Colombier
267063afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
267163afb9a5SDavid du Colombier sshdebug(nil, "received S2C MAC algs: %s", buf);
267263afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
267363afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
267463afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j)
267563afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0)
267663afb9a5SDavid du Colombier goto foundm2;
267763afb9a5SDavid du Colombier sshdebug(nil, "s2c mac algs not in cryptos");
267863afb9a5SDavid du Colombier free(buf);
267963afb9a5SDavid du Colombier return -1;
268063afb9a5SDavid du Colombier foundm2:
268163afb9a5SDavid du Colombier p->c->nscmac = j;
268263afb9a5SDavid du Colombier
268363afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
268463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
268563afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
268663afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
268763afb9a5SDavid du Colombier free(buf);
268863afb9a5SDavid du Colombier if (*q)
268963afb9a5SDavid du Colombier return 1;
269063afb9a5SDavid du Colombier return 0;
269163afb9a5SDavid du Colombier }
269263afb9a5SDavid du Colombier
269363afb9a5SDavid du Colombier int
validatekexc(Packet * p)269463afb9a5SDavid du Colombier validatekexc(Packet *p)
269563afb9a5SDavid du Colombier {
269663afb9a5SDavid du Colombier uchar *q;
269763afb9a5SDavid du Colombier char *toks[Maxtoks];
269863afb9a5SDavid du Colombier int i, j, n;
269963afb9a5SDavid du Colombier char *buf;
270063afb9a5SDavid du Colombier
270163afb9a5SDavid du Colombier buf = emalloc9p(Bigbufsz);
270263afb9a5SDavid du Colombier q = p->payload + 17;
270363afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
270463afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
270563afb9a5SDavid du Colombier for (j = 0; j < nelem(kexes); ++j)
270663afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
270763afb9a5SDavid du Colombier if (strcmp(toks[i], kexes[j]->name) == 0)
270863afb9a5SDavid du Colombier goto foundk;
270963afb9a5SDavid du Colombier free(buf);
271063afb9a5SDavid du Colombier return -1;
271163afb9a5SDavid du Colombier foundk:
271263afb9a5SDavid du Colombier p->c->kexalg = j;
271363afb9a5SDavid du Colombier
271463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
271563afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
271663afb9a5SDavid du Colombier for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
271763afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
271863afb9a5SDavid du Colombier if (strcmp(toks[i], pkas[j]->name) == 0)
271963afb9a5SDavid du Colombier goto foundpka;
272063afb9a5SDavid du Colombier free(buf);
272163afb9a5SDavid du Colombier return -1;
272263afb9a5SDavid du Colombier foundpka:
272363afb9a5SDavid du Colombier p->c->pkalg = j;
272463afb9a5SDavid du Colombier
272563afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
272663afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
272763afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j)
272863afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
272963afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0)
273063afb9a5SDavid du Colombier goto foundc1;
273163afb9a5SDavid du Colombier free(buf);
273263afb9a5SDavid du Colombier return -1;
273363afb9a5SDavid du Colombier foundc1:
273463afb9a5SDavid du Colombier p->c->ncscrypt = j;
273563afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
273663afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
273763afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j)
273863afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
273963afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0)
274063afb9a5SDavid du Colombier goto foundc2;
274163afb9a5SDavid du Colombier free(buf);
274263afb9a5SDavid du Colombier return -1;
274363afb9a5SDavid du Colombier foundc2:
274463afb9a5SDavid du Colombier p->c->nsccrypt = j;
274563afb9a5SDavid du Colombier
274663afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
274763afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
274863afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j)
274963afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
275063afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0)
275163afb9a5SDavid du Colombier goto foundm1;
275263afb9a5SDavid du Colombier free(buf);
275363afb9a5SDavid du Colombier return -1;
275463afb9a5SDavid du Colombier foundm1:
275563afb9a5SDavid du Colombier p->c->ncsmac = j;
275663afb9a5SDavid du Colombier
275763afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
275863afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ",");
275963afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j)
276063afb9a5SDavid du Colombier for (i = 0; i < n; ++i)
276163afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0)
276263afb9a5SDavid du Colombier goto foundm2;
276363afb9a5SDavid du Colombier free(buf);
276463afb9a5SDavid du Colombier return -1;
276563afb9a5SDavid du Colombier foundm2:
276663afb9a5SDavid du Colombier p->c->nscmac = j;
276763afb9a5SDavid du Colombier
276863afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
276963afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
277063afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
277163afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil);
277263afb9a5SDavid du Colombier free(buf);
277363afb9a5SDavid du Colombier return *q != 0;
277463afb9a5SDavid du Colombier }
277563afb9a5SDavid du Colombier
277663afb9a5SDavid du Colombier int
memrandom(void * p,int n)277763afb9a5SDavid du Colombier memrandom(void *p, int n)
277863afb9a5SDavid du Colombier {
277963afb9a5SDavid du Colombier uchar *cp;
278063afb9a5SDavid du Colombier
278163afb9a5SDavid du Colombier for (cp = (uchar*)p; n > 0; n--)
278263afb9a5SDavid du Colombier *cp++ = fastrand();
278363afb9a5SDavid du Colombier return 0;
278463afb9a5SDavid du Colombier }
278563afb9a5SDavid du Colombier
278663afb9a5SDavid du Colombier /*
278763afb9a5SDavid du Colombier * create a change uid capability
278863afb9a5SDavid du Colombier */
278963afb9a5SDavid du Colombier char*
mkcap(char * from,char * to)279063afb9a5SDavid du Colombier mkcap(char *from, char *to)
279163afb9a5SDavid du Colombier {
279263afb9a5SDavid du Colombier int fd, fromtosz;
279363afb9a5SDavid du Colombier char *cap, *key;
279463afb9a5SDavid du Colombier uchar rand[SHA1dlen], hash[SHA1dlen];
279563afb9a5SDavid du Colombier
279663afb9a5SDavid du Colombier fd = open("#¤/caphash", OWRITE);
279763afb9a5SDavid du Colombier if (fd < 0)
279863afb9a5SDavid du Colombier sshlog(nil, "can't open #¤/caphash: %r");
279963afb9a5SDavid du Colombier
280063afb9a5SDavid du Colombier /* create the capability */
280163afb9a5SDavid du Colombier fromtosz = strlen(from) + 1 + strlen(to) + 1;
280263afb9a5SDavid du Colombier cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1);
280363afb9a5SDavid du Colombier snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to);
280463afb9a5SDavid du Colombier memrandom(rand, sizeof(rand));
280563afb9a5SDavid du Colombier key = cap + fromtosz;
280663afb9a5SDavid du Colombier enc64(key, sizeof(rand)*3, rand, sizeof(rand));
280763afb9a5SDavid du Colombier
280863afb9a5SDavid du Colombier /* hash the capability */
280963afb9a5SDavid du Colombier hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
281063afb9a5SDavid du Colombier
281163afb9a5SDavid du Colombier /* give the kernel the hash */
281263afb9a5SDavid du Colombier key[-1] = '@';
281363afb9a5SDavid du Colombier sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash);
281463afb9a5SDavid du Colombier if (write(fd, hash, SHA1dlen) != SHA1dlen) {
281563afb9a5SDavid du Colombier close(fd);
281663afb9a5SDavid du Colombier free(cap);
281763afb9a5SDavid du Colombier return nil;
281863afb9a5SDavid du Colombier }
281963afb9a5SDavid du Colombier close(fd);
282063afb9a5SDavid du Colombier return cap;
282163afb9a5SDavid du Colombier }
282263afb9a5SDavid du Colombier
282363afb9a5SDavid du Colombier /*
282463afb9a5SDavid du Colombier * ask keyfs (assumes we are on an auth server)
282563afb9a5SDavid du Colombier */
282663afb9a5SDavid du Colombier static AuthInfo *
keyfsauth(char * me,char * user,char * pw,char * key1,char * key2)282763afb9a5SDavid du Colombier keyfsauth(char *me, char *user, char *pw, char *key1, char *key2)
282863afb9a5SDavid du Colombier {
282963afb9a5SDavid du Colombier int fd;
283063afb9a5SDavid du Colombier char path[Arbpathlen];
283163afb9a5SDavid du Colombier AuthInfo *ai;
283263afb9a5SDavid du Colombier
283363afb9a5SDavid du Colombier if (passtokey(key1, pw) == 0)
283463afb9a5SDavid du Colombier return nil;
283563afb9a5SDavid du Colombier
283663afb9a5SDavid du Colombier snprint(path, Arbpathlen, "/mnt/keys/%s/key", user);
283763afb9a5SDavid du Colombier if ((fd = open(path, OREAD)) < 0) {
283863afb9a5SDavid du Colombier werrstr("Invalid user %s", user);
283963afb9a5SDavid du Colombier return nil;
284063afb9a5SDavid du Colombier }
284163afb9a5SDavid du Colombier if (read(fd, key2, DESKEYLEN) != DESKEYLEN) {
284263afb9a5SDavid du Colombier close(fd);
284363afb9a5SDavid du Colombier werrstr("Password mismatch 1");
284463afb9a5SDavid du Colombier return nil;
284563afb9a5SDavid du Colombier }
284663afb9a5SDavid du Colombier close(fd);
284763afb9a5SDavid du Colombier
284863afb9a5SDavid du Colombier if (memcmp(key1, key2, DESKEYLEN) != 0) {
284963afb9a5SDavid du Colombier werrstr("Password mismatch 2");
285063afb9a5SDavid du Colombier return nil;
285163afb9a5SDavid du Colombier }
285263afb9a5SDavid du Colombier
285363afb9a5SDavid du Colombier ai = emalloc9p(sizeof(AuthInfo));
285463afb9a5SDavid du Colombier ai->cuid = estrdup9p(user);
285563afb9a5SDavid du Colombier ai->suid = estrdup9p(me);
285663afb9a5SDavid du Colombier ai->cap = mkcap(me, user);
285763afb9a5SDavid du Colombier ai->nsecret = 0;
285863afb9a5SDavid du Colombier ai->secret = (uchar *)estrdup9p("");
285963afb9a5SDavid du Colombier return ai;
286063afb9a5SDavid du Colombier }
286163afb9a5SDavid du Colombier
286263afb9a5SDavid du Colombier static void
userauthfailed(Packet * p2)286363afb9a5SDavid du Colombier userauthfailed(Packet *p2)
286463afb9a5SDavid du Colombier {
286563afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_FAILURE);
286663afb9a5SDavid du Colombier add_string(p2, "password,publickey");
286763afb9a5SDavid du Colombier add_byte(p2, 0);
286863afb9a5SDavid du Colombier }
286963afb9a5SDavid du Colombier
287063afb9a5SDavid du Colombier static int
authreqpk(Packet * p,Packet * p2,Conn * c,char * user,uchar * q,char * alg,char * blob,char * sig,char * service,char * me)287163afb9a5SDavid du Colombier authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q,
287263afb9a5SDavid du Colombier char *alg, char *blob, char *sig, char *service, char *me)
287363afb9a5SDavid du Colombier {
287463afb9a5SDavid du Colombier int n, thisway, nblob, nsig;
287563afb9a5SDavid du Colombier char method[32];
287663afb9a5SDavid du Colombier
287763afb9a5SDavid du Colombier sshdebug(c, "auth_req publickey for user %s", user);
287863afb9a5SDavid du Colombier thisway = *q == '\0';
287963afb9a5SDavid du Colombier q = get_string(p, q+1, alg, Arbpathlen, nil);
288063afb9a5SDavid du Colombier q = get_string(p, q, blob, Blobsz, &nblob);
288163afb9a5SDavid du Colombier if (thisway) {
288263afb9a5SDavid du Colombier /*
288363afb9a5SDavid du Colombier * Should really check to see if this user can
288463afb9a5SDavid du Colombier * be authed this way.
288563afb9a5SDavid du Colombier */
288663afb9a5SDavid du Colombier for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
288763afb9a5SDavid du Colombier strcmp(pkas[n]->name, alg) != 0; ++n)
288863afb9a5SDavid du Colombier ;
288963afb9a5SDavid du Colombier if (n >= nelem(pkas) || pkas[n] == nil) {
289063afb9a5SDavid du Colombier userauthfailed(p2);
289163afb9a5SDavid du Colombier return -1;
289263afb9a5SDavid du Colombier }
289363afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_PK_OK);
289463afb9a5SDavid du Colombier add_string(p2, alg);
289563afb9a5SDavid du Colombier add_block(p2, blob, nblob);
2896*48207d97SDavid du Colombier return 1;
289763afb9a5SDavid du Colombier }
289863afb9a5SDavid du Colombier
289963afb9a5SDavid du Colombier get_string(p, q, sig, Blobsz, &nsig);
290063afb9a5SDavid du Colombier for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
290163afb9a5SDavid du Colombier strcmp(pkas[n]->name, alg) != 0; ++n)
290263afb9a5SDavid du Colombier ;
290363afb9a5SDavid du Colombier if (n >= nelem(pkas) || pkas[n] == nil) {
290463afb9a5SDavid du Colombier userauthfailed(p2);
290563afb9a5SDavid du Colombier return -1;
290663afb9a5SDavid du Colombier }
290763afb9a5SDavid du Colombier
290863afb9a5SDavid du Colombier add_block(p2, c->sessid, SHA1dlen);
290963afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
291063afb9a5SDavid du Colombier add_string(p2, user);
291163afb9a5SDavid du Colombier add_string(p2, service);
291263afb9a5SDavid du Colombier add_string(p2, method);
291363afb9a5SDavid du Colombier add_byte(p2, 1);
291463afb9a5SDavid du Colombier add_string(p2, alg);
291563afb9a5SDavid du Colombier add_block(p2, blob, nblob);
291663afb9a5SDavid du Colombier if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig)
291763afb9a5SDavid du Colombier == 0) {
291863afb9a5SDavid du Colombier init_packet(p2);
291963afb9a5SDavid du Colombier p2->c = c;
292063afb9a5SDavid du Colombier sshlog(c, "public key login failed");
292163afb9a5SDavid du Colombier userauthfailed(p2);
292263afb9a5SDavid du Colombier return -1;
292363afb9a5SDavid du Colombier }
292463afb9a5SDavid du Colombier free(c->cap);
292563afb9a5SDavid du Colombier c->cap = mkcap(me, user);
292663afb9a5SDavid du Colombier init_packet(p2);
292763afb9a5SDavid du Colombier p2->c = c;
292863afb9a5SDavid du Colombier sshlog(c, "logged in by public key");
292963afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
293063afb9a5SDavid du Colombier return 0;
293163afb9a5SDavid du Colombier }
293263afb9a5SDavid du Colombier
293363afb9a5SDavid du Colombier int
auth_req(Packet * p,Conn * c)293463afb9a5SDavid du Colombier auth_req(Packet *p, Conn *c)
293563afb9a5SDavid du Colombier {
293663afb9a5SDavid du Colombier int n, ret;
293763afb9a5SDavid du Colombier char *alg, *blob, *sig, *service, *me, *user, *pw, *path;
293863afb9a5SDavid du Colombier char key1[DESKEYLEN], key2[DESKEYLEN], method[32];
293963afb9a5SDavid du Colombier uchar *q;
294063afb9a5SDavid du Colombier AuthInfo *ai;
294163afb9a5SDavid du Colombier Packet *p2;
294263afb9a5SDavid du Colombier
294363afb9a5SDavid du Colombier service = emalloc9p(Arbpathlen);
294463afb9a5SDavid du Colombier me = emalloc9p(Arbpathlen);
294563afb9a5SDavid du Colombier user = emalloc9p(Arbpathlen);
294663afb9a5SDavid du Colombier pw = emalloc9p(Arbpathlen);
294763afb9a5SDavid du Colombier alg = emalloc9p(Arbpathlen);
294863afb9a5SDavid du Colombier path = emalloc9p(Arbpathlen);
294963afb9a5SDavid du Colombier blob = emalloc9p(Blobsz);
295063afb9a5SDavid du Colombier sig = emalloc9p(Blobsz);
295163afb9a5SDavid du Colombier ret = -1; /* failure is default */
295263afb9a5SDavid du Colombier
295363afb9a5SDavid du Colombier q = get_string(p, p->payload + 1, user, Arbpathlen, nil);
295463afb9a5SDavid du Colombier free(c->user);
295563afb9a5SDavid du Colombier c->user = estrdup9p(user);
295663afb9a5SDavid du Colombier q = get_string(p, q, service, Arbpathlen, nil);
295763afb9a5SDavid du Colombier q = get_string(p, q, method, sizeof method, nil);
295863afb9a5SDavid du Colombier sshdebug(c, "got userauth request: %s %s %s", user, service, method);
295963afb9a5SDavid du Colombier
296063afb9a5SDavid du Colombier readfile("/dev/user", me, Arbpathlen);
296163afb9a5SDavid du Colombier
296263afb9a5SDavid du Colombier p2 = new_packet(c);
296363afb9a5SDavid du Colombier if (strcmp(method, "publickey") == 0)
296463afb9a5SDavid du Colombier ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me);
296563afb9a5SDavid du Colombier else if (strcmp(method, "password") == 0) {
296663afb9a5SDavid du Colombier get_string(p, q + 1, pw, Arbpathlen, nil);
296763afb9a5SDavid du Colombier // sshdebug(c, "%s", pw); /* bad idea to log passwords */
296863afb9a5SDavid du Colombier sshdebug(c, "auth_req password");
296963afb9a5SDavid du Colombier if (kflag)
297063afb9a5SDavid du Colombier ai = keyfsauth(me, user, pw, key1, key2);
297163afb9a5SDavid du Colombier else
297263afb9a5SDavid du Colombier ai = auth_userpasswd(user, pw);
297363afb9a5SDavid du Colombier if (ai == nil) {
297463afb9a5SDavid du Colombier sshlog(c, "login failed: %r");
297563afb9a5SDavid du Colombier userauthfailed(p2);
297663afb9a5SDavid du Colombier } else {
297763afb9a5SDavid du Colombier sshdebug(c, "auth successful: cuid %s suid %s cap %s",
297863afb9a5SDavid du Colombier ai->cuid, ai->suid, ai->cap);
297963afb9a5SDavid du Colombier free(c->cap);
298063afb9a5SDavid du Colombier if (strcmp(user, me) == 0)
298163afb9a5SDavid du Colombier c->cap = estrdup9p("n/a");
298263afb9a5SDavid du Colombier else
298363afb9a5SDavid du Colombier c->cap = estrdup9p(ai->cap);
298463afb9a5SDavid du Colombier sshlog(c, "logged in by password");
298563afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
298663afb9a5SDavid du Colombier auth_freeAI(ai);
298763afb9a5SDavid du Colombier ret = 0;
298863afb9a5SDavid du Colombier }
298963afb9a5SDavid du Colombier } else
299063afb9a5SDavid du Colombier userauthfailed(p2);
299163afb9a5SDavid du Colombier
299263afb9a5SDavid du Colombier n = finish_packet(p2);
299363afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p2->nlength, n);
299463afb9a5SDavid du Colombier
299563afb9a5SDavid du Colombier free(service);
299663afb9a5SDavid du Colombier free(me);
299763afb9a5SDavid du Colombier free(user);
299863afb9a5SDavid du Colombier free(pw);
299963afb9a5SDavid du Colombier free(alg);
300063afb9a5SDavid du Colombier free(blob);
300163afb9a5SDavid du Colombier free(sig);
300263afb9a5SDavid du Colombier free(path);
300363afb9a5SDavid du Colombier free(p2);
300463afb9a5SDavid du Colombier return ret;
300563afb9a5SDavid du Colombier }
300663afb9a5SDavid du Colombier
300763afb9a5SDavid du Colombier int
client_auth(Conn * c,Ioproc * io)300863afb9a5SDavid du Colombier client_auth(Conn *c, Ioproc *io)
300963afb9a5SDavid du Colombier {
301063afb9a5SDavid du Colombier Packet *p2, *p3, *p4;
301163afb9a5SDavid du Colombier char *r, *s;
301263afb9a5SDavid du Colombier mpint *ek, *nk;
301363afb9a5SDavid du Colombier int i, n;
301463afb9a5SDavid du Colombier
301563afb9a5SDavid du Colombier sshdebug(c, "client_auth");
301663afb9a5SDavid du Colombier if (!c->password && !c->authkey)
301763afb9a5SDavid du Colombier return -1;
301863afb9a5SDavid du Colombier
301963afb9a5SDavid du Colombier p2 = new_packet(c);
302063afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
302163afb9a5SDavid du Colombier add_string(p2, c->user);
302263afb9a5SDavid du Colombier add_string(p2, c->service);
302363afb9a5SDavid du Colombier if (c->password) {
302463afb9a5SDavid du Colombier add_string(p2, "password");
302563afb9a5SDavid du Colombier add_byte(p2, 0);
302663afb9a5SDavid du Colombier add_string(p2, c->password);
302763afb9a5SDavid du Colombier sshdebug(c, "client_auth using password for svc %s", c->service);
302863afb9a5SDavid du Colombier } else {
302963afb9a5SDavid du Colombier sshdebug(c, "client_auth trying rsa public key");
303063afb9a5SDavid du Colombier add_string(p2, "publickey");
303163afb9a5SDavid du Colombier add_byte(p2, 1);
303263afb9a5SDavid du Colombier add_string(p2, "ssh-rsa");
303363afb9a5SDavid du Colombier
303463afb9a5SDavid du Colombier r = strstr(c->authkey, " ek=");
303563afb9a5SDavid du Colombier s = strstr(c->authkey, " n=");
303663afb9a5SDavid du Colombier if (!r || !s) {
303763afb9a5SDavid du Colombier shutdown(c);
303863afb9a5SDavid du Colombier free(p2);
303963afb9a5SDavid du Colombier sshdebug(c, "client_auth no rsa key");
304063afb9a5SDavid du Colombier return -1;
304163afb9a5SDavid du Colombier }
304263afb9a5SDavid du Colombier ek = strtomp(r+4, nil, 16, nil);
304363afb9a5SDavid du Colombier nk = strtomp(s+3, nil, 16, nil);
304463afb9a5SDavid du Colombier
304563afb9a5SDavid du Colombier p3 = new_packet(c);
304663afb9a5SDavid du Colombier add_string(p3, "ssh-rsa");
304763afb9a5SDavid du Colombier add_mp(p3, ek);
304863afb9a5SDavid du Colombier add_mp(p3, nk);
304963afb9a5SDavid du Colombier add_block(p2, p3->payload, p3->rlength-1);
305063afb9a5SDavid du Colombier
305163afb9a5SDavid du Colombier p4 = new_packet(c);
305263afb9a5SDavid du Colombier add_block(p4, c->sessid, SHA1dlen);
305363afb9a5SDavid du Colombier add_byte(p4, SSH_MSG_USERAUTH_REQUEST);
305463afb9a5SDavid du Colombier add_string(p4, c->user);
305563afb9a5SDavid du Colombier add_string(p4, c->service);
305663afb9a5SDavid du Colombier add_string(p4, "publickey");
305763afb9a5SDavid du Colombier add_byte(p4, 1);
305863afb9a5SDavid du Colombier add_string(p4, "ssh-rsa");
305963afb9a5SDavid du Colombier add_block(p4, p3->payload, p3->rlength-1);
306063afb9a5SDavid du Colombier mpfree(ek);
306163afb9a5SDavid du Colombier mpfree(nk);
306263afb9a5SDavid du Colombier free(p3);
306363afb9a5SDavid du Colombier
306463afb9a5SDavid du Colombier for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0;
306563afb9a5SDavid du Colombier ++i)
306663afb9a5SDavid du Colombier ;
306763afb9a5SDavid du Colombier sshdebug(c, "client_auth rsa signing alg %d: %r", i);
306863afb9a5SDavid du Colombier if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) {
306963afb9a5SDavid du Colombier sshdebug(c, "client_auth rsa signing failed: %r");
307063afb9a5SDavid du Colombier free(p4);
307163afb9a5SDavid du Colombier free(p2);
307263afb9a5SDavid du Colombier return -1;
307363afb9a5SDavid du Colombier }
307463afb9a5SDavid du Colombier add_block(p2, p3->payload, p3->rlength-1);
307563afb9a5SDavid du Colombier free(p3);
307663afb9a5SDavid du Colombier free(p4);
307763afb9a5SDavid du Colombier }
307863afb9a5SDavid du Colombier
307963afb9a5SDavid du Colombier n = finish_packet(p2);
308063afb9a5SDavid du Colombier if (writeio(io, c->datafd, p2->nlength, n) != n)
308163afb9a5SDavid du Colombier sshdebug(c, "client_auth write failed: %r");
308263afb9a5SDavid du Colombier free(p2);
308363afb9a5SDavid du Colombier return 0;
308463afb9a5SDavid du Colombier }
308563afb9a5SDavid du Colombier
308663afb9a5SDavid du Colombier /* should use auth_getkey or something similar */
308763afb9a5SDavid du Colombier char *
factlookup(int nattr,int nreq,char * attrs[])308863afb9a5SDavid du Colombier factlookup(int nattr, int nreq, char *attrs[])
308963afb9a5SDavid du Colombier {
309063afb9a5SDavid du Colombier Biobuf *bp;
309163afb9a5SDavid du Colombier char *buf, *toks[Maxtoks], *res, *q;
309263afb9a5SDavid du Colombier int ntok, nmatch, maxmatch;
309363afb9a5SDavid du Colombier int i, j;
309463afb9a5SDavid du Colombier
309563afb9a5SDavid du Colombier res = nil;
309663afb9a5SDavid du Colombier bp = Bopen("/mnt/factotum/ctl", OREAD);
309763afb9a5SDavid du Colombier if (bp == nil)
309863afb9a5SDavid du Colombier return nil;
309963afb9a5SDavid du Colombier maxmatch = 0;
310063afb9a5SDavid du Colombier while (buf = Brdstr(bp, '\n', 1)) {
310163afb9a5SDavid du Colombier q = estrdup9p(buf);
310263afb9a5SDavid du Colombier ntok = gettokens(buf, toks, nelem(toks), " ");
310363afb9a5SDavid du Colombier nmatch = 0;
310463afb9a5SDavid du Colombier for (i = 0; i < nattr; ++i) {
310563afb9a5SDavid du Colombier for (j = 0; j < ntok; ++j)
310663afb9a5SDavid du Colombier if (strcmp(attrs[i], toks[j]) == 0) {
310763afb9a5SDavid du Colombier ++nmatch;
310863afb9a5SDavid du Colombier break;
310963afb9a5SDavid du Colombier }
311063afb9a5SDavid du Colombier if (i < nreq && j >= ntok)
311163afb9a5SDavid du Colombier break;
311263afb9a5SDavid du Colombier }
311363afb9a5SDavid du Colombier if (i >= nattr && nmatch > maxmatch) {
311463afb9a5SDavid du Colombier free(res);
311563afb9a5SDavid du Colombier res = q;
311663afb9a5SDavid du Colombier maxmatch = nmatch;
311763afb9a5SDavid du Colombier } else
311863afb9a5SDavid du Colombier free(q);
311963afb9a5SDavid du Colombier free(buf);
312063afb9a5SDavid du Colombier }
312163afb9a5SDavid du Colombier Bterm(bp);
312263afb9a5SDavid du Colombier return res;
312363afb9a5SDavid du Colombier }
312463afb9a5SDavid du Colombier
312563afb9a5SDavid du Colombier void
shutdown(Conn * c)312663afb9a5SDavid du Colombier shutdown(Conn *c)
312763afb9a5SDavid du Colombier {
312863afb9a5SDavid du Colombier Plist *p;
312963afb9a5SDavid du Colombier SSHChan *sc;
313063afb9a5SDavid du Colombier int i, ostate;
313163afb9a5SDavid du Colombier
313263afb9a5SDavid du Colombier sshdebug(c, "shutting down connection %d", c->id);
313363afb9a5SDavid du Colombier ostate = c->state;
313463afb9a5SDavid du Colombier if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 &&
313563afb9a5SDavid du Colombier c->datafile->ref <= 2 && c->listenfile->ref <= 2 &&
313663afb9a5SDavid du Colombier c->localfile->ref <= 2 && c->remotefile->ref <= 2 &&
313763afb9a5SDavid du Colombier c->statusfile->ref <= 2)
313863afb9a5SDavid du Colombier c->state = Closed;
313963afb9a5SDavid du Colombier else {
314063afb9a5SDavid du Colombier if (c->state != Closed)
314163afb9a5SDavid du Colombier c->state = Closing;
314263afb9a5SDavid du Colombier sshdebug(c, "clone %ld ctl %ld data %ld listen %ld "
314363afb9a5SDavid du Colombier "local %ld remote %ld status %ld",
314463afb9a5SDavid du Colombier c->clonefile->ref, c->ctlfile->ref, c->datafile->ref,
314563afb9a5SDavid du Colombier c->listenfile->ref, c->localfile->ref, c->remotefile->ref,
314663afb9a5SDavid du Colombier c->statusfile->ref);
314763afb9a5SDavid du Colombier }
314863afb9a5SDavid du Colombier if (ostate == Closed || ostate == Closing) {
314963afb9a5SDavid du Colombier c->state = Closed;
315063afb9a5SDavid du Colombier return;
315163afb9a5SDavid du Colombier }
315263afb9a5SDavid du Colombier if (c->role == Server && c->remote)
315363afb9a5SDavid du Colombier sshlog(c, "closing connection");
315463afb9a5SDavid du Colombier hangupconn(c);
315563afb9a5SDavid du Colombier if (c->dio) {
315663afb9a5SDavid du Colombier closeioproc(c->dio);
315763afb9a5SDavid du Colombier c->dio = nil;
315863afb9a5SDavid du Colombier }
315963afb9a5SDavid du Colombier
316063afb9a5SDavid du Colombier c->decrypt = -1;
316163afb9a5SDavid du Colombier c->inmac = -1;
316263afb9a5SDavid du Colombier c->nchan = 0;
316363afb9a5SDavid du Colombier free(c->otherid);
316463afb9a5SDavid du Colombier free(c->s2ccs);
316563afb9a5SDavid du Colombier c->s2ccs = nil;
316663afb9a5SDavid du Colombier free(c->c2scs);
316763afb9a5SDavid du Colombier c->c2scs = nil;
316863afb9a5SDavid du Colombier free(c->remote);
316963afb9a5SDavid du Colombier c->remote = nil;
317063afb9a5SDavid du Colombier if (c->x) {
317163afb9a5SDavid du Colombier mpfree(c->x);
317263afb9a5SDavid du Colombier c->x = nil;
317363afb9a5SDavid du Colombier }
317463afb9a5SDavid du Colombier if (c->e) {
317563afb9a5SDavid du Colombier mpfree(c->e);
317663afb9a5SDavid du Colombier c->e = nil;
317763afb9a5SDavid du Colombier }
317863afb9a5SDavid du Colombier free(c->user);
317963afb9a5SDavid du Colombier c->user = nil;
318063afb9a5SDavid du Colombier free(c->service);
318163afb9a5SDavid du Colombier c->service = nil;
318263afb9a5SDavid du Colombier c->otherid = nil;
318363afb9a5SDavid du Colombier qlock(&c->l);
318463afb9a5SDavid du Colombier rwakeupall(&c->r);
318563afb9a5SDavid du Colombier qunlock(&c->l);
318663afb9a5SDavid du Colombier for (i = 0; i < MAXCONN; ++i) {
318763afb9a5SDavid du Colombier sc = c->chans[i];
318863afb9a5SDavid du Colombier if (sc == nil)
318963afb9a5SDavid du Colombier continue;
319063afb9a5SDavid du Colombier free(sc->ann);
319163afb9a5SDavid du Colombier sc->ann = nil;
319263afb9a5SDavid du Colombier if (sc->state != Empty && sc->state != Closed) {
319363afb9a5SDavid du Colombier sc->state = Closed;
319463afb9a5SDavid du Colombier sc->lreq = nil;
319563afb9a5SDavid du Colombier while (sc->dataq != nil) {
319663afb9a5SDavid du Colombier p = sc->dataq;
319763afb9a5SDavid du Colombier sc->dataq = p->next;
319863afb9a5SDavid du Colombier free(p->pack);
319963afb9a5SDavid du Colombier free(p);
320063afb9a5SDavid du Colombier }
320163afb9a5SDavid du Colombier while (sc->reqq != nil) {
320263afb9a5SDavid du Colombier p = sc->reqq;
320363afb9a5SDavid du Colombier sc->reqq = p->next;
320463afb9a5SDavid du Colombier free(p->pack);
320563afb9a5SDavid du Colombier free(p);
320663afb9a5SDavid du Colombier }
320763afb9a5SDavid du Colombier qlock(&c->l);
320863afb9a5SDavid du Colombier rwakeupall(&sc->r);
320963afb9a5SDavid du Colombier nbsendul(sc->inchan, 1);
321063afb9a5SDavid du Colombier nbsendul(sc->reqchan, 1);
321163afb9a5SDavid du Colombier chanclose(sc->inchan);
321263afb9a5SDavid du Colombier chanclose(sc->reqchan);
321363afb9a5SDavid du Colombier qunlock(&c->l);
321463afb9a5SDavid du Colombier }
321563afb9a5SDavid du Colombier }
321663afb9a5SDavid du Colombier qlock(&availlck);
321763afb9a5SDavid du Colombier rwakeup(&availrend);
321863afb9a5SDavid du Colombier qunlock(&availlck);
321963afb9a5SDavid du Colombier sshdebug(c, "done processing shutdown of connection %d", c->id);
322063afb9a5SDavid du Colombier }
3221