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 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 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 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 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 16763afb9a5SDavid du Colombier threadmain(int argc, char *argv[]) 16863afb9a5SDavid du Colombier { 16963afb9a5SDavid du Colombier char *p, *srvpt = nil; 17063afb9a5SDavid du Colombier 17163afb9a5SDavid du Colombier threadsetname("main"); 172*d7e44d0dSDavid du Colombier nokeyverify = 1; /* temporary until verification is fixed */ 17363afb9a5SDavid du Colombier ARGBEGIN { 17463afb9a5SDavid du Colombier case '9': 17563afb9a5SDavid du Colombier chatty9p = 1; 17663afb9a5SDavid du Colombier break; 17763afb9a5SDavid du Colombier case 'd': 17863afb9a5SDavid du Colombier debug++; 17963afb9a5SDavid du Colombier break; 18063afb9a5SDavid du Colombier case 'k': 18163afb9a5SDavid du Colombier kflag = 1; 18263afb9a5SDavid du Colombier break; 18363afb9a5SDavid du Colombier case 'm': 18463afb9a5SDavid du Colombier mntpt = EARGF(usage()); 18563afb9a5SDavid du Colombier break; 18663afb9a5SDavid du Colombier case 's': 18763afb9a5SDavid du Colombier srvpt = EARGF(usage()); 18863afb9a5SDavid du Colombier break; 18963afb9a5SDavid du Colombier case 'v': 19063afb9a5SDavid du Colombier nokeyverify = 1; 19163afb9a5SDavid du Colombier break; 192*d7e44d0dSDavid du Colombier case 'V': 193*d7e44d0dSDavid du Colombier nokeyverify = 0; 194*d7e44d0dSDavid du Colombier break; 19563afb9a5SDavid du Colombier default: 19663afb9a5SDavid du Colombier usage(); 19763afb9a5SDavid du Colombier break; 19863afb9a5SDavid du Colombier } ARGEND; 19963afb9a5SDavid du Colombier 20063afb9a5SDavid du Colombier p = getenv("nosshkeyverify"); 201*d7e44d0dSDavid du Colombier if (p && p[0] != '\0') 20263afb9a5SDavid du Colombier nokeyverify = 1; 20363afb9a5SDavid du Colombier free(p); 20463afb9a5SDavid du Colombier 20563afb9a5SDavid du Colombier if (readfile("/dev/user", uid, sizeof uid) <= 0) 20663afb9a5SDavid du Colombier strcpy(uid, "none"); 20763afb9a5SDavid du Colombier 20863afb9a5SDavid du Colombier keymbox.mchan = chancreate(4, 0); 20963afb9a5SDavid du Colombier availrend.l = &availlck; 21063afb9a5SDavid du Colombier dh_init(pkas); 21163afb9a5SDavid du Colombier 21263afb9a5SDavid du Colombier /* become a daemon */ 21363afb9a5SDavid du Colombier if (rfork(RFNOTEG) < 0) 21463afb9a5SDavid du Colombier fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0); 21563afb9a5SDavid du Colombier server(mntpt, srvpt); 216e5a0fbb7SDavid du Colombier threadexits(nil); 21763afb9a5SDavid du Colombier } 21863afb9a5SDavid du Colombier 21963afb9a5SDavid du Colombier int 22063afb9a5SDavid du Colombier readio(Ioproc *io, int fd, void *buf, int n) 22163afb9a5SDavid du Colombier { 22263afb9a5SDavid du Colombier if (io) 22363afb9a5SDavid du Colombier return ioread(io, fd, buf, n); 22463afb9a5SDavid du Colombier else 22563afb9a5SDavid du Colombier return read(fd, buf, n); 22663afb9a5SDavid du Colombier } 22763afb9a5SDavid du Colombier 22863afb9a5SDavid du Colombier int 22963afb9a5SDavid du Colombier writeio(Ioproc *io, int fd, void *buf, int n) 23063afb9a5SDavid du Colombier { 23163afb9a5SDavid du Colombier if (io) 23263afb9a5SDavid du Colombier return iowrite(io, fd, buf, n); 23363afb9a5SDavid du Colombier else 23463afb9a5SDavid du Colombier return write(fd, buf, n); 23563afb9a5SDavid du Colombier } 23663afb9a5SDavid du Colombier 23763afb9a5SDavid du Colombier int 23863afb9a5SDavid du Colombier read9pmsg(int fd, void *abuf, uint n) 23963afb9a5SDavid du Colombier { 24063afb9a5SDavid du Colombier int m, len; 24163afb9a5SDavid du Colombier uchar *buf; 24263afb9a5SDavid du Colombier 24363afb9a5SDavid du Colombier if (io9p == nil) 24463afb9a5SDavid du Colombier io9p = ioproc(); 24563afb9a5SDavid du Colombier 24663afb9a5SDavid du Colombier buf = abuf; 24763afb9a5SDavid du Colombier 24863afb9a5SDavid du Colombier /* read count */ 24963afb9a5SDavid du Colombier m = ioreadn(io9p, fd, buf, BIT32SZ); 25063afb9a5SDavid du Colombier if(m != BIT32SZ){ 25163afb9a5SDavid du Colombier if(m < 0) 25263afb9a5SDavid du Colombier return -1; 25363afb9a5SDavid du Colombier return 0; 25463afb9a5SDavid du Colombier } 25563afb9a5SDavid du Colombier 25663afb9a5SDavid du Colombier len = GBIT32(buf); 25763afb9a5SDavid du Colombier if(len <= BIT32SZ || len > n){ 25863afb9a5SDavid du Colombier werrstr("bad length in 9P2000 message header"); 25963afb9a5SDavid du Colombier return -1; 26063afb9a5SDavid du Colombier } 26163afb9a5SDavid du Colombier len -= BIT32SZ; 26263afb9a5SDavid du Colombier m = ioreadn(io9p, fd, buf+BIT32SZ, len); 26363afb9a5SDavid du Colombier if(m < len) 26463afb9a5SDavid du Colombier return 0; 26563afb9a5SDavid du Colombier return BIT32SZ+m; 26663afb9a5SDavid du Colombier } 26763afb9a5SDavid du Colombier 26863afb9a5SDavid du Colombier void 26963afb9a5SDavid du Colombier stend(Srv *) 27063afb9a5SDavid du Colombier { 27163afb9a5SDavid du Colombier closeioproc(io9p); 27263afb9a5SDavid du Colombier threadkillgrp(threadgetgrp()); 27363afb9a5SDavid du Colombier } 27463afb9a5SDavid du Colombier 27563afb9a5SDavid du Colombier void 27663afb9a5SDavid du Colombier server(char *mntpt, char *srvpt) 27763afb9a5SDavid du Colombier { 27863afb9a5SDavid du Colombier Dir d; 27963afb9a5SDavid du Colombier char *p; 28063afb9a5SDavid du Colombier int fd; 28163afb9a5SDavid du Colombier 28263afb9a5SDavid du Colombier netsshsrv.tree = alloctree(uid, uid, 0777, nil); 28363afb9a5SDavid du Colombier rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR, 28463afb9a5SDavid du Colombier (void*)Qroot); 28563afb9a5SDavid du Colombier clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone); 28663afb9a5SDavid du Colombier ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl); 28763afb9a5SDavid du Colombier keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); 28863afb9a5SDavid du Colombier 289e5a0fbb7SDavid du Colombier /* 290e5a0fbb7SDavid du Colombier * needs to be MBEFORE in case there are previous, now defunct, 291e5a0fbb7SDavid du Colombier * netssh processes mounted in mntpt. 292e5a0fbb7SDavid du Colombier */ 293e5a0fbb7SDavid du Colombier threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE); 29463afb9a5SDavid du Colombier 29563afb9a5SDavid du Colombier p = esmprint("%s/cs", mntpt); 29663afb9a5SDavid du Colombier fd = open(p, OWRITE); 29763afb9a5SDavid du Colombier free(p); 29863afb9a5SDavid du Colombier if (fd >= 0) { 29963afb9a5SDavid du Colombier fprint(fd, "add ssh"); 30063afb9a5SDavid du Colombier close(fd); 30163afb9a5SDavid du Colombier } 30263afb9a5SDavid du Colombier if (srvpt) { 30363afb9a5SDavid du Colombier nulldir(&d); 30463afb9a5SDavid du Colombier d.mode = 0666; 30563afb9a5SDavid du Colombier p = esmprint("/srv/%s", srvpt); 30663afb9a5SDavid du Colombier dirwstat(p, &d); 30763afb9a5SDavid du Colombier free(p); 30863afb9a5SDavid du Colombier } 30963afb9a5SDavid du Colombier sshdebug(nil, "server started for %s", getuser()); 31063afb9a5SDavid du Colombier } 31163afb9a5SDavid du Colombier 31263afb9a5SDavid du Colombier static void 31363afb9a5SDavid du Colombier respexit(Conn *c, Req *r, void *freeme, char *msg) 31463afb9a5SDavid du Colombier { 31563afb9a5SDavid du Colombier if (msg) 31663afb9a5SDavid du Colombier sshdebug(c, "%s", msg); 31763afb9a5SDavid du Colombier r->aux = 0; 31863afb9a5SDavid du Colombier respond(r, msg); 31963afb9a5SDavid du Colombier free(freeme); 32063afb9a5SDavid du Colombier threadexits(nil); /* maybe use msg here */ 32163afb9a5SDavid du Colombier } 32263afb9a5SDavid du Colombier 32363afb9a5SDavid du Colombier void 32463afb9a5SDavid du Colombier stopen(Req *r) 32563afb9a5SDavid du Colombier { 32663afb9a5SDavid du Colombier int lev, xconn, fd; 32763afb9a5SDavid du Colombier uvlong qidpath; 32863afb9a5SDavid du Colombier char *p; 32963afb9a5SDavid du Colombier char buf[32]; 33063afb9a5SDavid du Colombier Conn *c; 33163afb9a5SDavid du Colombier SSHChan *sc; 33263afb9a5SDavid du Colombier 33363afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 33463afb9a5SDavid du Colombier lev = qidpath >> Levshift; 33563afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) { 33663afb9a5SDavid du Colombier default: 33763afb9a5SDavid du Colombier respond(r, nil); 33863afb9a5SDavid du Colombier break; 33963afb9a5SDavid du Colombier case Qlisten: 34063afb9a5SDavid du Colombier r->aux = (void *)threadcreate((lev == Connection? 34163afb9a5SDavid du Colombier stlisconn: stlischan), r, Defstk); 34263afb9a5SDavid du Colombier break; 34363afb9a5SDavid du Colombier case Qclone: 34463afb9a5SDavid du Colombier switch (lev) { 34563afb9a5SDavid du Colombier case Top: 34663afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */ 34763afb9a5SDavid du Colombier p = esmprint("%s/tcp/clone", mntpt); 34863afb9a5SDavid du Colombier fd = open(p, ORDWR); 34963afb9a5SDavid du Colombier if (fd < 0) { 35063afb9a5SDavid du Colombier sshdebug(nil, "stopen: open %s failed: %r", p); 35163afb9a5SDavid du Colombier free(p); 35263afb9a5SDavid du Colombier responderror(r); 35363afb9a5SDavid du Colombier return; 35463afb9a5SDavid du Colombier } 35563afb9a5SDavid du Colombier free(p); 35663afb9a5SDavid du Colombier 35763afb9a5SDavid du Colombier c = alloc_conn(); 35863afb9a5SDavid du Colombier if (c == nil) { 35963afb9a5SDavid du Colombier close(fd); 36063afb9a5SDavid du Colombier respond(r, "no more connections"); 36163afb9a5SDavid du Colombier return; 36263afb9a5SDavid du Colombier } 36363afb9a5SDavid du Colombier c->ctlfd = fd; 36463afb9a5SDavid du Colombier c->poisoned = 0; 36563afb9a5SDavid du Colombier filedup(r, c->ctlfile); 36663afb9a5SDavid du Colombier sshlog(c, "new connection on fd %d", fd); 36763afb9a5SDavid du Colombier break; 36863afb9a5SDavid du Colombier case Connection: 36963afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 37063afb9a5SDavid du Colombier c = connections[xconn]; 37163afb9a5SDavid du Colombier if (c == nil) { 37263afb9a5SDavid du Colombier respond(r, "bad connection"); 37363afb9a5SDavid du Colombier return; 37463afb9a5SDavid du Colombier } 37563afb9a5SDavid du Colombier sc = alloc_chan(c); 37663afb9a5SDavid du Colombier if (sc == nil) { 37763afb9a5SDavid du Colombier respond(r, "no more channels"); 37863afb9a5SDavid du Colombier return; 37963afb9a5SDavid du Colombier } 38063afb9a5SDavid du Colombier filedup(r, sc->ctl); 38163afb9a5SDavid du Colombier break; 38263afb9a5SDavid du Colombier default: 38363afb9a5SDavid du Colombier snprint(buf, sizeof buf, "bad level %d", lev); 38463afb9a5SDavid du Colombier readstr(r, buf); 38563afb9a5SDavid du Colombier break; 38663afb9a5SDavid du Colombier } 38763afb9a5SDavid du Colombier respond(r, nil); 38863afb9a5SDavid du Colombier break; 38963afb9a5SDavid du Colombier } 39063afb9a5SDavid du Colombier } 39163afb9a5SDavid du Colombier 39263afb9a5SDavid du Colombier static void 39363afb9a5SDavid du Colombier listerrexit(Req *r, Ioproc *io, Conn *cl) 39463afb9a5SDavid du Colombier { 39563afb9a5SDavid du Colombier r->aux = 0; 39663afb9a5SDavid du Colombier responderror(r); 39763afb9a5SDavid du Colombier closeioproc(io); 39863afb9a5SDavid du Colombier shutdown(cl); 39963afb9a5SDavid du Colombier threadexits(nil); 40063afb9a5SDavid du Colombier } 40163afb9a5SDavid du Colombier 40263afb9a5SDavid du Colombier void 40363afb9a5SDavid du Colombier stlisconn(void *a) 40463afb9a5SDavid du Colombier { 40563afb9a5SDavid du Colombier int xconn, fd, n; 40663afb9a5SDavid du Colombier uvlong qidpath; 40763afb9a5SDavid du Colombier char *msg; 40863afb9a5SDavid du Colombier char buf[Numbsz], path[NETPATHLEN]; 40963afb9a5SDavid du Colombier Conn *c, *cl; 41063afb9a5SDavid du Colombier Ioproc *io; 41163afb9a5SDavid du Colombier Req *r; 41263afb9a5SDavid du Colombier 41363afb9a5SDavid du Colombier threadsetname("stlisconn"); 41463afb9a5SDavid du Colombier r = a; 41563afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 41663afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 41763afb9a5SDavid du Colombier 41863afb9a5SDavid du Colombier cl = connections[xconn]; 41963afb9a5SDavid du Colombier if (cl == nil) { 42063afb9a5SDavid du Colombier sshlog(cl, "bad connection"); 42163afb9a5SDavid du Colombier respond(r, "bad connection"); 42263afb9a5SDavid du Colombier threadexits("bad connection"); 42363afb9a5SDavid du Colombier } 42463afb9a5SDavid du Colombier if (cl->poisoned) { 42563afb9a5SDavid du Colombier sshdebug(cl, "stlisconn conn %d poisoned", xconn); 42663afb9a5SDavid du Colombier r->aux = 0; 42763afb9a5SDavid du Colombier respond(r, "top level listen conn poisoned"); 42863afb9a5SDavid du Colombier threadexits("top level listen conn poisoned"); 42963afb9a5SDavid du Colombier } 43063afb9a5SDavid du Colombier if (cl->ctlfd < 0) { 43163afb9a5SDavid du Colombier sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn); 43263afb9a5SDavid du Colombier r->aux = 0; 43363afb9a5SDavid du Colombier respond(r, "top level listen with closed fd"); 43463afb9a5SDavid du Colombier shutdown(cl); 43563afb9a5SDavid du Colombier cl->poisoned = 1; /* no more use until ctlfd is set */ 43663afb9a5SDavid du Colombier threadexits("top level listen with closed fd"); 43763afb9a5SDavid du Colombier } 43863afb9a5SDavid du Colombier 43963afb9a5SDavid du Colombier io = ioproc(); 44063afb9a5SDavid du Colombier 44163afb9a5SDavid du Colombier /* read xconn's tcp conn's ctl file */ 44263afb9a5SDavid du Colombier seek(cl->ctlfd, 0, 0); 44363afb9a5SDavid du Colombier n = ioread(io, cl->ctlfd, buf, sizeof buf - 1); 44463afb9a5SDavid du Colombier if (n == 0) { 44563afb9a5SDavid du Colombier sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd); 44663afb9a5SDavid du Colombier listerrexit(r, io, cl); 44763afb9a5SDavid du Colombier } else if (n < 0) { 44863afb9a5SDavid du Colombier sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd); 44963afb9a5SDavid du Colombier listerrexit(r, io, cl); 45063afb9a5SDavid du Colombier } 45163afb9a5SDavid du Colombier buf[n] = '\0'; 45263afb9a5SDavid du Colombier 45363afb9a5SDavid du Colombier cl->state = Listening; 45463afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */ 45563afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf); 45663afb9a5SDavid du Colombier for(;;) { 45763afb9a5SDavid du Colombier fd = ioopen(io, path, ORDWR); 45863afb9a5SDavid du Colombier if (fd < 0) 45963afb9a5SDavid du Colombier listerrexit(r, io, cl); 46063afb9a5SDavid du Colombier c = alloc_conn(); 46163afb9a5SDavid du Colombier if (c) 46263afb9a5SDavid du Colombier break; 46363afb9a5SDavid du Colombier n = ioread(io, fd, buf, sizeof buf - 1); 46463afb9a5SDavid du Colombier if (n <= 0) 46563afb9a5SDavid du Colombier listerrexit(r, io, cl); 46663afb9a5SDavid du Colombier buf[n] = '\0'; 46763afb9a5SDavid du Colombier msg = smprint("reject %s no available connections", buf); 46863afb9a5SDavid du Colombier iowrite(io, fd, msg, strlen(msg)); 46963afb9a5SDavid du Colombier free(msg); 47063afb9a5SDavid du Colombier close(fd); /* surely ioclose? */ 47163afb9a5SDavid du Colombier } 47263afb9a5SDavid du Colombier c->ctlfd = fd; 47363afb9a5SDavid du Colombier if (c->ctlfd < 0) { 47463afb9a5SDavid du Colombier sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn); 47563afb9a5SDavid du Colombier threadexitsall("stlisconn c->ctlfd < 0"); 47663afb9a5SDavid du Colombier } 47763afb9a5SDavid du Colombier c->poisoned = 0; 47863afb9a5SDavid du Colombier c->stifle = 1; /* defer server; was for coexistence */ 47963afb9a5SDavid du Colombier filedup(r, c->ctlfile); 48063afb9a5SDavid du Colombier sshdebug(c, "responding to listen open"); 48163afb9a5SDavid du Colombier r->aux = 0; 48263afb9a5SDavid du Colombier respond(r, nil); 48363afb9a5SDavid du Colombier closeioproc(io); 48463afb9a5SDavid du Colombier threadexits(nil); 48563afb9a5SDavid du Colombier } 48663afb9a5SDavid du Colombier 48763afb9a5SDavid du Colombier void 48863afb9a5SDavid du Colombier stlischan(void *a) 48963afb9a5SDavid du Colombier { 49063afb9a5SDavid du Colombier Req *r; 49163afb9a5SDavid du Colombier Packet *p2; 49263afb9a5SDavid du Colombier Ioproc *io; 49363afb9a5SDavid du Colombier Conn *c; 49463afb9a5SDavid du Colombier SSHChan *sc; 49563afb9a5SDavid du Colombier int i, n, xconn; 49663afb9a5SDavid du Colombier uvlong qidpath; 49763afb9a5SDavid du Colombier 49863afb9a5SDavid du Colombier threadsetname("stlischan"); 49963afb9a5SDavid du Colombier r = a; 50063afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 50163afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 50263afb9a5SDavid du Colombier c = connections[xconn]; 50363afb9a5SDavid du Colombier if (c == nil) { 50463afb9a5SDavid du Colombier respond(r, "bad channel"); 50563afb9a5SDavid du Colombier sshlog(c, "bad channel"); 50663afb9a5SDavid du Colombier threadexits(nil); 50763afb9a5SDavid du Colombier } 50863afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing) 50963afb9a5SDavid du Colombier respexit(c, r, nil, "channel listen on closed connection"); 51063afb9a5SDavid du Colombier sc = c->chans[qidpath & Chanmask]; 51163afb9a5SDavid du Colombier 51263afb9a5SDavid du Colombier qlock(&c->l); 51363afb9a5SDavid du Colombier sc->lreq = r; 51463afb9a5SDavid du Colombier for (i = 0; i < c->nchan; ++i) 51563afb9a5SDavid du Colombier if (c->chans[i] && c->chans[i]->state == Opening && 51663afb9a5SDavid du Colombier c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0) 51763afb9a5SDavid du Colombier break; 51863afb9a5SDavid du Colombier if (i >= c->nchan) { 51963afb9a5SDavid du Colombier sc->state = Listening; 52063afb9a5SDavid du Colombier rsleep(&sc->r); 52163afb9a5SDavid du Colombier i = sc->waker; 52263afb9a5SDavid du Colombier if (i < 0) { 52363afb9a5SDavid du Colombier qunlock(&c->l); 52463afb9a5SDavid du Colombier r->aux = 0; 52563afb9a5SDavid du Colombier responderror(r); 52663afb9a5SDavid du Colombier threadexits(nil); 52763afb9a5SDavid du Colombier } 52863afb9a5SDavid du Colombier } else 52963afb9a5SDavid du Colombier rwakeup(&c->chans[i]->r); 53063afb9a5SDavid du Colombier qunlock(&c->l); 53163afb9a5SDavid du Colombier 53263afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing || c->state == Eof) 53363afb9a5SDavid du Colombier respexit(c, r, nil, "channel listen on closed connection"); 53463afb9a5SDavid du Colombier c->chans[i]->state = Established; 53563afb9a5SDavid du Colombier 53663afb9a5SDavid du Colombier p2 = new_packet(c); 53763afb9a5SDavid du Colombier c->chans[i]->rwindow = Maxpayload; 53863afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 53963afb9a5SDavid du Colombier hnputl(p2->payload + 1, c->chans[i]->otherid); 54063afb9a5SDavid du Colombier hnputl(p2->payload + 5, c->chans[i]->id); 54163afb9a5SDavid du Colombier hnputl(p2->payload + 9, Maxpayload); 54263afb9a5SDavid du Colombier hnputl(p2->payload + 13, Maxrpcbuf); 54363afb9a5SDavid du Colombier p2->rlength = 18; 54463afb9a5SDavid du Colombier n = finish_packet(p2); 54563afb9a5SDavid du Colombier filedup(r, c->chans[i]->ctl); 54663afb9a5SDavid du Colombier 54763afb9a5SDavid du Colombier io = ioproc(); 54863afb9a5SDavid du Colombier n = iowrite(io, c->datafd, p2->nlength, n); 54963afb9a5SDavid du Colombier closeioproc(io); 55063afb9a5SDavid du Colombier 55163afb9a5SDavid du Colombier free(p2); 55263afb9a5SDavid du Colombier 55363afb9a5SDavid du Colombier sshdebug(c, "responding to chan listen open"); 55463afb9a5SDavid du Colombier r->aux = 0; 55563afb9a5SDavid du Colombier if (n < 0) 55663afb9a5SDavid du Colombier responderror(r); 55763afb9a5SDavid du Colombier else 55863afb9a5SDavid du Colombier respond(r, nil); 55963afb9a5SDavid du Colombier threadexits(nil); 56063afb9a5SDavid du Colombier } 56163afb9a5SDavid du Colombier 56263afb9a5SDavid du Colombier void 56363afb9a5SDavid du Colombier getdata(Conn *c, SSHChan *sc, Req *r) 56463afb9a5SDavid du Colombier { 56563afb9a5SDavid du Colombier Packet *p; 56663afb9a5SDavid du Colombier Plist *d; 56763afb9a5SDavid du Colombier int n; 56863afb9a5SDavid du Colombier 56963afb9a5SDavid du Colombier n = r->ifcall.count; 57063afb9a5SDavid du Colombier if (sc->dataq->rem < n) 57163afb9a5SDavid du Colombier n = sc->dataq->rem; 57263afb9a5SDavid du Colombier if (n > Maxrpcbuf) 57363afb9a5SDavid du Colombier n = Maxrpcbuf; 57463afb9a5SDavid du Colombier r->ifcall.offset = 0; 57563afb9a5SDavid du Colombier 57663afb9a5SDavid du Colombier readbuf(r, sc->dataq->st, n); 57763afb9a5SDavid du Colombier sc->dataq->st += n; 57863afb9a5SDavid du Colombier sc->dataq->rem -= n; 57963afb9a5SDavid du Colombier sc->inrqueue -= n; 58063afb9a5SDavid du Colombier if (sc->dataq->rem <= 0) { 58163afb9a5SDavid du Colombier d = sc->dataq; 58263afb9a5SDavid du Colombier sc->dataq = sc->dataq->next; 58363afb9a5SDavid du Colombier if (d->pack->tlength > sc->rwindow) 58463afb9a5SDavid du Colombier sc->rwindow = 0; 58563afb9a5SDavid du Colombier else 58663afb9a5SDavid du Colombier sc->rwindow -= d->pack->tlength; 58763afb9a5SDavid du Colombier free(d->pack); 58863afb9a5SDavid du Colombier free(d); 58963afb9a5SDavid du Colombier } 59063afb9a5SDavid du Colombier if (sc->rwindow < 16*1024) { /* magic. half-way, maybe? */ 59163afb9a5SDavid du Colombier sc->rwindow += Maxpayload; 59263afb9a5SDavid du Colombier sshdebug(c, "increasing receive window to %lud, inq %lud\n", 59363afb9a5SDavid du Colombier argv0, sc->rwindow, sc->inrqueue); 59463afb9a5SDavid du Colombier p = new_packet(c); 59563afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST); 59663afb9a5SDavid du Colombier hnputl(p->payload+1, sc->otherid); 59763afb9a5SDavid du Colombier hnputl(p->payload+5, Maxpayload); 59863afb9a5SDavid du Colombier p->rlength += 8; 59963afb9a5SDavid du Colombier n = finish_packet(p); 60063afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 60163afb9a5SDavid du Colombier free(p); 60263afb9a5SDavid du Colombier } 60363afb9a5SDavid du Colombier r->aux = 0; 60463afb9a5SDavid du Colombier respond(r, nil); 60563afb9a5SDavid du Colombier } 60663afb9a5SDavid du Colombier 60763afb9a5SDavid du Colombier void 60863afb9a5SDavid du Colombier stread(Req *r) 60963afb9a5SDavid du Colombier { 61063afb9a5SDavid du Colombier Conn *c; 61163afb9a5SDavid du Colombier SSHChan *sc; 61263afb9a5SDavid du Colombier int n, lev, cnum, xconn; 61363afb9a5SDavid du Colombier uvlong qidpath; 61463afb9a5SDavid du Colombier char buf[Arbbufsz], path[NETPATHLEN]; 61563afb9a5SDavid du Colombier 61663afb9a5SDavid du Colombier threadsetname("stread"); 61763afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 61863afb9a5SDavid du Colombier lev = qidpath >> Levshift; 61963afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 62063afb9a5SDavid du Colombier c = connections[xconn]; 62163afb9a5SDavid du Colombier if (c == nil) { 62263afb9a5SDavid du Colombier if (lev != Top || (qidpath & Qtypemask) != Qreqrem) { 62363afb9a5SDavid du Colombier respond(r, "Invalid connection"); 62463afb9a5SDavid du Colombier return; 62563afb9a5SDavid du Colombier } 62663afb9a5SDavid du Colombier cnum = 0; 62763afb9a5SDavid du Colombier sc = nil; 62863afb9a5SDavid du Colombier } else { 62963afb9a5SDavid du Colombier cnum = qidpath & Chanmask; 63063afb9a5SDavid du Colombier sc = c->chans[cnum]; 63163afb9a5SDavid du Colombier } 63263afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) { 63363afb9a5SDavid du Colombier case Qctl: 63463afb9a5SDavid du Colombier case Qlisten: 63563afb9a5SDavid du Colombier if (r->ifcall.offset != 0) { 63663afb9a5SDavid du Colombier respond(r, nil); 63763afb9a5SDavid du Colombier break; 63863afb9a5SDavid du Colombier } 63963afb9a5SDavid du Colombier switch (lev) { 64063afb9a5SDavid du Colombier case Top: 64163afb9a5SDavid du Colombier readstr(r, st_names[c->state]); 64263afb9a5SDavid du Colombier break; 64363afb9a5SDavid du Colombier case Connection: 64463afb9a5SDavid du Colombier case Subchannel: 64563afb9a5SDavid du Colombier snprint(buf, sizeof buf, "%d", lev == Connection? 64663afb9a5SDavid du Colombier xconn: cnum); 64763afb9a5SDavid du Colombier readstr(r, buf); 64863afb9a5SDavid du Colombier break; 64963afb9a5SDavid du Colombier default: 65063afb9a5SDavid du Colombier snprint(buf, sizeof buf, "stread error, level %d", lev); 65163afb9a5SDavid du Colombier respond(r, buf); 65263afb9a5SDavid du Colombier return; 65363afb9a5SDavid du Colombier } 65463afb9a5SDavid du Colombier respond(r, nil); 65563afb9a5SDavid du Colombier break; 65663afb9a5SDavid du Colombier case Qclone: 65763afb9a5SDavid du Colombier if (r->ifcall.offset != 0) { 65863afb9a5SDavid du Colombier respond(r, nil); 65963afb9a5SDavid du Colombier break; 66063afb9a5SDavid du Colombier } 66163afb9a5SDavid du Colombier readstr(r, "Congratulations, you've achieved the impossible\n"); 66263afb9a5SDavid du Colombier respond(r, nil); 66363afb9a5SDavid du Colombier break; 66463afb9a5SDavid du Colombier case Qdata: 66563afb9a5SDavid du Colombier if (lev == Top) { 66663afb9a5SDavid du Colombier respond(r, nil); 66763afb9a5SDavid du Colombier break; 66863afb9a5SDavid du Colombier } 66963afb9a5SDavid du Colombier if (lev == Connection) { 67063afb9a5SDavid du Colombier if (0 && c->stifle) { /* was for coexistence */ 67163afb9a5SDavid du Colombier c->stifle = 0; 67263afb9a5SDavid du Colombier if (deferredinit(c) < 0) { 67363afb9a5SDavid du Colombier respond(r, "deferredinit failed"); 67463afb9a5SDavid du Colombier break; 67563afb9a5SDavid du Colombier } 67663afb9a5SDavid du Colombier } 67763afb9a5SDavid du Colombier if (c->cap) /* auth capability? */ 67863afb9a5SDavid du Colombier readstr(r, c->cap); 67963afb9a5SDavid du Colombier respond(r, nil); 68063afb9a5SDavid du Colombier break; 68163afb9a5SDavid du Colombier } 68263afb9a5SDavid du Colombier 68363afb9a5SDavid du Colombier r->aux = (void *)threadcreate(readdata, r, Defstk); 68463afb9a5SDavid du Colombier break; 68563afb9a5SDavid du Colombier case Qlocal: 68663afb9a5SDavid du Colombier if (lev == Connection) 68763afb9a5SDavid du Colombier if (c->ctlfd < 0) 68863afb9a5SDavid du Colombier readstr(r, "::!0\n"); 68963afb9a5SDavid du Colombier else { 69063afb9a5SDavid du Colombier n = pread(c->ctlfd, buf, 10, 0); // magic 10 69163afb9a5SDavid du Colombier buf[n >= 0? n: 0] = '\0'; 69263afb9a5SDavid du Colombier snprint(path, sizeof path, "%s/tcp/%s/local", 69363afb9a5SDavid du Colombier mntpt, buf); 69463afb9a5SDavid du Colombier readfile(path, buf, sizeof buf); 69563afb9a5SDavid du Colombier readstr(r, buf); 69663afb9a5SDavid du Colombier } 69763afb9a5SDavid du Colombier respond(r, nil); 69863afb9a5SDavid du Colombier break; 69963afb9a5SDavid du Colombier case Qreqrem: 70063afb9a5SDavid du Colombier r->aux = (void *)threadcreate(readreqrem, r, Defstk); 70163afb9a5SDavid du Colombier break; 70263afb9a5SDavid du Colombier case Qstatus: 70363afb9a5SDavid du Colombier switch (lev) { 70463afb9a5SDavid du Colombier case Top: 70563afb9a5SDavid du Colombier readstr(r, "Impossible"); 70663afb9a5SDavid du Colombier break; 70763afb9a5SDavid du Colombier case Connection: 70863afb9a5SDavid du Colombier readstr(r, (uint)c->state > Closed? 70963afb9a5SDavid du Colombier "Unknown": st_names[c->state]); 71063afb9a5SDavid du Colombier break; 71163afb9a5SDavid du Colombier case Subchannel: 71263afb9a5SDavid du Colombier readstr(r, (uint)sc->state > Closed? 71363afb9a5SDavid du Colombier "Unknown": st_names[sc->state]); 71463afb9a5SDavid du Colombier break; 71563afb9a5SDavid du Colombier } 71663afb9a5SDavid du Colombier respond(r, nil); 71763afb9a5SDavid du Colombier break; 71863afb9a5SDavid du Colombier case Qtcp: 71963afb9a5SDavid du Colombier /* connection number of underlying tcp connection */ 72063afb9a5SDavid du Colombier if (lev == Connection) 72163afb9a5SDavid du Colombier if (c->ctlfd < 0) 72263afb9a5SDavid du Colombier readstr(r, "-1\n"); 72363afb9a5SDavid du Colombier else { 72463afb9a5SDavid du Colombier n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */ 72563afb9a5SDavid du Colombier buf[n >= 0? n: 0] = '\0'; 72663afb9a5SDavid du Colombier readstr(r, buf); 72763afb9a5SDavid du Colombier } 72863afb9a5SDavid du Colombier respond(r, nil); 72963afb9a5SDavid du Colombier break; 73063afb9a5SDavid du Colombier default: 73163afb9a5SDavid du Colombier respond(r, nil); 73263afb9a5SDavid du Colombier break; 73363afb9a5SDavid du Colombier } 73463afb9a5SDavid du Colombier } 73563afb9a5SDavid du Colombier 73663afb9a5SDavid du Colombier void 73763afb9a5SDavid du Colombier readreqrem(void *a) 73863afb9a5SDavid du Colombier { 73963afb9a5SDavid du Colombier Ioproc *io; 74063afb9a5SDavid du Colombier Req *r; 74163afb9a5SDavid du Colombier Conn *c; 74263afb9a5SDavid du Colombier SSHChan *sc; 74363afb9a5SDavid du Colombier int fd, n, lev, cnum, xconn; 74463afb9a5SDavid du Colombier uvlong qidpath; 74563afb9a5SDavid du Colombier char buf[Arbbufsz], path[NETPATHLEN]; 74663afb9a5SDavid du Colombier 74763afb9a5SDavid du Colombier threadsetname("readreqrem"); 74863afb9a5SDavid du Colombier r = a; 74963afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 75063afb9a5SDavid du Colombier lev = qidpath >> Levshift; 75163afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 75263afb9a5SDavid du Colombier c = connections[xconn]; 75363afb9a5SDavid du Colombier if (c == nil) { 75463afb9a5SDavid du Colombier if (lev != Top) { 75563afb9a5SDavid du Colombier respond(r, "Invalid connection"); 75663afb9a5SDavid du Colombier return; 75763afb9a5SDavid du Colombier } 75863afb9a5SDavid du Colombier sc = nil; 75963afb9a5SDavid du Colombier } else { 76063afb9a5SDavid du Colombier cnum = qidpath & Chanmask; 76163afb9a5SDavid du Colombier sc = c->chans[cnum]; 76263afb9a5SDavid du Colombier } 76363afb9a5SDavid du Colombier switch (lev) { 76463afb9a5SDavid du Colombier case Top: 76563afb9a5SDavid du Colombier if (r->ifcall.offset == 0 && keymbox.state != Empty) { 76663afb9a5SDavid du Colombier r->aux = 0; 76763afb9a5SDavid du Colombier respond(r, "Key file collision"); /* WTF? */ 76863afb9a5SDavid du Colombier break; 76963afb9a5SDavid du Colombier } 77063afb9a5SDavid du Colombier if (r->ifcall.offset != 0) { 77163afb9a5SDavid du Colombier readstr(r, keymbox.msg); 77263afb9a5SDavid du Colombier r->aux = 0; 77363afb9a5SDavid du Colombier respond(r, nil); 77463afb9a5SDavid du Colombier if (r->ifcall.offset + r->ifcall.count >= 77563afb9a5SDavid du Colombier strlen(keymbox.msg)) 77663afb9a5SDavid du Colombier keymbox.state = Empty; 77763afb9a5SDavid du Colombier else 77863afb9a5SDavid du Colombier keymbox.state = Allocated; 77963afb9a5SDavid du Colombier break; 78063afb9a5SDavid du Colombier } 78163afb9a5SDavid du Colombier keymbox.state = Allocated; 78263afb9a5SDavid du Colombier for(;;) { 78363afb9a5SDavid du Colombier if (keymbox.msg == nil) 78463afb9a5SDavid du Colombier if (recv(keymbox.mchan, nil) < 0) { 78563afb9a5SDavid du Colombier r->aux = 0; 78663afb9a5SDavid du Colombier responderror(r); 78763afb9a5SDavid du Colombier keymbox.state = Empty; 78863afb9a5SDavid du Colombier threadexits(nil); 78963afb9a5SDavid du Colombier } 79063afb9a5SDavid du Colombier if (keymbox.state == Empty) 79163afb9a5SDavid du Colombier break; 79263afb9a5SDavid du Colombier else if (keymbox.state == Allocated) { 79363afb9a5SDavid du Colombier if (keymbox.msg) { 79463afb9a5SDavid du Colombier readstr(r, keymbox.msg); 79563afb9a5SDavid du Colombier if (r->ifcall.offset + r->ifcall.count 79663afb9a5SDavid du Colombier >= strlen(keymbox.msg)) { 79763afb9a5SDavid du Colombier free(keymbox.msg); 79863afb9a5SDavid du Colombier keymbox.msg = nil; 79963afb9a5SDavid du Colombier keymbox.state = Empty; 80063afb9a5SDavid du Colombier } 80163afb9a5SDavid du Colombier } 80263afb9a5SDavid du Colombier break; 80363afb9a5SDavid du Colombier } 80463afb9a5SDavid du Colombier } 80563afb9a5SDavid du Colombier r->aux = 0; 80663afb9a5SDavid du Colombier respond(r, nil); 80763afb9a5SDavid du Colombier break; 80863afb9a5SDavid du Colombier case Connection: 80963afb9a5SDavid du Colombier if (c->ctlfd >= 0) { 81063afb9a5SDavid du Colombier io = ioproc(); 81163afb9a5SDavid du Colombier seek(c->ctlfd, 0, 0); 81263afb9a5SDavid du Colombier n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */ 81363afb9a5SDavid du Colombier if (n < 0) { 81463afb9a5SDavid du Colombier r->aux = 0; 81563afb9a5SDavid du Colombier responderror(r); 81663afb9a5SDavid du Colombier closeioproc(io); 81763afb9a5SDavid du Colombier break; 81863afb9a5SDavid du Colombier } 81963afb9a5SDavid du Colombier buf[n] = '\0'; 82063afb9a5SDavid du Colombier snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf); 82163afb9a5SDavid du Colombier if ((fd = ioopen(io, path, OREAD)) < 0 || 82263afb9a5SDavid du Colombier (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) { 82363afb9a5SDavid du Colombier r->aux = 0; 82463afb9a5SDavid du Colombier responderror(r); 82563afb9a5SDavid du Colombier if (fd >= 0) 82663afb9a5SDavid du Colombier ioclose(io, fd); 82763afb9a5SDavid du Colombier closeioproc(io); 82863afb9a5SDavid du Colombier break; 82963afb9a5SDavid du Colombier } 83063afb9a5SDavid du Colombier ioclose(io, fd); 83163afb9a5SDavid du Colombier closeioproc(io); 83263afb9a5SDavid du Colombier buf[n] = '\0'; 83363afb9a5SDavid du Colombier readstr(r, buf); 83463afb9a5SDavid du Colombier } else 83563afb9a5SDavid du Colombier readstr(r, "::!0\n"); 83663afb9a5SDavid du Colombier r->aux = 0; 83763afb9a5SDavid du Colombier respond(r, nil); 83863afb9a5SDavid du Colombier break; 83963afb9a5SDavid du Colombier case Subchannel: 84063afb9a5SDavid du Colombier if ((sc->state == Closed || sc->state == Closing || 84163afb9a5SDavid du Colombier sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) { 84263afb9a5SDavid du Colombier sshdebug(c, "sending EOF1 to channel request listener"); 84363afb9a5SDavid du Colombier r->aux = 0; 84463afb9a5SDavid du Colombier respond(r, nil); 84563afb9a5SDavid du Colombier break; 84663afb9a5SDavid du Colombier } 84763afb9a5SDavid du Colombier while (sc->reqq == nil) { 84863afb9a5SDavid du Colombier if (recv(sc->reqchan, nil) < 0) { 84963afb9a5SDavid du Colombier r->aux = 0; 85063afb9a5SDavid du Colombier responderror(r); 85163afb9a5SDavid du Colombier threadexits(nil); 85263afb9a5SDavid du Colombier } 85363afb9a5SDavid du Colombier if ((sc->state == Closed || sc->state == Closing || 85463afb9a5SDavid du Colombier sc->state == Eof) && sc->reqq == nil && 85563afb9a5SDavid du Colombier sc->dataq == nil) { 85663afb9a5SDavid du Colombier sshdebug(c, "sending EOF2 to channel request " 85763afb9a5SDavid du Colombier "listener"); 85863afb9a5SDavid du Colombier respexit(c, r, nil, nil); 85963afb9a5SDavid du Colombier } 86063afb9a5SDavid du Colombier } 86163afb9a5SDavid du Colombier n = r->ifcall.count; 86263afb9a5SDavid du Colombier if (sc->reqq->rem < n) 86363afb9a5SDavid du Colombier n = sc->reqq->rem; 86463afb9a5SDavid du Colombier if (n > Maxrpcbuf) 86563afb9a5SDavid du Colombier n = Maxrpcbuf; 86663afb9a5SDavid du Colombier r->ifcall.offset = 0; 86763afb9a5SDavid du Colombier readbuf(r, sc->reqq->st, n); 86863afb9a5SDavid du Colombier sc->reqq->st += n; 86963afb9a5SDavid du Colombier sc->reqq->rem -= n; 87063afb9a5SDavid du Colombier if (sc->reqq->rem <= 0) { 87163afb9a5SDavid du Colombier Plist *d = sc->reqq; 87263afb9a5SDavid du Colombier sc->reqq = sc->reqq->next; 87363afb9a5SDavid du Colombier free(d->pack); 87463afb9a5SDavid du Colombier free(d); 87563afb9a5SDavid du Colombier } 87663afb9a5SDavid du Colombier r->aux = 0; 87763afb9a5SDavid du Colombier respond(r, nil); 87863afb9a5SDavid du Colombier break; 87963afb9a5SDavid du Colombier } 88063afb9a5SDavid du Colombier threadexits(nil); 88163afb9a5SDavid du Colombier } 88263afb9a5SDavid du Colombier 88363afb9a5SDavid du Colombier void 88463afb9a5SDavid du Colombier readdata(void *a) 88563afb9a5SDavid du Colombier { 88663afb9a5SDavid du Colombier Req *r; 88763afb9a5SDavid du Colombier Conn *c; 88863afb9a5SDavid du Colombier SSHChan *sc; 88963afb9a5SDavid du Colombier int cnum, xconn; 89063afb9a5SDavid du Colombier uvlong qidpath; 89163afb9a5SDavid du Colombier 89263afb9a5SDavid du Colombier threadsetname("readdata"); 89363afb9a5SDavid du Colombier r = a; 89463afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 89563afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 89663afb9a5SDavid du Colombier c = connections[xconn]; 89763afb9a5SDavid du Colombier if (c == nil) { 89863afb9a5SDavid du Colombier respond(r, "bad connection"); 89963afb9a5SDavid du Colombier sshlog(c, "bad connection"); 90063afb9a5SDavid du Colombier threadexits(nil); 90163afb9a5SDavid du Colombier } 90263afb9a5SDavid du Colombier cnum = qidpath & Chanmask; 90363afb9a5SDavid du Colombier sc = c->chans[cnum]; 90463afb9a5SDavid du Colombier if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing || 90563afb9a5SDavid du Colombier sc->state == Eof)) { 90663afb9a5SDavid du Colombier sshdebug(c, "sending EOF1 to channel listener"); 90763afb9a5SDavid du Colombier r->aux = 0; 90863afb9a5SDavid du Colombier respond(r, nil); 90963afb9a5SDavid du Colombier threadexits(nil); 91063afb9a5SDavid du Colombier } 91163afb9a5SDavid du Colombier if (sc->dataq != nil) { 91263afb9a5SDavid du Colombier getdata(c, sc, r); 91363afb9a5SDavid du Colombier threadexits(nil); 91463afb9a5SDavid du Colombier } 91563afb9a5SDavid du Colombier while (sc->dataq == nil) { 91663afb9a5SDavid du Colombier if (recv(sc->inchan, nil) < 0) { 91763afb9a5SDavid du Colombier sshdebug(c, "got interrupt/error in readdata %r"); 91863afb9a5SDavid du Colombier r->aux = 0; 91963afb9a5SDavid du Colombier responderror(r); 92063afb9a5SDavid du Colombier threadexits(nil); 92163afb9a5SDavid du Colombier } 92263afb9a5SDavid du Colombier if (sc->dataq == nil && (sc->state == Closed || 92363afb9a5SDavid du Colombier sc->state == Closing || sc->state == Eof)) { 92463afb9a5SDavid du Colombier sshdebug(c, "sending EOF2 to channel listener"); 92563afb9a5SDavid du Colombier r->aux = 0; 92663afb9a5SDavid du Colombier respond(r, nil); 92763afb9a5SDavid du Colombier threadexits(nil); 92863afb9a5SDavid du Colombier } 92963afb9a5SDavid du Colombier } 93063afb9a5SDavid du Colombier getdata(c, sc, r); 93163afb9a5SDavid du Colombier threadexits(nil); 93263afb9a5SDavid du Colombier } 93363afb9a5SDavid du Colombier 93463afb9a5SDavid du Colombier void 93563afb9a5SDavid du Colombier stwrite(Req *r) 93663afb9a5SDavid du Colombier { 93763afb9a5SDavid du Colombier Conn *c; 93863afb9a5SDavid du Colombier SSHChan *ch; 93963afb9a5SDavid du Colombier int lev, xconn; 94063afb9a5SDavid du Colombier uvlong qidpath; 94163afb9a5SDavid du Colombier 94263afb9a5SDavid du Colombier threadsetname("stwrite"); 94363afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 94463afb9a5SDavid du Colombier lev = qidpath >> Levshift; 94563afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 94663afb9a5SDavid du Colombier c = connections[xconn]; 94763afb9a5SDavid du Colombier if (c == nil) { 94863afb9a5SDavid du Colombier respond(r, "invalid connection"); 94963afb9a5SDavid du Colombier return; 95063afb9a5SDavid du Colombier } 95163afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask]; 95263afb9a5SDavid du Colombier switch ((ulong)(qidpath & Qtypemask)) { 95363afb9a5SDavid du Colombier case Qclone: 95463afb9a5SDavid du Colombier case Qctl: 95563afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writectlproc, r, Defstk); 95663afb9a5SDavid du Colombier break; 95763afb9a5SDavid du Colombier case Qdata: 95863afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count; 95963afb9a5SDavid du Colombier if (lev == Top || lev == Connection || 96063afb9a5SDavid du Colombier c->state == Closed || c->state == Closing || 96163afb9a5SDavid du Colombier ch->state == Closed || ch->state == Closing) { 96263afb9a5SDavid du Colombier respond(r, nil); 96363afb9a5SDavid du Colombier break; 96463afb9a5SDavid du Colombier } 96563afb9a5SDavid du Colombier if (0 && c->stifle) { /* was for coexistence */ 96663afb9a5SDavid du Colombier c->stifle = 0; 96763afb9a5SDavid du Colombier if (deferredinit(c) < 0) { 96863afb9a5SDavid du Colombier respond(r, "deferredinit failed"); 96963afb9a5SDavid du Colombier break; 97063afb9a5SDavid du Colombier } 97163afb9a5SDavid du Colombier } 97263afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writedataproc, r, Defstk); 97363afb9a5SDavid du Colombier break; 97463afb9a5SDavid du Colombier case Qreqrem: 97563afb9a5SDavid du Colombier r->aux = (void *)threadcreate(writereqremproc, r, Defstk); 97663afb9a5SDavid du Colombier break; 97763afb9a5SDavid du Colombier default: 97863afb9a5SDavid du Colombier respond(r, nil); 97963afb9a5SDavid du Colombier break; 98063afb9a5SDavid du Colombier } 98163afb9a5SDavid du Colombier } 98263afb9a5SDavid du Colombier 98363afb9a5SDavid du Colombier static int 98463afb9a5SDavid du Colombier dialbyhand(Conn *c, int ntok, char *toks[]) 98563afb9a5SDavid du Colombier { 98663afb9a5SDavid du Colombier /* 98763afb9a5SDavid du Colombier * this uses /net/tcp to connect directly. 98863afb9a5SDavid du Colombier * should use dial(2) instead of doing it by hand. 98963afb9a5SDavid du Colombier */ 99063afb9a5SDavid du Colombier sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: ""); 99163afb9a5SDavid du Colombier return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: ""); 99263afb9a5SDavid du Colombier } 99363afb9a5SDavid du Colombier 99463afb9a5SDavid du Colombier static void 99563afb9a5SDavid du Colombier userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[]) 99663afb9a5SDavid du Colombier { 99763afb9a5SDavid du Colombier int n; 99863afb9a5SDavid du Colombier char *attrs[5]; 99963afb9a5SDavid du Colombier Packet *p; 100063afb9a5SDavid du Colombier 100163afb9a5SDavid du Colombier if (ntok < 3 || ntok > 4) 100263afb9a5SDavid du Colombier respexit(c, r, buf, "bad connect command"); 100363afb9a5SDavid du Colombier if (!c->service) 100463afb9a5SDavid du Colombier c->service = estrdup9p(toks[0]); 100563afb9a5SDavid du Colombier if (c->user) 100663afb9a5SDavid du Colombier free(c->user); 100763afb9a5SDavid du Colombier c->user = estrdup9p(toks[2]); 100863afb9a5SDavid du Colombier sshdebug(c, "userauth for user %s", c->user); 100963afb9a5SDavid du Colombier 101063afb9a5SDavid du Colombier if (ntok == 4 && strcmp(toks[1], "k") == 0) { 101163afb9a5SDavid du Colombier if (c->authkey) { 101263afb9a5SDavid du Colombier free(c->authkey); 101363afb9a5SDavid du Colombier c->authkey = nil; 101463afb9a5SDavid du Colombier } 101563afb9a5SDavid du Colombier if (c->password) 101663afb9a5SDavid du Colombier free(c->password); 101763afb9a5SDavid du Colombier c->password = estrdup9p(toks[3]); 101863afb9a5SDavid du Colombier sshdebug(c, "userauth got password"); 101963afb9a5SDavid du Colombier } else { 102063afb9a5SDavid du Colombier if (c->password) { 102163afb9a5SDavid du Colombier free(c->password); 102263afb9a5SDavid du Colombier c->password = nil; 102363afb9a5SDavid du Colombier } 102463afb9a5SDavid du Colombier memset(attrs, 0, sizeof attrs); 102563afb9a5SDavid du Colombier attrs[0] = "proto=rsa"; 102663afb9a5SDavid du Colombier attrs[1] = "!dk?"; 102763afb9a5SDavid du Colombier attrs[2] = smprint("user=%s", c->user); 102863afb9a5SDavid du Colombier attrs[3] = smprint("sys=%s", c->remote); 102963afb9a5SDavid du Colombier if (c->authkey) 103063afb9a5SDavid du Colombier free(c->authkey); 103163afb9a5SDavid du Colombier sshdebug(c, "userauth trying rsa"); 103263afb9a5SDavid du Colombier if (ntok == 3) 103363afb9a5SDavid du Colombier c->authkey = factlookup(4, 2, attrs); 103463afb9a5SDavid du Colombier else { 103563afb9a5SDavid du Colombier attrs[4] = toks[3]; 103663afb9a5SDavid du Colombier c->authkey = factlookup(5, 2, attrs); 103763afb9a5SDavid du Colombier } 103863afb9a5SDavid du Colombier free(attrs[2]); 103963afb9a5SDavid du Colombier free(attrs[3]); 104063afb9a5SDavid du Colombier } 104163afb9a5SDavid du Colombier 104263afb9a5SDavid du Colombier if (!c->password && !c->authkey) 104363afb9a5SDavid du Colombier respexit(c, r, buf, "no auth info"); 104463afb9a5SDavid du Colombier else if (c->state != Authing) { 104563afb9a5SDavid du Colombier p = new_packet(c); 104663afb9a5SDavid du Colombier add_byte(p, SSH_MSG_SERVICE_REQUEST); 104763afb9a5SDavid du Colombier add_string(p, c->service); 104863afb9a5SDavid du Colombier n = finish_packet(p); 104963afb9a5SDavid du Colombier sshdebug(c, "sending msg svc req for %s", c->service); 105063afb9a5SDavid du Colombier if (writeio(c->dio, c->datafd, p->nlength, n) != n) { 105163afb9a5SDavid du Colombier sshdebug(c, "authing write failed: %r"); 105263afb9a5SDavid du Colombier free(p); 105363afb9a5SDavid du Colombier r->aux = 0; 105463afb9a5SDavid du Colombier responderror(r); 105563afb9a5SDavid du Colombier free(buf); 105663afb9a5SDavid du Colombier threadexits(nil); 105763afb9a5SDavid du Colombier } 105863afb9a5SDavid du Colombier free(p); 105963afb9a5SDavid du Colombier } else 106063afb9a5SDavid du Colombier if (client_auth(c, c->dio) < 0) 106163afb9a5SDavid du Colombier respexit(c, r, buf, "ssh-userauth client auth failed"); 106263afb9a5SDavid du Colombier qlock(&c->l); 106363afb9a5SDavid du Colombier if (c->state != Established) { 106463afb9a5SDavid du Colombier sshdebug(c, "sleeping for auth"); 106563afb9a5SDavid du Colombier rsleep(&c->r); 106663afb9a5SDavid du Colombier } 106763afb9a5SDavid du Colombier qunlock(&c->l); 106863afb9a5SDavid du Colombier if (c->state != Established) 106963afb9a5SDavid du Colombier respexit(c, r, buf, "ssh-userath auth failed (not Established)"); 107063afb9a5SDavid du Colombier } 107163afb9a5SDavid du Colombier 107263afb9a5SDavid du Colombier void 107363afb9a5SDavid du Colombier writectlproc(void *a) 107463afb9a5SDavid du Colombier { 107563afb9a5SDavid du Colombier Req *r; 107663afb9a5SDavid du Colombier Packet *p; 107763afb9a5SDavid du Colombier Conn *c; 107863afb9a5SDavid du Colombier SSHChan *ch; 107963afb9a5SDavid du Colombier char *tcpconn2, *buf, *toks[4]; 108063afb9a5SDavid du Colombier int n, ntok, lev, xconn; 108163afb9a5SDavid du Colombier uvlong qidpath; 108263afb9a5SDavid du Colombier char path[NETPATHLEN], tcpconn[Numbsz]; 108363afb9a5SDavid du Colombier 108463afb9a5SDavid du Colombier threadsetname("writectlproc"); 108563afb9a5SDavid du Colombier r = a; 108663afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 108763afb9a5SDavid du Colombier lev = qidpath >> Levshift; 108863afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 108963afb9a5SDavid du Colombier 109063afb9a5SDavid du Colombier c = connections[xconn]; 109163afb9a5SDavid du Colombier if (c == nil) { 109263afb9a5SDavid du Colombier respond(r, "bad connection"); 109363afb9a5SDavid du Colombier sshlog(c, "bad connection"); 109463afb9a5SDavid du Colombier threadexits(nil); 109563afb9a5SDavid du Colombier } 109663afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask]; 109763afb9a5SDavid du Colombier 109863afb9a5SDavid du Colombier if (r->ifcall.count <= Numbsz) 109963afb9a5SDavid du Colombier buf = emalloc9p(Numbsz + 1); 110063afb9a5SDavid du Colombier else 110163afb9a5SDavid du Colombier buf = emalloc9p(r->ifcall.count + 1); 110263afb9a5SDavid du Colombier memmove(buf, r->ifcall.data, r->ifcall.count); 110363afb9a5SDavid du Colombier buf[r->ifcall.count] = '\0'; 110463afb9a5SDavid du Colombier 110563afb9a5SDavid du Colombier sshdebug(c, "level %d writectl: %s", lev, buf); 110663afb9a5SDavid du Colombier ntok = tokenize(buf, toks, nelem(toks)); 110763afb9a5SDavid du Colombier switch (lev) { 110863afb9a5SDavid du Colombier case Connection: 110963afb9a5SDavid du Colombier if (strcmp(toks[0], "id") == 0) { /* was for sshswitch */ 111063afb9a5SDavid du Colombier if (ntok < 2) 111163afb9a5SDavid du Colombier respexit(c, r, buf, "bad id request"); 111263afb9a5SDavid du Colombier strncpy(c->idstring, toks[1], sizeof c->idstring); 111363afb9a5SDavid du Colombier sshdebug(c, "id %s", toks[1]); 111463afb9a5SDavid du Colombier break; 111563afb9a5SDavid du Colombier } 111663afb9a5SDavid du Colombier if (strcmp(toks[0], "connect") == 0) { 111763afb9a5SDavid du Colombier if (ntok < 2) 111863afb9a5SDavid du Colombier respexit(c, r, buf, "bad connect request"); 111963afb9a5SDavid du Colombier /* 112063afb9a5SDavid du Colombier * should use dial(2) instead of doing it by hand. 112163afb9a5SDavid du Colombier */ 112263afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn)); 112363afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); 112463afb9a5SDavid du Colombier dialbyhand(c, ntok, toks); 112563afb9a5SDavid du Colombier 112663afb9a5SDavid du Colombier c->role = Client; 112763afb9a5SDavid du Colombier /* Override the PKA list; we can take any in */ 112863afb9a5SDavid du Colombier pkas[0] = &rsa_pka; 112963afb9a5SDavid du Colombier pkas[1] = &dss_pka; 113063afb9a5SDavid du Colombier pkas[2] = nil; 113163afb9a5SDavid du Colombier tcpconn2 = estrdup9p(tcpconn); 113263afb9a5SDavid du Colombier 113363afb9a5SDavid du Colombier /* swap id strings, negotiate crypto */ 113463afb9a5SDavid du Colombier if (dohandshake(c, tcpconn2) < 0) { 113563afb9a5SDavid du Colombier sshlog(c, "connect handshake failed: " 113663afb9a5SDavid du Colombier "tcp conn %s", tcpconn2); 113763afb9a5SDavid du Colombier free(tcpconn2); 113863afb9a5SDavid du Colombier respexit(c, r, buf, "connect handshake failed"); 113963afb9a5SDavid du Colombier } 114063afb9a5SDavid du Colombier free(tcpconn2); 114163afb9a5SDavid du Colombier keymbox.state = Empty; 114263afb9a5SDavid du Colombier nbsendul(keymbox.mchan, 1); 114363afb9a5SDavid du Colombier break; 114463afb9a5SDavid du Colombier } 114563afb9a5SDavid du Colombier 114663afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing) 114763afb9a5SDavid du Colombier respexit(c, r, buf, "connection closed"); 114863afb9a5SDavid du Colombier if (strcmp(toks[0], "ssh-userauth") == 0) 114963afb9a5SDavid du Colombier userauth(c, r, buf, ntok, toks); 115063afb9a5SDavid du Colombier else if (strcmp(toks[0], "ssh-connection") == 0) { 115163afb9a5SDavid du Colombier /* your ad here */ 115263afb9a5SDavid du Colombier } else if (strcmp(toks[0], "hangup") == 0) { 115363afb9a5SDavid du Colombier if (c->rpid >= 0) 115463afb9a5SDavid du Colombier threadint(c->rpid); 115563afb9a5SDavid du Colombier shutdown(c); 115663afb9a5SDavid du Colombier } else if (strcmp(toks[0], "announce") == 0) { 115763afb9a5SDavid du Colombier sshdebug(c, "got %s argument for announce", toks[1]); 115863afb9a5SDavid du Colombier write(c->ctlfd, r->ifcall.data, r->ifcall.count); 115963afb9a5SDavid du Colombier } else if (strcmp(toks[0], "accept") == 0) { 116063afb9a5SDavid du Colombier /* should use dial(2) instead of diddling /net/tcp */ 116163afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn)); 116263afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); 116363afb9a5SDavid du Colombier fprint(c->ctlfd, "accept %s", tcpconn); 116463afb9a5SDavid du Colombier 116563afb9a5SDavid du Colombier c->role = Server; 116663afb9a5SDavid du Colombier tcpconn2 = estrdup9p(tcpconn); 116763afb9a5SDavid du Colombier /* swap id strings, negotiate crypto */ 116863afb9a5SDavid du Colombier if (dohandshake(c, tcpconn2) < 0) { 116963afb9a5SDavid du Colombier sshlog(c, "accept handshake failed: " 117063afb9a5SDavid du Colombier "tcp conn %s", tcpconn2); 117163afb9a5SDavid du Colombier free(tcpconn2); 117263afb9a5SDavid du Colombier shutdown(c); 117363afb9a5SDavid du Colombier respexit(c, r, buf, "accept handshake failed"); 117463afb9a5SDavid du Colombier } 117563afb9a5SDavid du Colombier free(tcpconn2); 117663afb9a5SDavid du Colombier } else if (strcmp(toks[0], "reject") == 0) { 117763afb9a5SDavid du Colombier memset(tcpconn, '\0', sizeof(tcpconn)); 117863afb9a5SDavid du Colombier pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); 117963afb9a5SDavid du Colombier 118063afb9a5SDavid du Colombier snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn); 118163afb9a5SDavid du Colombier c->datafd = open(path, ORDWR); 118263afb9a5SDavid du Colombier 118363afb9a5SDavid du Colombier p = new_packet(c); 118463afb9a5SDavid du Colombier add_byte(p, SSH_MSG_DISCONNECT); 118563afb9a5SDavid du Colombier add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT); 118663afb9a5SDavid du Colombier add_string(p, toks[2]); 118763afb9a5SDavid du Colombier add_string(p, "EN"); 118863afb9a5SDavid du Colombier n = finish_packet(p); 118963afb9a5SDavid du Colombier if (c->dio && c->datafd >= 0) 119063afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 119163afb9a5SDavid du Colombier free(p); 119263afb9a5SDavid du Colombier if (c->ctlfd >= 0) 119363afb9a5SDavid du Colombier fprint(c->ctlfd, "reject %s %s", buf, toks[2]); 119463afb9a5SDavid du Colombier if (c->rpid >= 0) 119563afb9a5SDavid du Colombier threadint(c->rpid); 119663afb9a5SDavid du Colombier shutdown(c); 119763afb9a5SDavid du Colombier } 119863afb9a5SDavid du Colombier break; 119963afb9a5SDavid du Colombier case Subchannel: 120063afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing) 120163afb9a5SDavid du Colombier respexit(c, r, buf, "channel closed"); 120263afb9a5SDavid du Colombier if (strcmp(toks[0], "connect") == 0) { 120363afb9a5SDavid du Colombier p = new_packet(c); 120463afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_OPEN); 120563afb9a5SDavid du Colombier sshdebug(c, "chan writectl: connect %s", 120663afb9a5SDavid du Colombier ntok > 1? toks[1]: "session"); 120763afb9a5SDavid du Colombier add_string(p, ntok > 1? toks[1]: "session"); 120863afb9a5SDavid du Colombier add_uint32(p, ch->id); 120963afb9a5SDavid du Colombier add_uint32(p, Maxpayload); 121063afb9a5SDavid du Colombier add_uint32(p, Maxrpcbuf); 121163afb9a5SDavid du Colombier /* more stuff if it's an x11 session */ 121263afb9a5SDavid du Colombier n = finish_packet(p); 121363afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 121463afb9a5SDavid du Colombier free(p); 121563afb9a5SDavid du Colombier qlock(&c->l); 121663afb9a5SDavid du Colombier if (ch->otherid == -1) 121763afb9a5SDavid du Colombier rsleep(&ch->r); 121863afb9a5SDavid du Colombier qunlock(&c->l); 121963afb9a5SDavid du Colombier } else if (strcmp(toks[0], "global") == 0) { 122063afb9a5SDavid du Colombier /* your ad here */ 122163afb9a5SDavid du Colombier } else if (strcmp(toks[0], "hangup") == 0) { 122263afb9a5SDavid du Colombier if (ch->state != Closed && ch->state != Closing) { 122363afb9a5SDavid du Colombier ch->state = Closing; 122463afb9a5SDavid du Colombier if (ch->otherid != -1) { 122563afb9a5SDavid du Colombier p = new_packet(c); 122663afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE); 122763afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 122863afb9a5SDavid du Colombier n = finish_packet(p); 122963afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 123063afb9a5SDavid du Colombier free(p); 123163afb9a5SDavid du Colombier } 123263afb9a5SDavid du Colombier qlock(&c->l); 123363afb9a5SDavid du Colombier rwakeup(&ch->r); 123463afb9a5SDavid du Colombier qunlock(&c->l); 123563afb9a5SDavid du Colombier nbsendul(ch->inchan, 1); 123663afb9a5SDavid du Colombier nbsendul(ch->reqchan, 1); 123763afb9a5SDavid du Colombier } 123863afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (c->chans[n] == nil || 123963afb9a5SDavid du Colombier c->chans[n]->state == Empty || 124063afb9a5SDavid du Colombier c->chans[n]->state == Closing || 124163afb9a5SDavid du Colombier c->chans[n]->state == Closed); ++n) 124263afb9a5SDavid du Colombier ; 124363afb9a5SDavid du Colombier if (n >= MAXCONN) { 124463afb9a5SDavid du Colombier if (c->rpid >= 0) 124563afb9a5SDavid du Colombier threadint(c->rpid); 124663afb9a5SDavid du Colombier shutdown(c); 124763afb9a5SDavid du Colombier } 124863afb9a5SDavid du Colombier } else if (strcmp(toks[0], "announce") == 0) { 124963afb9a5SDavid du Colombier sshdebug(c, "got argument `%s' for chan announce", 125063afb9a5SDavid du Colombier toks[1]); 125163afb9a5SDavid du Colombier free(ch->ann); 125263afb9a5SDavid du Colombier ch->ann = estrdup9p(toks[1]); 125363afb9a5SDavid du Colombier } 125463afb9a5SDavid du Colombier break; 125563afb9a5SDavid du Colombier } 125663afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count; 125763afb9a5SDavid du Colombier r->aux = 0; 125863afb9a5SDavid du Colombier respond(r, nil); 125963afb9a5SDavid du Colombier free(buf); 126063afb9a5SDavid du Colombier threadexits(nil); 126163afb9a5SDavid du Colombier } 126263afb9a5SDavid du Colombier 126363afb9a5SDavid du Colombier void 126463afb9a5SDavid du Colombier writereqremproc(void *a) 126563afb9a5SDavid du Colombier { 126663afb9a5SDavid du Colombier Req *r; 126763afb9a5SDavid du Colombier Packet *p; 126863afb9a5SDavid du Colombier Conn *c; 126963afb9a5SDavid du Colombier SSHChan *ch; 127063afb9a5SDavid du Colombier char *cmd, *q, *buf, *toks[4]; 127163afb9a5SDavid du Colombier int n, ntok, lev, xconn; 127263afb9a5SDavid du Colombier uvlong qidpath; 127363afb9a5SDavid du Colombier 127463afb9a5SDavid du Colombier threadsetname("writereqremproc"); 127563afb9a5SDavid du Colombier r = a; 127663afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 127763afb9a5SDavid du Colombier lev = qidpath >> Levshift; 127863afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 127963afb9a5SDavid du Colombier c = connections[xconn]; 128063afb9a5SDavid du Colombier if (c == nil) { 128163afb9a5SDavid du Colombier respond(r, "Invalid connection"); 128263afb9a5SDavid du Colombier threadexits(nil); 128363afb9a5SDavid du Colombier } 128463afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask]; 128563afb9a5SDavid du Colombier if (r->ifcall.count <= 10) 128663afb9a5SDavid du Colombier buf = emalloc9p(10 + 1); 128763afb9a5SDavid du Colombier else 128863afb9a5SDavid du Colombier buf = emalloc9p(r->ifcall.count + 1); 128963afb9a5SDavid du Colombier memmove(buf, r->ifcall.data, r->ifcall.count); 129063afb9a5SDavid du Colombier buf[r->ifcall.count] = '\0'; 129163afb9a5SDavid du Colombier sshdebug(c, "writereqrem: %s", buf); 129263afb9a5SDavid du Colombier ntok = tokenize(buf, toks, nelem(toks)); 129363afb9a5SDavid du Colombier 129463afb9a5SDavid du Colombier if (lev == Top) { 129563afb9a5SDavid du Colombier free(keymbox.msg); 129663afb9a5SDavid du Colombier keymbox.msg = buf; 129763afb9a5SDavid du Colombier nbsendul(keymbox.mchan, 1); 129863afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count; 129963afb9a5SDavid du Colombier respexit(c, r, nil, nil); 130063afb9a5SDavid du Colombier } 130163afb9a5SDavid du Colombier 130263afb9a5SDavid du Colombier r->ofcall.count = r->ifcall.count; 130363afb9a5SDavid du Colombier if (c->state == Closed || c->state == Closing || 130463afb9a5SDavid du Colombier ch->state == Closed || ch->state == Closing) 130563afb9a5SDavid du Colombier respexit(c, r, buf, nil); 130663afb9a5SDavid du Colombier 130763afb9a5SDavid du Colombier p = new_packet(c); 130863afb9a5SDavid du Colombier if (strcmp(toks[0], "success") == 0) { 130963afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_SUCCESS); 131063afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 131163afb9a5SDavid du Colombier } else if (strcmp(toks[0], "failure") == 0) { 131263afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_FAILURE); 131363afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 131463afb9a5SDavid du Colombier } else if (strcmp(toks[0], "close") == 0) { 131563afb9a5SDavid du Colombier ch->state = Closing; 131663afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE); 131763afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 131863afb9a5SDavid du Colombier } else if (strcmp(toks[0], "shell") == 0) { 131963afb9a5SDavid du Colombier ch->state = Established; 132063afb9a5SDavid du Colombier /* 132163afb9a5SDavid du Colombier * Some servers *cough*OpenSSH*cough* don't seem to be able 132263afb9a5SDavid du Colombier * to intelligently handle a shell with no pty. 132363afb9a5SDavid du Colombier */ 132463afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST); 132563afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 132663afb9a5SDavid du Colombier add_string(p, "pty-req"); 132763afb9a5SDavid du Colombier add_byte(p, 0); 132863afb9a5SDavid du Colombier if (ntok == 1) 132963afb9a5SDavid du Colombier add_string(p, "dumb"); 133063afb9a5SDavid du Colombier else 133163afb9a5SDavid du Colombier add_string(p, toks[1]); 133263afb9a5SDavid du Colombier add_uint32(p, 0); 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_string(p, ""); 133763afb9a5SDavid du Colombier n = finish_packet(p); 133863afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 133963afb9a5SDavid du Colombier init_packet(p); 134063afb9a5SDavid du Colombier p->c = c; 134163afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST); 134263afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 134363afb9a5SDavid du Colombier add_string(p, "shell"); 134463afb9a5SDavid du Colombier add_byte(p, 0); 134563afb9a5SDavid du Colombier sshdebug(c, "sending shell request: rlength=%lud twindow=%lud", 134663afb9a5SDavid du Colombier p->rlength, ch->twindow); 134763afb9a5SDavid du Colombier } else if (strcmp(toks[0], "exec") == 0) { 134863afb9a5SDavid du Colombier ch->state = Established; 134963afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_REQUEST); 135063afb9a5SDavid du Colombier add_uint32(p, ch->otherid); 135163afb9a5SDavid du Colombier add_string(p, "exec"); 135263afb9a5SDavid du Colombier add_byte(p, 0); 1353*d7e44d0dSDavid du Colombier 135463afb9a5SDavid du Colombier cmd = emalloc9p(Bigbufsz); 135563afb9a5SDavid du Colombier q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]); 135663afb9a5SDavid du Colombier for (n = 2; n < ntok; ++n) { 1357*d7e44d0dSDavid du Colombier q = seprint(q, cmd+Bigbufsz, " %q", toks[n]); 135863afb9a5SDavid du Colombier if (q == nil) 135963afb9a5SDavid du Colombier break; 136063afb9a5SDavid du Colombier } 136163afb9a5SDavid du Colombier add_string(p, cmd); 136263afb9a5SDavid du Colombier free(cmd); 136363afb9a5SDavid du Colombier } else 136463afb9a5SDavid du Colombier respexit(c, r, buf, "bad request command"); 136563afb9a5SDavid du Colombier n = finish_packet(p); 136663afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 136763afb9a5SDavid du Colombier free(p); 136863afb9a5SDavid du Colombier respexit(c, r, buf, nil); 136963afb9a5SDavid du Colombier } 137063afb9a5SDavid du Colombier 137163afb9a5SDavid du Colombier void 137263afb9a5SDavid du Colombier writedataproc(void *a) 137363afb9a5SDavid du Colombier { 137463afb9a5SDavid du Colombier Req *r; 137563afb9a5SDavid du Colombier Packet *p; 137663afb9a5SDavid du Colombier Conn *c; 137763afb9a5SDavid du Colombier SSHChan *ch; 137863afb9a5SDavid du Colombier int n, xconn; 137963afb9a5SDavid du Colombier uvlong qidpath; 138063afb9a5SDavid du Colombier 138163afb9a5SDavid du Colombier threadsetname("writedataproc"); 138263afb9a5SDavid du Colombier r = a; 138363afb9a5SDavid du Colombier qidpath = (uvlong)r->fid->file->aux; 138463afb9a5SDavid du Colombier xconn = (qidpath >> Connshift) & Connmask; 138563afb9a5SDavid du Colombier c = connections[xconn]; 138663afb9a5SDavid du Colombier if (c == nil) { 138763afb9a5SDavid du Colombier respond(r, "Invalid connection"); 138863afb9a5SDavid du Colombier threadexits(nil); 138963afb9a5SDavid du Colombier } 139063afb9a5SDavid du Colombier ch = c->chans[qidpath & Chanmask]; 139163afb9a5SDavid du Colombier 139263afb9a5SDavid du Colombier p = new_packet(c); 139363afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_DATA); 139463afb9a5SDavid du Colombier hnputl(p->payload+1, ch->otherid); 139563afb9a5SDavid du Colombier p->rlength += 4; 139663afb9a5SDavid du Colombier add_block(p, r->ifcall.data, r->ifcall.count); 139763afb9a5SDavid du Colombier n = finish_packet(p); 139863afb9a5SDavid du Colombier 139963afb9a5SDavid du Colombier if (ch->sent + p->rlength > ch->twindow) { 140063afb9a5SDavid du Colombier qlock(&ch->xmtlock); 140163afb9a5SDavid du Colombier while (ch->sent + p->rlength > ch->twindow) 140263afb9a5SDavid du Colombier rsleep(&ch->xmtrendez); 140363afb9a5SDavid du Colombier qunlock(&ch->xmtlock); 140463afb9a5SDavid du Colombier } 140563afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 140663afb9a5SDavid du Colombier respexit(c, r, p, nil); 140763afb9a5SDavid du Colombier } 140863afb9a5SDavid du Colombier 140963afb9a5SDavid du Colombier /* 141063afb9a5SDavid du Colombier * Although this is named stclunk, it's attached to the destroyfid 141163afb9a5SDavid du Colombier * member of the Srv struct. It turns out there's no member 141263afb9a5SDavid du Colombier * called clunk. But if there are no other references, a 9P Tclunk 141363afb9a5SDavid du Colombier * will end up calling destroyfid. 141463afb9a5SDavid du Colombier */ 141563afb9a5SDavid du Colombier void 141663afb9a5SDavid du Colombier stclunk(Fid *f) 141763afb9a5SDavid du Colombier { 141863afb9a5SDavid du Colombier Packet *p; 141963afb9a5SDavid du Colombier Conn *c; 142063afb9a5SDavid du Colombier SSHChan *sc; 142163afb9a5SDavid du Colombier int n, lev, cnum, chnum; 142263afb9a5SDavid du Colombier uvlong qidpath; 142363afb9a5SDavid du Colombier 142463afb9a5SDavid du Colombier threadsetname("stclunk"); 142563afb9a5SDavid du Colombier if (f == nil || f->file == nil) 142663afb9a5SDavid du Colombier return; 142763afb9a5SDavid du Colombier qidpath = (uvlong)f->file->aux; 142863afb9a5SDavid du Colombier lev = qidpath >> Levshift; 142963afb9a5SDavid du Colombier cnum = (qidpath >> Connshift) & Connmask; 143063afb9a5SDavid du Colombier chnum = qidpath & Chanmask; 143163afb9a5SDavid du Colombier c = connections[cnum]; 143263afb9a5SDavid du Colombier sshdebug(c, "got clunk on file: %#llux %d %d %d: %s", 143363afb9a5SDavid du Colombier qidpath, lev, cnum, chnum, f->file->name); 143463afb9a5SDavid du Colombier /* qidpath test implies conn 0, chan 0 */ 143563afb9a5SDavid du Colombier if (lev == Top && qidpath == Qreqrem) { 143663afb9a5SDavid du Colombier if (keymbox.state != Empty) { 143763afb9a5SDavid du Colombier keymbox.state = Empty; 143863afb9a5SDavid du Colombier // nbsendul(keymbox.mchan, 1); 143963afb9a5SDavid du Colombier } 144063afb9a5SDavid du Colombier keymbox.msg = nil; 144163afb9a5SDavid du Colombier return; 144263afb9a5SDavid du Colombier } 144363afb9a5SDavid du Colombier 144463afb9a5SDavid du Colombier if (c == nil) 144563afb9a5SDavid du Colombier return; 144663afb9a5SDavid du Colombier if (lev == Connection && (qidpath & Qtypemask) == Qctl && 144763afb9a5SDavid du Colombier (c->state == Opening || c->state == Negotiating || 144863afb9a5SDavid du Colombier c->state == Authing)) { 144963afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (!c->chans[n] || 145063afb9a5SDavid du Colombier c->chans[n]->state == Empty || 145163afb9a5SDavid du Colombier c->chans[n]->state == Closed || 145263afb9a5SDavid du Colombier c->chans[n]->state == Closing); ++n) 145363afb9a5SDavid du Colombier ; 145463afb9a5SDavid du Colombier if (n >= MAXCONN) { 145563afb9a5SDavid du Colombier if (c->rpid >= 0) 145663afb9a5SDavid du Colombier threadint(c->rpid); 145763afb9a5SDavid du Colombier shutdown(c); 145863afb9a5SDavid du Colombier } 145963afb9a5SDavid du Colombier return; 146063afb9a5SDavid du Colombier } 146163afb9a5SDavid du Colombier 146263afb9a5SDavid du Colombier sc = c->chans[chnum]; 146363afb9a5SDavid du Colombier if (lev != Subchannel) 146463afb9a5SDavid du Colombier return; 146563afb9a5SDavid du Colombier if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) { 146663afb9a5SDavid du Colombier qlock(&c->l); 146763afb9a5SDavid du Colombier if (sc->state != Closed) { 146863afb9a5SDavid du Colombier sc->state = Closed; 146963afb9a5SDavid du Colombier chanclose(sc->inchan); 147063afb9a5SDavid du Colombier chanclose(sc->reqchan); 147163afb9a5SDavid du Colombier } 147263afb9a5SDavid du Colombier qunlock(&c->l); 147363afb9a5SDavid du Colombier } else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty && 147463afb9a5SDavid du Colombier sc->state != Closed && sc->state != Closing) { 147563afb9a5SDavid du Colombier if (f->file != sc->data && f->file != sc->request) { 147663afb9a5SDavid du Colombier sshlog(c, "great evil is upon us; destroying a fid " 147763afb9a5SDavid du Colombier "we didn't create"); 147863afb9a5SDavid du Colombier return; 147963afb9a5SDavid du Colombier } 148063afb9a5SDavid du Colombier 148163afb9a5SDavid du Colombier p = new_packet(c); 148263afb9a5SDavid du Colombier add_byte(p, SSH_MSG_CHANNEL_CLOSE); 148363afb9a5SDavid du Colombier hnputl(p->payload+1, sc->otherid); 148463afb9a5SDavid du Colombier p->rlength += 4; 148563afb9a5SDavid du Colombier n = finish_packet(p); 148663afb9a5SDavid du Colombier sc->state = Closing; 148763afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p->nlength, n); 148863afb9a5SDavid du Colombier free(p); 148963afb9a5SDavid du Colombier 149063afb9a5SDavid du Colombier qlock(&c->l); 149163afb9a5SDavid du Colombier rwakeup(&sc->r); 149263afb9a5SDavid du Colombier qunlock(&c->l); 149363afb9a5SDavid du Colombier nbsendul(sc->inchan, 1); 149463afb9a5SDavid du Colombier nbsendul(sc->reqchan, 1); 149563afb9a5SDavid du Colombier } 149663afb9a5SDavid du Colombier for (n = 0; n < MAXCONN && (!c->chans[n] || 149763afb9a5SDavid du Colombier c->chans[n]->state == Empty || c->chans[n]->state == Closed || 149863afb9a5SDavid du Colombier c->chans[n]->state == Closing); ++n) 149963afb9a5SDavid du Colombier ; 150063afb9a5SDavid du Colombier if (n >= MAXCONN) { 150163afb9a5SDavid du Colombier if (c->rpid >= 0) 150263afb9a5SDavid du Colombier threadint(c->rpid); 150363afb9a5SDavid du Colombier shutdown(c); 150463afb9a5SDavid du Colombier } 150563afb9a5SDavid du Colombier } 150663afb9a5SDavid du Colombier 150763afb9a5SDavid du Colombier void 150863afb9a5SDavid du Colombier stflush(Req *r) 150963afb9a5SDavid du Colombier { 151063afb9a5SDavid du Colombier Req *or; 151163afb9a5SDavid du Colombier uvlong qidpath; 151263afb9a5SDavid du Colombier 151363afb9a5SDavid du Colombier threadsetname("stflush"); 151463afb9a5SDavid du Colombier or = r->oldreq; 151563afb9a5SDavid du Colombier qidpath = (uvlong)or->fid->file->aux; 151663afb9a5SDavid du Colombier sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p", 151763afb9a5SDavid du Colombier argv0, qidpath, qidpath >> Levshift, 151863afb9a5SDavid du Colombier (qidpath >> Connshift) & Connmask, qidpath & Chanmask, 151963afb9a5SDavid du Colombier or->fid->file->name, or->aux); 152063afb9a5SDavid du Colombier if (!or->aux) 152163afb9a5SDavid du Colombier respond(or, "interrupted"); 152263afb9a5SDavid du Colombier else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten && 152363afb9a5SDavid du Colombier (qidpath >> Levshift) == Connection || 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 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 * 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 * 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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); 2418*d7e44d0dSDavid 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 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 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)) { 253763afb9a5SDavid du Colombier case 0: 253863afb9a5SDavid du Colombier establish(c); 253963afb9a5SDavid du Colombier break; 254063afb9a5SDavid du Colombier case -1: 254163afb9a5SDavid du Colombier break; 254263afb9a5SDavid du Colombier case -2: 254363afb9a5SDavid du Colombier bail(c, p, p2, "in userauth request"); 254463afb9a5SDavid du Colombier } 254563afb9a5SDavid du Colombier break; 254663afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_FAILURE: 254763afb9a5SDavid du Colombier qlock(&c->l); 254863afb9a5SDavid du Colombier rwakeup(&c->r); 254963afb9a5SDavid du Colombier qunlock(&c->l); 255063afb9a5SDavid du Colombier break; 255163afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_SUCCESS: 255263afb9a5SDavid du Colombier establish(c); 255363afb9a5SDavid du Colombier break; 255463afb9a5SDavid du Colombier case SSH_MSG_USERAUTH_BANNER: 255563afb9a5SDavid du Colombier break; 255663afb9a5SDavid du Colombier } 255763afb9a5SDavid du Colombier } else if (c->state == Established) { 255863afb9a5SDavid du Colombier if (established(c, p, p2, buf, sizeof buf) < 0) 255963afb9a5SDavid du Colombier bail(c, p, p2, "from established state"); 256063afb9a5SDavid du Colombier } else { 256163afb9a5SDavid du Colombier sshdebug(c, "connection %d in bad state, reader exiting", c->id); 256263afb9a5SDavid du Colombier bail(c, p, p2, "bad conn state"); 256363afb9a5SDavid du Colombier } 256463afb9a5SDavid du Colombier } 256563afb9a5SDavid du Colombier 256663afb9a5SDavid du Colombier void 256763afb9a5SDavid du Colombier reader(void *a) 256863afb9a5SDavid du Colombier { 256963afb9a5SDavid du Colombier Conn *c; 257063afb9a5SDavid du Colombier Packet *p, *p2; 257163afb9a5SDavid du Colombier 257263afb9a5SDavid du Colombier threadsetname("reader"); 257363afb9a5SDavid du Colombier c = a; 257463afb9a5SDavid du Colombier c->rpid = threadid(); 257563afb9a5SDavid du Colombier sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid); 257663afb9a5SDavid du Colombier threadsetname("reader"); 257763afb9a5SDavid du Colombier p = new_packet(c); 257863afb9a5SDavid du Colombier p2 = new_packet(c); 257963afb9a5SDavid du Colombier c->rio = ioproc(); 258063afb9a5SDavid du Colombier for(;;) 258163afb9a5SDavid du Colombier reader0(c, p, p2); 258263afb9a5SDavid du Colombier } 258363afb9a5SDavid du Colombier 258463afb9a5SDavid du Colombier int 258563afb9a5SDavid du Colombier validatekex(Conn *c, Packet *p) 258663afb9a5SDavid du Colombier { 258763afb9a5SDavid du Colombier if (c->role == Server) 258863afb9a5SDavid du Colombier return validatekexs(p); 258963afb9a5SDavid du Colombier else 259063afb9a5SDavid du Colombier return validatekexc(p); 259163afb9a5SDavid du Colombier } 259263afb9a5SDavid du Colombier 259363afb9a5SDavid du Colombier int 259463afb9a5SDavid du Colombier validatekexs(Packet *p) 259563afb9a5SDavid du Colombier { 259663afb9a5SDavid du Colombier uchar *q; 259763afb9a5SDavid du Colombier char *toks[Maxtoks]; 259863afb9a5SDavid du Colombier int i, j, n; 259963afb9a5SDavid du Colombier char *buf; 260063afb9a5SDavid du Colombier 260163afb9a5SDavid du Colombier buf = emalloc9p(Bigbufsz); 260263afb9a5SDavid du Colombier q = p->payload + 17; 260363afb9a5SDavid du Colombier 260463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 260563afb9a5SDavid du Colombier sshdebug(nil, "received KEX algs: %s", buf); 260663afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 260763afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 260863afb9a5SDavid du Colombier for (j = 0; j < nelem(kexes); ++j) 260963afb9a5SDavid du Colombier if (strcmp(toks[i], kexes[j]->name) == 0) 261063afb9a5SDavid du Colombier goto foundk; 261163afb9a5SDavid du Colombier sshdebug(nil, "kex algs not in kexes"); 261263afb9a5SDavid du Colombier free(buf); 261363afb9a5SDavid du Colombier return -1; 261463afb9a5SDavid du Colombier foundk: 261563afb9a5SDavid du Colombier p->c->kexalg = j; 261663afb9a5SDavid du Colombier 261763afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 261863afb9a5SDavid du Colombier sshdebug(nil, "received host key algs: %s", buf); 261963afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 262063afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 262163afb9a5SDavid du Colombier for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) 262263afb9a5SDavid du Colombier if (strcmp(toks[i], pkas[j]->name) == 0) 262363afb9a5SDavid du Colombier goto foundpka; 262463afb9a5SDavid du Colombier sshdebug(nil, "host key algs not in pkas"); 262563afb9a5SDavid du Colombier free(buf); 262663afb9a5SDavid du Colombier return -1; 262763afb9a5SDavid du Colombier foundpka: 262863afb9a5SDavid du Colombier p->c->pkalg = j; 262963afb9a5SDavid du Colombier 263063afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 263163afb9a5SDavid du Colombier sshdebug(nil, "received C2S crypto algs: %s", buf); 263263afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 263363afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 263463afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j) 263563afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0) 263663afb9a5SDavid du Colombier goto foundc1; 263763afb9a5SDavid du Colombier sshdebug(nil, "c2s crypto algs not in cryptos"); 263863afb9a5SDavid du Colombier free(buf); 263963afb9a5SDavid du Colombier return -1; 264063afb9a5SDavid du Colombier foundc1: 264163afb9a5SDavid du Colombier p->c->ncscrypt = j; 264263afb9a5SDavid du Colombier 264363afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 264463afb9a5SDavid du Colombier sshdebug(nil, "received S2C crypto algs: %s", buf); 264563afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 264663afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 264763afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j) 264863afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0) 264963afb9a5SDavid du Colombier goto foundc2; 265063afb9a5SDavid du Colombier sshdebug(nil, "s2c crypto algs not in cryptos"); 265163afb9a5SDavid du Colombier free(buf); 265263afb9a5SDavid du Colombier return -1; 265363afb9a5SDavid du Colombier foundc2: 265463afb9a5SDavid du Colombier p->c->nsccrypt = j; 265563afb9a5SDavid du Colombier 265663afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 265763afb9a5SDavid du Colombier sshdebug(nil, "received C2S MAC algs: %s", buf); 265863afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 265963afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 266063afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j) 266163afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0) 266263afb9a5SDavid du Colombier goto foundm1; 266363afb9a5SDavid du Colombier sshdebug(nil, "c2s mac algs not in cryptos"); 266463afb9a5SDavid du Colombier free(buf); 266563afb9a5SDavid du Colombier return -1; 266663afb9a5SDavid du Colombier foundm1: 266763afb9a5SDavid du Colombier p->c->ncsmac = j; 266863afb9a5SDavid du Colombier 266963afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 267063afb9a5SDavid du Colombier sshdebug(nil, "received S2C MAC algs: %s", buf); 267163afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 267263afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 267363afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j) 267463afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0) 267563afb9a5SDavid du Colombier goto foundm2; 267663afb9a5SDavid du Colombier sshdebug(nil, "s2c mac algs not in cryptos"); 267763afb9a5SDavid du Colombier free(buf); 267863afb9a5SDavid du Colombier return -1; 267963afb9a5SDavid du Colombier foundm2: 268063afb9a5SDavid du Colombier p->c->nscmac = j; 268163afb9a5SDavid du Colombier 268263afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 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 free(buf); 268763afb9a5SDavid du Colombier if (*q) 268863afb9a5SDavid du Colombier return 1; 268963afb9a5SDavid du Colombier return 0; 269063afb9a5SDavid du Colombier } 269163afb9a5SDavid du Colombier 269263afb9a5SDavid du Colombier int 269363afb9a5SDavid du Colombier validatekexc(Packet *p) 269463afb9a5SDavid du Colombier { 269563afb9a5SDavid du Colombier uchar *q; 269663afb9a5SDavid du Colombier char *toks[Maxtoks]; 269763afb9a5SDavid du Colombier int i, j, n; 269863afb9a5SDavid du Colombier char *buf; 269963afb9a5SDavid du Colombier 270063afb9a5SDavid du Colombier buf = emalloc9p(Bigbufsz); 270163afb9a5SDavid du Colombier q = p->payload + 17; 270263afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 270363afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 270463afb9a5SDavid du Colombier for (j = 0; j < nelem(kexes); ++j) 270563afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 270663afb9a5SDavid du Colombier if (strcmp(toks[i], kexes[j]->name) == 0) 270763afb9a5SDavid du Colombier goto foundk; 270863afb9a5SDavid du Colombier free(buf); 270963afb9a5SDavid du Colombier return -1; 271063afb9a5SDavid du Colombier foundk: 271163afb9a5SDavid du Colombier p->c->kexalg = j; 271263afb9a5SDavid du Colombier 271363afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 271463afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 271563afb9a5SDavid du Colombier for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) 271663afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 271763afb9a5SDavid du Colombier if (strcmp(toks[i], pkas[j]->name) == 0) 271863afb9a5SDavid du Colombier goto foundpka; 271963afb9a5SDavid du Colombier free(buf); 272063afb9a5SDavid du Colombier return -1; 272163afb9a5SDavid du Colombier foundpka: 272263afb9a5SDavid du Colombier p->c->pkalg = j; 272363afb9a5SDavid du Colombier 272463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 272563afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 272663afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j) 272763afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 272863afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0) 272963afb9a5SDavid du Colombier goto foundc1; 273063afb9a5SDavid du Colombier free(buf); 273163afb9a5SDavid du Colombier return -1; 273263afb9a5SDavid du Colombier foundc1: 273363afb9a5SDavid du Colombier p->c->ncscrypt = j; 273463afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 273563afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 273663afb9a5SDavid du Colombier for (j = 0; j < nelem(cryptos); ++j) 273763afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 273863afb9a5SDavid du Colombier if (strcmp(toks[i], cryptos[j]->name) == 0) 273963afb9a5SDavid du Colombier goto foundc2; 274063afb9a5SDavid du Colombier free(buf); 274163afb9a5SDavid du Colombier return -1; 274263afb9a5SDavid du Colombier foundc2: 274363afb9a5SDavid du Colombier p->c->nsccrypt = j; 274463afb9a5SDavid du Colombier 274563afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 274663afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 274763afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j) 274863afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 274963afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0) 275063afb9a5SDavid du Colombier goto foundm1; 275163afb9a5SDavid du Colombier free(buf); 275263afb9a5SDavid du Colombier return -1; 275363afb9a5SDavid du Colombier foundm1: 275463afb9a5SDavid du Colombier p->c->ncsmac = j; 275563afb9a5SDavid du Colombier 275663afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 275763afb9a5SDavid du Colombier n = gettokens(buf, toks, nelem(toks), ","); 275863afb9a5SDavid du Colombier for (j = 0; j < nelem(macnames); ++j) 275963afb9a5SDavid du Colombier for (i = 0; i < n; ++i) 276063afb9a5SDavid du Colombier if (strcmp(toks[i], macnames[j]) == 0) 276163afb9a5SDavid du Colombier goto foundm2; 276263afb9a5SDavid du Colombier free(buf); 276363afb9a5SDavid du Colombier return -1; 276463afb9a5SDavid du Colombier foundm2: 276563afb9a5SDavid du Colombier p->c->nscmac = j; 276663afb9a5SDavid du Colombier 276763afb9a5SDavid du Colombier q = get_string(p, q, buf, Bigbufsz, nil); 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 free(buf); 277263afb9a5SDavid du Colombier return *q != 0; 277363afb9a5SDavid du Colombier } 277463afb9a5SDavid du Colombier 277563afb9a5SDavid du Colombier int 277663afb9a5SDavid du Colombier memrandom(void *p, int n) 277763afb9a5SDavid du Colombier { 277863afb9a5SDavid du Colombier uchar *cp; 277963afb9a5SDavid du Colombier 278063afb9a5SDavid du Colombier for (cp = (uchar*)p; n > 0; n--) 278163afb9a5SDavid du Colombier *cp++ = fastrand(); 278263afb9a5SDavid du Colombier return 0; 278363afb9a5SDavid du Colombier } 278463afb9a5SDavid du Colombier 278563afb9a5SDavid du Colombier /* 278663afb9a5SDavid du Colombier * create a change uid capability 278763afb9a5SDavid du Colombier */ 278863afb9a5SDavid du Colombier char* 278963afb9a5SDavid du Colombier mkcap(char *from, char *to) 279063afb9a5SDavid du Colombier { 279163afb9a5SDavid du Colombier int fd, fromtosz; 279263afb9a5SDavid du Colombier char *cap, *key; 279363afb9a5SDavid du Colombier uchar rand[SHA1dlen], hash[SHA1dlen]; 279463afb9a5SDavid du Colombier 279563afb9a5SDavid du Colombier fd = open("#¤/caphash", OWRITE); 279663afb9a5SDavid du Colombier if (fd < 0) 279763afb9a5SDavid du Colombier sshlog(nil, "can't open #¤/caphash: %r"); 279863afb9a5SDavid du Colombier 279963afb9a5SDavid du Colombier /* create the capability */ 280063afb9a5SDavid du Colombier fromtosz = strlen(from) + 1 + strlen(to) + 1; 280163afb9a5SDavid du Colombier cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1); 280263afb9a5SDavid du Colombier snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to); 280363afb9a5SDavid du Colombier memrandom(rand, sizeof(rand)); 280463afb9a5SDavid du Colombier key = cap + fromtosz; 280563afb9a5SDavid du Colombier enc64(key, sizeof(rand)*3, rand, sizeof(rand)); 280663afb9a5SDavid du Colombier 280763afb9a5SDavid du Colombier /* hash the capability */ 280863afb9a5SDavid du Colombier hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil); 280963afb9a5SDavid du Colombier 281063afb9a5SDavid du Colombier /* give the kernel the hash */ 281163afb9a5SDavid du Colombier key[-1] = '@'; 281263afb9a5SDavid du Colombier sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash); 281363afb9a5SDavid du Colombier if (write(fd, hash, SHA1dlen) != SHA1dlen) { 281463afb9a5SDavid du Colombier close(fd); 281563afb9a5SDavid du Colombier free(cap); 281663afb9a5SDavid du Colombier return nil; 281763afb9a5SDavid du Colombier } 281863afb9a5SDavid du Colombier close(fd); 281963afb9a5SDavid du Colombier return cap; 282063afb9a5SDavid du Colombier } 282163afb9a5SDavid du Colombier 282263afb9a5SDavid du Colombier /* 282363afb9a5SDavid du Colombier * ask keyfs (assumes we are on an auth server) 282463afb9a5SDavid du Colombier */ 282563afb9a5SDavid du Colombier static AuthInfo * 282663afb9a5SDavid du Colombier keyfsauth(char *me, char *user, char *pw, char *key1, char *key2) 282763afb9a5SDavid du Colombier { 282863afb9a5SDavid du Colombier int fd; 282963afb9a5SDavid du Colombier char path[Arbpathlen]; 283063afb9a5SDavid du Colombier AuthInfo *ai; 283163afb9a5SDavid du Colombier 283263afb9a5SDavid du Colombier if (passtokey(key1, pw) == 0) 283363afb9a5SDavid du Colombier return nil; 283463afb9a5SDavid du Colombier 283563afb9a5SDavid du Colombier snprint(path, Arbpathlen, "/mnt/keys/%s/key", user); 283663afb9a5SDavid du Colombier if ((fd = open(path, OREAD)) < 0) { 283763afb9a5SDavid du Colombier werrstr("Invalid user %s", user); 283863afb9a5SDavid du Colombier return nil; 283963afb9a5SDavid du Colombier } 284063afb9a5SDavid du Colombier if (read(fd, key2, DESKEYLEN) != DESKEYLEN) { 284163afb9a5SDavid du Colombier close(fd); 284263afb9a5SDavid du Colombier werrstr("Password mismatch 1"); 284363afb9a5SDavid du Colombier return nil; 284463afb9a5SDavid du Colombier } 284563afb9a5SDavid du Colombier close(fd); 284663afb9a5SDavid du Colombier 284763afb9a5SDavid du Colombier if (memcmp(key1, key2, DESKEYLEN) != 0) { 284863afb9a5SDavid du Colombier werrstr("Password mismatch 2"); 284963afb9a5SDavid du Colombier return nil; 285063afb9a5SDavid du Colombier } 285163afb9a5SDavid du Colombier 285263afb9a5SDavid du Colombier ai = emalloc9p(sizeof(AuthInfo)); 285363afb9a5SDavid du Colombier ai->cuid = estrdup9p(user); 285463afb9a5SDavid du Colombier ai->suid = estrdup9p(me); 285563afb9a5SDavid du Colombier ai->cap = mkcap(me, user); 285663afb9a5SDavid du Colombier ai->nsecret = 0; 285763afb9a5SDavid du Colombier ai->secret = (uchar *)estrdup9p(""); 285863afb9a5SDavid du Colombier return ai; 285963afb9a5SDavid du Colombier } 286063afb9a5SDavid du Colombier 286163afb9a5SDavid du Colombier static void 286263afb9a5SDavid du Colombier userauthfailed(Packet *p2) 286363afb9a5SDavid du Colombier { 286463afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_FAILURE); 286563afb9a5SDavid du Colombier add_string(p2, "password,publickey"); 286663afb9a5SDavid du Colombier add_byte(p2, 0); 286763afb9a5SDavid du Colombier } 286863afb9a5SDavid du Colombier 286963afb9a5SDavid du Colombier static int 287063afb9a5SDavid du Colombier authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q, 287163afb9a5SDavid du Colombier char *alg, char *blob, char *sig, char *service, char *me) 287263afb9a5SDavid du Colombier { 287363afb9a5SDavid du Colombier int n, thisway, nblob, nsig; 287463afb9a5SDavid du Colombier char method[32]; 287563afb9a5SDavid du Colombier 287663afb9a5SDavid du Colombier sshdebug(c, "auth_req publickey for user %s", user); 287763afb9a5SDavid du Colombier thisway = *q == '\0'; 287863afb9a5SDavid du Colombier q = get_string(p, q+1, alg, Arbpathlen, nil); 287963afb9a5SDavid du Colombier q = get_string(p, q, blob, Blobsz, &nblob); 288063afb9a5SDavid du Colombier if (thisway) { 288163afb9a5SDavid du Colombier /* 288263afb9a5SDavid du Colombier * Should really check to see if this user can 288363afb9a5SDavid du Colombier * be authed this way. 288463afb9a5SDavid du Colombier */ 288563afb9a5SDavid du Colombier for (n = 0; n < nelem(pkas) && pkas[n] != nil && 288663afb9a5SDavid du Colombier strcmp(pkas[n]->name, alg) != 0; ++n) 288763afb9a5SDavid du Colombier ; 288863afb9a5SDavid du Colombier if (n >= nelem(pkas) || pkas[n] == nil) { 288963afb9a5SDavid du Colombier userauthfailed(p2); 289063afb9a5SDavid du Colombier return -1; 289163afb9a5SDavid du Colombier } 289263afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_PK_OK); 289363afb9a5SDavid du Colombier add_string(p2, alg); 289463afb9a5SDavid du Colombier add_block(p2, blob, nblob); 289563afb9a5SDavid du Colombier return 0; 289663afb9a5SDavid du Colombier } 289763afb9a5SDavid du Colombier 289863afb9a5SDavid du Colombier get_string(p, q, sig, Blobsz, &nsig); 289963afb9a5SDavid du Colombier for (n = 0; n < nelem(pkas) && pkas[n] != nil && 290063afb9a5SDavid du Colombier strcmp(pkas[n]->name, alg) != 0; ++n) 290163afb9a5SDavid du Colombier ; 290263afb9a5SDavid du Colombier if (n >= nelem(pkas) || pkas[n] == nil) { 290363afb9a5SDavid du Colombier userauthfailed(p2); 290463afb9a5SDavid du Colombier return -1; 290563afb9a5SDavid du Colombier } 290663afb9a5SDavid du Colombier 290763afb9a5SDavid du Colombier add_block(p2, c->sessid, SHA1dlen); 290863afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_REQUEST); 290963afb9a5SDavid du Colombier add_string(p2, user); 291063afb9a5SDavid du Colombier add_string(p2, service); 291163afb9a5SDavid du Colombier add_string(p2, method); 291263afb9a5SDavid du Colombier add_byte(p2, 1); 291363afb9a5SDavid du Colombier add_string(p2, alg); 291463afb9a5SDavid du Colombier add_block(p2, blob, nblob); 291563afb9a5SDavid du Colombier if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig) 291663afb9a5SDavid du Colombier == 0) { 291763afb9a5SDavid du Colombier init_packet(p2); 291863afb9a5SDavid du Colombier p2->c = c; 291963afb9a5SDavid du Colombier sshlog(c, "public key login failed"); 292063afb9a5SDavid du Colombier userauthfailed(p2); 292163afb9a5SDavid du Colombier return -1; 292263afb9a5SDavid du Colombier } 292363afb9a5SDavid du Colombier free(c->cap); 292463afb9a5SDavid du Colombier c->cap = mkcap(me, user); 292563afb9a5SDavid du Colombier init_packet(p2); 292663afb9a5SDavid du Colombier p2->c = c; 292763afb9a5SDavid du Colombier sshlog(c, "logged in by public key"); 292863afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); 292963afb9a5SDavid du Colombier return 0; 293063afb9a5SDavid du Colombier } 293163afb9a5SDavid du Colombier 293263afb9a5SDavid du Colombier int 293363afb9a5SDavid du Colombier auth_req(Packet *p, Conn *c) 293463afb9a5SDavid du Colombier { 293563afb9a5SDavid du Colombier int n, ret; 293663afb9a5SDavid du Colombier char *alg, *blob, *sig, *service, *me, *user, *pw, *path; 293763afb9a5SDavid du Colombier char key1[DESKEYLEN], key2[DESKEYLEN], method[32]; 293863afb9a5SDavid du Colombier uchar *q; 293963afb9a5SDavid du Colombier AuthInfo *ai; 294063afb9a5SDavid du Colombier Packet *p2; 294163afb9a5SDavid du Colombier 294263afb9a5SDavid du Colombier service = emalloc9p(Arbpathlen); 294363afb9a5SDavid du Colombier me = emalloc9p(Arbpathlen); 294463afb9a5SDavid du Colombier user = emalloc9p(Arbpathlen); 294563afb9a5SDavid du Colombier pw = emalloc9p(Arbpathlen); 294663afb9a5SDavid du Colombier alg = emalloc9p(Arbpathlen); 294763afb9a5SDavid du Colombier path = emalloc9p(Arbpathlen); 294863afb9a5SDavid du Colombier blob = emalloc9p(Blobsz); 294963afb9a5SDavid du Colombier sig = emalloc9p(Blobsz); 295063afb9a5SDavid du Colombier ret = -1; /* failure is default */ 295163afb9a5SDavid du Colombier 295263afb9a5SDavid du Colombier q = get_string(p, p->payload + 1, user, Arbpathlen, nil); 295363afb9a5SDavid du Colombier free(c->user); 295463afb9a5SDavid du Colombier c->user = estrdup9p(user); 295563afb9a5SDavid du Colombier q = get_string(p, q, service, Arbpathlen, nil); 295663afb9a5SDavid du Colombier q = get_string(p, q, method, sizeof method, nil); 295763afb9a5SDavid du Colombier sshdebug(c, "got userauth request: %s %s %s", user, service, method); 295863afb9a5SDavid du Colombier 295963afb9a5SDavid du Colombier readfile("/dev/user", me, Arbpathlen); 296063afb9a5SDavid du Colombier 296163afb9a5SDavid du Colombier p2 = new_packet(c); 296263afb9a5SDavid du Colombier if (strcmp(method, "publickey") == 0) 296363afb9a5SDavid du Colombier ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me); 296463afb9a5SDavid du Colombier else if (strcmp(method, "password") == 0) { 296563afb9a5SDavid du Colombier get_string(p, q + 1, pw, Arbpathlen, nil); 296663afb9a5SDavid du Colombier // sshdebug(c, "%s", pw); /* bad idea to log passwords */ 296763afb9a5SDavid du Colombier sshdebug(c, "auth_req password"); 296863afb9a5SDavid du Colombier if (kflag) 296963afb9a5SDavid du Colombier ai = keyfsauth(me, user, pw, key1, key2); 297063afb9a5SDavid du Colombier else 297163afb9a5SDavid du Colombier ai = auth_userpasswd(user, pw); 297263afb9a5SDavid du Colombier if (ai == nil) { 297363afb9a5SDavid du Colombier sshlog(c, "login failed: %r"); 297463afb9a5SDavid du Colombier userauthfailed(p2); 297563afb9a5SDavid du Colombier } else { 297663afb9a5SDavid du Colombier sshdebug(c, "auth successful: cuid %s suid %s cap %s", 297763afb9a5SDavid du Colombier ai->cuid, ai->suid, ai->cap); 297863afb9a5SDavid du Colombier free(c->cap); 297963afb9a5SDavid du Colombier if (strcmp(user, me) == 0) 298063afb9a5SDavid du Colombier c->cap = estrdup9p("n/a"); 298163afb9a5SDavid du Colombier else 298263afb9a5SDavid du Colombier c->cap = estrdup9p(ai->cap); 298363afb9a5SDavid du Colombier sshlog(c, "logged in by password"); 298463afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); 298563afb9a5SDavid du Colombier auth_freeAI(ai); 298663afb9a5SDavid du Colombier ret = 0; 298763afb9a5SDavid du Colombier } 298863afb9a5SDavid du Colombier } else 298963afb9a5SDavid du Colombier userauthfailed(p2); 299063afb9a5SDavid du Colombier 299163afb9a5SDavid du Colombier n = finish_packet(p2); 299263afb9a5SDavid du Colombier iowrite(c->dio, c->datafd, p2->nlength, n); 299363afb9a5SDavid du Colombier 299463afb9a5SDavid du Colombier free(service); 299563afb9a5SDavid du Colombier free(me); 299663afb9a5SDavid du Colombier free(user); 299763afb9a5SDavid du Colombier free(pw); 299863afb9a5SDavid du Colombier free(alg); 299963afb9a5SDavid du Colombier free(blob); 300063afb9a5SDavid du Colombier free(sig); 300163afb9a5SDavid du Colombier free(path); 300263afb9a5SDavid du Colombier free(p2); 300363afb9a5SDavid du Colombier return ret; 300463afb9a5SDavid du Colombier } 300563afb9a5SDavid du Colombier 300663afb9a5SDavid du Colombier int 300763afb9a5SDavid du Colombier client_auth(Conn *c, Ioproc *io) 300863afb9a5SDavid du Colombier { 300963afb9a5SDavid du Colombier Packet *p2, *p3, *p4; 301063afb9a5SDavid du Colombier char *r, *s; 301163afb9a5SDavid du Colombier mpint *ek, *nk; 301263afb9a5SDavid du Colombier int i, n; 301363afb9a5SDavid du Colombier 301463afb9a5SDavid du Colombier sshdebug(c, "client_auth"); 301563afb9a5SDavid du Colombier if (!c->password && !c->authkey) 301663afb9a5SDavid du Colombier return -1; 301763afb9a5SDavid du Colombier 301863afb9a5SDavid du Colombier p2 = new_packet(c); 301963afb9a5SDavid du Colombier add_byte(p2, SSH_MSG_USERAUTH_REQUEST); 302063afb9a5SDavid du Colombier add_string(p2, c->user); 302163afb9a5SDavid du Colombier add_string(p2, c->service); 302263afb9a5SDavid du Colombier if (c->password) { 302363afb9a5SDavid du Colombier add_string(p2, "password"); 302463afb9a5SDavid du Colombier add_byte(p2, 0); 302563afb9a5SDavid du Colombier add_string(p2, c->password); 302663afb9a5SDavid du Colombier sshdebug(c, "client_auth using password for svc %s", c->service); 302763afb9a5SDavid du Colombier } else { 302863afb9a5SDavid du Colombier sshdebug(c, "client_auth trying rsa public key"); 302963afb9a5SDavid du Colombier add_string(p2, "publickey"); 303063afb9a5SDavid du Colombier add_byte(p2, 1); 303163afb9a5SDavid du Colombier add_string(p2, "ssh-rsa"); 303263afb9a5SDavid du Colombier 303363afb9a5SDavid du Colombier r = strstr(c->authkey, " ek="); 303463afb9a5SDavid du Colombier s = strstr(c->authkey, " n="); 303563afb9a5SDavid du Colombier if (!r || !s) { 303663afb9a5SDavid du Colombier shutdown(c); 303763afb9a5SDavid du Colombier free(p2); 303863afb9a5SDavid du Colombier sshdebug(c, "client_auth no rsa key"); 303963afb9a5SDavid du Colombier return -1; 304063afb9a5SDavid du Colombier } 304163afb9a5SDavid du Colombier ek = strtomp(r+4, nil, 16, nil); 304263afb9a5SDavid du Colombier nk = strtomp(s+3, nil, 16, nil); 304363afb9a5SDavid du Colombier 304463afb9a5SDavid du Colombier p3 = new_packet(c); 304563afb9a5SDavid du Colombier add_string(p3, "ssh-rsa"); 304663afb9a5SDavid du Colombier add_mp(p3, ek); 304763afb9a5SDavid du Colombier add_mp(p3, nk); 304863afb9a5SDavid du Colombier add_block(p2, p3->payload, p3->rlength-1); 304963afb9a5SDavid du Colombier 305063afb9a5SDavid du Colombier p4 = new_packet(c); 305163afb9a5SDavid du Colombier add_block(p4, c->sessid, SHA1dlen); 305263afb9a5SDavid du Colombier add_byte(p4, SSH_MSG_USERAUTH_REQUEST); 305363afb9a5SDavid du Colombier add_string(p4, c->user); 305463afb9a5SDavid du Colombier add_string(p4, c->service); 305563afb9a5SDavid du Colombier add_string(p4, "publickey"); 305663afb9a5SDavid du Colombier add_byte(p4, 1); 305763afb9a5SDavid du Colombier add_string(p4, "ssh-rsa"); 305863afb9a5SDavid du Colombier add_block(p4, p3->payload, p3->rlength-1); 305963afb9a5SDavid du Colombier mpfree(ek); 306063afb9a5SDavid du Colombier mpfree(nk); 306163afb9a5SDavid du Colombier free(p3); 306263afb9a5SDavid du Colombier 306363afb9a5SDavid du Colombier for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0; 306463afb9a5SDavid du Colombier ++i) 306563afb9a5SDavid du Colombier ; 306663afb9a5SDavid du Colombier sshdebug(c, "client_auth rsa signing alg %d: %r", i); 306763afb9a5SDavid du Colombier if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) { 306863afb9a5SDavid du Colombier sshdebug(c, "client_auth rsa signing failed: %r"); 306963afb9a5SDavid du Colombier free(p4); 307063afb9a5SDavid du Colombier free(p2); 307163afb9a5SDavid du Colombier return -1; 307263afb9a5SDavid du Colombier } 307363afb9a5SDavid du Colombier add_block(p2, p3->payload, p3->rlength-1); 307463afb9a5SDavid du Colombier free(p3); 307563afb9a5SDavid du Colombier free(p4); 307663afb9a5SDavid du Colombier } 307763afb9a5SDavid du Colombier 307863afb9a5SDavid du Colombier n = finish_packet(p2); 307963afb9a5SDavid du Colombier if (writeio(io, c->datafd, p2->nlength, n) != n) 308063afb9a5SDavid du Colombier sshdebug(c, "client_auth write failed: %r"); 308163afb9a5SDavid du Colombier free(p2); 308263afb9a5SDavid du Colombier return 0; 308363afb9a5SDavid du Colombier } 308463afb9a5SDavid du Colombier 308563afb9a5SDavid du Colombier /* should use auth_getkey or something similar */ 308663afb9a5SDavid du Colombier char * 308763afb9a5SDavid du Colombier factlookup(int nattr, int nreq, char *attrs[]) 308863afb9a5SDavid du Colombier { 308963afb9a5SDavid du Colombier Biobuf *bp; 309063afb9a5SDavid du Colombier char *buf, *toks[Maxtoks], *res, *q; 309163afb9a5SDavid du Colombier int ntok, nmatch, maxmatch; 309263afb9a5SDavid du Colombier int i, j; 309363afb9a5SDavid du Colombier 309463afb9a5SDavid du Colombier res = nil; 309563afb9a5SDavid du Colombier bp = Bopen("/mnt/factotum/ctl", OREAD); 309663afb9a5SDavid du Colombier if (bp == nil) 309763afb9a5SDavid du Colombier return nil; 309863afb9a5SDavid du Colombier maxmatch = 0; 309963afb9a5SDavid du Colombier while (buf = Brdstr(bp, '\n', 1)) { 310063afb9a5SDavid du Colombier q = estrdup9p(buf); 310163afb9a5SDavid du Colombier ntok = gettokens(buf, toks, nelem(toks), " "); 310263afb9a5SDavid du Colombier nmatch = 0; 310363afb9a5SDavid du Colombier for (i = 0; i < nattr; ++i) { 310463afb9a5SDavid du Colombier for (j = 0; j < ntok; ++j) 310563afb9a5SDavid du Colombier if (strcmp(attrs[i], toks[j]) == 0) { 310663afb9a5SDavid du Colombier ++nmatch; 310763afb9a5SDavid du Colombier break; 310863afb9a5SDavid du Colombier } 310963afb9a5SDavid du Colombier if (i < nreq && j >= ntok) 311063afb9a5SDavid du Colombier break; 311163afb9a5SDavid du Colombier } 311263afb9a5SDavid du Colombier if (i >= nattr && nmatch > maxmatch) { 311363afb9a5SDavid du Colombier free(res); 311463afb9a5SDavid du Colombier res = q; 311563afb9a5SDavid du Colombier maxmatch = nmatch; 311663afb9a5SDavid du Colombier } else 311763afb9a5SDavid du Colombier free(q); 311863afb9a5SDavid du Colombier free(buf); 311963afb9a5SDavid du Colombier } 312063afb9a5SDavid du Colombier Bterm(bp); 312163afb9a5SDavid du Colombier return res; 312263afb9a5SDavid du Colombier } 312363afb9a5SDavid du Colombier 312463afb9a5SDavid du Colombier void 312563afb9a5SDavid du Colombier shutdown(Conn *c) 312663afb9a5SDavid du Colombier { 312763afb9a5SDavid du Colombier Plist *p; 312863afb9a5SDavid du Colombier SSHChan *sc; 312963afb9a5SDavid du Colombier int i, ostate; 313063afb9a5SDavid du Colombier 313163afb9a5SDavid du Colombier sshdebug(c, "shutting down connection %d", c->id); 313263afb9a5SDavid du Colombier ostate = c->state; 313363afb9a5SDavid du Colombier if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 && 313463afb9a5SDavid du Colombier c->datafile->ref <= 2 && c->listenfile->ref <= 2 && 313563afb9a5SDavid du Colombier c->localfile->ref <= 2 && c->remotefile->ref <= 2 && 313663afb9a5SDavid du Colombier c->statusfile->ref <= 2) 313763afb9a5SDavid du Colombier c->state = Closed; 313863afb9a5SDavid du Colombier else { 313963afb9a5SDavid du Colombier if (c->state != Closed) 314063afb9a5SDavid du Colombier c->state = Closing; 314163afb9a5SDavid du Colombier sshdebug(c, "clone %ld ctl %ld data %ld listen %ld " 314263afb9a5SDavid du Colombier "local %ld remote %ld status %ld", 314363afb9a5SDavid du Colombier c->clonefile->ref, c->ctlfile->ref, c->datafile->ref, 314463afb9a5SDavid du Colombier c->listenfile->ref, c->localfile->ref, c->remotefile->ref, 314563afb9a5SDavid du Colombier c->statusfile->ref); 314663afb9a5SDavid du Colombier } 314763afb9a5SDavid du Colombier if (ostate == Closed || ostate == Closing) { 314863afb9a5SDavid du Colombier c->state = Closed; 314963afb9a5SDavid du Colombier return; 315063afb9a5SDavid du Colombier } 315163afb9a5SDavid du Colombier if (c->role == Server && c->remote) 315263afb9a5SDavid du Colombier sshlog(c, "closing connection"); 315363afb9a5SDavid du Colombier hangupconn(c); 315463afb9a5SDavid du Colombier if (c->dio) { 315563afb9a5SDavid du Colombier closeioproc(c->dio); 315663afb9a5SDavid du Colombier c->dio = nil; 315763afb9a5SDavid du Colombier } 315863afb9a5SDavid du Colombier 315963afb9a5SDavid du Colombier c->decrypt = -1; 316063afb9a5SDavid du Colombier c->inmac = -1; 316163afb9a5SDavid du Colombier c->nchan = 0; 316263afb9a5SDavid du Colombier free(c->otherid); 316363afb9a5SDavid du Colombier free(c->s2ccs); 316463afb9a5SDavid du Colombier c->s2ccs = nil; 316563afb9a5SDavid du Colombier free(c->c2scs); 316663afb9a5SDavid du Colombier c->c2scs = nil; 316763afb9a5SDavid du Colombier free(c->remote); 316863afb9a5SDavid du Colombier c->remote = nil; 316963afb9a5SDavid du Colombier if (c->x) { 317063afb9a5SDavid du Colombier mpfree(c->x); 317163afb9a5SDavid du Colombier c->x = nil; 317263afb9a5SDavid du Colombier } 317363afb9a5SDavid du Colombier if (c->e) { 317463afb9a5SDavid du Colombier mpfree(c->e); 317563afb9a5SDavid du Colombier c->e = nil; 317663afb9a5SDavid du Colombier } 317763afb9a5SDavid du Colombier free(c->user); 317863afb9a5SDavid du Colombier c->user = nil; 317963afb9a5SDavid du Colombier free(c->service); 318063afb9a5SDavid du Colombier c->service = nil; 318163afb9a5SDavid du Colombier c->otherid = nil; 318263afb9a5SDavid du Colombier qlock(&c->l); 318363afb9a5SDavid du Colombier rwakeupall(&c->r); 318463afb9a5SDavid du Colombier qunlock(&c->l); 318563afb9a5SDavid du Colombier for (i = 0; i < MAXCONN; ++i) { 318663afb9a5SDavid du Colombier sc = c->chans[i]; 318763afb9a5SDavid du Colombier if (sc == nil) 318863afb9a5SDavid du Colombier continue; 318963afb9a5SDavid du Colombier free(sc->ann); 319063afb9a5SDavid du Colombier sc->ann = nil; 319163afb9a5SDavid du Colombier if (sc->state != Empty && sc->state != Closed) { 319263afb9a5SDavid du Colombier sc->state = Closed; 319363afb9a5SDavid du Colombier sc->lreq = nil; 319463afb9a5SDavid du Colombier while (sc->dataq != nil) { 319563afb9a5SDavid du Colombier p = sc->dataq; 319663afb9a5SDavid du Colombier sc->dataq = p->next; 319763afb9a5SDavid du Colombier free(p->pack); 319863afb9a5SDavid du Colombier free(p); 319963afb9a5SDavid du Colombier } 320063afb9a5SDavid du Colombier while (sc->reqq != nil) { 320163afb9a5SDavid du Colombier p = sc->reqq; 320263afb9a5SDavid du Colombier sc->reqq = p->next; 320363afb9a5SDavid du Colombier free(p->pack); 320463afb9a5SDavid du Colombier free(p); 320563afb9a5SDavid du Colombier } 320663afb9a5SDavid du Colombier qlock(&c->l); 320763afb9a5SDavid du Colombier rwakeupall(&sc->r); 320863afb9a5SDavid du Colombier nbsendul(sc->inchan, 1); 320963afb9a5SDavid du Colombier nbsendul(sc->reqchan, 1); 321063afb9a5SDavid du Colombier chanclose(sc->inchan); 321163afb9a5SDavid du Colombier chanclose(sc->reqchan); 321263afb9a5SDavid du Colombier qunlock(&c->l); 321363afb9a5SDavid du Colombier } 321463afb9a5SDavid du Colombier } 321563afb9a5SDavid du Colombier qlock(&availlck); 321663afb9a5SDavid du Colombier rwakeup(&availrend); 321763afb9a5SDavid du Colombier qunlock(&availlck); 321863afb9a5SDavid du Colombier sshdebug(c, "done processing shutdown of connection %d", c->id); 321963afb9a5SDavid du Colombier } 3220