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