ssh2 ... un-remixed Reference: /n/patches.lsub.org/patch/ssh2 Date: Tue Apr 24 23:48:39 CES 2012 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/ssh2/blob2rsa.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2/blob2rsa.c Wed Feb 22 05:05:06 2012 @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +void +main(int argc, char *argv[]) +{ + mpint *x, *y; + char *name, *e, *n; + int l1, l2, l3, k; + char buf[4096]; + + if(argc != 2){ + fprint(2, "usage: blob2rsa user"); + exits("usage"); + } + fmtinstall('M', mpfmt); + k = read(0, buf, 4096); + if(k < 0) + fprint(2, "read error: %r\n"); + l1 = nhgetl(buf); + name = buf + 4; + l2 = nhgetl(buf+4+l1); + e = buf+4+l1+4; + l3 = nhgetl(buf+4+l1+4+l2); + n = buf+4+l1+4+l2+4; + name[l1] = 0; + print("key proto=rsa service=%s user=%s ", name, argv[1]); + x = betomp((uchar *)e, l2, nil); + y = betomp((uchar *)n, l3, nil); + print("ek=%M n=%M\n", x, y); +} --- /sys/src/cmd/ssh2/cipher3des.c Thu Apr 19 15:20:20 2012 +++ /sys/src/cmd/ssh2/cipher3des.c Thu Dec 8 21:30:25 2011 @@ -5,9 +5,10 @@ #include #include <9p.h> #include -#include "netssh.h" +#include "sshtun.h" -struct CipherState { +struct CipherState +{ DES3state state; }; @@ -19,10 +20,11 @@ cs = emalloc9p(sizeof(CipherState)); if(dir){ - memmove(key, c->s2cek, sizeof key); + memmove(key, c->s2cek, 24); setupDES3state(&cs->state, key, c->s2civ); - } else { - memmove(key, c->c2sek, sizeof key); + } + else{ + memmove(key, c->c2sek, 24); setupDES3state(&cs->state, key, c->c2siv); } return cs; @@ -40,10 +42,12 @@ des3CBCdecrypt(buf, nbuf, &cs->state); } -Cipher cipher3des = { +Cipher cipher3des = +{ "3des-cbc", 8, init3des, encrypt3des, decrypt3des, }; + --- /sys/src/cmd/ssh2/cipheraes.c Thu Apr 19 15:20:20 2012 +++ /sys/src/cmd/ssh2/cipheraes.c Thu Dec 8 21:30:25 2011 @@ -5,11 +5,12 @@ #include #include <9p.h> #include -#include "netssh.h" +#include "sshtun.h" static QLock aeslock; -struct CipherState { +struct CipherState +{ AESstate state; }; @@ -20,10 +21,12 @@ qlock(&aeslock); cs = emalloc9p(sizeof(CipherState)); - if(dir) + if(dir){ setupAESstate(&cs->state, c->s2cek, bits/8, c->s2civ); - else + } + else{ setupAESstate(&cs->state, c->c2sek, bits/8, c->c2siv); + } qunlock(&aeslock); return cs; } @@ -66,7 +69,8 @@ qunlock(&aeslock); } -Cipher cipheraes128 = { +Cipher cipheraes128 = +{ "aes128-cbc", AESbsize, initaes128, @@ -74,7 +78,8 @@ decryptaes, }; -Cipher cipheraes192 = { +Cipher cipheraes192 = +{ "aes192-cbc", AESbsize, initaes192, @@ -82,7 +87,8 @@ decryptaes, }; -Cipher cipheraes256 = { +Cipher cipheraes256 = +{ "aes256-cbc", AESbsize, initaes256, --- /sys/src/cmd/ssh2/cipherblowfish.c Thu Apr 19 15:20:20 2012 +++ /sys/src/cmd/ssh2/cipherblowfish.c Thu Dec 8 21:30:25 2011 @@ -5,36 +5,32 @@ #include #include <9p.h> #include -#include "netssh.h" +#include "sshtun.h" -struct CipherState { - BFstate state; +struct CipherState +{ + BFstate state; }; static CipherState* initblowfish(Conn *c, int dir) { - int i; CipherState *cs; - if (debug > 1) { - fprint(2, "initblowfish dir:%d\ns2cek: ", dir); - for(i = 0; i < 16; ++i) - fprint(2, "%02x", c->s2cek[i]); - fprint(2, "\nc2sek: "); - for(i = 0; i < 16; ++i) - fprint(2, "%02x", c->c2sek[i]); - fprint(2, "\ns2civ: "); - for(i = 0; i < 8; ++i) - fprint(2, "%02x", c->s2civ[i]); - fprint(2, "\nc2siv: "); - for(i = 0; i < 8; ++i) - fprint(2, "%02x", c->c2siv[i]); - fprint(2, "\n"); - } +int i; +fprint(2, "initblowfish dir:%d\ns2cek: ", dir); +for(i = 0; i < 16; ++i) fprint(2, "%02x", c->s2cek[i]); +fprint(2, "\nc2sek: "); +for(i = 0; i < 16; ++i) fprint(2, "%02x", c->c2sek[i]); +fprint(2, "\ns2civ: "); +for(i = 0; i < 8; ++i) fprint(2, "%02x", c->s2civ[i]); +fprint(2, "\nc2siv: "); +for(i = 0; i < 8; ++i) fprint(2, "%02x", c->c2siv[i]); +fprint(2, "\n"); + cs = emalloc9p(sizeof(CipherState)); - memset(cs, '\0', sizeof *cs); - fprint(2, "cs: %p\n", cs); +memset(cs, '\0', sizeof(CipherState)); +fprint(2,"cs:%p\n", cs); if(dir) setupBFstate(&cs->state, c->s2cek, 16, c->s2civ); else @@ -51,13 +47,14 @@ static void decryptblowfish(CipherState *cs, uchar *buf, int nbuf) { -fprint(2, "cs: %p, nb:%d\n", cs, nbuf); +fprint(2,"cs:%p, nb:%d\n", cs, nbuf); fprint(2, "before decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]); bfCBCdecrypt(buf, nbuf, &cs->state); fprint(2, "after decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]); } -Cipher cipherblowfish = { +Cipher cipherblowfish = +{ "blowfish-cbc", 8, initblowfish, --- /sys/src/cmd/ssh2/cipherrc4.c Thu Apr 19 15:20:20 2012 +++ /sys/src/cmd/ssh2/cipherrc4.c Thu Dec 8 21:30:25 2011 @@ -5,9 +5,10 @@ #include #include <9p.h> #include -#include "netssh.h" +#include "sshtun.h" -struct CipherState { +struct CipherState +{ RC4state state; }; @@ -36,7 +37,8 @@ rc4(&cs->state, buf, nbuf); } -Cipher cipherrc4 = { +Cipher cipherrc4 = +{ "arcfour", 8, initrc4, --- /sys/src/cmd/ssh2/dh.c Thu Apr 19 15:20:21 2012 +++ /sys/src/cmd/ssh2/dh.c Tue Mar 20 10:25:25 2012 @@ -8,7 +8,7 @@ #include #include #include -#include "netssh.h" +#include "sshtun.h" static int dh_server(Conn *, Packet *, mpint *, int); static void genkeys(Conn *, uchar [], mpint *); @@ -40,72 +40,64 @@ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; -mpint *two, *p1, *p14; -int nokeyverify; +mpint *two, *p1, *p14; static DSApriv mydsskey; static RSApriv myrsakey; void dh_init(PKA *pkas[]) { - char *buf, *p, *st, *end; + char *p, *st, *end; int fd, n, k; + char *buf; - if(debug > 1) - sshdebug(nil, "dh_init"); + buf = emalloc9p(4096); k = 0; pkas[k] = nil; + st = buf; + end = buf + 4096; fmtinstall('M', mpfmt); two = strtomp("2", nil, 10, nil); p1 = strtomp(group1p, nil, 16, nil); p14 = strtomp(group14p, nil, 16, nil); - /* * this really should be done through factotum */ p = getenv("rsakey"); - if (p != nil) { - remove("/env/rsakey"); - st = buf = p; - end = buf + strlen(p); - } else { - /* - * it would be better to use bio and rdline here instead of - * reading all of factotum's contents into memory at once. - */ - buf = emalloc9p(Maxfactotum); + if (p == nil) { fd = open("rsakey", OREAD); - if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0) - goto norsa; - n = readn(fd, buf, Maxfactotum - 1); - buf[n >= 0? n: 0] = 0; + if (fd < 0) { + fd = open("/mnt/factotum/ctl", OREAD); + if (fd < 0) + goto initdss; + } + n = read(fd, buf, 4095); + buf[n] = 0; close(fd); - assert(n < Maxfactotum - 1); - st = strstr(buf, "proto=rsa"); - if (st == nil) { - sshlog(nil, "no proto=rsa key in factotum"); - goto norsa; - } + if (st == nil) + goto initdss; end = st; - for (; st > buf && *st != '\n'; --st) - ; - for (; end < buf + Maxfactotum && *end != '\n'; ++end) - ; + for (; st > buf && *st != '\n'; --st) ; + for (; end < buf+4096 && *end != '\n'; ++end) ; + } + else { + strncpy(buf, p, 4095); + remove("/env/rsakey"); } p = strstr(st, " n="); if (p == nil || p > end) { - sshlog(nil, "no key (n) found"); + fprint(2, "No key (n) found\n"); free(buf); return; } myrsakey.pub.n = strtomp(p+3, nil, 16, nil); if (debug > 1) - sshdebug(nil, "n=%M", myrsakey.pub.n); + fprint(2, "n=%M\n", myrsakey.pub.n); p = strstr(st, " ek="); if (p == nil || p > end) { - sshlog(nil, "no key (ek) found"); + fprint(2, "No key (ek) found\n"); free(buf); return; } @@ -113,78 +105,71 @@ pkas[k] = nil; myrsakey.pub.ek = strtomp(p+4, nil, 16, nil); if (debug > 1) - sshdebug(nil, "ek=%M", myrsakey.pub.ek); + fprint(2, "ek=%M\n", myrsakey.pub.ek); p = strstr(st, " !dk="); if (p == nil) { p = strstr(st, "!dk?"); if (p == nil || p > end) { - // sshlog(nil, "no key (dk) found"); + // fprint(2, "No key (dk) found\n"); free(buf); return; } - goto norsa; + else + goto initdss; } myrsakey.dk = strtomp(p+5, nil, 16, nil); if (debug > 1) - sshdebug(nil, "dk=%M", myrsakey.dk); -norsa: - free(buf); + fprint(2, "dk=%M\n", myrsakey.dk); +initdss: p = getenv("dsskey"); - if (p != nil) { - remove("/env/dsskey"); - buf = p; - end = buf + strlen(p); - } else { - /* - * it would be better to use bio and rdline here instead of - * reading all of factotum's contents into memory at once. - */ - buf = emalloc9p(Maxfactotum); + if (p == nil) { fd = open("dsskey", OREAD); - if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0) - return; - n = readn(fd, buf, Maxfactotum - 1); - buf[n >= 0? n: 0] = 0; + if (fd < 0) { + fd = open("/mnt/factotum/ctl", OREAD); + if (fd < 0) + goto initdss; + } + n = read(fd, buf, 4095); + buf[n] = 0; close(fd); - assert(n < Maxfactotum - 1); - st = strstr(buf, "proto=dsa"); if (st == nil) { - sshlog(nil, "no proto=dsa key in factotum"); free(buf); return; } end = st; - for (; st > buf && *st != '\n'; --st) - ; - for (; end < buf + Maxfactotum && *end != '\n'; ++end) - ; + for (; st > buf && *st != '\n'; --st) ; + for (; end < buf+4096 && *end != '\n'; ++end) ; + } + else { + strncpy(buf, p, 4095); + remove("/env/dsskey"); } p = strstr(buf, " p="); if (p == nil || p > end) { - sshlog(nil, "no key (p) found"); + fprint(2, "No key (p) found\n"); free(buf); return; } mydsskey.pub.p = strtomp(p+3, nil, 16, nil); p = strstr(buf, " q="); if (p == nil || p > end) { - sshlog(nil, "no key (q) found"); + fprint(2, "No key (q) found\n"); free(buf); return; } mydsskey.pub.q = strtomp(p+3, nil, 16, nil); p = strstr(buf, " alpha="); if (p == nil || p > end) { - sshlog(nil, "no key (g) found"); + fprint(2, "No key (g) found\n"); free(buf); return; } mydsskey.pub.alpha = strtomp(p+7, nil, 16, nil); p = strstr(buf, " key="); if (p == nil || p > end) { - sshlog(nil, "no key (y) found"); + fprint(2, "No key (y) found\n"); free(buf); return; } @@ -195,7 +180,7 @@ if (p == nil) { p = strstr(buf, "!secret?"); if (p == nil || p > end) - sshlog(nil, "no key (x) found"); + fprint(2, "No key (x) found\n"); free(buf); return; } @@ -209,7 +194,7 @@ Packet *ks; if (myrsakey.pub.ek == nil || myrsakey.pub.n == nil) { - sshlog(c, "no public RSA key info"); + fprint(2, "No public RSA key info\n"); return nil; } ks = new_packet(c); @@ -224,21 +209,19 @@ { int n, i; uchar hh[SHA1dlen]; - static uchar sha1der[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, - 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, - }; + static uchar sha1der[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; sha1(h, SHA1dlen, hh, nil); - n = nb - (nelem(sha1der) + SHA1dlen) - 3; + n = nb - (15 + SHA1dlen) - 3; i = 0; em[i++] = 0; em[i++] = 1; memset(em + i, 0xff, n); i += n; em[i++] = 0; - memmove(em + i, sha1der, sizeof sha1der); - i += sizeof sha1der; + memmove(em + i, sha1der, sizeof(sha1der)); + i += sizeof(sha1der); memmove(em + i, hh, SHA1dlen); } @@ -266,28 +249,29 @@ mpfree(mm); mpfree(s); free(em); - } else { + } + else { fd = open("/mnt/factotum/rpc", ORDWR); if (fd < 0) return nil; sha1(m, nm, hh, nil); ar = auth_allocrpc(fd); - if (ar == nil || - auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok || - auth_rpc(ar, "write", hh, SHA1dlen) != ARok || - auth_rpc(ar, "read", nil, 0) != ARok || - ar->arg == nil) { - sshdebug(c, "got error in factotum: %r"); + if (ar == nil || auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok + || auth_rpc(ar, "write", hh, SHA1dlen) != ARok + || auth_rpc(ar, "read", nil, 0) != ARok) { + if (debug) + fprint(2, "got error in factotum: %r\n"); auth_freerpc(ar); close(fd); return nil; } + close(fd); + if (ar->arg == nil) + return nil; sstr = emalloc9p(ar->narg); memmove(sstr, ar->arg, ar->narg); n = ar->narg; - auth_freerpc(ar); - close(fd); } sig = new_packet(c); add_string(sig, pkas[c->pkalg]->name); @@ -304,18 +288,23 @@ static int rsa_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int) { - int fd, n, retval, nbit; - char *buf, *p, *sigblob; - uchar *sstr, *em; - uchar hh[SHA1dlen]; - mpint *s, *mm, *rsa_exponent, *host_modulus; AuthRpc *ar; - - sshdebug(c, "in rsa_verify for connection: %d", c->id); - SET(rsa_exponent, host_modulus); - USED(rsa_exponent, host_modulus); - if (0 && rsa_exponent) { - nbit = mpsignif(host_modulus); +// mpint *s, *mm; + char *p; + int fd, n, retval; +// int nbit; + uchar hh[SHA1dlen]; +// uchar *sstr, *em; + char *sigblob; + char *buf; + + sigblob = emalloc9p(512); + buf = emalloc9p(256); + if (debug) + fprint(2, "In rsa_verify for connection: %d\n", c->id); +#ifdef UNDEF + if (rsa_exponent) { + nbit = mpsignif (host_modulus); n = (nbit + 7) / 8; em = emalloc9p(n); /* Compute s: RFC 3447 */ @@ -330,44 +319,58 @@ mpfree(s); retval = memcmp(sig, sstr, n); free(sstr); - retval = (retval == 0); - } else { + free(sigblob); + free(buf); + if (retval == 0) + return 1; + return 0; + } + else { +#endif retval = 1; fd = open("/mnt/factotum/rpc", ORDWR); if (fd < 0) { - sshdebug(c, "could not open factotum RPC: %r"); + if (debug) + fprint(2, "Could not open factotum RPC: %r\n"); + free(sigblob); + free(buf); return 0; } - buf = emalloc9p(Blobsz / 2); - sigblob = emalloc9p(Blobsz); - p = (char *)get_string(nil, (uchar *)sig, buf, Blobsz / 2, nil); - get_string(nil, (uchar *)p, sigblob, Blobsz, &n); + p = (char *)get_string(nil, (uchar *)sig, buf, 256, nil); + get_string(nil, (uchar *)p, sigblob, 512, &n); sha1(m, nm, hh, nil); if (user != nil) p = smprint("role=verify proto=rsa user=%s", user); else p = smprint("role=verify proto=rsa sys=%s", c->remote); - ar = auth_allocrpc(fd); - if (ar == nil || auth_rpc(ar, "start", p, strlen(p)) != ARok || - auth_rpc(ar, "write", hh, SHA1dlen) != ARok || - auth_rpc(ar, "write", sigblob, n) != ARok || - auth_rpc(ar, "read", nil, 0) != ARok) { - sshdebug(c, "got error in factotum: %r"); - retval = 0; - } else { - sshdebug(c, "factotum returned %s", ar->ibuf); - if (strstr(ar->ibuf, "does not verify") != nil) - retval = -1; + if (ar == nil || auth_rpc(ar, "start", p, strlen(p)) != ARok + || auth_rpc(ar, "write", hh, SHA1dlen) != ARok + || auth_rpc(ar, "write", sigblob, n) != ARok + || auth_rpc(ar, "read", nil, 0) != ARok) { + if (debug) + fprint(2, "got error in factotum: %r\n"); + auth_freerpc(ar); + free(p); + close(fd); + free(sigblob); + free(buf); + return 0; } + if (debug) + fprint(2, "Factotum returned %s\n", ar->ibuf); + if (strstr(ar->ibuf, "does not verify")) + retval = -1; if (ar != nil) auth_freerpc(ar); free(p); close(fd); free(sigblob); free(buf); + return retval; +#ifdef UNDEF } - return retval; +#endif } static Packet * @@ -406,23 +409,24 @@ mptobe(s->s, sstr+SHA1dlen, SHA1dlen, nil); dsasigfree(s); mpfree(mm); - } else { + } + else { fd = open("/mnt/factotum/rpc", ORDWR); if (fd < 0) return nil; ar = auth_allocrpc(fd); - if (ar == nil || - auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok || - auth_rpc(ar, "write", sstr, SHA1dlen) != ARok || - auth_rpc(ar, "read", nil, 0) != ARok) { - sshdebug(c, "got error in factotum: %r"); + if (ar == nil || auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok + || auth_rpc(ar, "write", sstr, SHA1dlen) != ARok + || auth_rpc(ar, "read", nil, 0) != ARok) { + if (debug) + fprint(2, "got error in factotum: %r\n"); auth_freerpc(ar); close(fd); return nil; } + close(fd); memmove(sstr, ar->arg, ar->narg); auth_freerpc(ar); - close(fd); } add_block(sig, sstr, 2*SHA1dlen); return sig; @@ -431,8 +435,14 @@ static int dss_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int nsig) { - sshdebug(c, "in dss_verify for connection: %d", c->id); - USED(m, nm, user, sig, nsig); + if (debug) + fprint(2, "In dss_verify for connection: %d\n", c->id); + USED(c); + USED(m); + USED(nm); + USED(user); + USED(sig); + USED(nsig); return 0; } @@ -453,35 +463,32 @@ { Packet *pack2, *ks, *sig; mpint *y, *e, *f, *k; - int n, ret; + int n; uchar h[SHA1dlen]; - ret = -1; qlock(&c->l); f = mpnew(nbit); k = mpnew(nbit); - /* Compute f: RFC4253 */ y = mprand(nbit / 8, genrandom, nil); if (debug > 1) - sshdebug(c, "y=%M", y); + fprint(2, "y=%M\n", y); mpexp(two, y, grp, f); if (debug > 1) - sshdebug(c, "f=%M", f); - + fprint(2, "f=%M\n", f); /* Compute k: RFC4253 */ if (debug > 1) dump_packet(pack1); e = get_mp(pack1->payload+1); if (debug > 1) - sshdebug(c, "e=%M", e); + fprint(2, "e=%M\n", e); mpexp(e, y, grp, k); if (debug > 1) - sshdebug(c, "k=%M", k); - + fprint(2, "k=%M\n", k); /* Compute H: RFC 4253 */ pack2 = new_packet(c); - sshdebug(c, "ID strings: %s---%s", c->otherid, MYID); + if (debug) + fprint(2, "ID strings: %s---%s\n", c->otherid, MYID); add_string(pack2, c->otherid); add_string(pack2, MYID); if (debug > 1) { @@ -491,28 +498,38 @@ dump_packet(c->skexinit); } add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1); - add_block(pack2, c->skexinit->payload, - c->skexinit->rlength - c->skexinit->pad_len - 1); - sig = nil; + add_block(pack2, c->skexinit->payload, c->skexinit->rlength - c->skexinit->pad_len - 1); ks = pkas[c->pkalg]->ks(c); - if (ks == nil) - goto err; + if (ks == nil) { + free(pack2); + mpfree(y); + mpfree(e); + mpfree(f); + mpfree(k); + return -1; + } add_block(pack2, ks->payload, ks->rlength - 1); add_mp(pack2, e); add_mp(pack2, f); add_mp(pack2, k); sha1(pack2->payload, pack2->rlength - 1, h, nil); - if (c->got_sessid == 0) { memmove(c->sessid, h, SHA1dlen); c->got_sessid = 1; } sig = pkas[c->pkalg]->sign(c, h, SHA1dlen); if (sig == nil) { - sshlog(c, "failed to generate signature: %r"); - goto err; + fprint(2, "Failed to generate signature\n"); + mpfree(f); + mpfree(e); + mpfree(k); + mpfree(y); + free(sig); + free(ks); + free(pack2); + qunlock(&c->l); + return -1; } - /* Send (K_s || f || s) to client: RFC4253 */ init_packet(pack2); pack2->c = c; @@ -520,11 +537,11 @@ add_block(pack2, ks->payload, ks->rlength - 1); add_mp(pack2, f); add_block(pack2, sig->payload, sig->rlength - 1); - if (debug > 1) + if (debug) dump_packet(pack2); n = finish_packet(pack2); if (debug > 1) { - sshdebug(c, "writing %d bytes: len %d", n, nhgetl(pack2->nlength)); + fprint(2, "Writing %d bytes: len:%d\n", n, nhgetl(pack2->nlength)); dump_packet(pack2); } iowrite(c->dio, c->datafd, pack2->nlength, n); @@ -537,8 +554,7 @@ add_byte(pack2, SSH_MSG_NEWKEYS); n = finish_packet(pack2); iowrite(c->dio, c->datafd, pack2->nlength, n); - ret = 0; -err: + mpfree(f); mpfree(e); mpfree(k); @@ -547,7 +563,7 @@ free(ks); free(pack2); qunlock(&c->l); - return ret; + return 0; } static int @@ -559,13 +575,11 @@ if (c->e) mpfree(c->e); c->e = mpnew(1024); - /* Compute e: RFC4253 */ if (c->x) mpfree(c->x); c->x = mprand(128, genrandom, nil); mpexp(two, c->x, p1, c->e); - p = new_packet(c); add_byte(p, SSH_MSG_KEXDH_INIT); add_mp(p, c->e); @@ -576,132 +590,33 @@ } static int -findkeyinuserring(Conn *c, RSApub *srvkey) -{ - int n; - char *home, *newkey, *r; - - home = getenv("home"); - if (home == nil) { - newkey = "No home directory for key file"; - free(keymbox.msg); - keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey); - return -1; - } - - r = smprint("%s/lib/keyring", home); - free(home); - if ((n = findkey(r, c->remote, srvkey)) != KeyOk) { - newkey = smprint("ek=%M n=%M", srvkey->ek, srvkey->n); - free(keymbox.msg); - keymbox.msg = smprint("%c%04ld%s", n == NoKeyFile || n == NoKey? - 'c': 'b', strlen(newkey), newkey); - free(newkey); - - nbsendul(keymbox.mchan, 1); - recvul(keymbox.mchan); - if (keymbox.msg == nil || keymbox.msg[0] == 'n') { - free(keymbox.msg); - keymbox.msg = nil; - newkey = "Server key reject"; - keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); - return -1; - } - sshdebug(c, "adding key"); - if (keymbox.msg[0] == 'y') - appendkey(r, c->remote, srvkey); - else if (keymbox.msg[0] == 'r') - replacekey(r, c->remote, srvkey); - } - free(r); - return 0; -} - -static int -verifyhostkey(Conn *c, RSApub *srvkey, Packet *sig) -{ - int fd, n; - char *newkey; - uchar h[SHA1dlen]; - - sshdebug(c, "verifying server signature"); - if (findkey("/sys/lib/ssh/keyring", c->remote, srvkey) != KeyOk && - findkeyinuserring(c, srvkey) < 0) { - nbsendul(keymbox.mchan, 1); - mpfree(srvkey->ek); - mpfree(srvkey->n); - return -2; - } - - newkey = smprint("key proto=rsa role=verify sys=%s size=%d ek=%M n=%M", - c->remote, mpsignif(srvkey->n), srvkey->ek, srvkey->n); - if (newkey == nil) { - sshlog(c, "out of memory"); - exits("out of memory"); - } - - fd = open("/mnt/factotum/ctl", OWRITE); - if (fd >= 0) - write(fd, newkey, strlen(newkey)); - /* leave fd open */ - else - sshdebug(c, "factotum open failed: %r"); - - free(newkey); - mpfree(srvkey->ek); - mpfree(srvkey->n); - free(keymbox.msg); - keymbox.msg = nil; - - n = pkas[c->pkalg]->verify(c, h, SHA1dlen, nil, (char *)sig->payload, - sig->rlength); - - /* fd is perhaps still open */ - if (fd >= 0) { - /* sys here is a dotted-quad ip address */ - newkey = smprint("delkey proto=rsa role=verify sys=%s", - c->remote); - if (newkey) { - seek(fd, 0, 0); - write(fd, newkey, strlen(newkey)); - free(newkey); - } - close(fd); - } - return n; -} - -static int dh_client12(Conn *c, Packet *p) { - int n, retval; - char *newkey; - char buf[10]; - uchar *q; - uchar h[SHA1dlen]; - mpint *f, *k; Packet *ks, *sig, *pack2; RSApub *srvkey; + mpint *f, *k; + char *newkey, *r, *home; + uchar *q; + int n, fd, retval; + uchar h[SHA1dlen]; + char buf[10]; - retval = -1; ks = new_packet(c); sig = new_packet(c); pack2 = new_packet(c); - - q = get_string(p, p->payload+1, (char *)ks->payload, Maxpktpay, &n); + q = get_string(p, p->payload+1, (char *)ks->payload, 35000, &n); ks->rlength = n + 1; f = get_mp(q); q += nhgetl(q) + 4; - get_string(p, q, (char *)sig->payload, Maxpktpay, &n); + get_string(p, q, (char *)sig->payload, 35000, &n); sig->rlength = n; k = mpnew(1024); mpexp(f, c->x, p1, k); - /* Compute H: RFC 4253 */ init_packet(pack2); pack2->c = c; if (debug > 1) - sshdebug(c, "ID strings: %s---%s", c->otherid, MYID); + fprint(2, "ID strings: %s---%s\n", c->otherid, MYID); add_string(pack2, MYID); add_string(pack2, c->otherid); if (debug > 1) { @@ -710,8 +625,7 @@ fprint(2, "\nsent kexinit:"); dump_packet(c->skexinit); } - add_block(pack2, c->skexinit->payload, - c->skexinit->rlength - c->skexinit->pad_len - 1); + add_block(pack2, c->skexinit->payload, c->skexinit->rlength - c->skexinit->pad_len - 1); add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1); add_block(pack2, ks->payload, ks->rlength - 1); add_mp(pack2, c->e); @@ -719,46 +633,117 @@ add_mp(pack2, k); sha1(pack2->payload, pack2->rlength - 1, h, nil); mpfree(f); - if (c->got_sessid == 0) { memmove(c->sessid, h, SHA1dlen); c->got_sessid = 1; } - - q = get_string(ks, ks->payload, buf, sizeof buf, nil); + if (debug) + fprint(2, "Verifying server signature\n"); + q = get_string(ks, ks->payload, buf, 10, nil); srvkey = emalloc9p(sizeof (RSApub)); srvkey->ek = get_mp(q); q += nhgetl(q) + 4; srvkey->n = get_mp(q); - - /* - * key verification is really pretty pedantic and - * not doing it lets us talk to ssh v1 implementations. - */ - if (nokeyverify) - n = 1; + retval = 0; + if (findkey("/sys/lib/ssh/keyring", c->remote, srvkey) != KeyOk) { + home = getenv("home"); + if (home == nil) { + newkey = "No home directory for key file"; + if (keymbox.msg) + free(keymbox.msg); + keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey); + nbsendul(keymbox.mchan, 1); + mpfree(srvkey->ek); + mpfree(srvkey->n); + mpfree(k); + free(ks); + free(sig); + free(pack2); + free(srvkey); + return -1; + } + r = smprint("%s/lib/keyring", home); + free(home); + if ((n = findkey(r, c->remote, srvkey)) != KeyOk) { + newkey = smprint("ek=%M n=%M", srvkey->ek, srvkey->n); + if (keymbox.msg) + free(keymbox.msg); + if (n == NoKeyFile || n == NoKey) + keymbox.msg = smprint("c%04ld%s", strlen(newkey), newkey); + else + keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey); + free(newkey); + nbsendul(keymbox.mchan, 1); + recvul(keymbox.mchan); + if (keymbox.msg == nil || keymbox.msg[0] == 'n') { + free(keymbox.msg); + keymbox.msg = nil; + newkey = "Server key reject"; + keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + nbsendul(keymbox.mchan, 1); + free(r); + mpfree(k); + mpfree(srvkey->ek); + mpfree(srvkey->n); + free(ks); + free(sig); + free(pack2); + free(srvkey); + return -1; + } + else { + if (debug) + fprint(2, "Adding key\n"); + if (keymbox.msg[0] == 'y') + appendkey(r, c->remote, srvkey); + else if (keymbox.msg[0] == 'r') + replacekey(r, c->remote, srvkey); + } + } + free(r); + } + newkey = smprint("key proto=rsa role=verify sys=%s size=%d ek=%M n=%M", + c->remote, mpsignif (srvkey->n), srvkey->ek, srvkey->n); + fd = open("/mnt/factotum/ctl", OWRITE); + if (fd >= 0) { + write(fd, newkey, strlen(newkey)); + close(fd); + } else - n = verifyhostkey(c, srvkey, sig); + if (debug) + fprint(2, "Factotum open failed: %r\n"); + free(newkey); + mpfree(srvkey->ek); + mpfree(srvkey->n); + if (keymbox.msg) + free(keymbox.msg); + keymbox.msg = nil; + n = pkas[c->pkalg]->verify(c, h, SHA1dlen, nil, (char *)sig->payload, sig->rlength); + newkey = smprint("delkey proto=rsa role=verify sys=%s", c->remote); + fd = open("/mnt/factotum/ctl", OWRITE); + if (fd >= 0) { + write(fd, newkey, strlen(newkey)); + close(fd); + } + free(newkey); switch (n) { - case -2: - goto out; case -1: - newkey = "Signature verification failed; try ssh -v"; - keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); - break; - case 0: - newkey = "Key verification dialog failed; try ssh -v"; + newkey = "Signature verifcation failed"; keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + retval = -1; break; case 1: keymbox.msg = smprint("o0000"); - retval = 0; + break; + case 0: + newkey = "Key verification dialog failed"; + keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + retval = -1; break; } nbsendul(keymbox.mchan, 1); if (retval == 0) genkeys(c, h, k); -out: mpfree(k); free(ks); free(sig); @@ -774,8 +759,8 @@ mpint *e, *x; int n; - /* Compute e: RFC4253 */ e = mpnew(2048); + /* Compute e: RFC4253 */ x = mprand(256, genrandom, nil); mpexp(two, x, p14, e); p = new_packet(c); @@ -796,106 +781,106 @@ } static void -initsha1pkt(Packet *pack2, mpint *k, uchar *h) -{ - init_packet(pack2); - add_mp(pack2, k); - add_packet(pack2, h, SHA1dlen); -} - -static void genkeys(Conn *c, uchar h[], mpint *k) { Packet *pack2; - char buf[82], *bp, *be; /* magic 82 */ + char buf[82], *bp, *be; int n; pack2 = new_packet(c); /* Compute 40 bytes (320 bits) of keys: each alg can use what it needs */ - /* Client to server IV */ if (debug > 1) { fprint(2, "k=%M\nh=", k); - for (n = 0; n < SHA1dlen; ++n) - fprint(2, "%02ux", h[n]); + for (n = 0; n < SHA1dlen; ++n) fprint(2, "%02ux", h[n]); fprint(2, "\nsessid="); - for (n = 0; n < SHA1dlen; ++n) - fprint(2, "%02ux", c->sessid[n]); + for (n = 0; n < SHA1dlen; ++n) fprint(2, "%02ux", c->sessid[n]); fprint(2, "\n"); } - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'A'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2siv, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->nc2siv, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2siv + SHA1dlen, nil); - /* Server to client IV */ - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'B'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2civ, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->ns2civ, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2civ + SHA1dlen, nil); - /* Client to server encryption key */ - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'C'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2sek, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->nc2sek, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2sek + SHA1dlen, nil); - /* Server to client encryption key */ - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'D'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2cek, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->ns2cek, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2cek + SHA1dlen, nil); - /* Client to server integrity key */ - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'E'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2sik, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->nc2sik, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->nc2sik + SHA1dlen, nil); - /* Server to client integrity key */ - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_byte(pack2, 'F'); add_packet(pack2, c->sessid, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2cik, nil); - initsha1pkt(pack2, k, h); + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); add_packet(pack2, c->ns2cik, SHA1dlen); sha1(pack2->payload, pack2->rlength - 1, c->ns2cik + SHA1dlen, nil); - if (debug > 1) { - be = buf + sizeof buf; + be = buf + 82; fprint(2, "Client to server IV:\n"); - for (n = 0, bp = buf; n < SHA1dlen*2; ++n) - bp = seprint(bp, be, "%02x", c->nc2siv[n]); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) bp = seprint(bp, be, "%02x", c->nc2siv[n]); fprint(2, "%s\n", buf); - fprint(2, "Server to client IV:\n"); - for (n = 0, bp = buf; n < SHA1dlen*2; ++n) - bp = seprint(bp, be, "%02x", c->ns2civ[n]); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) bp = seprint(bp, be, "%02x", c->ns2civ[n]); fprint(2, "%s\n", buf); - fprint(2, "Client to server EK:\n"); - for (n = 0, bp = buf; n < SHA1dlen*2; ++n) - bp = seprint(bp, be, "%02x", c->nc2sek[n]); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) bp = seprint(bp, be, "%02x", c->nc2sek[n]); fprint(2, "%s\n", buf); - fprint(2, "Server to client EK:\n"); - for (n = 0, bp = buf; n < SHA1dlen*2; ++n) - bp = seprint(bp, be, "%02x", c->ns2cek[n]); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) bp = seprint(bp, be, "%02x", c->ns2cek[n]); fprint(2, "%s\n", buf); } free(pack2); --- /sys/src/cmd/ssh2/dial.c Thu Apr 19 15:20:22 2012 +++ /sys/src/cmd/ssh2/dial.c Wed Feb 22 05:02:42 2012 @@ -1,33 +1,25 @@ -/* - * dial - connect to a service (parallel version) - */ #include #include #include -typedef struct Conn Conn; -typedef struct Dest Dest; typedef struct DS DS; +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); + enum { Maxstring = 128, Maxpath = 256, - - Maxcsreply = 64*80, /* this is probably overly generous */ - /* - * this should be a plausible slight overestimate for non-interactive - * use even if it's ridiculously long for interactive use. - */ - Maxconnms = 2*60*1000, /* 2 minutes */ }; struct DS { /* dist string */ char buf[Maxstring]; - char *netdir; /* e.g., /net.alt */ - char *proto; /* e.g., tcp */ - char *rem; /* e.g., host!service */ + char *netdir; + char *proto; + char *rem; /* other args */ char *local; @@ -35,45 +27,12 @@ int *cfdp; }; -/* - * malloc these; they need to be writable by this proc & all children. - * the stack is private to each proc, and static allocation in the data - * segment would not permit concurrent dials within a multi-process program. - */ -struct Conn { - int pid; - int dead; - - int dfd; - int cfd; - char dir[NETPATHLEN]; - char err[ERRMAX]; -}; -struct Dest { - Conn *conn; /* allocated array */ - Conn *connend; - int nkid; - - long oalarm; - int naddrs; - - QLock winlck; - int winner; /* index into conn[] */ - - char *nextaddr; - char addrlist[Maxcsreply]; -}; - -static int call(char*, char*, DS*, Dest*, Conn*); -static int csdial(DS*); -static void _dial_string_parse(char*, DS*); - /* * the dialstring is of the form '[/net/]proto!dest' */ -static int -dialimpl(char *dest, char *local, char *dir, int *cfdp) +int +dial(char *dest, char *local, char *dir, int *cfdp) { DS ds; int rv; @@ -111,245 +70,11 @@ return rv; } -/* - * the thread library can't cope with rfork(RFMEM|RFPROC), - * so it must override this with a private version of dial. - */ -int (*_dial)(char *, char *, char *, int *) = dialimpl; - -int -dial(char *dest, char *local, char *dir, int *cfdp) -{ - return (*_dial)(dest, local, dir, cfdp); -} - -static int -connsalloc(Dest *dp, int addrs) -{ - Conn *conn; - - free(dp->conn); - dp->connend = nil; - assert(addrs > 0); - - dp->conn = mallocz(addrs * sizeof *dp->conn, 1); - if(dp->conn == nil) - return -1; - dp->connend = dp->conn + addrs; - for(conn = dp->conn; conn < dp->connend; conn++) - conn->cfd = conn->dfd = -1; - return 0; -} - -static void -freedest(Dest *dp) -{ - long oalarm; - - if (dp == nil) - return; - oalarm = dp->oalarm; - free(dp->conn); - free(dp); - if (oalarm >= 0) - alarm(oalarm); -} - -static void -closeopenfd(int *fdp) -{ - if (*fdp >= 0) { - close(*fdp); - *fdp = -1; - } -} - -static void -notedeath(Dest *dp, char *exitsts) -{ - int i, n, pid; - char *fields[5]; /* pid + 3 times + error */ - Conn *conn; - - for (i = 0; i < nelem(fields); i++) - fields[i] = ""; - n = tokenize(exitsts, fields, nelem(fields)); - if (n < 4) - return; - pid = atoi(fields[0]); - if (pid <= 0) - return; - for (conn = dp->conn; conn < dp->connend; conn++) - if (conn->pid == pid && !conn->dead) { /* it's one we know? */ - if (conn - dp->conn != dp->winner) { - closeopenfd(&conn->dfd); - closeopenfd(&conn->cfd); - } - strncpy(conn->err, fields[4], sizeof conn->err); - conn->dead = 1; - return; - } - /* not a proc that we forked */ -} - -static int -outstandingprocs(Dest *dp) -{ - Conn *conn; - - for (conn = dp->conn; conn < dp->connend; conn++) - if (!conn->dead) - return 1; - return 0; -} - -static int -reap(Dest *dp) -{ - char exitsts[2*ERRMAX]; - - if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) { - notedeath(dp, exitsts); - return 0; - } - return -1; -} - -static int -fillinds(DS *ds, Dest *dp) -{ - Conn *conn; - - if (dp->winner < 0) - return -1; - conn = &dp->conn[dp->winner]; - if (ds->cfdp) - *ds->cfdp = conn->cfd; - if (ds->dir) - strncpy(ds->dir, conn->dir, NETPATHLEN); - return conn->dfd; -} - -static int -connectwait(Dest *dp, char *besterr) -{ - Conn *conn; - - /* wait for a winner or all attempts to time out */ - while (dp->winner < 0 && reap(dp) >= 0) - ; - - /* kill all of our still-live kids & reap them */ - for (conn = dp->conn; conn < dp->connend; conn++) - if (!conn->dead) - postnote(PNPROC, conn->pid, "alarm"); - while (reap(dp) >= 0) - ; - - /* rummage about and report some error string */ - for (conn = dp->conn; conn < dp->connend; conn++) - if (conn - dp->conn != dp->winner && conn->dead && - conn->err[0]) { - strncpy(besterr, conn->err, ERRMAX); - break; - } - return dp->winner; -} - -static int -parsecs(Dest *dp, char **clonep, char **destp) -{ - char *dest, *p; - - dest = strchr(dp->nextaddr, ' '); - if(dest == nil) - return -1; - *dest++ = '\0'; - p = strchr(dest, '\n'); - if(p == nil) - return -1; - *p++ = '\0'; - *clonep = dp->nextaddr; - *destp = dest; - dp->nextaddr = p; /* advance to next line */ - return 0; -} - -static void -pickuperr(char *besterr, char *err) -{ - err[0] = '\0'; - errstr(err, ERRMAX); - if(strstr(err, "does not exist") == 0) - strcpy(besterr, err); -} - -static void -catcher(void *, char *s) -{ - if (strstr(s, "alarm") != nil) - noted(NCONT); - else - noted(NDFLT); -} - -/* - * try all addresses in parallel and take the first one that answers; - * this helps when systems have ip v4 and v6 addresses but are - * only reachable from here on one (or some) of them. - */ -static int -dialmulti(DS *ds, Dest *dp) -{ - int rv, kid, kidme; - char *clone, *dest; - char err[ERRMAX], besterr[ERRMAX]; - - dp->winner = -1; - dp->nkid = 0; - while(dp->winner < 0 && *dp->nextaddr != '\0' && - parsecs(dp, &clone, &dest) >= 0) { - kidme = dp->nkid++; /* make private copy on stack */ - kid = rfork(RFPROC|RFMEM); /* spin off a call attempt */ - if (kid < 0) - --dp->nkid; - else if (kid == 0) { - /* only in kid, to avoid atnotify callbacks in parent */ - notify(catcher); - - *besterr = '\0'; - rv = call(clone, dest, ds, dp, &dp->conn[kidme]); - if(rv < 0) - pickuperr(besterr, err); - _exits(besterr); /* avoid atexit callbacks */ - } - } - rv = connectwait(dp, besterr); - if(rv < 0 && *besterr) - werrstr("%s", besterr); - else - werrstr("%s", err); - return rv; -} - static int csdial(DS *ds) { - int n, fd, rv, addrs, bleft; - char c; - char *addrp, *clone2, *dest; - char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; - Dest *dp; - - dp = mallocz(sizeof *dp, 1); - if(dp == nil) - return -1; - dp->winner = -1; - dp->oalarm = alarm(0); - if (connsalloc(dp, 1) < 0) { /* room for a single conn. */ - freedest(dp); - return -1; - } + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; /* * open connection server @@ -359,10 +84,7 @@ if(fd < 0){ /* no connection server, don't translate */ snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); - rv = call(clone, ds->rem, ds, dp, &dp->conn[0]); - fillinds(ds, dp); - freedest(dp); - return rv; + return call(clone, ds->rem, ds); } /* @@ -371,60 +93,43 @@ snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); if(write(fd, buf, strlen(buf)) < 0){ close(fd); - freedest(dp); return -1; } /* - * read all addresses from the connection server. + * loop through each address from the connection server till + * we get one that works. */ + *besterr = 0; + rv = -1; seek(fd, 0, 0); - addrs = 0; - addrp = dp->nextaddr = dp->addrlist; - bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ - while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) { - if (addrp[n-1] != '\n') - addrp[n++] = '\n'; - addrs++; - addrp += n; - bleft -= n; + while((n = read(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + strcpy(besterr, err); } - /* - * if we haven't read all of cs's output, assume the last line might - * have been truncated and ignore it. we really don't expect this - * to happen. - */ - if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1) - addrs--; close(fd); - *besterr = 0; - rv = -1; /* pessimistic default */ - dp->naddrs = addrs; - if (addrs == 0) - werrstr("no address to dial"); - else if (addrs == 1) { - /* common case: dial one address without forking */ - if (parsecs(dp, &clone2, &dest) >= 0 && - (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) { - pickuperr(besterr, err); - werrstr("%s", besterr); - } - } else if (connsalloc(dp, addrs) >= 0) - rv = dialmulti(ds, dp); - - /* fill in results */ - if (rv >= 0 && dp->winner >= 0) - rv = fillinds(ds, dp); - - freedest(dp); + if(rv < 0 && *besterr) + werrstr("%s", besterr); + else + werrstr("%s", err); return rv; } static int -call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn) +call(char *clone, char *dest, DS *ds) { - int fd, cfd, n, calleralarm, oalarm; + int fd, cfd, n; char cname[Maxpath], name[Maxpath], data[Maxpath], *p; /* because cs is in a different name space, replace the mount point */ @@ -438,15 +143,14 @@ p = clone; snprint(cname, sizeof cname, "%s/%s", ds->netdir, p); - conn->pid = getpid(); - conn->cfd = cfd = open(cname, ORDWR); + cfd = open(cname, ORDWR); if(cfd < 0) return -1; /* get directory name */ n = read(cfd, name, sizeof(name)-1); if(n < 0){ - closeopenfd(&conn->cfd); + close(cfd); return -1; } name[n] = 0; @@ -456,80 +160,40 @@ p = strrchr(cname, '/'); *p = 0; if(ds->dir) - snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name); + snprint(ds->dir, NETPATHLEN, "%s/%s", cname, name); snprint(data, sizeof(data), "%s/%s/data", cname, name); - /* should be no alarm pending now; re-instate caller's alarm, if any */ - calleralarm = dp->oalarm > 0; - if (calleralarm) - alarm(dp->oalarm); - else if (dp->naddrs > 1) /* in a sub-process? */ - alarm(Maxconnms); - /* connect */ if(ds->local) snprint(name, sizeof(name), "connect %s %s", dest, ds->local); else snprint(name, sizeof(name), "connect %s", dest); if(write(cfd, name, strlen(name)) < 0){ - closeopenfd(&conn->cfd); + close(cfd); return -1; } - oalarm = alarm(0); /* don't let alarm interrupt critical section */ - if (calleralarm) - dp->oalarm = oalarm; /* time has passed, so update user's */ - /* open data connection */ - conn->dfd = fd = open(data, ORDWR); + fd = open(data, ORDWR); if(fd < 0){ - closeopenfd(&conn->cfd); - alarm(dp->oalarm); + close(cfd); return -1; } - if(ds->cfdp == nil) - closeopenfd(&conn->cfd); - - n = conn - dp->conn; - if (dp->winner < 0) { - qlock(&dp->winlck); - if (dp->winner < 0 && conn < dp->connend) - dp->winner = n; - qunlock(&dp->winlck); - } - alarm(calleralarm? dp->oalarm: 0); + if(ds->cfdp) + *ds->cfdp = cfd; + else + close(cfd); return fd; } /* - * assume p points at first '!' in dial string. st is start of dial string. - * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as - * part of the proto string, so skip numeric components. - * returns pointer to delimiter after right-most non-numeric component. - */ -static char * -backoverchans(char *st, char *p) -{ - char *sl; - - for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) { - while (--p >= st && isascii(*p) && isdigit(*p)) - ; - if (p < st || *p != '/') - break; /* "net.alt2" or ran off start */ - while (p > st && p[-1] == '/') /* skip runs of slashes */ - p--; - } - return sl; -} - -/* * parse a dial string */ static void _dial_string_parse(char *str, DS *ds) { char *p, *p2; + int nondig; strncpy(ds->buf, str, Maxstring); ds->buf[Maxstring-1] = 0; @@ -544,11 +208,12 @@ ds->netdir = 0; ds->proto = ds->buf; } else { - p2 = backoverchans(ds->buf, p); - - /* back over last component of netdir (proto) */ - while (--p2 > ds->buf && *p2 != '/') - ; + nondig = 0; + do { + for(p2 = ds->buf+1; *p2 != '/'; p2++) + if(!isdigit(*p2)) + nondig=1; + } while(!nondig); *p2++ = 0; ds->netdir = ds->buf; ds->proto = p2; --- /sys/src/cmd/ssh2/mkfile Thu Apr 19 15:20:32 2012 +++ /sys/src/cmd/ssh2/mkfile Tue Apr 3 05:55:18 2012 @@ -1,14 +1,13 @@ dup.out +$O.rsa2ssh2: \ + transport.$O -magic.out:D: $HFILES $CFILES - magic >$target +$O.ssh2key: \ + transport.$O -long.funcs:D: $CFILES - funclen *.c | sort -nr | awk '$1 > 24' >$target +$BIN/sshtun: $O.sshtun + cp $O.sshtun $BIN/sshtun --- /sys/src/cmd/ssh2/pubkey.c Thu Apr 19 15:20:36 2012 +++ /sys/src/cmd/ssh2/pubkey.c Wed Feb 22 05:04:18 2012 @@ -6,11 +6,7 @@ #include #include #include <9p.h> -#include "netssh.h" - -enum { - Arbsz = 256, -}; +#include "sshtun.h" static int parsepubkey(char *s, RSApub *key, char **sp, int base) @@ -21,7 +17,7 @@ z = nil; n = strtoul(s, &p, 10); host = nil; - if(n < Arbsz || !isspace(*p)){ /* maybe this is a host name */ + if(n < 256 || !isspace(*p)){ /* maybe this is a host name */ host = s; s = strpbrk(s, " \t"); if(s == nil) @@ -31,17 +27,17 @@ s += strspn(s, " \t"); n = strtoul(s, &p, 10); - if(n < Arbsz || !isspace(*p)){ + if(n < 256 || !isspace(*p)){ if(z) *z = ' '; return -1; } } - /* Arbsz is just a sanity check */ - if((key->ek = strtomp(p, &p, base, nil)) == nil || - (key->n = strtomp(p, &p, base, nil)) == nil || - (*p != '\0' && !isspace(*p)) || mpsignif(key->n) < Arbsz) { + if((key->ek = strtomp(p, &p, base, nil)) == nil + || (key->n = strtomp(p, &p, base, nil)) == nil + || (*p != '\0' && !isspace(*p)) + || mpsignif(key->n) < 256){ /* 256 is just a sanity check */ mpfree(key->ek); mpfree(key->n); key->ek = nil; @@ -74,33 +70,39 @@ if(key == nil) return nil; - for (; (s = Brdstr(b, '\n', 1)) != nil; free(s)) - if(s[0] != '#'){ - if(parsepubkey(s, key, sp, 10) == 0 || - parsepubkey(s, key, sp, 16) == 0) - return key; - fprint(2, "warning: skipping line '%s'; cannot parse\n", - s); - } - free(key); - return nil; + for(;;){ + if((s = Brdstr(b, '\n', 1)) == nil){ + free(key); + return nil; + } + if(s[0]=='#'){ + free(s); + continue; + } + if(parsepubkey(s, key, sp, 10)==0 + || parsepubkey(s, key, sp, 16)==0) + return key; + fprint(2, "warning: skipping line '%s'; cannot parse\n", s); + free(s); + } } static int match(char *pattern, char *aliases) { - char *s, *snext, *a, *anext, *ae; + char *s, *snext; + char *a, *anext, *ae; - for(s = pattern; s && *s; s = snext){ - if((snext = strchr(s, ',')) != nil) + for(s=pattern; s && *s; s=snext){ + if((snext=strchr(s, ',')) != nil) *snext++ = '\0'; - for(a = aliases; a && *a; a = anext){ - if((anext = strchr(a, ',')) != nil){ + for(a=aliases; a && *a; a=anext){ + if((anext=strchr(a, ',')) != nil){ ae = anext; anext++; }else - ae = a + strlen(a); - if(ae - a == strlen(s) && memcmp(s, a, ae - a) == 0) + ae = a+strlen(a); + if(ae-a == strlen(s) && memcmp(s, a, ae-a)==0) return 0; } } @@ -122,8 +124,7 @@ if ((k = readpublickey(b, &h)) == nil) break; if (match(h, host) == 0) { - if (mpcmp(k->n, key->n) == 0 && - mpcmp(k->ek, key->ek) == 0) + if (mpcmp(k->n, key->n) == 0 && mpcmp(k->ek, key->ek) == 0) res = KeyOk; else res = KeyWrong; @@ -140,87 +141,89 @@ int replacekey(char *keyfile, char *host, RSApub *hostkey) { - int ret; char *h, *nkey, *p; Biobuf *br, *bw; Dir *d, nd; RSApub *k; - ret = -1; - d = nil; nkey = smprint("%s.new", keyfile); if(nkey == nil) return -1; - if((br = Bopen(keyfile, OREAD)) == nil) - goto out; + if((br = Bopen(keyfile, OREAD)) == nil){ + free(nkey); + return -1; + } if((bw = Bopen(nkey, OWRITE)) == nil){ Bterm(br); - goto out; + free(nkey); + return -1; } while((k = readpublickey(br, &h)) != nil){ - if(match(h, host) != 0) + if(match(h, host) != 0){ Bprint(bw, "%s %d %.10M %.10M\n", h, mpsignif(k->n), k->ek, k->n); + } free(h); rsapubfree(k); } - Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n), - hostkey->ek, hostkey->n); + Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n), hostkey->ek, hostkey->n); Bterm(bw); Bterm(br); d = dirstat(nkey); if(d == nil){ fprint(2, "new key file disappeared?\n"); - goto out; + free(nkey); + return -1; } p = strrchr(d->name, '.'); - if(p == nil || strcmp(p, ".new") != 0){ - fprint(2, "%s: new key file changed names? %s to %s\n", - argv0, nkey, d->name); - goto out; + if(p==nil || strcmp(p, ".new")!=0){ + fprint(2, "new key file changed names? %s to %s\n", nkey, d->name); + free(d); + free(nkey); + return -1; } *p = '\0'; nulldir(&nd); nd.name = d->name; if(remove(keyfile) < 0){ - fprint(2, "%s: error removing %s: %r\n", argv0, keyfile); - goto out; + fprint(2, "error removing %s: %r\n", keyfile); + free(d); + free(nkey); + return -1; } if(dirwstat(nkey, &nd) < 0){ - fprint(2, "%s: error renaming %s to %s: %r\n", - argv0, nkey, d->name); - goto out; + fprint(2, "error renaming %s to %s: %r\n", nkey, d->name); + free(nkey); + free(d); + return -1; } - ret = 0; -out: free(d); free(nkey); - return ret; + return 0; } int appendkey(char *keyfile, char *host, RSApub *key) { - int fd, ret; + int fd; - ret = -1; if((fd = open(keyfile, OWRITE)) < 0){ fd = create(keyfile, OWRITE, 0666); if(fd < 0){ - fprint(2, "%s: can't open nor create %s: %r\n", - argv0, keyfile); + fprint(2, "cannot open nor create %s: %r\n", keyfile); return -1; } } - if(seek(fd, 0, 2) >= 0 && - fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n), - key->ek, key->n) >= 0) - ret = 0; + if(seek(fd, 0, 2) < 0 + || fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n), key->ek, key->n) < 0){ + close(fd); + return -1; + } close(fd); - return ret; + return 0; } --- /sys/src/cmd/ssh2/rsa2ssh2.c Thu Apr 19 15:20:36 2012 +++ /sys/src/cmd/ssh2/rsa2ssh2.c Fri Dec 16 20:21:35 2011 @@ -7,7 +7,7 @@ #include #include #include -#include "netssh.h" +#include "sshtun.h" Cipher *cryptos[1]; int debug; @@ -15,7 +15,7 @@ void usage(void) { - fprint(2, "usage: %s [file]\n", argv0); + fprint(2, "usage: rsa2ssh2 [file]\n"); exits("usage"); } @@ -26,7 +26,7 @@ char *ep, *np, *user; mpint *e, *n; int fd, m; - char key[Maxrpcbuf], encpub[Maxrpcbuf]; + char key[8192], encpub[8192]; ARGBEGIN { default: @@ -34,7 +34,6 @@ } ARGEND if (argc > 1) usage(); - user = getenv("user"); if (argc == 0) fd = 0; @@ -43,22 +42,25 @@ if (fd < 0) usage(); } - m = read(fd, key, Maxrpcbuf - 1); + m = read(fd, key, 8191); close(fd); - key[m >= 0? m: 0] = 0; - + key[m] = 0; ep = strstr(key, " ek="); np = strstr(key, " n="); - if (ep == nil || np == nil) - sysfatal("bad key file\n"); + if (ep == nil || np == nil) { + fprint(2, "Invalid key file\n"); + exits("invalid"); + } e = strtomp(ep+4, nil, 16, nil); n = strtomp(np+3, nil, 16, nil); p = new_packet(nil); add_string(p, "ssh-rsa"); add_mp(p, e); add_mp(p, n); - if ((m = enc64(encpub, Maxrpcbuf, p->payload, p->rlength-1)) < 0) - sysfatal("base-64 encoding failed\n"); + if ((m = enc64(encpub, 8192, p->payload, p->rlength-1)) < 0) { + fprint(2, "Base 64 encoding failed\n"); + exits("fail"); + } print("ssh-rsa "); write(1, encpub, m); if (user) --- /sys/src/cmd/ssh2/scp.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2/scp.c Sun Apr 15 22:26:55 2012 @@ -0,0 +1,801 @@ +#include +#include +#include + +int +isatty(int fd) +{ + char buf[64]; + + buf[0] = '\0'; + fd2path(fd, buf, sizeof buf); + if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) + return 1; + return 0; +} + +#define OK 0x00 +#define ERROR 0x01 +#define FATAL 0x02 + +char *progname; + +int dflag; +int fflag; +int iflag; +int pflag; +int rflag; +int tflag; +int vflag; + +int remote; + +char *exitflag = nil; + +void scperror(int, char*, ...); +void mustbedir(char*); +void receive(char*); +char *fileaftercolon(char*); +void destislocal(char *cmd, int argc, char *argv[], char *dest); +void destisremote(char *cmd, int argc, char *argv[], char *host, char *dest); +int remotessh(char *host, char *cmd); +void send(char*); +void senddir(char*, int, Dir*); +int getresponse(void); + +char theuser[32]; + +char ssh[] = "/bin/ssh"; + +int remotefd0; +int remotefd1; + +int +runcommand(char *cmd) +{ + Waitmsg *w; + int pid; + char *argv[4]; + + if (cmd == nil) + return -1; + switch(pid = fork()){ + case -1: + return -1; + case 0: + argv[0] = "rc"; + argv[1] = "-c"; + argv[2] = cmd; + argv[3] = nil; + exec("/bin/rc", argv); + exits("exec failed"); + } + for(;;){ + w = wait(); + if(w == nil) + return -1; + if(w->pid == pid) + break; + free(w); + } + if(w->msg[0]){ + free(w); + return -1; + } + free(w); + return 1; +} + +void +vprint(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + static char *name; + + if(vflag == 0) + return; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + if(name == nil){ + name = sysname(); + if(name == nil) + name = ""; + } + fprint(2, "%s: %s\n", name, buf); +} + +void +usage(void) +{ + fprint(2, "usage: scp [-Iidfprtv] source ... destination\n"); + exits("usage"); +} + + +#pragma varargck type "F" int +#pragma varargck type "V" char* +static int flag; + +/* flag: if integer flag, take following char *value */ +int +flagfmt(Fmt *f) +{ + flag = va_arg(f->args, int); + return 0; +} + +/* flag: if previous integer flag, take char *value */ +int +valfmt(Fmt *f) +{ + char *value; + + value = va_arg(f->args, char*); + if(flag) + return fmtprint(f, " %s", value); + return 0; +} + +void +sendokresponse(void) +{ + char ok = OK; + + write(remotefd1, &ok, 1); +} + +void +main(int argc, char *argv[]) +{ + int i, fd; + char cmd[32]; + char *p; + + progname = argv[0]; + fmtinstall('F', flagfmt); + fmtinstall('V', valfmt); + iflag = -1; + + ARGBEGIN { + case 'I': + iflag = 0; + break; + case 'i': + iflag = 1; + break; + case 'd': + dflag++; + break; + case 'f': + fflag++; + remote++; + break; + case 'p': + pflag++; + break; + case 'r': + rflag++; + break; + case 't': + tflag++; + remote++; + break; + case 'v': + vflag++; + break; + default: + usage(); + } ARGEND + + if(iflag == -1) + iflag = isatty(0); + + remotefd0 = 0; + remotefd1 = 1; + + if(fflag){ + getresponse(); + for(i=0; i 2) + dflag = 1; + + i = 0; + fd = open("/dev/user", OREAD); + if(fd >= 0){ + i = read(fd, theuser, sizeof theuser - 1); + close(fd); + } + if(i <= 0) + scperror(1, "can't read /dev/user: %r"); + + remotefd0 = -1; + remotefd1 = -1; + + snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V", + dflag, "-d", + pflag, "-p", + rflag, "-r", + vflag, "-v"); + + p = fileaftercolon(argv[argc-1]); + if(p != nil) /* send to remote machine. */ + destisremote(cmd, argc-1, argv, argv[argc-1], p); + else{ + if(dflag) + mustbedir(argv[argc-1]); + destislocal(cmd, argc-1, argv, argv[argc-1]); + } + + exits(exitflag); +} + +void +destislocal(char *cmd, int argc, char *argv[], char *dst) +{ + int i; + char *src; + char buf[8192]; + + for(i = 0; iqid.type & QTDIR)) { + scperror(0, "%s: protocol botch: directory requrest for non-directory", dir); + return d; + } + }else{ + /* create it writeable; will fix later */ + setmodes = 1; + fd = create(dir, OREAD, DMDIR|mode|0700); + if (fd < 0){ + scperror(0, "%s: can't create: %r", dir); + return d; + } + d = dirfstat(fd); + close(fd); + if(d == nil){ + scperror(0, "%s: can't stat: %r", dir); + return d; + } + } + receive(dir); + if(settimes || setmodes){ + nulldir(&nd); + if(settimes){ + nd.atime = atime; + nd.mtime = mtime; + d->atime = nd.atime; + d->mtime = nd.mtime; + } + if(setmodes){ + nd.mode = DMDIR | (mode & 0777); + d->mode = nd.mode; + } + if(dirwstat(dir, &nd) < 0){ + scperror(0, "can't wstat %s: %r", dir); + free(d); + return nil; + } + } + return d; +} + +void +receive(char *dest) +{ + int isdir, settimes, mode; + int exists, n, i, fd, m; + int errors; + ulong atime, mtime; + uvlong size; + char buf[32*1024], *p; + char name[8192]; + Dir *d; + Dir nd; + + mtime = 0L; + atime = 0L; + settimes = 0; + isdir = 0; + if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) { + isdir = 1; + } + if(dflag && !isdir) + scperror(1, "%s: not a directory: %r", dest); + + sendokresponse(); + + for (;;) { + readhdr(buf, sizeof buf); + + switch(buf[0]){ + case ERROR: + case FATAL: + if(!remote) + fprint(2, "%s\n", buf+1); + exitflag = "bad receive"; + if(buf[0] == FATAL) + exits(exitflag); + continue; + + case 'E': + sendokresponse(); + return; + + case 'T': + settimes = 1; + p = buf + 1; + mtime = strtol(p, &p, 10); + if(*p++ != ' '){ + Badtime: + scperror(1, "bad time format: %s", buf+1); + } + strtol(p, &p, 10); + if(*p++ != ' ') + goto Badtime; + atime = strtol(p, &p, 10); + if(*p++ != ' ') + goto Badtime; + strtol(p, &p, 10); + if(*p++ != 0) + goto Badtime; + + sendokresponse(); + continue; + + case 'D': + case 'C': + p = buf + 1; + mode = strtol(p, &p, 8); + if (*p++ != ' '){ + Badmode: + scperror(1, "bad mode/size format: %s", buf+1); + } + size = strtoll(p, &p, 10); + if(*p++ != ' ') + goto Badmode; + + if(isdir){ + if(dest[0] == '\0') + snprint(name, sizeof name, "%s", p); + else + snprint(name, sizeof name, "%s/%s", dest, p); + }else + snprint(name, sizeof name, "%s", dest); + if(strlen(name) > sizeof name-UTFmax) + scperror(1, "file name too long: %s", dest); + + exists = 1; + free(d); + if((d = dirstat(name)) == nil) + exists = 0; + + if(buf[0] == 'D'){ + vprint("receive directory %s", name); + d = receivedir(name, exists, d, settimes, atime, mtime, mode); + settimes = 0; + continue; + } + + vprint("receive file %s by %s", name, getuser()); + fd = create(name, OWRITE, mode); + if(fd < 0){ + scperror(0, "can't create %s: %r", name); + continue; + } + sendokresponse(); + + /* + * Committed to receive size bytes + */ + errors = 0; + for(i = 0; i < size; i += m){ + n = sizeof buf; + if(n > size - i) + n = size - i; + m = readn(remotefd0, buf, n); + if(m <= 0) + scperror(1, "read error on connection: %r"); + if(errors == 0){ + n = write(fd, buf, m); + if(n != m) + errors = 1; + } + } + + /* if file exists, modes could be wrong */ + if(errors) + scperror(0, "%s: write error: %r", name); + else if(settimes || (exists && (d->mode&0777) != (mode&0777))){ + nulldir(&nd); + if(settimes){ + settimes = 0; + nd.atime = atime; + nd.mtime = mtime; + } + if(exists && (d->mode&0777) != (mode&0777)) + nd.mode = (d->mode & ~0777) | (mode&0777); + if(dirwstat(name, &nd) < 0) + scperror(0, "can't wstat %s: %r", name); + } + free(d); + d = nil; + close(fd); + getresponse(); + if(errors) + exits("write error"); + sendokresponse(); + break; + + default: + scperror(0, "unrecognized header type char %c", buf[0]); + scperror(1, "input line: %s", buf); + } + } +} + +/* + * Lastelem is called when we have a Dir with the final element, but if the file + * has been bound, we want the original name that was used rather than + * the contents of the stat buffer, so do this lexically. + */ +char* +lastelem(char *file) +{ + char *elem; + + elem = strrchr(file, '/'); + if(elem == nil) + return file; + return elem+1; +} + +void +send(char *file) +{ + Dir *d; + uvlong i; + int m, n, fd; + char buf[32*1024]; + + if((fd = open(file, OREAD)) < 0){ + scperror(0, "can't open %s: %r", file); + return; + } + if((d = dirfstat(fd)) == nil){ + scperror(0, "can't fstat %s: %r", file); + goto Return; + } + + if(d->qid.type & QTDIR){ + if(rflag) + senddir(file, fd, d); + else + scperror(0, "%s: is a directory", file); + goto Return; + } + + if(pflag){ + fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime); + if(getresponse() < 0) + goto Return; + } + + fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file)); + if(getresponse() < 0) + goto Return; + + /* + * We are now committed to send d.length bytes, regardless + */ + for(i=0; ilength; i+=m){ + n = sizeof buf; + if(n > d->length - i) + n = d->length - i; + m = readn(fd, buf, n); + if(m <= 0) + break; + if(write(remotefd1, buf, m) != m) + scperror(0, "%s: %r", file); + } + + if(i == d->length) + sendokresponse(); + else{ + /* continue to send gibberish up to d.length */ + for(; ilength; i+=n){ + n = sizeof buf; + if(n > d->length - i) + n = d->length - i; + write(remotefd1, buf, n); + } + scperror(0, "%s: %r", file); + } + + getresponse(); + + Return: + free(d); + close(fd); +} + +int +getresponse(void) +{ + uchar first, byte, buf[256]; + int i; + + if (read(remotefd0, &first, 1) != 1) + scperror(1, "lost connection"); + + if(first == 0) + return 0; + + i = 0; + if(first > FATAL){ + fprint(2, "scp: unexpected response character 0x%.2ux\n", first); + buf[i++] = first; + } + + /* read error message up to newline */ + for(;;){ + if(read(remotefd0, &byte, 1) != 1) + scperror(1, "response: dropped connection"); + if(byte == '\n') + break; + if(i < sizeof buf) + buf[i++] = byte; + } + + exitflag = "bad response"; + if(!remote) + fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf); + + if (first == ERROR) + return -1; + exits(exitflag); + return 0; /* not reached */ +} + +void +senddir(char *name, int fd, Dir *dirp) +{ + Dir *d, *dir; + int n; + char file[8192]; + + if(pflag){ + fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime); + if(getresponse() < 0) + return; + } + + vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name)); + + fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name); + if(getresponse() < 0) + return; + + n = dirreadall(fd, &dir); + for(d = dir; d < &dir[n]; d++){ + /* shouldn't happen with plan 9, but worth checking anyway */ + if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0) + continue; + if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){ + scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name); + continue; + } + send(file); + } + free(dir); + fprint(remotefd1, "E\n"); + getresponse(); +} + +int +remotessh(char *host, char *cmd) +{ + int i, p[2]; + char *arg[32]; + + vprint("remotessh: %s: %s", host, cmd); + + if(pipe(p) < 0) + scperror(1, "pipe: %r"); + + switch(fork()){ + case -1: + scperror(1, "fork: %r"); + + case 0: + /* child */ + close(p[0]); + dup(p[1], 0); + dup(p[1], 1); + for (i = 3; i < 100; i++) + close(i); + + i = 0; + arg[i++] = ssh; + arg[i++] = "-x"; + arg[i++] = "-a"; + arg[i++] = "-m"; + if(iflag) + arg[i++] = "-i"; + if(vflag) + arg[i++] = "-v"; + arg[i++] = host; + arg[i++] = cmd; + arg[i] = nil; + + exec(ssh, arg); + exits("exec failed"); + + default: + /* parent */ + close(p[1]); + remotefd0 = p[0]; + remotefd1 = p[0]; + } + return 0; +} + +void +scperror(int exit, char *fmt, ...) +{ + char buf[2048]; + va_list arg; + + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + fprint(remotefd1, "%cscp: %s\n", ERROR, buf); + + if (!remote) + fprint(2, "scp: %s\n", buf); + exitflag = buf; + if(exit) + exits(exitflag); +} + +char * +fileaftercolon(char *file) +{ + char *c, *s; + + c = utfrune(file, ':'); + if(c == nil) + return nil; + + /* colon must be in middle of name to be a separator */ + if(c == file) + return nil; + + /* does slash occur before colon? */ + s = utfrune(file, '/'); + if(s != nil && s < c) + return nil; + + *c++ = '\0'; + if(*c == '\0') + return "."; + return c; +} + +void +mustbedir(char *file) +{ + Dir *d; + + if((d = dirstat(file)) == nil){ + scperror(1, "%s: %r", file); + return; + } + if(!(d->qid.type & QTDIR)) + scperror(1, "%s: Not a directory", file); + free(d); +} --- /sys/src/cmd/ssh2/ssh.c Mon Apr 23 15:42:36 2012 +++ /sys/src/cmd/ssh2/ssh.c Sun Apr 15 22:26:44 2012 @@ -1,21 +1,16 @@ -/* - * ssh - remote login via SSH v2 - * /net/ssh does most of the work; we copy bytes back and forth - */ #include #include #include -#include "ssh2.h" -int doauth(int, char *); int isatty(int); +int doauth(int, char *); char *user, *remote; -char *netdir = "/net"; +char *netdir, *subsystem; int debug = 0; - static int stripcr = 0; static int mflag = 0; +static int cooked = 0; static int iflag = -1; static int nopw = 0, nopka = 0; static int chpid; @@ -24,199 +19,54 @@ void usage(void) { - fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr " - "[cmd [args]]\n", argv0); + fprint(2, "usage: ssh [-CdkKmr] [-s subsystem] [-l user] [-n dir] [-z attr=val] addr [cmd [args]]\n"); exits("usage"); } -/* - * this is probably overkill except writing "kill" to notefd; - * file descriptors are closed by the kernel upon exit. - */ -static void -shutdown(void) -{ - if (cctlfd > 0) { - fprint(cctlfd, "rawoff"); - close(cctlfd); - } - if (consfd > 0) - close(consfd); - if (reqfd > 0) { - fprint(reqfd, "close"); - close(reqfd); - } - close(dfd2); - close(dfd1); - close(cfd2); - close(cfd1); - - fprint(notefd, "kill"); - close(notefd); -} - -static void -bail(char *sts) -{ - shutdown(); - exits(sts); -} - int handler(void *, char *) { - char *nf; + char *p; int fd; if (chpid) { - nf = esmprint("/proc/%d/note", chpid); - fd = open(nf, OWRITE); + p = smprint("/proc/%d/note", chpid); + fd = open(p, OWRITE); + free(p); fprint(fd, "interrupt"); close(fd); - free(nf); } - shutdown(); + if(!cooked) + fprint(cctlfd, "rawoff"); + close(cctlfd); + close(consfd); + fprint(reqfd, "close"); + close(reqfd); + close(dfd2); + close(dfd1); + close(cfd2); + close(cfd1); + write(notefd, "kill", 4); + close(notefd); return 1; } -static void -parseargs(void) -{ - int n; - char *p, *q; - - q = strchr(remote, '@'); - if (q != nil) { - user = remote; - *q++ = 0; - remote = q; - } - - q = strchr(remote, '!'); - if (q) { - n = q - remote; - netdir = malloc(n+1); - if (netdir == nil) - sysfatal("out of memory"); - strncpy(netdir, remote, n+1); - netdir[n] = '\0'; - - p = strrchr(netdir, '/'); - if (p == nil) { - free(netdir); - netdir = "/net"; - } else if (strcmp(p+1, "ssh") == 0) - *p = '\0'; - else - remote = esmprint("%s/ssh", netdir); - } - -} - -static void -catcher(void *, char *s) -{ - if (strstr(s, "alarm") != nil) - noted(NCONT); - else - noted(NDFLT); -} - -static int -timedmount(int fd, int afd, char *mntpt, int flag, char *aname) -{ - int oalarm, ret; - - notify(catcher); - oalarm = alarm(5*1000); /* don't get stuck here */ - ret = mount(fd, afd, mntpt, flag, aname); - alarm(oalarm); - return ret; -} - -static void -mounttunnel(char *srv) -{ - int fd; - - if (debug) - fprint(2, "%s: mounting %s on /net\n", argv0, srv); - fd = open(srv, OREAD); - if (fd < 0) { - if (debug) - fprint(2, "%s: can't open %s: %r\n", argv0, srv); - } else if (timedmount(fd, -1, netdir, MBEFORE, "") < 0) { - fprint(2, "can't mount %s on %s: %r\n", srv, netdir); - close(fd); - } -} - -static void -newtunnel(char *myname) -{ - int kid, pid; - - if(debug) - fprint(2, "%s: starting new netssh for key access\n", argv0); - kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */); - if (kid == 0) { -// for (fd = 3; fd < 40; fd++) -// close(fd); - execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil); - sysfatal("no /bin/netssh: %r"); - } else if (kid < 0) - sysfatal("fork failed: %r"); - while ((pid = waitpid()) != kid && pid >= 0) - ; -} - -static void -starttunnel(void) -{ - char *keys, *mysrv, *myname; - - keys = esmprint("%s/ssh/keys", netdir); - myname = esmprint("ssh.%s", getuser()); - mysrv = esmprint("/srv/%s", myname); - - if (access(keys, ORDWR) < 0) - mounttunnel("/srv/netssh"); /* old name */ - if (access(keys, ORDWR) < 0) - mounttunnel("/srv/ssh"); - if (access(keys, ORDWR) < 0) - mounttunnel(mysrv); - if (access(keys, ORDWR) < 0) - newtunnel(myname); - if (access(keys, ORDWR) < 0) - mounttunnel(mysrv); - - /* if we *still* can't see our own tunnel, throw a tantrum. */ - if (access(keys, ORDWR) < 0) - sysfatal("%s inaccessible: %r", keys); /* WTF? */ - - free(myname); - free(mysrv); - free(keys); -} - int cmdmode(void) { int n, m; - char buf[Arbbufsz]; + char buf[256]; - for(;;) { + while (1) { reprompt: - print("\n>>> "); + write(1, "\n>>> ", 5); n = 0; do { - m = read(0, buf + n, sizeof buf - n - 1); - if (m <= 0) - return 1; + m = read(0, buf + n, 255 - n); write(1, buf + n, m); n += m; buf[n] = '\0'; - if (buf[n-1] == ('u' & 037)) + if (buf[n-1] == 0x15) goto reprompt; } while (buf[n-1] != '\n' && buf[n-1] != '\r'); switch (buf[0]) { @@ -227,288 +77,58 @@ return 1; case 'c': return 0; + case 'C': + cooked = 1 - cooked; + if(cooked) + fprint(cctlfd, "rawoff"); + else + fprint(cctlfd, "rawon"); + return 0; case 'r': - stripcr = !stripcr; + stripcr = 1 - stripcr; return 0; - case 'h': + default: + print("C - toggle cooked (local echo) mode\n"); print("c - continue\n"); print("h - help\n"); print("q - quit\n"); print("r - toggle carriage return stripping\n"); break; - default: - print("unknown command\n"); - break; - } - } -} - -static void -keyprompt(char *buf, int size, int n) -{ - if (*buf == 'c') { - fprint(kconsfd, "The following key has been offered by the server:\n"); - write(kconsfd, buf+5, n); - fprint(kconsfd, "\n\n"); - fprint(kconsfd, "Add this key? (yes, no, session) "); - } else { - fprint(kconsfd, "The following key does NOT match the known " - "key(s) for the server:\n"); - write(kconsfd, buf+5, n); - fprint(kconsfd, "\n\n"); - fprint(kconsfd, "Add this key? (yes, no, session, replace) "); - } - n = read(kconsfd, buf, size - 1); - if (n <= 0) - return; - write(keyfd, buf, n); /* user's response -> /net/ssh/keys */ - seek(keyfd, 0, 2); - if (readn(keyfd, buf, 5) <= 0) - return; - buf[5] = 0; - n = strtol(buf+1, nil, 10); - n = readn(keyfd, buf+5, n); - if (n <= 0) - return; - buf[n+5] = 0; - - switch (*buf) { - case 'b': - case 'f': - fprint(kconsfd, "%s\n", buf+5); - case 'o': - close(keyfd); - close(kconsfd); - } -} - -/* talk the undocumented /net/ssh/keys protcol */ -static void -keyproc(char *buf, int size) -{ - int n; - char *p; - - if (size < 6) - exits("keyproc buffer too small"); - keyfd = -1; - p = esmprint("%s/ssh/keys", netdir); - keyfd = open(p, OREAD); - if (keyfd < 0) { - chpid = 0; - sysfatal("failed to open ssh keys in %s: %r", p); - } - - kconsfd = open("/dev/cons", ORDWR); - if (kconsfd < 0) - nopw = 1; - - buf[0] = 0; - n = read(keyfd, buf, 5); /* reading /net/ssh/keys */ - if (n < 0) - sysfatal("%s read: %r", p); - buf[5] = 0; - n = strtol(buf+1, nil, 10); - n = readn(keyfd, buf+5, n); - buf[n+5] = 0; - free(p); - - switch (*buf) { - case 'f': - if (kconsfd >= 0) - fprint(kconsfd, "%s\n", buf+5); - /* fall through */ - case 'o': - close(keyfd); - if (kconsfd >= 0) - close(kconsfd); - break; - default: - if (kconsfd >= 0) - keyprompt(buf, size, n); - else { - fprint(keyfd, "n"); - close(keyfd); - } - break; - } - chpid = 0; - exits(nil); -} - -/* - * start a subproc to copy from network to stdout - * while we copy from stdin to network. - */ -static void -bidircopy(char *buf, int size) -{ - int i, n, lstart; - char *path, *p, *q; - - rfork(RFNOTEG); - path = esmprint("/proc/%d/notepg", getpid()); - notefd = open(path, OWRITE); - - switch (rfork(RFPROC|RFMEM|RFNOWAIT)) { - case 0: - while ((n = read(dfd2, buf, size - 1)) > 0) { - if (!stripcr) - p = buf + n; - else - for (i = 0, p = buf, q = buf; i < n; ++i, ++q) - if (*q != '\r') - *p++ = *q; - if (p != buf) - write(1, buf, p-buf); - } - /* - * don't bother; it will be obvious when the user's prompt - * changes. - * - * fprint(2, "%s: Connection closed by server\n", argv0); - */ - break; - default: - lstart = 1; - while ((n = read(0, buf, size - 1)) > 0) { - if (!mflag && lstart && buf[0] == 0x1c) - if (cmdmode()) - break; - else - continue; - lstart = (buf[n-1] == '\n' || buf[n-1] == '\r'); - write(dfd2, buf, n); - } - /* - * don't bother; it will be obvious when the user's prompt - * changes. - * - * fprint(2, "%s: EOF on client side\n", argv0); - */ - break; - case -1: - fprint(2, "%s: fork error: %r\n", argv0); - break; - } - - bail(nil); -} - -static int -connect(char *buf, int size) -{ - int nfd, n; - char *dir, *ds, *nf; - - dir = esmprint("%s/ssh", netdir); - ds = netmkaddr(remote, dir, "22"); /* tcp port 22 is ssh */ - free(dir); - - dfd1 = dial(ds, nil, nil, &cfd1); - if (dfd1 < 0) { - fprint(2, "%s: dial conn %s: %r\n", argv0, ds); - if (chpid) { - nf = esmprint("/proc/%d/note", chpid); - nfd = open(nf, OWRITE); - fprint(nfd, "interrupt"); - close(nfd); - } - exits("can't dial"); - } - - seek(cfd1, 0, 0); - n = read(cfd1, buf, size - 1); - buf[n >= 0? n: 0] = 0; - return atoi(buf); -} - -static int -chanconnect(int conn, char *buf, int size) -{ - int n; - char *path; - - path = esmprint("%s/ssh/%d!session", netdir, conn); - dfd2 = dial(path, nil, nil, &cfd2); - if (dfd2 < 0) { - fprint(2, "%s: dial chan %s: %r\n", argv0, path); - bail("dial"); - } - free(path); - - n = read(cfd2, buf, size - 1); - buf[n >= 0? n: 0] = 0; - return atoi(buf); -} - -static void -remotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size) -{ - int i; - char *path, *q, *ep; - - path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan); - reqfd = open(path, OWRITE); - if (reqfd < 0) - bail("can't open request chan"); - if (argc == 0) - if (readfile("/env/TERM", buf, size) < 0) - fprint(reqfd, "shell"); - else - fprint(reqfd, "shell %s", buf); - else { - assert(size >= Bigbufsz); - ep = buf + Bigbufsz; - q = seprint(buf, ep, "exec"); - for (i = 0; i < argc; ++i) - q = seprint(q, ep, " %q", argv[i]); - if (q >= ep) { - fprint(2, "%s: command too long\n", argv0); - fprint(reqfd, "close"); - bail("cmd too long"); } - write(reqfd, buf, q - buf); } } -void main(int argc, char *argv[]) { + char *p, *q, *path; char *whichkey; - int conn, chan, n; - char buf[Copybufsz]; + int conn, chan, n, i, lstart, nfd; + char buf[32*1024]; - reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd = - notefd = keyfd = -1; + keyfd = -1; whichkey = nil; ARGBEGIN { - case 'A': /* auth protos */ - case 'c': /* ciphers */ - fprint(2, "%s: sorry, -%c is not supported\n", argv0, ARGC()); - break; - case 'a': /* compat? */ - case 'C': /* cooked mode */ - case 'f': /* agent forwarding */ - case 'p': /* force pty */ - case 'P': /* force no pty */ - case 'R': /* force raw mode on pty */ - case 'v': /* scp compat */ - case 'w': /* send window-size changes */ - case 'x': /* unix compat: no x11 forwarding */ + case 'C': + cooked = 1; break; case 'd': debug++; break; - case 'I': /* non-interactive */ + case 'l': + user = EARGF(usage()); + break; + case 'r': + stripcr = 1; + break; + case 'I': iflag = 0; break; - case 'i': /* interactive: scp & rx do it */ + case 'i': /* Used by scp */ iflag = 1; break; - case 'l': - case 'u': - user = EARGF(usage()); + case 'v': + case 'a': + case 'x': break; case 'k': nopka = 1; @@ -522,54 +142,269 @@ case 'n': netdir = EARGF(usage()); break; - case 'r': - stripcr = 1; + case 's': /* Used by sftpfs */ + subsystem = EARGF(usage()); break; case 'z': whichkey = EARGF(usage()); break; default: usage(); + break; } ARGEND; if (argc == 0) usage(); - if (iflag == -1) iflag = isatty(0); - remote = *argv++; + remote = *argv; + ++argv; --argc; - - parseargs(); - + if (q = strchr(remote, '@')) { + *q = 0; + user = remote; + remote = q+1; + } + if (!netdir) { + q = strchr(remote, '!'); + if (q ) { + n = q-remote; + netdir = malloc(n+1); + strncpy(netdir, remote, n); + netdir[n] = '\0'; + p = strrchr(netdir, '/'); + if (p) { + if (strcmp(p+1, "ssh") == 0) + *p = '\0'; + else + remote = smprint("%s/ssh", netdir); + } + else { + free(netdir); + netdir = nil; + } + } + } if (!user) user = getuser(); - if (user == nil || remote == nil) - sysfatal("out of memory"); - - starttunnel(); - - /* fork subproc to handle keys; don't wait for it */ - if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0) - keyproc(buf, sizeof buf); + if (netdir) + p = smprint("%s/ssh", netdir); + else + p = smprint("/net/ssh"); + if (access(p, OREAD) < 0) { + if ((n = rfork(RFPROC|RFMEM|RFNOTEG)) == 0) { + if (netdir) + execl("/bin/sshtun", "sshtun", "-m", netdir, nil); + else + execl("/bin/sshtun", "sshtun", nil); + exits(nil); + } + do { + i = waitpid(); + } while (i != n && i >= 0); + } + free(p); + if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0) { + if (netdir) + p = smprint("%s/ssh/keys", netdir); + else + p = smprint("/net/ssh/keys"); + keyfd = open(p, ORDWR); + free(p); + if (keyfd < 0) { + // fprint(2, "failed to open sskeys: %r\n"); + chpid = 0; + exits(nil); + } + kconsfd = open("/dev/cons", ORDWR); + if (kconsfd < 0) + nopw = 1; + n = read(keyfd, buf, 5); + buf[5] = 0; + if (n < 0) + exits(nil); + n = strtol(buf+1, nil, 10); + n = readn(keyfd, buf+5, n); + buf[n+5] = 0; + switch (*buf) { + case 'f': + if (kconsfd >= 0) + fprint(kconsfd, "%s\n", buf+5); + case 'o': + close(keyfd); + if (kconsfd >= 0) + close(kconsfd); + break; + default: + if (kconsfd >= 0) { + if (*buf == 'c') { + fprint(kconsfd, "The following key has been offered by the server:\n"); + write(kconsfd, buf+5, n); + fprint(kconsfd, "\n\n"); + fprint(kconsfd, "Add this key? (yes, no, session) "); + } + else { + fprint(kconsfd, "The following key does NOT match the known key(s) for the server:\n"); + write(kconsfd, buf+5, n); + fprint(kconsfd, "\n\n"); + fprint(kconsfd, "Add this key? (yes, no, session, replace) "); + } + n = read(kconsfd, buf, 10); + write(keyfd, buf, n); + seek(keyfd, 0, 2); + readn(keyfd, buf, 5); + buf[5] = 0; + n = strtol(buf+1, nil, 10); + n = readn(keyfd, buf+5, n); + buf[n+5] = 0; + switch (*buf) { + case 'b': + case 'f': + fprint(kconsfd, "%s\n", buf+5); + case 'o': + close(keyfd); + close(kconsfd); + } + } + else { + fprint(keyfd, "n"); + close(keyfd); + } + } + chpid = 0; + exits(nil); + } chpid = n; - atnotify(handler, 1); - - /* connect and learn connection number */ - conn = connect(buf, sizeof buf); - + atnotify(handler,1); + if (netdir) + p = smprint("%s/ssh", netdir); + else + p = smprint("ssh"); + q = netmkaddr(remote, p, "22"); + free(p); + dfd1 = dial(q, nil, nil, &cfd1); + if (dfd1 < 0) { + fprint(2, "%s: dial: %r\n", argv0); + if (chpid) { + p = smprint("/proc/%d/note", chpid); + nfd = open(p, OWRITE); + fprint(nfd, "interrupt"); + } + exits(nil); + } + seek(cfd1, 0, 0); + n = read(cfd1, buf, 10); + buf[n] = 0; + conn = atoi(buf); consfd = open("/dev/cons", ORDWR); cctlfd = open("/dev/consctl", OWRITE); - fprint(cctlfd, "rawon"); + if(!cooked && subsystem == nil) + fprint(cctlfd, "rawon"); if (doauth(cfd1, whichkey) < 0) - bail("doauth"); + goto bail; - /* connect a channel of conn and learn channel number */ - chan = chanconnect(conn, buf, sizeof buf); + if (netdir) + path = smprint("%s/ssh/%d!session",netdir, conn); + else + path = smprint("/net/ssh/%d!session", conn); - /* open request channel, request shell or command execution */ - remotecmd(argc, argv, conn, chan, buf, sizeof buf); + dfd2 = dial(path, nil, nil, &cfd2); + if (dfd2 < 0) { + fprint(2, "%s: dial: %r\n", argv0); + goto bail; + } + n = read(cfd2, buf, 10); + buf[n] = 0; + chan = atoi(buf); + free(path); + if (netdir) + path = smprint("%s/ssh/%d/%d/request", netdir, conn, chan); + else + path = smprint("/net/ssh/%d/%d/request", conn, chan); - bidircopy(buf, sizeof buf); + reqfd = open(path, OWRITE); + if(subsystem){ + fprint(reqfd, "subsystem %s", subsystem); + } + else if (argc == 0) { + if ((i = open("/env/TERM", OREAD)) < 0) + fprint(reqfd, "shell"); + else { + n = read(i, buf, 32); + buf[n] = 0; + fprint(reqfd, "shell %s", buf); + close(i); + } + } + else { + q = buf; + for (i = 0; i < argc; ++i) { + q = seprint(q, buf+1024, " %s", argv[i]); + if (q == nil) + break; + } + if (q != nil) + fprint(reqfd, "exec%s", buf); + else { + fprint(2, "Command too long\n"); + fprint(reqfd, "close"); + goto bail; + } + } + rfork(RFNOTEG); + path = smprint("/proc/%d/notepg", getpid()); + notefd = open(path, OWRITE); + switch (rfork(RFPROC|RFMEM|RFNOWAIT)) { + case 0: + while (1) { + n = read(dfd2, buf, 32*1024); + if (n <= 0) + break; + if (stripcr) { + for (i = 0, p = buf, q = buf; i < n; ++i, ++q) + if (*q != '\r') + *p++ = *q; + } + else + p = buf + n; + write(1, buf, p-buf); + } + fprint(2, "Connection closed by server\n"); + break; + case -1: + fprint(2, "fork error: %r\n"); + goto bail; + default: + lstart = 1; + while (1) { + n = read(0, buf, 32*1024); + if (n <= 0) + break; + if (!mflag && lstart && buf[0] == 0x1c) { + if (cmdmode()) + break; + else + continue; + } + lstart = (buf[n-1] == '\n' || buf[n-1] == '\r'); + write(dfd2, buf, n); + } + fprint(2, "EOF on client side\n"); + break; + } +bail: + fprint(cctlfd, "rawoff"); + close(cctlfd); + close(consfd); + fprint(reqfd, "close"); + close(reqfd); + close(dfd2); + close(dfd1); + close(cfd2); + close(cfd1); + write(notefd, "kill", 4); + close(notefd); + exits(nil); + return 0; } int @@ -579,7 +414,9 @@ buf[0] = '\0'; fd2path(fd, buf, sizeof buf); - return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; + if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) + return 1; + return 0; } int @@ -587,7 +424,6 @@ { UserPasswd *up; int n; - char path[Arbpathlen]; if (!nopka) { if (whichkey) @@ -599,23 +435,15 @@ } if (nopw) return -1; - /* - up = auth_getuserpasswd(iflag? auth_getkey: nil, - "proto=pass service=ssh server=%q user=%q !password?", + up = auth_getuserpasswd(iflag ? auth_getkey : nil, "proto=pass service=ssh server=%q user=%q", remote, user); - */ - up = auth_getuserpasswd(iflag? auth_getkey: nil, - "proto=pass service=ssh server=%q user=%q", remote, user); if (up == nil) { - fprint(2, "%s: didn't get password: %r\n", argv0); + fprint(2, "Failure to get password: %r\n"); return -1; } - n = fprint(cfd1, "ssh-userauth k %s %s", user, up->passwd); + n = fprint(cfd1, "ssh-userauth k %s %q", user, up->passwd); if (n >= 0) return 0; - path[0] = '\0'; - fd2path(cfd1, path, sizeof path); - fprint(2, "%s: auth ctl msg `ssh-userauth k %s ' for %s: %r\n", - argv0, user, path); + fprint(2, "auth %r\n"); return -1; } --- /sys/src/cmd/ssh2/ssh2key.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2/ssh2key.c Wed Feb 22 05:04:17 2012 @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include "sshtun.h" + +Cipher *cryptos[1]; +int debug; + +void +usage(void) +{ + fprint(2, "usage: ssh2key [file]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Packet *p; + char *ep, *np, *user; + mpint *e, *n; + int fd, m; + char key[8192], encpub[8192]; + + ARGBEGIN { + default: + usage(); + } ARGEND + if (argc > 1) + usage(); + user = getenv("user"); + if (argc == 0) + fd = 0; + else { + fd = open(argv[0], OREAD); + if (fd < 0) + usage(); + } + m = read(fd, key, 8191); + close(fd); + key[m] = 0; + ep = strstr(key, " ek="); + np = strstr(key, " n="); + if (ep == nil || np == nil) { + fprint(2, "Invalid key file\n"); + exits("invalid"); + } + e = strtomp(ep+4, nil, 16, nil); + n = strtomp(np+3, nil, 16, nil); + p = new_packet(nil); + add_string(p, "ssh-rsa"); + add_mp(p, e); + add_mp(p, n); + if ((m = enc64(encpub, 8192, p->payload, p->rlength-1)) < 0) { + fprint(2, "Base 64 encoding failed\n"); + exits("fail"); + } + print("ssh-rsa "); + write(1, encpub, m); + if (user) + print(" %s\n", user); +} --- /sys/src/cmd/ssh2/sshsession.c Mon Apr 23 15:42:36 2012 +++ /sys/src/cmd/ssh2/sshsession.c Tue Apr 3 05:54:58 2012 @@ -1,205 +1,44 @@ -/* - * ssh server - serve SSH protocol v2 - * /net/ssh does most of the work; we copy bytes back and forth - */ #include #include #include #include -#include "ssh2.h" -char *confine(char *, char *); -char *get_string(char *, char *); void newchannel(int, char *, int); +char *get_string(char *, char *); +char *confine(char *, char *); void runcmd(int, int, char *, char *, char *, char *); -int errfd, toppid, sflag, tflag, prevent; -int debug; -char *idstring; -char *netdir = "/net"; -char *nsfile = nil; -char *restdir; +int errfd, slfd, toppid, sflag, tflag, prevent; char *shell; +char *restdir; char *srvpt; +char *nsfile = nil; char *uname; void usage(void) { - fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] " - "[-n ns] [-t] [netdir]\n", argv0); + fprint(2, "usage: sshsession [-s shell] [-r restdir] [-R restdir] [-S srvpt] [-n namespace] [-t]\n"); exits("usage"); } -static int -getctlfd(void) -{ - int ctlfd; - char *name; - - name = smprint("%s/clone", netdir); - ctlfd = -1; - if (name) - ctlfd = open(name, ORDWR); - if (ctlfd < 0) { - syslog(0, "ssh", "server can't clone: %s: %r", name); - exits("open clone"); - } - free(name); - return ctlfd; -} - -static int -getdatafd(int ctlfd) -{ - int fd; - char *name; - - name = smprint("%s/data", netdir); - fd = -1; - if (name) - fd = open(name, OREAD); - if (fd < 0) { - syslog(0, "ssh", "can't open %s: %r", name); - hangup(ctlfd); - exits("open data"); - } - free(name); - return fd; -} - -static void -auth(char *buf, int n, int ctlfd) -{ - int fd; - - fd = open("#ยค/capuse", OWRITE); - if (fd < 0) { - syslog(0, "ssh", "server can't open capuse: %r"); - hangup(ctlfd); - exits("capuse"); - } - if (write(fd, buf, n) != n) { - syslog(0, "ssh", "server write `%.*s' to capuse failed: %r", - n, buf); - hangup(ctlfd); - exits("capuse"); - } - close(fd); -} - -/* - * start tunnel if there isn't one, though it's probably too late - * since caphash will likely be closed. - */ -static void -mounttunnel(int ctlfd) -{ - int fd; - char *p; - - if (access(netdir, AEXIST) >= 0) - return; - - p = smprint("/srv/%s", srvpt? srvpt: "ssh"); - fd = -1; - if (p) - fd = open(p, ORDWR); - if (fd < 0) { - syslog(0, "ssh", "can't open %s: %r", p); - hangup(ctlfd); - exits("open"); - } - if (mount(fd, -1, netdir, MBEFORE, "") < 0) { - syslog(0, "ssh", "can't mount in /net: %r"); - hangup(ctlfd); - exits("can't mount"); - } -} - -static int -authnewns(int ctlfd, char *buf, int size, int n) -{ - char *p, *q; - - USED(size); - if (n <= 0) - return 0; - buf[n] = '\0'; - if (strcmp(buf, "n/a") == 0) - return 0; - - auth(buf, n, ctlfd); - - p = strchr(buf, '@'); - if (p == nil) - return 0; - ++p; - q = strchr(p, '@'); - if (q) { - *q = '\0'; - uname = strdup(p); - } - if (!tflag && newns(p, nsfile) < 0) { - syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile); - return -1; - } - return 0; -} - -static void -listenloop(char *listfile, int ctlfd, char *buf, int size) -{ - int fd, n; - - while ((fd = open(listfile, ORDWR)) >= 0) { - n = read(fd, buf, size - 1); - fprint(errfd, "read from listen file returned %d\n", n); - if (n <= 0) { - syslog(0, "ssh", "read on listen failed: %r"); - break; - } - buf[n >= 0? n: 0] = '\0'; - fprint(errfd, "read %s\n", buf); - - switch (fork()) { - case 0: /* child */ - close(ctlfd); - newchannel(fd, netdir, atoi(buf)); /* never returns */ - case -1: - syslog(0, "ssh", "fork failed: %r"); - hangup(ctlfd); - exits("fork"); - } - close(fd); - } - if (fd < 0) - syslog(0, "ssh", "listen failed: %r"); -} - -void main(int argc, char *argv[]) { - char *listfile; + char *netdir, *filnam, *p, *q; int ctlfd, fd, n; - char buf[Arbpathlen], path[Arbpathlen]; + char buf[128]; rfork(RFNOTEG); toppid = getpid(); + errfd = create("/tmp/ssh.err", OWRITE, 0664); + slfd = open("/dev/syslog", OWRITE); shell = "/bin/rc -il"; ARGBEGIN { - case 'd': - debug++; - break; - case 'i': - idstring = EARGF(usage()); - break; case 'n': nsfile = EARGF(usage()); break; case 'R': prevent = 1; - /* fall through */ case 'r': restdir = EARGF(usage()); break; @@ -207,194 +46,266 @@ sflag = 1; shell = EARGF(usage()); break; - case 'S': - srvpt = EARGF(usage()); - break; case 't': tflag = 1; break; + case 'S': + srvpt = EARGF(usage()); + break; default: usage(); break; } ARGEND; - errfd = -1; - if (debug) - errfd = 2; - - /* work out network connection's directory */ - if (argc >= 1) - netdir = argv[0]; - else /* invoked by listen1 */ - netdir = getenv("net"); - if (netdir == nil) { - syslog(0, "ssh", "server netdir is nil"); - exits("nil netdir"); - } - syslog(0, "ssh", "server netdir is %s", netdir); - uname = getenv("user"); if (uname == nil) uname = "none"; - - /* extract dfd and cfd from netdir */ - ctlfd = getctlfd(); - fd = getdatafd(ctlfd); - - n = read(fd, buf, sizeof buf - 1); - if (n < 0) { - syslog(0, "ssh", "server read error for data file: %r"); - hangup(ctlfd); - exits("read cap"); + netdir = getenv("net"); + fprint(errfd, "net is %s\n", netdir); + filnam = smprint("%s/clone", netdir); + ctlfd = open(filnam, ORDWR); + if (ctlfd < 0) { + fprint(errfd, "could not clone: %s: %r\n", filnam); + exits(nil); } + free(filnam); + filnam = smprint("%s/data", netdir); + fd = open(filnam, OREAD); + if (fd < 0) { + fprint(errfd, "Couldn't open data: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + n = read(fd, buf, 128); close(fd); - authnewns(ctlfd, buf, sizeof buf, n); - - /* get connection number in buf */ - n = read(ctlfd, buf, sizeof buf - 1); - buf[n >= 0? n: 0] = '\0'; - - /* tell netssh our id string */ - fd2path(ctlfd, path, sizeof path); - if (0 && idstring) { /* was for coexistence */ - syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s", - buf, idstring, path); - fprint(ctlfd, "id %s", idstring); + free(filnam); + if (n < 0) { + fprint(errfd, "Read error for cap: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); } - - /* announce */ + else if (n > 0) { + buf[n] = '\0'; + if (strcmp(buf, "n/a") != 0) { + fd = open("#ยค/capuse", OWRITE); + if (fd < 0) { + fprint(errfd, "Couldn't open capuse: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + if (write(fd, buf, n) < 0) { + fprint(errfd, "Write to capuse failed: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + close(fd); + p = strchr(buf, '@'); + if (p) { + ++p; + q = strchr(p, '@'); + if (q) { + *q = '\0'; + uname = strdup(p); + } + if (!tflag) { + if (newns(p, nsfile) < 0) + fprint(errfd, "newns failed: %r\n"); + } + } + } + } + n = read(ctlfd, buf, 128); + buf[n] = '\0'; fprint(ctlfd, "announce session"); - - /* construct listen file name */ - listfile = smprint("%s/%s/listen", netdir, buf); - if (listfile == nil) { - syslog(0, "ssh", "out of memory"); - exits("out of memory"); - } - syslog(0, "ssh", "server listen is %s", listfile); - - mounttunnel(ctlfd); - listenloop(listfile, ctlfd, buf, sizeof buf); - hangup(ctlfd); - exits(nil); -} - -/* an abbreviation. note the assumed variables. */ -#define REPLY(s) if (want_reply) fprint(reqfd, s); - -static void -forkshell(char *cmd, int reqfd, int datafd, int want_reply) -{ - switch (fork()) { - case 0: - if (sflag) - snprint(cmd, sizeof cmd, "-s%s", shell); - else - cmd[0] = '\0'; - USED(cmd); - syslog(0, "ssh", "server starting ssh shell for %s", uname); - /* runcmd doesn't return */ - runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil); - case -1: - REPLY("failure"); - syslog(0, "ssh", "server can't fork: %r"); - exits("fork"); + filnam = smprint("%s/%s/listen", netdir, buf); + fprint(errfd, "listen is %s\n", filnam); + if (access(netdir, AEXIST) < 0) { + p = smprint("/srv/%s", srvpt ? srvpt : "sshtun"); + fd = open(p, ORDWR); + if (fd < 0) { + fprint(errfd, "srv open failed; %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + mount(fd, -1, "/net", MBEFORE, ""); } -} - -static void -forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply) -{ - char *q; - - switch (fork()) { - case 0: - if (restdir && chdir(restdir) < 0) { - syslog(0, "ssh", "can't chdir(%s): %r", restdir); - exits("can't chdir"); - } - if (!prevent || (q = getenv("sshsession")) && - strcmp(q, "allow") == 0) - get_string(p+1, cmd); - else - confine(p+1, cmd); - syslog(0, "ssh", "server running `%s' for %s", cmd, uname); - /* runcmd doesn't return */ - runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd); - case -1: - REPLY("failure"); - syslog(0, "ssh", "server can't fork: %r"); - exits("fork"); + while (1) { + fd = open(filnam, ORDWR); + if (fd < 0) { + fprint(errfd, "listen failed: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + n = read(fd, buf, 128); + fprint(errfd, "read from listen file returned %d\n", n); + if (n <= 0) { + fprint(errfd, "read on listen failed: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + } + buf[n] = '\0'; + fprint(errfd, "read %s\n", buf); + switch (fork()) { + case 0: + close(ctlfd); + newchannel(fd, netdir, atoi(buf)); + break; + case -1: + fprint(errfd, "fork failed: %r\n"); + fprint(ctlfd, "hangup"); + exits(nil); + break; + default: + close(fd); + break; + } } } void newchannel(int fd, char *conndir, int channum) { - char *p, *reqfile, *datafile; - int n, reqfd, datafd, want_reply, already_done; - char buf[Maxpayload], cmd[Bigbufsz]; + char *p, *q, *reqfile, *datafile; + int n, reqfd, datafd, motdfd, want_reply, already_done; + char buf[32768], buf2[10240], cmd[1024]; close(fd); - already_done = 0; reqfile = smprint("%s/%d/request", conndir, channum); - if (reqfile == nil) - sysfatal("out of memory"); reqfd = open(reqfile, ORDWR); if (reqfd < 0) { - syslog(0, "ssh", "can't open request file %s: %r", reqfile); - exits("net"); + fprint(errfd, "Couldn't open request file: %r\n"); + exits(nil); } datafile = smprint("%s/%d/data", conndir, channum); - if (datafile == nil) - sysfatal("out of memory"); datafd = open(datafile, ORDWR); if (datafd < 0) { - syslog(0, "ssh", "can't open data file %s: %r", datafile); - exits("net"); + fprint(errfd, "Couldn't open data file: %r\n"); + exits(nil); } - while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) { + while (1) { + n = read(reqfd, buf, 32768); fprint(errfd, "read from request file returned %d\n", n); - for (p = buf; p < buf + n && *p != ' '; ++p) - ; - *p++ = '\0'; + if (n == 0) { + exits(nil); + } + else if (n < 0) { + fprint(errfd, "Read failed: %r\n"); + exits(nil); + } + for (p = buf; p < buf + n && *p != ' '; ++p) ; + *p = '\0'; + ++p; want_reply = (*p == 't'); - /* shell, exec, and various flavours of failure */ - if (strcmp(buf, "shell") == 0) { + if (strcmp(buf, "pty-req") == 0) { + if (want_reply) + fprint(reqfd, "success"); + } + else if (strcmp(buf, "x11-req") == 0) { + if (want_reply) + fprint(reqfd, "failure"); + } + else if (strcmp(buf, "env") == 0) { + if (want_reply) + fprint(reqfd, "failure"); + } + else if (strcmp(buf, "shell") == 0) { if (already_done) { - REPLY("failure"); + if (want_reply) + fprint(reqfd, "failure"); continue; } - forkshell(cmd, reqfd, datafd, want_reply); - already_done = 1; - REPLY("success"); - } else if (strcmp(buf, "exec") == 0) { + switch (fork()) { + case 0: + if (sflag) + snprint(cmd, 1024, "-s%s", shell); + else + snprint(cmd, 1024, ""); + if (slfd > 0) + fprint(slfd, "starting ssh shell for %s\n", uname); + else + syslog(1, "ssh", "starting ssh shell for %s", uname); + motdfd = open("/sys/lib/motd", OREAD); + if (motdfd >= 0) { + while ((n = read(motdfd, buf, 8192)) > 0) { + p = buf2; + for (q = buf; q < buf+n; ++q) { + if (*q == '\n') + *p++ = '\r'; + *p++ = *q; + } + write(datafd, buf2, p-buf2); + } + close(motdfd); + } +// runcmd(reqfd, datafd, "con", "/bin/conssim", cmd, nil); + runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil); + exits(nil); + case -1: + if (want_reply) + fprint(reqfd, "failure"); + fprint(2, "Cannot fork: %r\n"); + exits(nil); + break; + default: + already_done = 1; + if (want_reply) + fprint(reqfd, "success"); + break; + } + } + else if (strcmp(buf, "exec") == 0) { if (already_done) { - REPLY("failure"); + if (want_reply) + fprint(reqfd, "failure"); continue; } - forkcmd(cmd, p, reqfd, datafd, want_reply); - already_done = 1; - REPLY("success"); - } else if (strcmp(buf, "pty-req") == 0 || - strcmp(buf, "window-change") == 0) { - REPLY("success"); - } else if (strcmp(buf, "x11-req") == 0 || - strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) { - REPLY("failure"); - } else if (strcmp(buf, "xon-xoff") == 0 || - strcmp(buf, "signal") == 0 || - strcmp(buf, "exit-status") == 0 || - strcmp(buf, "exit-signal") == 0) { - ; - } else - syslog(0, "ssh", "server unknown channel request: %s", - buf); + switch (fork()) { + case 0: + if (restdir) + chdir(restdir); + if (!prevent || (q = getenv("sshsession")) && strcmp(q, "allow") == 0) + get_string(p+1, cmd); + else + confine(p+1, cmd); + if (slfd > 0) + fprint(slfd, "running %s for %s\n", cmd, uname); + else + syslog(1, "ssh", "running %s for %s", cmd, uname); + runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd); + exits(nil); + case -1: + if (want_reply) + fprint(reqfd, "failure"); + fprint(errfd, "Cannot fork: %r\n"); + exits(nil); + break; + default: + already_done = 1; + if (want_reply) + fprint(reqfd, "success"); + break; + } + } + else if (strcmp(buf, "subsystem") == 0) { + if (want_reply) + fprint(reqfd, "failure"); + } + else if (strcmp(buf, "window-change") == 0) { + if (want_reply) + fprint(reqfd, "success"); + } + else if (strcmp(buf, "xon-xoff") == 0) { + } + else if (strcmp(buf, "signal") == 0) { + } + else if (strcmp(buf, "exit-status") == 0) { + } + else if (strcmp(buf, "exit-signal") == 0) { + } + else + fprint(errfd, "Unknown channel request: %s\n", buf); } - if (n < 0) - syslog(0, "ssh", "server read failed: %r"); - exits(nil); } char * @@ -414,25 +325,25 @@ confine(char *q, char *s) { int i, n, m; - char *p, *e, *r, *buf, *toks[Maxtoks]; + char *p, *e, *r, *buf, *toks[32]; n = nhgetl(q); q += 4; buf = malloc(n+1); - if (buf == nil) - return nil; memmove(buf, q, n); buf[n] = 0; - m = tokenize(buf, toks, nelem(toks)); + m = tokenize(buf, toks, 32); e = s + n + 1; for (i = 0, r = s; i < m; ++i) { p = strrchr(toks[i], '/'); - if (p == nil) - r = seprint(r, e, "%s ", toks[i]); - else if (p[0] != '\0' && p[1] != '\0') - r = seprint(r, e, "%s ", p+1); + if (p) { + if (*(p+1)) + r = seprint(r, e, "%s ", p+1); + else + r = seprint(r, e, ". "); + } else - r = seprint(r, e, ". "); + r = seprint(r, e, "%s ", toks[i]); } free(buf); q += n; @@ -445,18 +356,16 @@ char *p; int fd, cmdpid, child; - cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG); + cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG); switch (cmdpid) { case -1: - syslog(0, "ssh", "fork failed: %r"); - exits("fork"); + fprint(errfd, "fork failed: %r\n"); + break; case 0: if (restdir == nil) { p = smprint("/usr/%s", uname); - if (p && access(p, AREAD) == 0 && chdir(p) < 0) { - syslog(0, "ssh", "can't chdir(%s): %r", p); - exits("can't chdir"); - } + if (access(p, AREAD) == 0) + chdir(p); free(p); } p = strrchr(cmd, '/'); @@ -464,7 +373,6 @@ ++p; else p = cmd; - dup(datafd, 0); dup(datafd, 1); dup(datafd, 2); @@ -472,26 +380,30 @@ putenv("service", svc); fprint(errfd, "starting %s\n", cmd); execl(cmd, p, arg1, arg2, nil); - - syslog(0, "ssh", "cannot exec %s: %r", cmd); - exits("exec"); + fprint(errfd, "cannot exec %s: %r\n", cmd); + break; default: close(datafd); - fprint(errfd, "waiting for child %d\n", cmdpid); - while ((child = waitpid()) != cmdpid && child != -1) + while (1) { + fprint(errfd, "waiting for child %d\n", cmdpid); + child = waitpid(); fprint(errfd, "child %d passed\n", child); + if (child == cmdpid || child == -1) + break; + } if (child == -1) fprint(errfd, "wait failed: %r\n"); - - syslog(0, "ssh", "server closing ssh session for %s", uname); + if (slfd > 0) + fprint(slfd, "closing ssh session for %s\n", uname); + else + syslog(1, "ssh", "closing ssh session for %s", uname); fprint(errfd, "closing connection\n"); - fprint(reqfd, "close"); + write(reqfd, "close", 5); p = smprint("/proc/%d/notepg", toppid); - if (p) { - fd = open(p, OWRITE); - fprint(fd, "interrupt"); - close(fd); - } - exits(nil); + fd = open(p, OWRITE); + write(fd, "interrupt", 3); + close(fd); + break; } + exits(nil); } --- /sys/src/cmd/ssh2/sshtun.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2/sshtun.c Sun Apr 15 22:26:44 2012 @@ -0,0 +1,2866 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include +#include "sshtun.h" + +void stend(Srv *); +void server(char *, char *); +void stopen(Req *); +void stlisten1(void *); +void stlisten2(void *); +void stread(Req *); +void readreqrem(void *); +void readdata(void *); +void stwrite(Req *); +void writectl(void *); +void writereqrem(void *); +void writedata(void *); +void stclunk(Fid *); +void stflush(Req *); +void filedup(Req *, File *); +Conn *alloc_conn(void); +SSHChan *alloc_chan(Conn *); +int dohandshake(Conn *, char *); +void send_kexinit(Conn *); +void reader(void *); +int validatekex(Conn *, Packet *); +int validatekexs(Packet *); +int validatekexc(Packet *); +int auth_req(Packet *, Conn *); +int client_auth(Conn *, Ioproc *); +char *factlookup(int, int, char *[]); +void shutdown(Conn *); + +Srv sshtunsrv = { + .open = stopen, + .read = stread, + .write = stwrite, + .flush = stflush, + .destroyfid = stclunk, + .end = stend, +}; + +Cipher *cryptos[] = { + &cipheraes128, + &cipheraes192, + &cipheraes256, +// &cipherblowfish, + &cipher3des, + &cipherrc4, +}; + +Kex *kexes[] = { + &dh1sha1, + &dh14sha1, +}; + +PKA *pkas[3]; + +char *macnames[] = { + "hmac-sha1", +}; + +char *st_names[] = { +[Empty] "Empty", +[Allocated] "Allocated", +[Initting] "Initting", +[Listening] "Listening", +[Opening] "Opening", +[Negotiating] "Negotiating", +[Authing] "Authing", +[Established] "Established", +[Eof] "Eof", +[Closing] "Closing", +[Closed] "Closed", +}; + +File *rootfile, *clonefile, *ctlfile, *keysfile; +Conn *connections[MAXCONN]; +char *mntpt = "/net"; +int debug; +int kflag; +int slfd; +char uid[32]; +MBox keymbox; +QLock availlck; +Rendez availrend; + +void +usage(void) +{ + fprint(2, "usage: sshtun [-d] [-k] [-m mntpt] [-s srvpt]\n"); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *srvpt = nil; + int fd, n; + + slfd = open("/dev/syslog", OWRITE); + ARGBEGIN { + case '9': + chatty9p = 1; + break; + case 'd': + debug++; + break; + case 'k': + kflag = 1; + break; + case 'm': + mntpt = EARGF(usage()); + break; + case 's': + srvpt = EARGF(usage()); + break; + default: + usage(); + break; + } ARGEND; + + fd = open("/dev/user", OREAD); + if (fd < 0) + strcpy(uid, "none"); + else { + n = read(fd, uid, 31); + if (n < 0) + strcpy(uid, "none"); + else + uid[n] = '\0'; + close(fd); + } + + keymbox.mchan = chancreate(4, 0); + availrend.l = &availlck; + dh_init(pkas); + + if (rfork(RFNOTEG) < 0) + fprint(2, "Failed to set process attributes: %r\n"); + server(mntpt, srvpt); +} + +Ioproc *io9p; + +int +read9pmsg(int fd, void *abuf, uint n) +{ + int m, len; + uchar *buf; + + if (io9p == nil) + io9p = ioproc(); + + buf = abuf; + + /* read count */ + m = ioreadn(io9p, fd, buf, BIT32SZ); + if(m != BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + werrstr("bad length in 9P2000 message header"); + return -1; + } + len -= BIT32SZ; + m = ioreadn(io9p, fd, buf+BIT32SZ, len); + if(m < len) + return 0; + return BIT32SZ+m; +} + +void +stend(Srv *) +{ + closeioproc(io9p); + threadkillgrp(threadgetgrp()); +} + +void +server(char *mntpt, char *srvpt) +{ + Dir d; + char *p; + int fd; + + sshtunsrv.tree = alloctree(uid, uid, 0777, nil); + rootfile = createfile(sshtunsrv.tree->root, "ssh", uid, 0555|DMDIR, (void*)RootFile); + clonefile = createfile(rootfile, "clone", uid, 0666, (void*)CloneFile); + ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)CtlFile); + keysfile = createfile(rootfile, "keys", uid, 0600, (void *)ReqRemFile); + threadpostmountsrv(&sshtunsrv, srvpt, mntpt, MAFTER); + p = smprint("%s/cs", mntpt); + fd = open(p, OWRITE); + free(p); + if (fd >= 0) { + fprint(fd, "add ssh"); + close(fd); + } + if (srvpt) { + nulldir(&d); + d.mode = 0666; + p = smprint("/srv/%s", srvpt); + dirwstat(p, &d); + free(p); + } +} + +void +stopen(Req *r) +{ + Conn *c; + SSHChan *sc; + char *p; + int lev, xconn, fnum, fd; + char buf[10]; + + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + switch (fnum & FileMask) { + case CloneFile: + switch (lev) { + case 0: + p = smprint("%s/tcp/clone", mntpt); + fd = open(p, ORDWR); + free(p); + if (fd < 0) { + responderror(r); + return; + } + c = alloc_conn(); + if (c == nil) { + respond(r, "No more connections"); + return; + } + c->ctlfd = fd; + filedup(r, c->ctlfile); + if (debug) + fprint(2, "new connection: %d\n", c->id); + break; + case 1: + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + return; + } + sc = alloc_chan(c); + if (sc == nil) { + respond(r, "No more channels"); + return; + } + filedup(r, sc->ctl); + break; + default: + snprint(buf, 10, "bad %d", lev); + readstr(r, buf); + break; + } + respond(r, nil); + break; + case ListenFile: + switch (lev) { + case 1: + r->aux = (void *)threadcreate(stlisten1, r, 8192); + break; + case 2: + r->aux = (void *)threadcreate(stlisten2, r, 8192); + break; + default: + respond(r, "not possible"); + break; + } + break; + default: + respond(r, nil); + break; + } +} + +void +stlisten1(void *a) +{ + Req *r; + Conn *c, *cl; + Ioproc *io; + char *msg; + int fnum, xconn, fd, n; + char buf[10], path[40]; + + r = a; + fnum = (uintptr)r->fid->file->aux; + xconn = (fnum >> CONNSHIFT) & ConnMask; + cl = connections[xconn]; + if (cl == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + memset(buf, '\0', sizeof(buf)); + io = ioproc(); + seek(cl->ctlfd, 0, 0); + if ((n = ioread(io, cl->ctlfd, buf, 10)) <= 0) + fprint(2, "read failed: %r\n"); + buf[n] = '\0'; + cl->state = Listening; + snprint(path, 40, "%s/tcp/%s/listen", mntpt, buf); + while (1) { + fd = ioopen(io, path, ORDWR); + if (fd < 0) { + r->aux = 0; + responderror(r); + closeioproc(io); + shutdown(cl); + threadexits(nil); + } + c = alloc_conn(); + if (c) + break; + n = ioread(io, fd, buf, 10); + if (n <= 0) { + r->aux = 0; + responderror(r); + closeioproc(io); + shutdown(cl); + threadexits(nil); + } + else { + buf[n] = '\0'; + msg = smprint("reject %s No available connections", buf); + iowrite(io, fd, msg, strlen(msg)); + free(msg); + } + close(fd); + } + c->ctlfd = fd; + filedup(r, c->ctlfile); + if (debug) + fprint(2, "**** responding to listen open ***\n"); + r->aux = 0; + respond(r, nil); + closeioproc(io); + threadexits(nil); +} + +void +stlisten2(void *a) +{ + Req *r; + Packet *p2; + Ioproc *io; + Conn *c; + SSHChan *sc; + int i, n, xconn, fnum; + + r = a; + fnum = (uintptr)r->fid->file->aux; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + if (c->state == Closed || c->state == Closing) { + r->aux = 0; + respond(r, "listen on a closed connection"); + threadexits(nil); + } + sc = c->chans[fnum & ConnMask]; + qlock(&c->l); + sc->lreq = r; + for (i = 0; i < c->nchan; ++i) + if (c->chans[i] && c->chans[i]->state == Opening && c->chans[i]->ann + && strcmp(c->chans[i]->ann, sc->ann) == 0) + break; + if (i >= c->nchan) { + sc->state = Listening; + rsleep(&sc->r); + i = sc->waker; + if (i < 0) { + qunlock(&c->l); + r->aux = 0; + responderror(r); + threadexits(nil); + } + } + else + rwakeup(&c->chans[i]->r); + qunlock(&c->l); + if (c->state == Closed || c->state == Closing || c->state == Eof) { + r->aux = 0; + respond(r, "Listen on a closed connection"); + threadexits(nil); + } + c->chans[i]->state = Established; + p2 = new_packet(c); + c->chans[i]->rwindow = 32*1024; + add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + hnputl(p2->payload + 1, c->chans[i]->otherid); + hnputl(p2->payload + 5, c->chans[i]->id); + hnputl(p2->payload + 9, 32*1024); + hnputl(p2->payload + 13, 8192); + p2->rlength = 18; + n = finish_packet(p2); + filedup(r, c->chans[i]->ctl); + io = ioproc(); + n = iowrite(io, c->datafd, p2->nlength, n); + free(p2); + closeioproc(io); + if (debug) + fprint(2, "*** Responding to chan listen open ***\n"); + r->aux = 0; + if (n < 0) + responderror(r); + else + respond(r, nil); + threadexits(nil); +} + +void +getdata(Conn *c, SSHChan *sc, Req *r) +{ + Packet *p; + Plist *d; + int n; + + n = r->ifcall.count; + if (sc->dataq->rem < n) + n = sc->dataq->rem; + if (n > 8192) + n = 8192; + r->ifcall.offset = 0; + readbuf(r, sc->dataq->st, n); + sc->dataq->st += n; + sc->dataq->rem -= n; + sc->inrqueue -= n; + if (sc->dataq->rem <= 0) { + d = sc->dataq; + sc->dataq = sc->dataq->next; + if (d->pack->tlength > sc->rwindow) + sc->rwindow = 0; + else + sc->rwindow -= d->pack->tlength; + free(d->pack); + free(d); + } + if (sc->rwindow < 16*1024) { + sc->rwindow += 32*1024; + if (debug) + fprint(2, "Increasing receive window to %lud, inq %lud\n", sc->rwindow, sc->inrqueue); + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST); + hnputl(p->payload+1, sc->otherid); + hnputl(p->payload+5, 32*1024); + p->rlength += 8; + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + } + r->aux = 0; + respond(r, nil); +} + +void +stread(Req *r) +{ + Conn *c; + SSHChan *sc; + int fd, n, lev, cnum, xconn, fnum; + char buf[256], path[40]; + + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + if (lev != 0 || (fnum & FileMask) != ReqRemFile) { + respond(r, "Invalid connection"); + return; + } + cnum = 0; + sc = nil; + } + else { + cnum = fnum & ConnMask; + sc = c->chans[cnum]; + } + switch (fnum & FileMask) { + case CtlFile: + case ListenFile: + if (r->ifcall.offset != 0) { + respond(r, nil); + break; + } + switch (lev) { + case 0: + readstr(r, st_names[c->state]); + break; + case 1: + snprint(buf, 256, "%d", xconn); + readstr(r, buf); + break; + case 2: + snprint(buf, 256, "%d", cnum); + readstr(r, buf); + break; + default: + snprint(buf, 256, "Internal error: level %d", lev); + respond(r, buf); + return; + break; + } + respond(r, nil); + break; + case CloneFile: + if (r->ifcall.offset != 0) { + respond(r, nil); + break; + } + readstr(r, "Congratulations, you've achieved the impossible\n"); + respond(r, nil); + break; + case DataFile: + if (lev == 0) { + respond(r, nil); + break; + } + if (lev == 1) { + if (c->cap) + readstr(r, c->cap); + respond(r, nil); + break; + } + + r->aux = (void *)threadcreate(readdata, r, 8192); + break; + case LocalFile: + if (lev == 1) { + if (c->ctlfd >= 0) { + n = pread(c->ctlfd, buf, 10, 0); + buf[n] = '\0'; + snprint(path, 40, "%s/tcp/%s/local", mntpt, buf); + fd = open(path, OREAD); + n = pread(fd, buf, 255, 0); + close(fd); + buf[n] = '\0'; + readstr(r, buf); + } + else + readstr(r, "::!0\n"); + } + respond(r, nil); + break; + case ReqRemFile: + r->aux = (void *)threadcreate(readreqrem, r, 8192); + break; + case StatusFile: + switch (lev) { + case 0: + readstr(r, "Impossible"); + break; + case 1: + if (c->state < 0 || c->state > Closed) + readstr(r, "Unknown"); + else + readstr(r, st_names[c->state]); + break; + case 2: + if (sc->state < 0 || sc->state > Closed) + readstr(r, "Unknown"); + else + readstr(r, st_names[sc->state]); + break; + } + respond(r, nil); + break; + default: + respond(r, nil); + break; + } +} + +void +readreqrem(void *a) +{ + Ioproc *io; + Req *r; + Conn *c; + SSHChan *sc; + int fd, n, lev, cnum, xconn, fnum; + char buf[256], path[40]; + + r = a; + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + if (lev != 0) { + respond(r, "Invalid connection"); + return; + } + sc = nil; + } + else { + cnum = fnum & ConnMask; + sc = c->chans[cnum]; + } + switch (lev) { + case 0: + if (r->ifcall.offset == 0 && keymbox.state != Empty) { + r->aux = 0; + respond(r, "Key file collision"); + break; + } + if (r->ifcall.offset != 0) { + readstr(r, keymbox.msg); + r->aux = 0; + respond(r, nil); + if (r->ifcall.offset + r->ifcall.count >= strlen(keymbox.msg)) + keymbox.state = Empty; + else + keymbox.state = Allocated; + break; + } + keymbox.state = Allocated; + while (1) { + if (keymbox.msg == nil) { + if (recv(keymbox.mchan, nil) < 0) { + r->aux = 0; + responderror(r); + keymbox.state = Empty; + threadexits(nil); + } + } + if (keymbox.state == Empty) { + break; + } + else if (keymbox.state == Allocated) { + if (keymbox.msg) { + readstr(r, keymbox.msg); + if (r->ifcall.offset + r->ifcall.count >= strlen(keymbox.msg)) { + free(keymbox.msg); + keymbox.msg = nil; + keymbox.state = Empty; + } + } + break; + } + } + r->aux = 0; + respond(r, nil); + break; + case 1: + if (c->ctlfd >= 0) { + io = ioproc(); + seek(c->ctlfd, 0, 0); + n = ioread(io, c->ctlfd, buf, 10); + if (n < 0) { + r->aux = 0; + responderror(r); + closeioproc(io); + break; + } + buf[n] = '\0'; + snprint(path, 40, "%s/tcp/%s/remote", mntpt, buf); + if ((fd = ioopen(io, path, OREAD)) < 0 || (n = ioread(io, fd, buf, 255)) < 0) { + r->aux = 0; + responderror(r); + if (fd >= 0) + ioclose(io, fd); + closeioproc(io); + break; + } + ioclose(io, fd); + closeioproc(io); + buf[n] = '\0'; + readstr(r, buf); + } + else + readstr(r, "::!0\n"); + r->aux = 0; + respond(r, nil); + break; + case 2: + if ((sc->state == Closed || sc->state == Closing || sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) { + if (debug) + fprint(2, "Sending EOF1 to channel request listener\n"); + r->aux = 0; + respond(r, nil); + break; + } + while (sc->reqq == nil) { + if (recv(sc->reqchan, nil) < 0) { + r->aux = 0; + respond(r, "interrupted"); + threadexits(nil); + } + if ((sc->state == Closed || sc->state == Closing || sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) { + if (debug) + fprint(2, "Sending EOF2 to channel request listener\n"); + r->aux = 0; + respond(r, nil); + threadexits(nil); + } + } + n = r->ifcall.count; + if (sc->reqq->rem < n) + n = sc->reqq->rem; + if (n > 8192) + n = 8192; + r->ifcall.offset = 0; + readbuf(r, sc->reqq->st, n); + sc->reqq->st += n; + sc->reqq->rem -= n; + if (sc->reqq->rem <= 0) { + Plist *d = sc->reqq; + sc->reqq = sc->reqq->next; + free(d->pack); + free(d); + } + r->aux = 0; + respond(r, nil); + break; + } + threadexits(nil); +} + +void +readdata(void *a) +{ + Req *r; + Conn *c; + SSHChan *sc; + int cnum, xconn, fnum; + + r = a; + fnum = (uintptr)r->fid->file->aux; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + cnum = fnum & ConnMask; + sc = c->chans[cnum]; + while (sc->dataq == nil) { + if (sc->state == Closed || sc->state == Closing || sc->state == Eof) { + if (debug) + fprint(2, "Sending EOF2 to channel listener\n"); + r->aux = 0; + respond(r, nil); + threadexits(nil); + } + if (recv(sc->inchan, nil) < 0) { + if (debug) + fprint(2, "Got intterrupt/error in readdata %r\n"); + r->aux = 0; + respond(r, "interrupted"); + threadexits(nil); + } + } + getdata(c, sc, r); + threadexits(nil); +} + +void +stwrite(Req *r) +{ + Conn *c; + SSHChan *ch; + int lev, fnum, xconn; + + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + return; + } + ch = c->chans[fnum & ConnMask]; + switch (fnum & FileMask) { + case CloneFile: + case CtlFile: + r->aux = (void *)threadcreate(writectl, r, 8192); + break; + case DataFile: + r->ofcall.count = r->ifcall.count; + if (lev < 2) { + respond(r, nil); + break; + } + if (c->state == Closed || c->state == Closing || ch->state == Closed || ch->state == Closing) { + respond(r, nil); + break; + } + r->aux = (void *)threadcreate(writedata, r, 8192); + break; + case ReqRemFile: + r->aux = (void *)threadcreate(writereqrem, r, 8192); + break; + default: + respond(r, nil); + break; + } +} + +void +writectl(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + char *q, *buf, *toks[4],*attrs[5]; + int n, ntok, lev, fnum, xconn; + char path[40], buf2[10]; + + r = a; + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + ch = c->chans[fnum & ConnMask]; + if (r->ifcall.count <= 10) + buf = emalloc9p(11); + else + buf = emalloc9p(r->ifcall.count + 1); + memmove(buf, r->ifcall.data, r->ifcall.count); + buf[r->ifcall.count] = '\0'; + ntok = tokenize(buf, toks, 4); + switch (lev) { + case 0: + break; + case 1: + if (strcmp(toks[0], "connect") == 0) { + if (ntok < 2) { + r->aux = 0; + free(buf); + respond(r, "Invalid connect request"); + threadexits(nil); + } + memset(buf2, '\0', sizeof(buf2)); + pread(c->ctlfd, buf2, 10, 0); + fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3 ? toks[2] : ""); + c->role = Client; + /* Override the PKA list; we can take any in */ + pkas[0] = &rsa_pka; + pkas[1] = &dss_pka; + pkas[2] = nil; + q = estrdup9p(buf2); + if (dohandshake(c, q) < 0) { + r->aux = 0; + respond(r, "handshake failed"); + free(q); + free(buf); + threadexits(nil); + } + free(q); + keymbox.state = Empty; + nbsendul(keymbox.mchan, 1); + break; + } + if (c->state == Closed || c->state == Closing) { + r->aux = 0; + respond(r, "connection closed"); + free(buf); + threadexits(nil); + } + if (strcmp(toks[0], "ssh-userauth") == 0) { + if (ntok < 3 || ntok > 4) { + r->aux = 0; + respond(r, "Invalid connection command"); + free(buf); + threadexits(nil); + } + if (!c->service) + c->service = estrdup9p(toks[0]); + if (c->user) + free(c->user); + c->user = estrdup9p(toks[2]); + if (ntok == 4 && strcmp(toks[1], "k") == 0) { + if (c->authkey) { + free(c->authkey); + c->authkey = nil; + } + if (c->password) + free(c->password); + c->password = estrdup9p(toks[3]); + } + else { + if (c->password) { + free(c->password); + c->password = nil; + } + attrs[0] = "proto=rsa"; + attrs[1] = "!dk?"; + attrs[2] = smprint("user=%s", c->user); + attrs[3] = smprint("sys=%s", c->remote); + if (c->authkey) + free(c->authkey); + if (ntok == 3) + c->authkey = factlookup(4, 2, attrs); + else { + attrs[4] = toks[3]; + c->authkey = factlookup(5, 2, attrs); + } + free(attrs[2]); + free(attrs[3]); + } + if (!c->password && !c->authkey) { + r->aux = 0; + respond(r, "no auth info"); + free(buf); + threadexits(nil); + } + else if (c->state != Authing) { + p = new_packet(c); + add_byte(p, SSH_MSG_SERVICE_REQUEST); + add_string(p, c->service); + n = finish_packet(p); + if (c->dio) { + if (iowrite(c->dio, c->datafd, p->nlength, n) < 0) { + r->aux = 0; + responderror(r); + free(p); + free(buf); + threadexits(nil); + } + } + else { + if (write(c->datafd, p->nlength, n) < 0) { + r->aux = 0; + responderror(r); + free(p); + free(buf); + threadexits(nil); + } + } + free(p); + } + else { + if (client_auth(c, c->dio) < 0) { + r->aux = 0; + respond(r, "auth failure"); + free(buf); + threadexits(nil); + } + } + qlock(&c->l); + if (c->state != Established) + rsleep(&c->r); + qunlock(&c->l); + if (c->state != Established) { + r->aux = 0; + respond(r, "Authentication failed"); + free(buf); + threadexits(nil); + } + break; + } + else if (strcmp(toks[0], "ssh-connection") == 0) { + } + else if (strcmp(toks[0], "hangup") == 0) { + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); + } + else if (strcmp(toks[0], "announce") == 0) { + if (debug) + print("Got %s argument for announce\n", toks[1]); + write(c->ctlfd, r->ifcall.data, r->ifcall.count); + } + else if (strcmp(toks[0], "accept") == 0) { + memset(buf2, '\0', sizeof(buf2)); + pread(c->ctlfd, buf2, 10, 0); + fprint(c->ctlfd, "accept %s", buf2); + c->role = Server; + q = estrdup9p(buf2); + if (dohandshake(c, q) < 0) { + r->aux = 0; + respond(r, "handshake failed"); + free(q); + shutdown(c); + free(buf); + threadexits(nil); + } + free(q); + } + else if (strcmp(toks[0], "reject") == 0) { + memset(buf2, '\0', sizeof(buf2)); + pread(c->ctlfd, buf2, 10, 0); + snprint(path, 40, "%s/tcp/%s/data", mntpt, buf2); + c->datafd = open(path, ORDWR); + p = new_packet(c); + add_byte(p, SSH_MSG_DISCONNECT); + add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT); + add_string(p, toks[2]); + add_string(p, "EN"); + n = finish_packet(p); + if (c->dio && c->datafd >= 0) + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + if (c->ctlfd >= 0) + fprint(c->ctlfd, "reject %s %s", buf, toks[2]); + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); + } + break; + case 2: + if (c->state == Closed || c->state == Closing) { + r->aux = 0; + respond(r, "connection closed"); + free(buf); + threadexits(nil); + } + if (strcmp(toks[0], "connect") == 0) { + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_OPEN); + if (ntok > 1) + add_string(p, toks[1]); + else + add_string(p, "session"); + add_uint32(p, ch->id); + add_uint32(p, 32*1024); + add_uint32(p, 8192); + /* more stuff if it's an x11 session */ + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + qlock(&c->l); + if (ch->otherid == -1) + rsleep(&ch->r); + qunlock(&c->l); + break; + } + else if (strcmp(toks[0], "global") == 0) { + } + else if (strcmp(toks[0], "hangup") == 0) { + if (ch->state != Closed && ch->state != Closing) { + ch->state = Closing; + if (ch->otherid != -1) { + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + add_uint32(p, ch->otherid); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + } + qlock(&c->l); + rwakeup(&ch->r); + qunlock(&c->l); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + } + for (n = 0; n < MAXCONN + && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closing + || c->chans[n]->state == Closed); ++n) ; + if (n >= MAXCONN) { + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); + } + } + else if (strcmp(toks[0], "announce") == 0) { + if (debug) + print("Got %s argument for announce\n", toks[1]); + if (ch->ann) + free(ch->ann); + ch->ann = estrdup9p(toks[1]); + } + break; + } + r->ofcall.count = r->ifcall.count; + r->aux = 0; + respond(r, nil); + free(buf); + threadexits(nil); +} + +void +writereqrem(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + char *q, *buf, *toks[4]; + int n, ntok, lev, fnum, xconn; + char *cmd; + + r = a; + fnum = (uintptr)r->fid->file->aux; + lev = fnum >> LEVSHIFT; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + ch = c->chans[fnum & ConnMask]; + if (r->ifcall.count <= 10) + buf = emalloc9p(11); + else + buf = emalloc9p(r->ifcall.count + 1); + memmove(buf, r->ifcall.data, r->ifcall.count); + buf[r->ifcall.count] = '\0'; + ntok = tokenize(buf, toks, 4); + + if (lev == 0) { + if (keymbox.msg) + free(keymbox.msg); + keymbox.msg = buf; + nbsendul(keymbox.mchan, 1); + r->ofcall.count = r->ifcall.count; + r->aux = 0; + respond(r,nil); + threadexits(nil); + } + + r->ofcall.count = r->ifcall.count; + if (c->state == Closed || c->state == Closing || ch->state == Closed || ch->state == Closing) { + r->aux = 0; + respond(r, nil); + free(buf); + threadexits(nil); + } + p = new_packet(c); + if (strcmp(toks[0], "success") == 0) { + add_byte(p, SSH_MSG_CHANNEL_SUCCESS); + add_uint32(p, ch->otherid); + } + else if (strcmp(toks[0], "failure") == 0) { + add_byte(p, SSH_MSG_CHANNEL_FAILURE); + add_uint32(p, ch->otherid); + } + else if (strcmp(toks[0], "close") == 0) { + ch->state = Closing; + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + add_uint32(p, ch->otherid); + } + else if (strcmp(toks[0], "shell") == 0) { + ch->state = Established; + /* + * Some servers *cough*OpenSSH*cough* don't seem to be able + * to intelligently handle a shell with no pty. + */ + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "pty-req"); + add_byte(p, 0); + if (ntok == 1) + add_string(p, "dumb"); + else + add_string(p, toks[1]); + add_uint32(p, 0); /* width, chars */ + add_uint32(p, 0); /* height, rows */ + add_uint32(p, 0); /* width, pixels */ + add_uint32(p, 0); /* height, pixels */ + add_string(p, ""); /* stty opts */ + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + init_packet(p); + p->c = c; + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "shell"); + add_byte(p, 0); + if (debug) + fprint(2, "Sending shell request: rlength=%lud twindow=%lud\n", p->rlength, ch->twindow); + } + else if (strcmp(toks[0], "exec") == 0) { + ch->state = Established; + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "exec"); + add_byte(p, 0); + cmd = malloc(1024); + q = seprint(cmd, cmd+1024, "%s", toks[1]); + for (n = 2; n < ntok; ++n) { + q = seprint(q, cmd+1024, " %s", toks[n]); + if (q == nil) + break; + } + add_string(p, cmd); + free(cmd); + } + else if (strcmp(toks[0], "subsystem") == 0) { + ch->state = Established; + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "subsystem"); + add_byte(p, 0); /* no reply */ + cmd = malloc(1024); + q = seprint(cmd, cmd+1024, "%s", toks[1]); + for (n = 2; n < ntok; ++n) { + q = seprint(q, cmd+1024, " %s", toks[n]); + if (q == nil) + break; + } + add_string(p, cmd); + free(cmd); + } + else { + r->aux = 0; + respond(r, "invalid request command"); + free(buf); + threadexits(nil); + } + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + r->aux = 0; + respond(r, nil); + free(buf); + threadexits(nil); +} + + +void +writedata(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + int n, fnum, xconn; + + r = a; + fnum = (uintptr)r->fid->file->aux; + xconn = (fnum >> CONNSHIFT) & ConnMask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + ch = c->chans[fnum & ConnMask]; + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_DATA); + hnputl(p->payload+1, ch->otherid); + p->rlength += 4; + add_block(p, r->ifcall.data, r->ifcall.count); + n = finish_packet(p); + if (ch->sent + p->rlength <= ch->twindow) { + iowrite(c->dio, c->datafd, p->nlength, n); + r->aux = 0; + respond(r, nil); + free(p); + threadexits(nil); + } + qlock(&ch->xmtlock); + while (ch->sent + p->rlength > ch->twindow) + rsleep(&ch->xmtrendez); + qunlock(&ch->xmtlock); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + r->aux = 0; + respond(r, nil); + threadexits(nil); +} + +/* + * Although this is named stclunk, it's attached to the destroyfid + * member of the Srv struct. It turns out there's no member + * called clunk. But if there are no other references, a 9P Tclunk + * will end up calling destroyfid. + */ +void +stclunk(Fid *f) +{ + Packet *p; + Conn *c; + SSHChan *sc; + int n, fnum, lev, cnum, chnum; + + if (f == nil || f->file == nil) + return; + fnum = (uintptr)f->file->aux; + lev = fnum >> LEVSHIFT; + cnum = (fnum >> CONNSHIFT) & ConnMask; + chnum = fnum & ConnMask; + if (debug) + fprint(2, "Got destroy fid on file: %x %d %d %d: %s\n", fnum, lev, cnum, chnum, f->file->name); + if (lev == 0 && fnum == ReqRemFile) { + if (keymbox.state != Empty) { + keymbox.state = Empty; + //nbsendul(keymbox.mchan, 1); + } + keymbox.msg = nil; + return; + } + c = connections[cnum]; + if (c == nil) + return; + if (lev == 1 && (fnum & FileMask) == CtlFile + && (c->state == Opening || c->state == Negotiating + || c->state == Authing)) { + for (n = 0; n < MAXCONN + && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closed || c->chans[n]->state == Closing); ++n) ; + if (n >= MAXCONN) { + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); + } + return; + } + sc = c->chans[chnum]; + if (lev == 2) { + if ((fnum & FileMask) == ListenFile && sc->state == Listening) { + qlock(&c->l); + if (sc->state != Closed) { + sc->state = Closed; + chanclose(sc->inchan); + chanclose(sc->reqchan); + } + qunlock(&c->l); + } + else if ((fnum & FileMask) == DataFile && sc->state != Empty + && sc->state != Closed && sc->state != Closing) { + if (f->file != sc->data && f->file != sc->request) { + fprint(2, "Great evil is upon us destroying a fid we didn't create\n"); + return; + } + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + hnputl(p->payload+1, sc->otherid); + p->rlength += 4; + n = finish_packet(p); + sc->state = Closing; + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + qlock(&c->l); + rwakeup(&sc->r); + qunlock(&c->l); + nbsendul(sc->inchan, 1); + nbsendul(sc->reqchan, 1); + } + for (n = 0; n < MAXCONN + && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closed || c->chans[n]->state == Closing); ++n) ; + if (n >= MAXCONN) { + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); + } + } +} + +void +stflush(Req *r) +{ + int fnum; + + fnum = (uintptr)r->oldreq->fid->file->aux; + if (debug) + fprint(2, "Got flush on file %x %d %d %d: %s %p\n", fnum, fnum >> LEVSHIFT, (fnum >> CONNSHIFT) & ConnMask, fnum & ConnMask, r->oldreq->fid->file->name, r->oldreq->aux); + if (r->oldreq->aux) { + if (r->oldreq->ifcall.type == Topen && (fnum & FileMask) == ListenFile && (fnum >> LEVSHIFT) == 1) { + threadint((uintptr)r->oldreq->aux); + } + else if(r->oldreq->ifcall.type == Tread && (fnum & FileMask) == DataFile && (fnum >> LEVSHIFT) == 2) { + threadint((uintptr)r->oldreq->aux); + } + else if(r->oldreq->ifcall.type == Tread && (fnum & FileMask) == ReqRemFile) { + threadint((uintptr)r->oldreq->aux); + } + else { + threadkill((uintptr)r->oldreq->aux); + r->oldreq->aux = 0; + respond(r->oldreq, "interrupted"); + } + } + else + respond(r->oldreq, "interrupted"); + respond(r, nil); +} + +void +filedup(Req *r, File *src) +{ + r->ofcall.qid = src->qid; + closefile(r->fid->file); + r->fid->file = src; + incref(src); +} + +Conn * +alloc_conn(void) +{ + static QLock aclock; + Conn *c; + int sconn, slev, i, s, firstnil; + char buf[20]; + + qlock(&aclock); + firstnil = -1; + for (i = 0; i < MAXCONN; ++i) { + if (connections[i] == nil) { + if (firstnil == -1) + firstnil = i; + continue; + } + s = connections[i]->state; + if (s == Empty || s == Closed) + break; + } + if (i >= MAXCONN) { + if (firstnil != -1) { + connections[firstnil] = emalloc9p(sizeof (Conn)); + memset(connections[firstnil], 0, sizeof (Conn)); + i = firstnil; + } + else { + qunlock(&aclock); + return nil; + } + } + sconn = i << CONNSHIFT; + c = connections[i]; + memset(&c->r, '\0', sizeof(Rendez)); + c->r.l = &c->l; + c->dio = ioproc(); + c->rio = nil; + c->state = Allocated; + c->role = Server; + c->id = i; + c->user = nil; + c->service = nil; + c->nchan = 0; + c->ctlfd = -1; + c->datafd = -1; + c->rpid = -1; + c->inseq = 0; + c->outseq = 0; + c->cscrypt = -1; + c->sccrypt = -1; + c->csmac = -1; + c->scmac = -1; + c->ncscrypt = -1; + c->nsccrypt = -1; + c->ncsmac = -1; + c->nscmac = -1; + c->encrypt = -1; + c->decrypt = -1; + c->outmac = -1; + c->inmac = -1; + if (c->e) { + mpfree(c->e); + c->e = nil; + } + if (c->x) { + mpfree(c->x); + c->x = nil; + } + slev = 1 << LEVSHIFT; + snprint(buf, 20, "%d", i); + if (c->dir == nil) { + c->dir = createfile(rootfile, buf, uid, 0555|DMDIR, (void *)(slev | sconn)); + c->clonefile = createfile(c->dir, "clone", uid, 0666, (void *)(slev | CloneFile | sconn)); + c->ctlfile = createfile(c->dir, "ctl", uid, 0666, (void *)(slev | CtlFile | sconn)); + c->datafile = createfile(c->dir, "data", uid, 0666, (void *)(slev | DataFile | sconn)); + c->listenfile = createfile(c->dir, "listen", uid, 0666, (void *)(slev | ListenFile | sconn)); + c->localfile = createfile(c->dir, "local", uid, 0444, (void *)(slev | LocalFile | sconn)); + c->remotefile = createfile(c->dir, "remote", uid, 0444, (void *)(slev | ReqRemFile | sconn)); + c->statusfile = createfile(c->dir, "status", uid, 0444, (void *)(slev | StatusFile | sconn)); + } +// c->skexinit = nil; +// c->rkexinit = nil; + c->got_sessid = 0; + c->otherid = nil; + c->inik = nil; + c->outik = nil; + c->s2ccs = nil; + c->c2scs = nil; + c->enccs = nil; + c->deccs = nil; + qunlock(&aclock); + return c; +} + +SSHChan * +alloc_chan(Conn *c) +{ + SSHChan *sc; + Plist *p; + int cnum, slev, sconn; + char buf[10]; + + if (c->nchan >= MAXCONN) + return nil; + qlock(&c->l); + cnum = c->nchan; + if (c->chans[cnum] == nil) { + c->chans[cnum] = emalloc9p(sizeof (SSHChan)); + memset(c->chans[cnum], 0, sizeof (SSHChan)); + } + sc = c->chans[cnum]; + snprint(buf, 10, "%d", cnum); + memset(&sc->r, '\0', sizeof(Rendez)); + sc->r.l = &c->l; + sc->id = cnum; + sc->otherid = -1; + sc->state = Empty; + sc->waker = -1; + sc->conn = c->id; + sc->sent = 0; + sc->twindow = 0; + sc->rwindow = 0; + sc->inrqueue = 0; + sc->ann = nil; + sc->lreq = nil; + slev = 2 << LEVSHIFT; + sconn = c->id << CONNSHIFT; + if (sc->dir == nil) { + sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR, (void *)(slev | sconn | cnum)); + sc->ctl = createfile(sc->dir, "ctl", uid, 0666, (void *)(slev | CtlFile | sconn | cnum)); + sc->data = createfile(sc->dir, "data", uid, 0666, (void *)(slev | DataFile | sconn | cnum)); + sc->listen = createfile(sc->dir, "listen", uid, 0666, (void *)(slev | ListenFile | sconn | cnum)); + sc->request = createfile(sc->dir, "request", uid, 0666, + (void *)(slev | ReqRemFile | sconn | cnum)); + sc->status = createfile(sc->dir, "status", uid, 0444, (void *)(slev | StatusFile | sconn | cnum)); + } + c->nchan++; + sc->dataq = nil; + sc->datatl = nil; + while (sc->reqq != nil) { + p = sc->reqq; + sc->reqq = p->next; + free(p->pack); + free(p); + } + sc->reqtl = nil; + if (sc->inchan) + chanfree(sc->inchan); + sc->inchan = chancreate(4, 0); + if (sc->reqchan) + chanfree(sc->reqchan); + sc->reqchan = chancreate(4, 0); + memset(&sc->xmtrendez, '\0', sizeof(Rendez)); + sc->xmtrendez.l = &sc->xmtlock; + qunlock(&c->l); + return sc; +} + +int +dohandshake(Conn *c, char *tcpchan) +{ + Ioproc *io; + char *p; + int fd, n; + char path[256], buf[32]; + + io = ioproc(); + snprint(path, 256, "%s/tcp/%s/remote", mntpt, tcpchan); + fd = ioopen(io, path, OREAD); + n = ioread(io, fd, buf, 31); + if (n > 0) { + buf[n] = 0; + p = strchr(buf, '!'); + if (p) + *p = 0; + if (c->remote) + free(c->remote); + c->remote = estrdup9p(buf); + } + ioclose(io, fd); + snprint(path, 256, "%s/tcp/%s/data", mntpt, tcpchan); + fd = ioopen(io, path, ORDWR); + if (fd < 0) { + closeioproc(io); + return -1; + } + c->datafd = fd; + + /* exchange versions--we're snobbishly only doing SSH2 */ + + snprint(path, 256, "%s\r\n", MYID); + iowrite(io, fd, path, strlen(path)); + p = path; + n = 0; + do { + if (ioread(io, fd, p, 1) < 0) { + fprint(2, "Read failure in ID exchange: %r\n"); + break; + } + ++n; + } while (*p++ != '\n'); + if (n < 5) { + close(fd); + snprint(path, 256, "%s/tcp/%s/ctl", mntpt, tcpchan); + fd = ioopen(io, path, OWRITE); + iowrite(io, fd, "hangup", 6); + ioclose(io, fd); + closeioproc(io); + return -1; + } + *p = 0; + if (debug) + fprint(2, "id string: %d:%s", n, path); + if (strncmp(path, "SSH-2", 5) != 0 && strncmp(path, "SSH-1.99", 8) != 0) { + ioclose(io, fd); + snprint(path, 256, "%s/tcp/%s/ctl", mntpt, tcpchan); + fd = ioopen(io,path, OWRITE); + iowrite(io, fd, "hangup", 6); + ioclose(io, fd); + closeioproc(io); + return -1; + } + closeioproc(io); + if (c->otherid) + free(c->otherid); + c->otherid = estrdup9p(path); + for (n = strlen(c->otherid) - 1; c->otherid[n] == '\r' || c->otherid[n] == '\n'; --n) + c->otherid[n] = '\0'; + c->state = Initting; + + /* start the reader thread */ + if (c->rpid < 0) + c->rpid = threadcreate(reader, c, 8192); + + /* + * negotiate the protocols + * We don't do the full negotiation here, because we also have + * to handle a re-negotiation request from the other end. So + * we just kick it off and let the receiver process take it from there. + */ + + send_kexinit(c); + + qlock(&c->l); + if ((c->role == Client && c->state != Negotiating) || (c->role == Server && c->state != Established)) + rsleep(&c->r); + qunlock(&c->l); + if (c->role == Server && c->state != Established) + return -1; + if (c->role == Client && c->state != Negotiating) + return -1; + return 0; +} + +void +send_kexinit(Conn *c) +{ + Packet *ptmp; + char *p, *e; + int msglen; + int i; + char *buf; + + if (debug) + fprint(2, "Initializing kexinit packet\n"); + if (c->skexinit != nil) + free(c->skexinit); + c->skexinit = new_packet(c); + buf = emalloc9p(1024); + buf[0] = (uchar)SSH_MSG_KEXINIT; + add_packet(c->skexinit, buf, 1); + for (i = 0; i < 16; ++i) + buf[i] = fastrand(); + add_packet(c->skexinit, buf, 16); /* cookie */ + e = buf+1023; + p = seprint(buf, e, "%s", kexes[0]->name); + for (i = 1; i < nelem(kexes); ++i) + p = seprint(p, e, ",%s", kexes[i]->name); + if (debug) + fprint(2, "Sent KEX algs: %s\n", buf); + add_string(c->skexinit, buf); /* Key exchange */ + if (pkas[0] == nil) + add_string(c->skexinit, ""); + else{ + p = seprint(buf, e, "%s", pkas[0]->name); + for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i) + p = seprint(p, e, ",%s", pkas[i]->name); + if (debug) + fprint(2, "Sent host key algs: %s\n", buf); + add_string(c->skexinit, buf); /* server's key algs */ + } + p = seprint(buf, e, "%s", cryptos[0]->name); + for (i = 1; i < nelem(cryptos); ++i) + p = seprint(p, e, ",%s", cryptos[i]->name); + if (debug) + fprint(2, "Sent crypto algs: %s\n", buf); + add_string(c->skexinit, buf); /* c->s crypto */ + add_string(c->skexinit, buf); /* s->c crypto */ + p = seprint(buf, e, "%s", macnames[0]); + for (i = 1; i < nelem(macnames); ++i) + p = seprint(p, e, ",%s", macnames[i]); + if (debug) + fprint(2, "Sent MAC algs: %s\n", buf); + add_string(c->skexinit, buf); /* c->s mac */ + add_string(c->skexinit, buf); /* s->c mac */ + add_string(c->skexinit, "none"); /* c->s compression */ + add_string(c->skexinit, "none"); /* s->c compression */ + add_string(c->skexinit, ""); /* c->s languages */ + add_string(c->skexinit, ""); /* s->c languages */ + memset(buf, 0, 5); + add_packet(c->skexinit, buf, 5); + ptmp = new_packet(c); + memmove(ptmp, c->skexinit, sizeof(Packet)); + msglen = finish_packet(ptmp); + if (c->dio && c->datafd >= 0) + iowrite(c->dio, c->datafd, ptmp->nlength, msglen); + free(ptmp); + free(buf); +} + +void +reader(void *a) +{ + Packet *p, *p2; + Plist *pl; + Conn *c; + SSHChan *ch; + uchar *q; + int i, n,nl, np, nm, nb, cnum; + char buf[256]; + + c = a; + c->rpid = threadid(); + if (debug) + fprint(2, "Starting reader for connection %d, pid:%d\n", c->id, c->rpid); + threadsetname("reader"); + p = new_packet(c); + p2 = new_packet(c); + c->rio = ioproc(); + while (1) { + nm = 0; + nb = 4; + if (c->decrypt != -1) + nb = cryptos[c->decrypt]->blklen; + if (debug) + fprint(2, "calling read for connection %d, state %d, nb %d, dc %d\n", + c->id, c->state, nb, c->decrypt); + if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) { + if (debug) + fprint(2, "Reader for connection %d exiting, nl=%d: %r\n", c->id, nl); + goto bail; + } + if (c->decrypt != -1) + cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb); + p->rlength = nhgetl(p->nlength); + if (debug) + fprint(2, "Got message length: %ld\n", p->rlength); + if (p->rlength > 35000) { + if (debug) + fprint(2, "Absurd packet length: %ld, unrecoverable decrypt failure\n", p->rlength); + goto bail; + } + np = ioreadn(c->rio, c->datafd, p->nlength+nb, p->rlength + 4 - nb); + if (c->inmac != -1) + nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4, 20); + n = nl + np + nm; + if (debug) { + fprint(2, "got message of %d bytes %d padding", n, p->pad_len); + if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) { + i = nhgetl(p->payload+1); + if (c->chans[i]) + fprint(2, " for channel %d win %lud", i, c->chans[i]->rwindow); + else + fprint(2, " for invalid channel %d", i); + } + fprint(2, ": first byte: %d\n", p->payload[0]); + } + if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != 20) { + if (debug) + fprint(2, "Got EOF/error on connection read: %d %d %r\n", np, nm); + goto bail; + } + p->tlength = n; + p->rlength = n - 4; + if (undo_packet(p) < 0) { + if (debug) + fprint(2, "Bad packet in connection %d: exiting\n", c->id); + goto bail; + } + if (c->state == Initting) { + if (p->payload[0] != SSH_MSG_KEXINIT) { + if (debug) + fprint(2, "Missing KEX init packet: %d\n", p->payload[0]); + goto bail; + } + if (c->rkexinit) + free(c->rkexinit); + c->rkexinit = new_packet(c); + memmove(c->rkexinit, p, sizeof(Packet)); + if (validatekex(c, p) < 0) { + if (debug) + fprint(2, "Algorithm mismatch\n"); + goto bail; + } + if (debug) + fprint(2, "Using %s Kex algorithm and %s PKA\n", + kexes[c->kexalg]->name, pkas[c->pkalg]->name); + if (c->role == Client) + kexes[c->kexalg]->clientkex1(c, p); + c->state = Negotiating; + } + else if (c->state == Negotiating) { + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, 256, nil); + fprint(2, "Got disconnect: %s\n", buf); + } + goto bail; + break; + case SSH_MSG_NEWKEYS: + /* + * If we're just updating, go straight to established, + * otherwise wait for authentication + */ + i = c->encrypt; + memmove(c->c2siv, c->nc2siv, SHA1dlen*2); + memmove(c->s2civ, c->ns2civ, SHA1dlen*2); + memmove(c->c2sek, c->nc2sek, SHA1dlen*2); + memmove(c->s2cek, c->ns2cek, SHA1dlen*2); + memmove(c->c2sik, c->nc2sik, SHA1dlen*2); + memmove(c->s2cik, c->ns2cik, SHA1dlen*2); + c->cscrypt = c->ncscrypt; + c->sccrypt = c->nsccrypt; + c->csmac = c->ncsmac; + c->scmac = c->nscmac; + c->c2scs = cryptos[c->cscrypt]->init(c, 0); + c->s2ccs = cryptos[c->sccrypt]->init(c, 1); + if (c->role == Server) { + c->encrypt = c->sccrypt; + c->decrypt = c->cscrypt; + c->outmac = c->scmac; + c->inmac = c->csmac; + c->enccs = c->s2ccs; + c->deccs = c->c2scs; + c->outik = c->s2cik; + c->inik = c->c2sik; + } + else{ + c->encrypt = c->cscrypt; + c->decrypt = c->sccrypt; + c->outmac = c->csmac; + c->inmac = c->scmac; + c->enccs = c->c2scs; + c->deccs = c->s2ccs; + c->outik = c->c2sik; + c->inik = c->s2cik; + } + if (debug) + fprint(2, "Using %s for encryption and %s for decryption\n", + cryptos[c->encrypt]->name, cryptos[c->decrypt]->name); + qlock(&c->l); + if (i != -1) + c->state = Established; + if (c->role == Client) { + rwakeup(&c->r); + } + qunlock(&c->l); + break; + case SSH_MSG_KEXDH_INIT: + kexes[c->kexalg]->serverkex(c, p); + break; + case SSH_MSG_KEXDH_REPLY: + init_packet(p2); + p2->c = c; + if (kexes[c->kexalg]->clientkex2(c, p) >= 0) { + add_byte(p2, SSH_MSG_NEWKEYS); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + } + else{ + add_byte(p2, SSH_MSG_DISCONNECT); + add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED); + add_string(p2, "Key exchange failure"); + add_string(p2, ""); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + shutdown(c); + free(p); + free(p2); + closeioproc(c->rio); + c->rio = nil; + c->rpid = -1; + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + threadexits(nil); + } + break; + case SSH_MSG_SERVICE_REQUEST: + get_string(p, p->payload + 1, buf, 256, nil); + if (debug) + fprint(2, "Got service request: %s\n", buf); + if (strcmp(buf, "ssh-userauth") == 0 || strcmp(buf, "ssh-connection") == 0) { + init_packet(p2); + p2->c = c; + if (slfd > 0) + fprint(slfd, "ssh connection from %s\n", c->remote); + else + syslog(1, "ssh", "ssh connection from %s", c->remote); + add_byte(p2, SSH_MSG_SERVICE_ACCEPT); + add_string(p2, buf); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + c->state = Authing; + } + else{ + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_DISCONNECT); + add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE); + add_string(p2, "Unknown service type"); + add_string(p2, ""); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + goto bail; + } + break; + case SSH_MSG_SERVICE_ACCEPT: + get_string(p, p->payload + 1, buf, 256, nil); + if (c->service && strcmp(c->service, "ssh-userauth") == 0) { + free(c->service); + c->service = estrdup9p("ssh-connection"); + } + if (debug) + fprint(2, "Got service accept: %s: responding with %s %s\n", buf, c->user, c->service); + n = client_auth(c, c->rio); + c->state = Authing; + if (n < 0) { + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + } + break; + default: + break; + } + } + else if (c->state == Authing) { + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, 256, nil); + fprint(2, "Got disconnect: %s\n", buf); + } + goto bail; + break; + case SSH_MSG_USERAUTH_REQUEST: + switch (auth_req(p, c)) { + case 0: + qlock(&c->l); + c->state = Established; + rwakeup(&c->r); + qunlock(&c->l); + break; + case -1: + break; + case -2: + goto bail; + break; + } + break; + case SSH_MSG_USERAUTH_FAILURE: + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + break; + case SSH_MSG_USERAUTH_SUCCESS: + qlock(&c->l); + c->state = Established; + rwakeup(&c->r); + qunlock(&c->l); + break; + case SSH_MSG_USERAUTH_BANNER: + break; + } + } + else if (c->state == Established) { + if (debug >1) { + fprint(2, "In Established state, got:\n"); + dump_packet(p); + } + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, 256, nil); + fprint(2, "Got disconnect: %s\n", buf); + } + goto bail; + break; + case SSH_MSG_IGNORE: + break; + case SSH_MSG_UNIMPLEMENTED: + break; + case SSH_MSG_DEBUG: + if (debug || p->payload[1]) { + get_string(p, p->payload + 2, buf, 256, nil); + fprint(2, "Got debug message: %s\n", buf); + } + break; + case SSH_MSG_KEXINIT: + send_kexinit(c); + if (c->rkexinit) + free(c->rkexinit); + c->rkexinit = new_packet(c); + memmove(c->rkexinit, p, sizeof(Packet)); + if (validatekex(c, p) < 0) { + if (debug) + fprint(2, "Algorithm mismatch\n"); + goto bail; + } + if (debug) + fprint(2, "Using %s Kex algorithm and %s PKA\n", + kexes[c->kexalg]->name, pkas[c->pkalg]->name); + c->state = Negotiating; + break; + case SSH_MSG_GLOBAL_REQUEST: + break; + case SSH_MSG_REQUEST_SUCCESS: + break; + case SSH_MSG_REQUEST_FAILURE: + break; + case SSH_MSG_CHANNEL_OPEN: + q = get_string(p, p->payload + 1, buf, 256, nil); + if (debug) + fprint(2, "Searching for a listener for channel type %s\n", buf); + ch = alloc_chan(c); + if (ch == nil) { + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE); + add_block(p2, p->payload + 5, 4); + hnputl(p2->payload + p2->rlength - 1, 4); + p2->rlength += 4; + add_string(p2, "No available channels"); + add_string(p2, "EN"); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + break; + } + if (debug) + fprint(2, "alloced channel %d for listener\n", ch->id); + qlock(&c->l); + ch->otherid = nhgetl(q); + ch->twindow = nhgetl(q+4); + if (debug) + fprint(2, "Got lock in channel open\n"); + for (i = 0; i < c->nchan; ++i) + if (c->chans[i] && c->chans[i]->state == Listening && c->chans[i]->ann + && strcmp(c->chans[i]->ann, buf) == 0) + break; + if (i >= c->nchan) { + if (debug) + fprint(2, "No listener: sleeping\n"); + ch->state = Opening; + if (ch->ann) + free(ch->ann); + ch->ann = estrdup9p(buf); + if (debug) + fprint(2, "Waiting for someone to announce %s\n", ch->ann); + rsleep(&ch->r); + } + else{ + if (debug) + fprint(2, "Found listener on channel %d\n", ch->id); + c->chans[i]->waker = ch->id; + rwakeup(&c->chans[i]->r); + } + qunlock(&c->l); + break; + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + qlock(&c->l); + ch->otherid = nhgetl(p->payload+5); + ch->twindow = nhgetl(p->payload+9); + rwakeup(&ch->r); + qunlock(&c->l); + break; + case SSH_MSG_CHANNEL_OPEN_FAILURE: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + qlock(&c->l); + rwakeup(&ch->r); + qunlock(&c->l); + goto bail; + break; + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + ch->twindow += nhgetl(p->payload + 5); + if (debug) + fprint(2, "New twindow for channel: %d: %lud\n", cnum, ch->twindow); + qlock(&ch->xmtlock); + rwakeup(&ch->xmtrendez); + qunlock(&ch->xmtlock); + break; + case SSH_MSG_CHANNEL_DATA: + case SSH_MSG_CHANNEL_EXTENDED_DATA: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + pl = emalloc9p(sizeof(Plist)); + pl->pack = emalloc9p(sizeof(Packet)); + memmove(pl->pack, p, sizeof(Packet)); + if (p->payload[0] == SSH_MSG_CHANNEL_DATA) { + pl->rem = nhgetl(p->payload+5); + pl->st = pl->pack->payload+9; + } + else { + pl->rem = nhgetl(p->payload+9); + pl->st = pl->pack->payload+13; + } + pl->next = nil; + if (ch->dataq == nil) + ch->dataq = pl; + else + ch->datatl->next = pl; + ch->datatl = pl; + ch->inrqueue += pl->rem; + nbsendul(ch->inchan, 1); + break; + case SSH_MSG_CHANNEL_EOF: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + if (ch->state != Closed && ch->state != Closing) { + ch->state = Eof; + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + } + break; + case SSH_MSG_CHANNEL_CLOSE: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + if (ch->state != Closed && ch->state != Closing) { + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_CHANNEL_CLOSE); + hnputl(p2->payload + 1, ch->otherid); + p2->rlength += 4; + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + } + qlock(&c->l); + if (ch->state != Closed) { + ch->state = Closed; + rwakeup(&ch->r); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + chanclose(ch->inchan); + chanclose(ch->reqchan); + } + qunlock(&c->l); + for (i = 0; i < MAXCONN && (!c->chans[i] || c->chans[i]->state == Empty || c->chans[i]->state == Closed); ++i) ; + if (i >= MAXCONN) { + goto bail; + } + break; + case SSH_MSG_CHANNEL_REQUEST: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + if (debug) + fprint(2, "Queueing channel request for channel: %d\n", cnum); + q = get_string(p, p->payload+5, buf, 256, nil); + pl = emalloc9p(sizeof(Plist)); + pl->pack = emalloc9p(sizeof(Packet)); + n = snprint((char *)pl->pack->payload, 32768, "%s %c", buf, *q ? 't': 'f'); + if (debug) + fprint(2, "request message begins: %s\n", (char *)pl->pack->payload); + memmove(pl->pack->payload + n, q+1, p->rlength - (11 + (n-2))); + pl->rem = p->rlength - 11 + 2; + pl->st = pl->pack->payload; + pl->next = nil; + if (ch->reqq == nil) + ch->reqq = pl; + else + ch->reqtl->next = pl; + ch->reqtl = pl; + nbsendul(ch->reqchan, 1); + break; + case SSH_MSG_CHANNEL_SUCCESS: + break; + case SSH_MSG_CHANNEL_FAILURE: + break; + default: + break; + } + } + else { + if (debug) + fprint(2, "Connection %d in invalid state, reader exiting\n", c->id); +bail: + shutdown(c); + free(p); + free(p2); + if (c->rio) { + closeioproc(c->rio); + c->rio = nil; + } + c->rpid = -1; + threadexits(nil); + } + } +} + +int +validatekex(Conn *c, Packet *p) +{ + if (c->role == Server) + return validatekexs(p); + else + return validatekexc(p); +} + +int +validatekexs(Packet *p) +{ + uchar *q; + char *toks[128]; + int i, j, n; + char *buf; + + buf = emalloc9p(1024); + q = p->payload + 17; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received KEX algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(kexes); ++j) + if (strcmp(toks[i], kexes[j]->name) == 0) + goto foundk; + free(buf); + return -1; +foundk: + p->c->kexalg = j; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received host key algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) + if (strcmp(toks[i], pkas[j]->name) == 0) + goto foundpka; + free(buf); + return -1; +foundpka: + p->c->pkalg = j; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received C2S crypto algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(cryptos); ++j) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc1; + free(buf); + return -1; +foundc1: + p->c->ncscrypt = j; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received S2C crypto algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(cryptos); ++j) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc2; + free(buf); + return -1; +foundc2: + p->c->nsccrypt = j; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received C2S MAC algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(macnames); ++j) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm1; + free(buf); + return -1; +foundm1: + p->c->ncsmac = j; + q = get_string(p, q, buf, 1024, nil); + if (debug) + fprint(2, "Received S2C MAC algs: %s\n", buf); + n = gettokens(buf, toks, 128, ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(macnames); ++j) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm2; + free(buf); + return -1; +foundm2: + p->c->nscmac = j; + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + free(buf); + if (*q) + return 1; + return 0; +} + +int +validatekexc(Packet *p) +{ + uchar *q; + char *toks[128]; + int i, j, n; + char *buf; + + buf = emalloc9p(1024); + q = p->payload + 17; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(kexes); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], kexes[j]->name) == 0) + goto foundk; + free(buf); + return -1; +foundk: + p->c->kexalg = j; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], pkas[j]->name) == 0) + goto foundpka; + free(buf); + return -1; +foundpka: + p->c->pkalg = j; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(cryptos); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc1; + free(buf); + return -1; +foundc1: + p->c->ncscrypt = j; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(cryptos); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc2; + free(buf); + return -1; +foundc2: + p->c->nsccrypt = j; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(macnames); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm1; + free(buf); + return -1; +foundm1: + p->c->ncsmac = j; + q = get_string(p, q, buf, 1024, nil); + n = gettokens(buf, toks, 128, ","); + for (j = 0; j < nelem(macnames); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm2; + free(buf); + return -1; +foundm2: + p->c->nscmac = j; + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + q = get_string(p, q, buf, 1024, nil); + free(buf); + if (*q) + return 1; + return 0; +} + +int +memrandom(void *p, int n) +{ + uchar *cp; + + for (cp = (uchar*)p; n > 0; n--) + *cp++ = fastrand(); + return 0; +} + +/* + * create a change uid capability + */ +char* +mkcap(char *from, char *to) +{ + uchar rand[20]; + char *cap; + char *key; + int fd, nfrom, nto; + uchar hash[SHA1dlen]; + + fd = open("#ยค/caphash", OWRITE); + + /* create the capability */ + nto = strlen(to); + nfrom = strlen(from); + cap = emalloc9p(nfrom+1+nto+1+sizeof(rand)*3+1); + snprint(cap, nfrom+1+nto+1+sizeof(rand)*3+1, "%s@%s", from, to); + memrandom(rand, sizeof(rand)); + key = cap+nfrom+1+nto+1; + enc64(key, sizeof(rand)*3, rand, sizeof(rand)); + + /* hash the capability */ + hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil); + + /* give the kernel the hash */ + key[-1] = '@'; + if (write(fd, hash, SHA1dlen) < 0) { + close(fd); + free(cap); + return nil; + } + close(fd); + return cap; +} + +int +auth_req(Packet *p, Conn *c) +{ + Packet *p2; + AuthInfo *ai; + uchar *q; + char *service, *me, *user, *pw, *path; + char *alg, *blob, *sig; + int n, fd, retval, nblob, nsig; + char method[32]; + char key1[DESKEYLEN], key2[DESKEYLEN]; + + service = emalloc9p(128); + me = emalloc9p(128); + user = emalloc9p(128); + pw = emalloc9p(128); + alg = emalloc9p(128); + blob = emalloc9p(512); + sig = emalloc9p(512); + path = emalloc9p(128); + retval = 0; + q = get_string(p, p->payload + 1, user, 128, nil); + if (c->user) + free(c->user); + c->user = estrdup9p(user); + q = get_string(p, q, service, 128, nil); + q = get_string(p, q, method, 32, nil); + if (debug) { + fprint(2, "Got userauth request: "); + fprint(2, " %s ", user); + fprint(2, " %s ", service); + fprint(2, " %s\n", method); + } + fd = open("/dev/user", OREAD); + n = read(fd, me, 127); + me[n] = '\0'; + close(fd); + p2 = new_packet(c); + if (strcmp(method, "publickey") == 0) { + if (*q == 0) { + /* Should really check to see if this user can be authed this way */ + q = get_string(p, q+1, alg, 128, nil); + get_string(p, q, blob, 512, &nblob); + for (n = 0; n < nelem(pkas) && pkas[n] != nil && strcmp(pkas[n]->name, alg) != 0; ++n) ; + if (n >= nelem(pkas) || pkas[n] == nil) { + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); + retval = -1; + } + else { + add_byte(p2, SSH_MSG_USERAUTH_PK_OK); + add_string(p2, alg); + add_block(p2, blob, nblob); + retval = -1; + } + } + else { + q = get_string(p, q+1, alg, 128, nil); + q = get_string(p, q, blob, 512, &nblob); + get_string(p, q, sig, 512, &nsig); + + for (n = 0; n < nelem(pkas) && pkas[n] != nil && strcmp(pkas[n]->name, alg) != 0; ++n) ; + if (n >= nelem(pkas) || pkas[n] == nil) { + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); + retval = -1; + } + else { + add_block(p2, c->sessid, SHA1dlen); + add_byte(p2, SSH_MSG_USERAUTH_REQUEST); + add_string(p2, user); + add_string(p2, service); + add_string(p2, method); + add_byte(p2, 1); + add_string(p2, alg); + add_block(p2, blob, nblob); + if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig)) { + if (c->cap != nil) + free(c->cap); + c->cap = mkcap(me, user); + init_packet(p2); + p2->c = c; + if (slfd > 0) + fprint(slfd, "ssh logged in as %s\n", user); + else + syslog(1, "ssh", "ssh logged in as %s", user); + add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); + } + else { + init_packet(p2); + p2->c = c; + if (slfd > 0) + fprint(slfd, "ssh public key login failure for %s\n", user); + else + syslog(1, "ssh", "ssh public key login failure for %s", user); + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); + retval = -1; + } + } + } + } + else if (strcmp(method, "password") == 0) { + ++q; + get_string(p, q, pw, 128, nil); + if (debug > 1) + fprint(2, "%s\n", pw); + ai = nil; + if (kflag) { + if (passtokey(key1, pw) == 0) + goto answer; + snprint(path, 128, "/mnt/keys/%s/key", user); + if ((fd = open(path, OREAD)) < 0) { + werrstr("Invalid user"); + goto answer; + } + if (read(fd, key2, DESKEYLEN) != DESKEYLEN) { + werrstr("Password mismatch"); + goto answer; + } + close(fd); + if (memcmp(key1, key2, DESKEYLEN) != 0) { + werrstr("Password mismatch"); + goto answer; + } + ai = emalloc9p(sizeof(AuthInfo)); + ai->cuid = estrdup9p(user); + ai->suid = estrdup9p(me); + ai->cap = mkcap(me, user); + ai->nsecret = 0; + ai->secret = (uchar *)estrdup9p(""); + } + else + ai = auth_userpasswd(user, pw); +answer: + if (ai == nil) { + if (debug) + fprint(2, "Auth error: %r\n"); + if (slfd > 0) + fprint(slfd, "ssh login failure for %s: %r\n", user); + else + syslog(1, "ssh", "ssh login failure for %s: %r", user); + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); + retval = -1; + } + else{ + if (debug) + fprint(2, "Auth successful: cuid is %s suid is %s cap is %s\n", ai->cuid, ai->suid, ai->cap); + if (c->cap != nil) + free(c->cap); + if (strcmp(user, me) == 0) + c->cap = estrdup9p("n/a"); + else + c->cap = estrdup9p(ai->cap); + if (slfd > 0) + fprint(slfd, "ssh logged in as %s\n", user); + else + syslog(1, "ssh", "ssh logged in as %s", user); + add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); + auth_freeAI(ai); + } + } + else { + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); + retval = -1; + } + n = finish_packet(p2); + iowrite(c->dio, c->datafd, p2->nlength, n); + + free(service); + free(me); + free(user); + free(pw); + free(alg); + free(blob); + free(sig); + free(path); + free(p2); + return retval; +} + +int +client_auth(Conn *c, Ioproc *io) +{ + Packet *p2, *p3, *p4; + char *r, *s; + mpint *ek, *nk; + int i, n; + + if (!c->password && !c->authkey) + return -1; + p2 = new_packet(c); + add_byte(p2, SSH_MSG_USERAUTH_REQUEST); + add_string(p2, c->user); + add_string(p2, c->service); + if (c->password) { + add_string(p2, "password"); + add_byte(p2, 0); + add_string(p2, c->password); + } + else { + add_string(p2, "publickey"); + add_byte(p2, 1); + add_string(p2, "ssh-rsa"); + + r = strstr(c->authkey, " ek="); + s = strstr(c->authkey, " n="); + if (!r || !s) { + shutdown(c); + free(p2); + return -1; + } + ek = strtomp(r+4, nil, 16, nil); + nk = strtomp(s+3, nil, 16, nil); + + p3 = new_packet(c); + add_string(p3, "ssh-rsa"); + add_mp(p3, ek); + add_mp(p3, nk); + add_block(p2, p3->payload, p3->rlength-1); + + p4 = new_packet(c); + add_block(p4, c->sessid, SHA1dlen); + add_byte(p4, SSH_MSG_USERAUTH_REQUEST); + add_string(p4, c->user); + add_string(p4, c->service); + add_string(p4, "publickey"); + add_byte(p4, 1); + add_string(p4, "ssh-rsa"); + add_block(p4, p3->payload, p3->rlength-1); + mpfree(ek); + mpfree(nk); + free(p3); + + for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name); ++i) ; + if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) { + free(p4); + free(p2); + return -1; + } + add_block(p2, p3->payload, p3->rlength-1); + free(p3); + free(p4); + } + n = finish_packet(p2); + if (io) + iowrite(io, c->datafd, p2->nlength, n); + else + write(c->datafd, p2->nlength, n); + free(p2); + return 0; +} + +char * +factlookup(int nattr, int nreq, char *attrs[]) +{ + Biobuf *bp; + char *buf, *toks[32], *res, *q; + int ntok, nmatch, maxmatch; + int i, j; + + res = nil; + bp = Bopen("/mnt/factotum/ctl", OREAD); + if (bp == nil) + return nil; + maxmatch = 0; + while (buf = Brdstr(bp, '\n', 1)) { + q = estrdup9p(buf); + ntok = gettokens(buf, toks, 32, " "); + nmatch = 0; + for (i = 0; i < nattr; ++i) { + for (j = 0; j < ntok; ++j) { + if (strcmp(attrs[i], toks[j]) == 0) { + ++nmatch; + break; + } + } + if (i < nreq && j >= ntok) + break; + } + if (i >= nattr && nmatch > maxmatch) { + free(res); + res = q; + maxmatch = nmatch; + } + else + free(q); + free(buf); + } + Bterm(bp); + return res; +} + +void +shutdown(Conn *c) +{ + Plist *p; + SSHChan *sc; + int i, ostate; + + if (debug) + fprint(2, "Shutting down connection %d\n", c->id); + ostate = c->state; + if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 && c->datafile->ref <= 2 + && c->listenfile->ref <= 2 && c->localfile->ref <= 2 + && c->remotefile->ref <= 2 && c->statusfile->ref <= 2) + c->state = Closed; + else { + if (c->state != Closed) + c->state = Closing; + if (debug) + fprint(2, "clone %ld ctl %ld data %ld listen %ld local %ld remote %ld status %ld\n", + c->clonefile->ref, c->ctlfile->ref, c->datafile->ref, c->listenfile->ref, c->localfile->ref, + c->remotefile->ref, c->statusfile->ref); + } + if (ostate == Closed || ostate == Closing) { + c->state = Closed; + return; + } + if (c->role == Server && c->remote) { + if (slfd > 0) + fprint(slfd, "closing ssh connection from %s\n", c->remote); + else + syslog(1, "ssh", "closing ssh connection from %s", c->remote); + } + fprint(c->ctlfd, "hangup"); + close(c->ctlfd); + close(c->datafd); + if (c->dio) { + closeioproc(c->dio); + c->dio = nil; + } + c->decrypt = -1; + c->inmac = -1; + c->ctlfd = -1; + c->datafd = -1; + c->nchan = 0; + free(c->otherid); + if (c->s2ccs) { + free(c->s2ccs); + c->s2ccs = nil; + } + if (c->c2scs) { + free(c->c2scs); + c->c2scs = nil; + } + if (c->remote) { + free(c->remote); + c->remote = nil; + } + if (c->x) { + mpfree(c->x); + c->x = nil; + } + if (c->e) { + mpfree(c->e); + c->e = nil; + } + if (c->user) { + free(c->user); + c->user = nil; + } + if (c->service) { + free(c->service); + c->service = nil; + } + c->otherid = nil; + qlock(&c->l); + rwakeupall(&c->r); + qunlock(&c->l); + for (i = 0; i < MAXCONN; ++i) { + sc = c->chans[i]; + if (sc == nil) + continue; + if (sc->ann) { + free(sc->ann); + sc->ann = nil; + } + if (sc->state != Empty && sc->state != Closed) { + sc->state = Closed; + sc->lreq = nil; + while (sc->dataq != nil) { + p = sc->dataq; + sc->dataq = p->next; + free(p->pack); + free(p); + } + while (sc->reqq != nil) { + p = sc->reqq; + sc->reqq = p->next; + free(p->pack); + free(p); + } + qlock(&c->l); + rwakeupall(&sc->r); + nbsendul(sc->inchan, 1); + nbsendul(sc->reqchan, 1); + chanclose(sc->inchan); + chanclose(sc->reqchan); + qunlock(&c->l); + } + } + qlock(&availlck); + rwakeup(&availrend); + qunlock(&availlck); + if (debug) + fprint(2, "Done processing shutdown of connection %d\n", c->id); +} --- /sys/src/cmd/ssh2/sshtun.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2/sshtun.h Wed Feb 22 05:02:26 2012 @@ -0,0 +1,262 @@ +#include +#define MYID "SSH-2.0-Plan9" + +#pragma varargck type "M" mpint* + +enum { + CONNSHIFT = 10, + MAXCONN = 1 << CONNSHIFT, + LEVSHIFT = 2 * CONNSHIFT + 3, + + RootFile = 0, + CloneFile = 1 << (2 * CONNSHIFT), + CtlFile = 2 << (2 * CONNSHIFT), + DataFile = 3 << (2 * CONNSHIFT), + ListenFile = 4 << (2 * CONNSHIFT), + LocalFile = 5 << (2 * CONNSHIFT), + ReqRemFile = 6 << (2 * CONNSHIFT), + StatusFile = 7 << (2* CONNSHIFT), + FileMask = 7 << (2 * CONNSHIFT), + ConnMask = (1 << CONNSHIFT) - 1, + + Server = 0, + Client, +}; + +/* + * The stylistic anomaly with these names of unbounded length + * is a result of following the RFCs in using the same names for + * these constants. I did that to make it easier to search and + * cross-reference between the code and the RFCs. + */ +enum /* SSH2 Protocol Packet Types */ +{ + SSH_MSG_DISCONNECT = 1, + SSH_MSG_IGNORE = 2, + SSH_MSG_UNIMPLEMENTED, + SSH_MSG_DEBUG, + SSH_MSG_SERVICE_REQUEST, + SSH_MSG_SERVICE_ACCEPT, + + SSH_MSG_KEXINIT = 20, + SSH_MSG_NEWKEYS, + + SSH_MSG_KEXDH_INIT = 30, + SSH_MSG_KEXDH_REPLY, + + SSH_MSG_USERAUTH_REQUEST = 50, + SSH_MSG_USERAUTH_FAILURE, + SSH_MSG_USERAUTH_SUCCESS, + SSH_MSG_USERAUTH_BANNER, + + SSH_MSG_USERAUTH_PK_OK = 60, + SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60, + + SSH_MSG_GLOBAL_REQUEST = 80, + SSH_MSG_REQUEST_SUCCESS, + SSH_MSG_REQUEST_FAILURE, + + SSH_MSG_CHANNEL_OPEN = 90, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SSH_MSG_CHANNEL_OPEN_FAILURE, + SSH_MSG_CHANNEL_WINDOW_ADJUST, + SSH_MSG_CHANNEL_DATA, + SSH_MSG_CHANNEL_EXTENDED_DATA, + SSH_MSG_CHANNEL_EOF, + SSH_MSG_CHANNEL_CLOSE, + SSH_MSG_CHANNEL_REQUEST, + SSH_MSG_CHANNEL_SUCCESS, + SSH_MSG_CHANNEL_FAILURE, +}; + +enum /* SSH2 reason codes */ +{ + SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, + SSH_DISCONNECT_PROTOCOL_ERROR, + SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + SSH_DISCONNECT_RESERVED, + SSH_DISCONNECT_MAC_ERROR, + SSH_DISCONNECT_COMPRESSION_ERROR, + SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, + SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, + SSH_DISCONNECT_CONNECTION_LOST, + SSH_DISCONNECT_BY_APPLICATION, + SSH_DISCONNECT_TOO_MANY_CONNECTIONS, + SSH_DISCONNECT_AUTH_CANCELLED_BY_USER, + SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, + SSH_DISCONNECT_ILLEGAL_USR_NAME, + + SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1, + SSH_OPEN_CONNECT_FAILED, + SSH_OPEN_UNKNOWN_CHANNEL_TYPE, + SSH_OPEN_RESOURCE_SHORTAGE, +}; + +enum /* SSH2 type code */ +{ + SSH_EXTENDED_DATA_STDERR = 1, +}; + +enum /* connection and channel states */ +{ + Empty = 0, + Allocated, + Initting, + Listening, + Opening, + Negotiating, + Authing, + Established, + Eof, + Closing, + Closed, +}; + +enum { + NoKeyFile, + NoKey, + KeyWrong, + KeyOk, +}; + +typedef struct Plist Plist; +typedef struct SSHChan SSHChan; +typedef struct Conn Conn; +typedef struct Packet Packet; +typedef struct Cipher Cipher; +typedef struct CipherState CipherState; +typedef struct Kex Kex; +typedef struct PKA PKA; +typedef struct MBox MBox; + +#pragma incomplete CipherState + +struct Plist { + Packet *pack; + uchar *st; + int rem; + Plist *next; +}; + +struct SSHChan { + Rendez r; + int id, otherid, state; + int waker; + int conn; + ulong rwindow, twindow; + ulong sent, inrqueue; + char *ann; + Req *lreq; + File *dir, *ctl, *data, *listen, *request, *status; + Plist *dataq, *datatl, *reqq, *reqtl; + Channel *inchan, *reqchan; + QLock xmtlock; + Rendez xmtrendez; +}; + +struct Conn { + QLock l; + Rendez r; + Ioproc *dio, *cio, *rio; + int state; + int role; + int id; + char *remote; + char *user, *password, *service; + char *cap; + char *authkey; + int nchan; + int datafd, ctlfd; + int rpid; + int inseq, outseq; + int kexalg, pkalg; + int cscrypt, ncscrypt, sccrypt, nsccrypt, csmac, ncsmac, scmac, nscmac; + int encrypt, decrypt, outmac, inmac; + File *dir, *clonefile, *ctlfile, *datafile, *listenfile, *localfile, *remotefile, *statusfile; + Packet *skexinit, *rkexinit; + mpint *x, *e; + int got_sessid; + uchar sessid[SHA1dlen]; + uchar c2siv[SHA1dlen*2], nc2siv[SHA1dlen*2], s2civ[SHA1dlen*2], ns2civ[SHA1dlen*2]; + uchar c2sek[SHA1dlen*2], nc2sek[SHA1dlen*2], s2cek[SHA1dlen*2], ns2cek[SHA1dlen*2]; + uchar c2sik[SHA1dlen*2], nc2sik[SHA1dlen*2], s2cik[SHA1dlen*2], ns2cik[SHA1dlen*2]; + char *otherid; + uchar *inik, *outik; + CipherState *s2ccs, *c2scs; + CipherState *enccs, *deccs; + SSHChan *chans[MAXCONN]; +}; + +struct Packet { + Conn *c; + ulong rlength, tlength; + uchar nlength[4]; + uchar pad_len; + uchar payload[35000]; +}; + +struct Cipher +{ + char *name; + int blklen; + CipherState *(*init)(Conn*, int); + void (*encrypt)(CipherState*, uchar*, int); + void (*decrypt)(CipherState*, uchar*, int); +}; + +struct Kex +{ + char *name; + int (*serverkex)(Conn *, Packet *); + int (*clientkex1)(Conn *, Packet *); + int (*clientkex2)(Conn *, Packet *); +}; + +struct PKA +{ + char *name; + Packet *(*ks)(Conn *); + Packet *(*sign)(Conn *, uchar *, int); + int (*verify)(Conn *, uchar *, int, char *, char *, int); +}; + +struct MBox +{ + Channel *mchan; + char *msg; + int state; +}; + +extern int debug; +extern Cipher cipherblowfish, cipher3des, cipherrc4; +extern Cipher cipheraes128, cipheraes192, cipheraes256; +extern Kex dh1sha1, dh14sha1; +extern PKA rsa_pka, dss_pka, *pkas[]; +extern sshkeychan[]; +extern MBox keymbox; + +/* pubkey.c */ +RSApub *readpublickey(Biobuf *, char **); +int findkey(char *, char *, RSApub *); +int replacekey(char *, char *, RSApub *); +int appendkey(char *, char *, RSApub *); + +/* dh.c */ +void dh_init(PKA *[]); + +/* transport.c */ +Packet *new_packet(Conn *); +void init_packet(Packet *); +void add_byte(Packet *, char); +void add_uint32(Packet *, ulong); +ulong get_uint32(Packet *, uchar **); +int add_packet(Packet *, void *, int); +void add_block(Packet *, void *, int); +void add_string(Packet *, char *); +uchar *get_string(Packet *, uchar *, char *, int, int *); +void add_mp(Packet *, mpint *); +mpint *get_mp(uchar *q); +int finish_packet(Packet *); +int undo_packet(Packet *); +void dump_packet(Packet *); --- /sys/src/cmd/ssh2/transport.c Thu Apr 19 15:20:39 2012 +++ /sys/src/cmd/ssh2/transport.c Wed Feb 22 05:02:02 2012 @@ -6,7 +6,7 @@ #include <9p.h> #include #include -#include "netssh.h" +#include "sshtun.h" extern Cipher *cryptos[]; @@ -54,7 +54,7 @@ int add_packet(Packet *p, void *data, int len) { - if(p->rlength + len > Maxpayload) + if(p->rlength + len > 32768) return -1; memmove(p->payload + p->rlength - 1, data, len); p->rlength += len; @@ -112,7 +112,7 @@ int n; q = p->payload + p->rlength - 1; - n = mptobe(x, q + 4, Maxpktpay - p->rlength + 1 - 4, nil); + n = mptobe(x, q + 4, 35000 - p->rlength + 1 - 4, nil); if(q[4] & 0x80){ memmove(q + 5, q + 4, n); q[4] = 0; @@ -125,7 +125,11 @@ mpint * get_mp(uchar *q) { - return betomp(q + 4, nhgetl(q), nil); + int n; + + n = nhgetl(q); + q += 4; + return betomp(q, n, nil); } int @@ -137,16 +141,15 @@ c = p->c; blklen = 8; - if(c && debug > 1) - fprint(2, "%s: in finish_packet: enc %d outmac %d len %ld\n", - argv0, c->encrypt, c->outmac, p->rlength); + if(debug > 1) + fprint(2, "In finish packet: enc:%d outmac:%d len:%ld\n", c->encrypt, c->outmac, p->rlength); if(c && c->encrypt != -1){ blklen = cryptos[c->encrypt]->blklen; if(blklen < 8) blklen = 8; } n1 = p->rlength - 1; - n2 = blklen - (n1 + 5) % blklen; + n2 = blklen - ((n1 + 5) % blklen); if(n2 < 4) n2 += blklen; p->pad_len = n2; @@ -157,7 +160,7 @@ maclen = 0; if(c && c->outmac != -1){ maclen = SHA1dlen; - buf = emalloc9p(Maxpktpay); + buf = emalloc9p(35000); hnputl(buf, c->outseq); memmove(buf + 4, p->nlength, p->rlength + 4); hmac_sha1(buf, p->rlength + 8, c->outik, maclen, q, nil); @@ -165,11 +168,9 @@ } if(c && c->encrypt != -1) cryptos[c->encrypt]->encrypt(c->enccs, p->nlength, p->rlength + 4); - if (c) - c->outseq++; + c->outseq++; if(debug > 1) - fprint(2, "%s: leaving finish packet: len %ld n1 %d n2 %d maclen %d\n", - argv0, p->rlength, n1, n2, maclen); + fprint(2, "leaving finish packet: len:%ld n1:%d n2:%d maclen:%d\n", p->rlength, n1, n2, maclen); return p->rlength + 4 + maclen; } @@ -190,20 +191,18 @@ if(c->decrypt != -1) nb = cryptos[c->decrypt]->blklen; if(c->inmac != -1) - p->rlength -= SHA1dlen; /* was magic 20 */ + p->rlength -= 20; nlength = nhgetl(p->nlength); if(c->decrypt != -1) - cryptos[c->decrypt]->decrypt(c->deccs, p->nlength + nb, - p->rlength + 4 - nb); + cryptos[c->decrypt]->decrypt(c->deccs, p->nlength + nb, p->rlength + 4 - nb); if(c->inmac != -1){ - buf = emalloc9p(Maxpktpay); + buf = emalloc9p(35000); hnputl(buf, c->inseq); memmove(buf + 4, p->nlength, nlength + 4); hmac_sha1(buf, nlength + 8, c->inik, SHA1dlen, rmac, nil); free(buf); if(memcmp(rmac, p->payload + nlength - 1, SHA1dlen) != 0){ - fprint(2, "%s: received MAC verification failed: seq=%d\n", - argv0, c->inseq); + fprint(2, "Received MAC verification failed: seq=%d\n", c->inseq); return -1; } } @@ -220,13 +219,14 @@ char *buf, *q, *e; fprint(2, "Length: %ld, Padding length: %d\n", p->rlength, p->pad_len); - q = buf = emalloc9p(Copybufsz); - e = buf + Copybufsz; + buf = emalloc9p(4096); + e = buf + 4096; + q = buf; for(i = 0; i < p->rlength - 1; ++i){ q = seprint(q, e, " %02x", p->payload[i]); if(i % 16 == 15) q = seprint(q, e, "\n"); - if(q - buf > Copybufsz - 4){ + if((q - buf) > 4092){ fprint(2, "%s", buf); q = buf; }