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