From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from pb-smtp2.pobox.com ([64.147.108.71]) by ewsd; Fri Jun 12 16:36:00 EDT 2020 Received: from pb-smtp2.pobox.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id BA80E5EB14 for <9front@9front.org>; Fri, 12 Jun 2020 16:35:47 -0400 (EDT) (envelope-from unobe@cpan.org) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=date :mime-version:content-type:content-transfer-encoding:subject:to :from:message-id; s=sasl; bh=lvyM3I2z0DQ5sTLRKjnPGRUCDfw=; b=mcw E5BZV8EPXwoIskCvnS7LiMXtA6EfbEhCSZ3fRpT1Wv3om1WDMsBtA6hrPx+SA9RI aj9Bl9QmGuth5p2lh/WSlTLXl46BRf0TeVvDZk9PrHdA1m6Sx4uQInY2gnXkgsnt C9HnJyvEXv/BmIgnIYMDQUAc1XQdv+WixHE/uKn8= Received: from pb-smtp2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id B13B15EB12 for <9front@9front.org>; Fri, 12 Jun 2020 16:35:47 -0400 (EDT) (envelope-from unobe@cpan.org) Received: from [10.0.1.31] (unknown [137.25.138.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pb-smtp2.pobox.com (Postfix) with ESMTPSA id C0AB05EB0D for <9front@9front.org>; Fri, 12 Jun 2020 16:35:46 -0400 (EDT) (envelope-from unobe@cpan.org) Date: Fri, 12 Jun 2020 20:35:44 +0000 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Subject: [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange To: 9front@9front.org From: Romano Message-ID: <8008101F-B2C7-4DB7-BFD9-5BA7E82DBC54@cpan.org> X-Pobox-Relay-ID: 4B8D8486-ACEC-11EA-8E03-D1361DBA3BAF-09620299!pb-smtp2.pobox.com List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: flexible patented ACPI over HTTP lifecycle plugin All, Below is a patch (also at http://okturing=2Ecom/src/8696/body ) that handl= es client and server algorithm negotations so that other key exchange, host= , cipher, et c=2E algorithms and implements ssh-dss key exchange=2E The pr= imary impetus for this was to use rsync=2Enet, which right now allows only = ssh-dss and ssh-ed25519 for key exchange=2E ed25519 is related to curve2551= 9 preferable, and there's an implementation that I'm planning to port to 9f= ront (djb's reference implementation[1] which apparently OpenSSH uses )=2E = However, the first step was to make sure the algorithm negotation bits were= all in order=2E I then noticed that dsa(2) provided the basics for impleme= ntation ssh-dss=2E Ssh-dss was deprecated by OpenSSH too hastily[2], so I'v= e implemented it so that I can use rsync=2Enet=2E Next, I hope to use ssh-e= d25519=2E This is my first real foray into C besides the little patch I sent in Apri= l=2E I hope the it's not too terrible and useful for others=2E [1] https://ed25519=2Ecr=2Eyp=2Eto/software=2Ehtml [2] https://security=2Estackexchange=2Ecom/questions/112802 diff -r 307fbb6fd10a sys/src/cmd/ssh=2Ec --- a/sys/src/cmd/ssh=2Ec Fri Jun 12 01:36:50 2020 +0200 +++ b/sys/src/cmd/ssh=2Ec Fri Jun 12 12:37:32 2020 -0700 @@ -1,3 +1,14 @@ +/* + 2020-June (Romano) + I think this would be better if refactored into its own subdir with sepa= rate files, e=2Eg=2E: + ssh/ssh=2Eh <-- structs, enums, et c=2E + ssh/debug=2Ec <-- debugging statement stuff, e=2Eg=2E printKexinitp() + ssh/alloc=2Ec <-- kexinitalloc() et c=2E + ssh/kex=2Ec <-- key exchange and host server algorithm stuff + ssh/algs=2Ec <-- cipher, mac, zip, and lang stuff + ssh/ssh=2Ec <-- main loop +*/ + #include #include #include @@ -5,6 +16,7 @@ #include #include =20 +/* Cf=2E RFC2450 & RFC8268 */ enum { MSG_DISCONNECT =3D 1, MSG_IGNORE, @@ -47,7 +59,7 @@ =20 =20 enum { - Overhead =3D 256, // enougth for MSG_CHANNEL_DATA header + Overhead =3D 256, // enough for MSG_CHANNEL_DATA header MaxPacket =3D 1<<15, WinPackets =3D 8, // (1<<15) * 8 =3D 256K }; @@ -76,6 +88,280 @@ Rendez; } Oneway; =20 +/* BEGIN RFC 4253 name-list agreement structure and implementation */ +struct nameseq { + char *val; + struct nameseq *next; +}; +typedef struct nameseq Nameseq; + +Nameseq* +nameseqalloc(void) +{ + Nameseq *nameseq; + + nameseq =3D mallocz(sizeof(*nameseq), 1); + if(nameseq =3D=3D nil) + sysfatal("nameseqalloc"); + return nameseq; +} + +void +nameseqfree(Nameseq *nameseq) +{ + if(nameseq =3D=3D nil) + return; + nameseqfree(nameseq->next); + free(nameseq); +} + +Nameseq * +getnameseq(char *c) +{ + Nameseq *orderp =3D nameseqalloc(); + Nameseq *pp =3D orderp; + char *str =3D strdup(c); + while (str =3D strtok(str, ",")) { + pp->val =3D str; + pp->next =3D nameseqalloc(); + pp =3D pp->next; + str =3D nil; + } + return orderp; +} + +char * +nameseqagree(char *c, char *s) +{ + Nameseq *cp, *sp, *startsp; + char *val =3D nil; + + if (strlen(c) =3D=3D 0 && strlen(s) =3D=3D 0 ) + return ""; + + cp =3D getnameseq(c); + startsp =3D sp =3D getnameseq(s); + while (cp) { + if (cp->val && sp->val && !strcmp(cp->val, sp->val)) { + val =3D strdup(cp->val); + break; + } + if(!(sp =3D sp->next)) { + if(!(cp =3D cp->next)) + break; + sp =3D startsp; + } + } + + nameseqfree(cp); + nameseqfree(sp); + return val; +} +/* END RFC 4253 name-list agreement implementation */ + +typedef struct algs { + char *cipher; + char *mac; + char *zip; + char *lang; +} Algs; +typedef Algs *Algsp; + +typedef struct kexhost { + char *kex; + char *host; +} Kexhost; +typedef Kexhost *Kexhostp; + +typedef struct kexother { + uint firstkexpkt; + ulong reserved; + uchar msg; + uchar cookie[16]; +} Kexother; +typedef Kexother *Kexotherp; + +typedef struct kexinit { + Kexhostp kh; + Algsp ctos; + Algsp stoc; + Kexotherp ko; +} Kexinit; +typedef Kexinit *Kexinitp; + +Algsp +algsalloc(void) +{ + Algsp data; + + data =3D mallocz(sizeof(data), 1); + if(data =3D=3D nil) + sysfatal("algsalloc"); + return data; +} + +Kexhostp +kexhostalloc(void) +{ + Kexhostp data; + + data =3D mallocz(sizeof(data), 1); + if(data =3D=3D nil) + sysfatal("kexhostalloc"); + return data; +} + +Kexotherp +kexotheralloc(void) +{ + Kexotherp data; + + data =3D mallocz(sizeof(data), 1); + if(data =3D=3D nil) + sysfatal("kexotheralloc"); + return data; +} + +void +printKexhostp(char *prefix, Kexhostp data) { + fprint(2, "%s ->%s: %s\n", prefix, "kex", data->kex ); + fprint(2, "%s ->%s: %s\n", prefix, "host", data->host ); +} + +Kexinitp +kexinitalloc(void) +{ + Kexinitp data; + + data =3D mallocz(sizeof(data), 1); + if(data =3D=3D nil) + sysfatal("kexinitalloc"); + data->ko =3D kexotheralloc(); + data->kh =3D kexhostalloc(); + data->ctos =3D algsalloc(); + data->stoc =3D algsalloc(); + return data; +} + +void +defKexotherp(Kexotherp kop) { + kop->msg =3D MSG_KEXINIT; + kop->firstkexpkt =3D 0; + kop->reserved =3D 0L; + genrandom(kop->cookie, sizeof(kop->cookie)); +} + +void +defKexhostp(Kexhostp khp) { + khp->kex =3D "curve25519-sha256,curve25519-sha256@libssh=2Eorg"; + + /* + ssh-dss is required per RFC4253=2E OpenSSH has deprecated its use, but + does there's no real basis to support that decision for larger modulo=2E + Support it as a secondary algorithm, most likely until ssh-ed25519 is + supported=2E + */ + khp->host =3D "ssh-rsa,ssh-dss"; +} + +void +defAlgsp(Algsp algsp) { + /* + 'At some future time, it is expected that another algorithm, one with be= tter + strength, will become so prevalent and ubiquitous that the use of + "3des-cbc" will be deprecated by another STANDARDS ACTION=2E' - RFC4253 + No standards action has yet deprecated it, but have not seen it supporte= d + by default in any server=2E + */ + algsp->cipher =3D "chacha20-poly1305@openssh=2Ecom"; +=09 + /* + hmac-sha1 is a required algorithm by RFC4253, and some servers still che= ck + for it; it's effectively ignored since chacha20-poly1305 cipher is AEAD= =2E + No RFC has deprecated it, only provided other algorithms to be placed + before it (e=2Eg=2E, SHA-2)=2E + */ + algsp->mac =3D "hmac-sha1"; +=09 + algsp->zip =3D "none"; + algsp->lang =3D ""; +} + +void +printKexotherp(char *prefix, Kexotherp data) { + fprint(2, "%s ->%s: %uc\n", prefix, "msg", data->msg ); + fprint(2, "%s ->%s: %uhd\n", prefix, "cookie", *data->cookie ); + fprint(2, "%s ->%s: %ud\n", prefix, "firstkexpkt", data->firstkexpkt ); + fprint(2, "%s ->%s: %uld\n", prefix, "reserved", data->reserved ); +} + +void +printAlgsp(char *prefix, Algsp data) { + fprint(2, "%s ->%s: %s\n", prefix, "cipher", data->cipher ); + fprint(2, "%s ->%s: %s\n", prefix, "mac", data->mac ); + fprint(2, "%s ->%s: %s\n", prefix, "zip", data->zip ); + fprint(2, "%s ->%s: %s\n", prefix, "lang", data->lang ); +} + +void +printKexinitp(char *prefix, Kexinitp data) { + fprint(2, "%s %llX:\n", prefix, (vlong)data); + fprint(2, "%s ko->\n", prefix); + printKexotherp(prefix, data->ko ); + fprint(2, "%s kh->\n", prefix); + printKexhostp(prefix, data->kh ); + fprint(2, "%s ctos->\n", prefix); + printAlgsp(prefix, data->ctos ); + fprint(2, "%s stoc->\n", prefix); + printAlgsp(prefix, data->stoc ); +} + +Kexhostp +khagree(Kexhostp a, Kexhostp b) { + Kexhostp c =3D kexhostalloc(); + c->kex =3D nameseqagree(a->kex, b->kex); + if(!c->kex) + sysfatal("Cannot agree on key exchange algorithm"); + + c->host =3D nameseqagree(a->host, b->host); + if(!c->host) + sysfatal("Cannot agree on host server algorithm"); + + return c; +} + +Algsp +algsagree(Algsp a, Algsp b, char * cxt) { + Algsp c =3D algsalloc(); + c->cipher =3D nameseqagree(a->cipher, b->cipher); + if(!c->cipher) + sysfatal(smprint("Cannot agree on cipher algorithm for %s", cxt)); + + c->mac =3D nameseqagree(a->mac, b->mac); + if(!c->mac) + sysfatal(smprint("Cannot agree on mac algorithm for %s", cxt)); + + c->zip =3D nameseqagree(a->zip, b->zip); + if(!c->zip) + sysfatal(smprint("Cannot agree on zip algorithm for %s", cxt)); + + c->lang =3D nameseqagree(a->lang, b->lang); + if(!c->lang) + sysfatal(smprint("Cannot agree on lang algorithm for %s", cxt)); + + return c; +} + +Kexinitp agreep; + +void +authagree(Kexinitp client, Kexinitp server) { + agreep =3D kexinitalloc(); + agreep->kh =3D khagree(client->kh, server->kh); + agreep->ctos =3D algsagree(client->ctos, server->ctos, "ctos"); + agreep->stoc =3D algsagree(client->stoc, server->stoc, "stoc"); +} + int nsid; uchar sid[256]; char thumb[2*SHA2_256dlen+1], *thumbfile; @@ -84,6 +370,7 @@ char *user, *service, *status, *host, *remote, *cmd; =20 Oneway recv, send; + void dispatch(void); =20 void @@ -163,6 +450,16 @@ memmove(p, s, u); p +=3D u; break; + /* Why does 's' exist with passing the string length, when it can just = be calculated with strlen()? */ + case 'S': + s =3D va_arg(a, char*); + u =3D strlen(s); + if(p+4 > e) goto err; + PUT4(p, u), p +=3D 4; + if(u > e-p) goto err; + memmove(p, s, u); + p +=3D u; + break; case 'u': u =3D va_arg(a, int); if(p+4 > e) goto err; @@ -211,6 +508,14 @@ *va_arg(a, int*) =3D u; p +=3D u; break; + /* Why does 's' exist with passing the string length? casting to (char = *) is null-terminated and can simply move that way: */ + case 'S': + if(p+4 > e) goto err; + u =3D GET4(p), p +=3D 4; + if(u > e-p) goto err; + *va_arg(a, char**)=3D (char*)p; + p +=3D u; + break; case '[': s =3D va_arg(a, void*); u =3D va_arg(a, int); @@ -280,7 +585,7 @@ send=2Er =3D send=2Eb; send=2Ew =3D send=2Eb+n; =20 -if(debug > 1) +if(debug > 2) fprint(2, "sendpkt: (%d) %=2E*H\n", send=2Er[0], (int)(send=2Ew-send=2Er= ), send=2Er); =20 if(nsid){ @@ -309,6 +614,26 @@ send=2Eseq++; } =20 +void +sendkexinitpkt(Kexinitp data) { + sendpkt( + "b[SSSSSSSSSSbu", + data->ko->msg, + data->ko->cookie, sizeof(data->ko->cookie), + data->kh->kex, + data->kh->host, + data->ctos->cipher, + data->stoc->cipher, + data->ctos->mac, + data->stoc->mac, + data->ctos->zip, + data->stoc->zip, + data->ctos->lang, + data->stoc->lang, + data->ko->firstkexpkt, + data->ko->reserved); +} + int readall(int fd, uchar *data, int len) { @@ -366,50 +691,99 @@ recv=2Ew =3D recv=2Er + n; recv=2Eseq++; =20 -if(debug > 1) +if(debug > 2) fprint(2, "recvpkt: (%d) %=2E*H\n", recv=2Er[0], (int)(recv=2Ew-recv=2Er= ), recv=2Er); =20 return recv=2Er[0]; } =20 -static char sshrsa[] =3D "ssh-rsa"; +Kexinitp +recvkexinit(void) { + + Kexinitp spropp =3D kexinitalloc(); + if(unpack(recv=2Er, recv=2Ew-recv=2Er, "b[SSSSSSSSSSbu", &(spropp->ko->m= sg), spropp->ko->cookie, sizeof(spropp->ko->cookie), &(spropp->kh->kex), &(= spropp->kh->host), &(spropp->ctos->cipher), &(spropp->stoc->cipher), &(spro= pp->ctos->mac), &(spropp->stoc->mac), &(spropp->ctos->zip), &(spropp->stoc-= >zip), &(spropp->ctos->lang), &(spropp->stoc->lang), &(spropp->ko->firstkex= pkt), &(spropp->ko->reserved) ) < 0) + sysfatal("bad MSG_KEXINIT reply"); + + if(debug > 1) + printKexinitp("server kexinit proposal", spropp); + + return spropp; +} + +typedef struct { + RSApub *rsa; + DSApub *dsa; +} Pub; + +static Pub *pub; =20 int -rsapub2ssh(RSApub *rsa, uchar *data, int len) +dsapub2ssh(uchar *data, int len) { - return pack(data, len, "smm", sshrsa, sizeof(sshrsa)-1, rsa->ek, rsa->n)= ; + return pack(data, len, "smmmm", "ssh-dss", sizeof("ssh-dss")-1, pub->dsa= ->p, pub->dsa->q, pub->dsa->alpha, pub->dsa->key); } =20 -RSApub* -ssh2rsapub(uchar *data, int len) +void +ssh2dsapub(uchar *data, int len) { - RSApub *pub; char *s; int n; =20 - pub =3D rsapuballoc(); - pub->n =3D mpnew(0); - pub->ek =3D mpnew(0); - if(unpack(data, len, "smm", &s, &n, pub->ek, pub->n) < 0 - || n !=3D sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) !=3D 0){ - rsapubfree(pub); - return nil; + pub->dsa =3D dsapuballoc(); + pub->dsa->p =3D mpnew(0); + pub->dsa->q =3D mpnew(0); + pub->dsa->alpha =3D mpnew(0); + pub->dsa->key =3D mpnew(0); + // RFC4253 Section 6=2E6: alpha is g, key is y + if(unpack(data, len, "smmmm", &s, &n, pub->dsa->p, pub->dsa->q, pub->dsa= ->alpha, pub->dsa->key ) < 0 + || n !=3D sizeof("ssh-dss")-1 || memcmp(s, "ssh-dss", n) !=3D 0){ + dsapubfree(pub->dsa); } - return pub; } =20 int -rsasig2ssh(RSApub *pub, mpint *S, uchar *data, int len) +dsasig2ssh(mpint *S, uchar *data, int len) { - int l =3D (mpsignif(pub->n)+7)/8; - if(4+7+4+l > len) - return -1; - mptober(S, data+4+7+4, l); - return pack(data, len, "ss", sshrsa, sizeof(sshrsa)-1, data+4+7+4, l); + DSApriv *dsapriv =3D dsagen(pub->dsa); + DSAsig *dsasig =3D dsasign(dsapriv, S); + mptobe(dsasig->r, data, SHA1dlen, nil); + mptobe(dsasig->s, data+SHA1dlen, SHA1dlen, nil); + dsasigfree(dsasig); + dsaprivfree(dsapriv); + return pack(data, len, "ss", "ssh-dss", sizeof("ssh-dss")-1, data, len+2= *SHA1dlen); +} + +int +dsasha1verify(uchar *data, int len, mpint *S) +{ + mpint *V; + int ret; + uchar digest[SHA1dlen]; + + sha1(data, len, digest, nil); + V =3D betomp(digest, SHA1dlen, nil); + ret =3D V !=3D nil; + if(ret){ + DSAsig *dsasig =3D dsasigalloc(); + dsasig->r =3D mpnew(0); + dsasig->s =3D mpnew(0); + mpright(S, 160, dsasig->r); + mptrunc(S, 160, dsasig->s); + if(debug > 2) { + fmtinstall('B', mpfmt); + fprint(2, "V: %B\n", V); + fprint(2, "S: %B\n", S); + fprint(2, "r:%B; s:%B\n", dsasig->r, dsasig->s); + } + ret =3D !dsaverify(pub->dsa, dsasig, V); + dsasigfree(dsasig); + mpfree(V); + } + return ret; } =20 mpint* -ssh2rsasig(uchar *data, int len) +ssh2sig(uchar *data, int len) { mpint *m; char *s; @@ -417,32 +791,65 @@ =20 m =3D mpnew(0); if(unpack(data, len, "sm", &s, &n, m) < 0 - || n !=3D sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) !=3D 0){ + || n !=3D sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != =3D 0){ mpfree(m); return nil; } return m; } =20 +static char sshrsa[] =3D "ssh-rsa"; + +int +rsapub2ssh(uchar *data, int len) +{ + return pack(data, len, "smm", agreep->kh->host, sizeof(agreep->kh->host)= -1, pub->rsa->ek, pub->rsa->n); +} + +void +ssh2rsapub(uchar *data, int len) +{ + char *s; + int n; + + pub->rsa =3D rsapuballoc(); + pub->rsa->n =3D mpnew(0); + pub->rsa->ek =3D mpnew(0); + if(unpack(data, len, "smm", &s, &n, pub->rsa->ek, pub->rsa->n) < 0 + || n !=3D sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != =3D 0){ + rsapubfree(pub->rsa); + } +} + +int +rsasig2ssh(mpint *S, uchar *data, int len) +{ + int l =3D (mpsignif(pub->rsa->n)+7)/8; + if(4+7+4+l > len) + return -1; + mptober(S, data+4+7+4, l); + return pack(data, len, "ss", agreep->kh->host, sizeof(agreep->kh->host)-= 1, data+4+7+4, l); +} + mpint* -pkcs1digest(uchar *data, int len, RSApub *pub) +pkcs1digest(uchar *data, int len) { uchar digest[SHA1dlen], buf[256]; =20 sha1(data, len, digest, nil); - return pkcs1padbuf(buf, asn1encodedigest(sha1, digest, buf, sizeof(buf))= , pub->n, 1); + return pkcs1padbuf(buf, asn1encodedigest(sha1, digest, buf, sizeof(buf))= , pub->rsa->n, 1); } =20 int -pkcs1verify(uchar *data, int len, RSApub *pub, mpint *S) +pkcs1verify(uchar *data, int len, mpint *S) { mpint *V; int ret; =20 - V =3D pkcs1digest(data, len, pub); + V =3D pkcs1digest(data, len); ret =3D V !=3D nil; if(ret){ - rsaencrypt(pub, S, S); + rsaencrypt(pub->rsa, S, S); ret =3D mpcmp(V, S) =3D=3D 0; mpfree(V); } @@ -486,37 +893,27 @@ void kex(int gotkexinit) { - static char kexalgs[] =3D "curve25519-sha256,curve25519-sha256@libssh=2E= org"; - static char cipheralgs[] =3D "chacha20-poly1305@openssh=2Ecom"; - static char zipalgs[] =3D "none"; - static char macalgs[] =3D "hmac-sha1"; /* work around for github=2Ecom *= / - static char langs[] =3D ""; + Kexinitp cpropp =3D kexinitalloc(); + defKexotherp(cpropp->ko); + defKexhostp(cpropp->kh); + defAlgsp(cpropp->ctos); + defAlgsp(cpropp->stoc); =20 - uchar cookie[16], x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *= ks, *sig; + if(debug > 1) + printKexinitp("client kexinit properties", cpropp); + + uchar x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *ks, *sig; uchar k12[2*ChachaKeylen]; int i, nk, nys, nks, nsig; DigestState *ds; mpint *S, *K; - RSApub *pub; + if (pub=3D=3Dnil) + pub =3D mallocz(sizeof(pub), 1); =20 ds =3D hashstr(send=2Ev, strlen(send=2Ev), nil);=09 ds =3D hashstr(recv=2Ev, strlen(recv=2Ev), ds); =20 - genrandom(cookie, sizeof(cookie)); - sendpkt("b[ssssssssssbu", MSG_KEXINIT, - cookie, sizeof(cookie), - kexalgs, sizeof(kexalgs)-1, - sshrsa, sizeof(sshrsa)-1, - cipheralgs, sizeof(cipheralgs)-1, - cipheralgs, sizeof(cipheralgs)-1, - macalgs, sizeof(macalgs)-1, - macalgs, sizeof(macalgs)-1, - zipalgs, sizeof(zipalgs)-1, - zipalgs, sizeof(zipalgs)-1, - langs, sizeof(langs)-1, - langs, sizeof(langs)-1, - 0, - 0); + sendkexinitpkt(cpropp); ds =3D hashstr(send=2Er, send=2Ew-send=2Er, ds); =20 if(!gotkexinit){ @@ -530,26 +927,12 @@ } ds =3D hashstr(recv=2Er, recv=2Ew-recv=2Er, ds); =20 - if(debug){ - char *tab[] =3D { - "kexalgs", "hostalgs", - "cipher1", "cipher2", - "mac1", "mac2", - "zip1", "zip2", - "lang1", "lang2", - nil, - }, **t, *s; - uchar *p =3D recv=2Er+17; - int n; - for(t=3Dtab; *t !=3D nil; t++){ - if(unpack(p, recv=2Ew-p, "s=2E", &s, &n, &p) < 0) - break; - fprint(2, "%s: %=2E*s\n", *t, utfnlen(s, n), s); - } - } - + Kexinitp spropp =3D recvkexinit(); + authagree(cpropp, spropp); + if (debug > 1) + printKexinitp("agreement", agreep); curve25519_dh_new(x, yc); - yc[31] &=3D ~0x80; + yc[31] &=3D ~0x80; /* curve25519_dh_new does & 0x80, so why reverse? */ =20 sendpkt("bs", MSG_ECDH_INIT, yc, sizeof(yc)); Next1: switch(recvpkt()){ @@ -559,13 +942,15 @@ case MSG_KEXINIT: sysfatal("inception"); case MSG_ECDH_REPLY: + + /* RFC5656; ys is being set to Q_S */ if(unpack(recv=2Er, recv=2Ew-recv=2Er, "_sss", &ks, &nks, &ys, &nys, &s= ig, &nsig) < 0) sysfatal("bad ECDH_REPLY"); break; } =20 if(nys !=3D 32) - sysfatal("bad server ECDH ephermal public key length"); + sysfatal("bad server ECDH ephemeral public key length"); =20 ds =3D hashstr(ks, nks, ds); ds =3D hashstr(yc, 32, ds); @@ -587,18 +972,29 @@ if(ok =3D=3D nil || !okThumbprint(h, sizeof(h), ok)){ if(ok !=3D nil) werrstr("unknown host"); fprint(2, "%s: %r\n", argv0); - fprint(2, "verify hostkey: %s %=2E*[\n", sshrsa, nks, ks); + fprint(2, "verify hostkey: %s %=2E*[\n", agreep->kh->host, nks, ks); fprint(2, "add thumbprint after verification:\n"); - fprint(2, "\techo 'ssh sha256=3D%s server=3D%s' >> %q\n", thumb, host,= thumbfile); + fprint(2, "\techo 'ssh sha256=3D%s server=3D%s type=3D%s' >> %q\n", th= umb, host, agreep->kh->host, thumbfile); sysfatal("checking hostkey failed: %r"); } freeThumbprints(ok); } =20 - if((pub =3D ssh2rsapub(ks, nks)) =3D=3D nil) - sysfatal("bad server public key"); - if((S =3D ssh2rsasig(sig, nsig)) =3D=3D nil) - sysfatal("bad server signature"); + // TODO: branch for ed25519 + if(!strcmp(agreep->kh->host,"ssh-dss")) { + ssh2dsapub(ks, nks); + if(pub->dsa =3D=3D nil) + sysfatal("bad server dss public key"); + if((S =3D ssh2sig(sig, nsig)) =3D=3D nil) + sysfatal("no server dss signature"); + } + else { + ssh2rsapub(ks, nks); + if(pub->rsa =3D=3D nil) + sysfatal("bad server rsa public key"); + if((S =3D ssh2sig(sig, nsig)) =3D=3D nil) + sysfatal("no server rsa signature"); + } =20 curve25519_dh_finish(x, ys, z); =20 @@ -609,10 +1005,17 @@ =20 ds =3D hashstr(k, nk, ds); sha2_256(nil, 0, h, ds); - if(!pkcs1verify(h, sizeof(h), pub, S)) - sysfatal("server verification failed"); + // TODO: branch for ssh-ed25519 + if(!strcmp(agreep->kh->host,"ssh-dss")){ + if(!dsasha1verify(h, sizeof(h), S)) + sysfatal("server dss verification failed"); + } + else { + if(!pkcs1verify(h, sizeof(h), S)) + sysfatal("server rsa verification failed"); + rsapubfree(pub->rsa); + } mpfree(S); - rsapubfree(pub); =20 sendpkt("b", MSG_NEWKEYS); Next2: switch(recvpkt()){ @@ -705,7 +1108,6 @@ char *s; mpint *S; AuthRpc *rpc; - RSApub *pub; =20 if(!authok(authmeth)) return -1; @@ -717,33 +1119,64 @@ return -1; } =20 - s =3D "proto=3Drsa service=3Dssh role=3Dclient"; + s =3D !strcmp(agreep->kh->host,"ssh-dss") ? "proto=3Ddsa service=3Dssh r= ole=3Dclient" : "proto=3Drsa service=3Dssh role=3Dclient"; if(auth_rpc(rpc, "start", s, strlen(s)) !=3D ARok){ auth_freerpc(rpc); close(afd); return -1; } =20 - pub =3D rsapuballoc(); - pub->n =3D mpnew(0); - pub->ek =3D mpnew(0); + // TODO: branch for ssh-ed25519 + if(!strcmp(agreep->kh->host,"ssh-dss")){ + pub->dsa =3D dsapuballoc(); + pub->dsa->p =3D mpnew(0); + pub->dsa->q =3D mpnew(0); + pub->dsa->alpha =3D mpnew(0); + pub->dsa->key =3D mpnew(0); + } + else { + pub->rsa =3D rsapuballoc(); + pub->rsa->n =3D mpnew(0); + pub->rsa->ek =3D mpnew(0); + } =20 while(auth_rpc(rpc, "read", nil, 0) =3D=3D ARok){ s =3D rpc->arg; - if(strtomp(s, &s, 16, pub->n) =3D=3D nil) - break; - if(*s++ !=3D ' ') - continue; - if(strtomp(s, nil, 16, pub->ek) =3D=3D nil) - continue; - npk =3D rsapub2ssh(pub, pk, sizeof(pk)); + // TODO: branch for ssh-ed25519 + if(!strcmp(agreep->kh->host,"ssh-dss")){ + // This seems brittle and dependent upon the order of values set in fa= ctotum + if(strtomp(s, &s, 16, pub->dsa->p) =3D=3D nil) + break; + if(*s++ !=3D ' ') + continue; + if(strtomp(s, nil, 16, pub->dsa->q) =3D=3D nil) + continue; + if(*s++ !=3D ' ') + continue; + if(strtomp(s, nil, 16, pub->dsa->alpha) =3D=3D nil) + continue; + if(*s++ !=3D ' ') + continue; + if(strtomp(s, nil, 16, pub->dsa->key) =3D=3D nil) + continue; + npk =3D dsapub2ssh(pk, sizeof(pk)); + } + else { + if(strtomp(s, &s, 16, pub->rsa->n) =3D=3D nil) + break; + if(*s++ !=3D ' ') + continue; + if(strtomp(s, nil, 16, pub->rsa->ek) =3D=3D nil) + continue; + npk =3D rsapub2ssh(pk, sizeof(pk)); + } =20 sendpkt("bsssbss", MSG_USERAUTH_REQUEST, user, strlen(user), service, strlen(service), authmeth, sizeof(authmeth)-1, 0, - sshrsa, sizeof(sshrsa)-1, + agreep->kh->host, sizeof(agreep->kh->host)-1, pk, npk); Next1: switch(recvpkt()){ default: @@ -766,9 +1199,16 @@ service, strlen(service), authmeth, sizeof(authmeth)-1, 1, - sshrsa, sizeof(sshrsa)-1, + agreep->kh->host, sizeof(agreep->kh->host)-1, pk, npk); - S =3D pkcs1digest(send=2Eb, n, pub); + // TODO branch for ssh-ed25519 + if (!strcmp(agreep->kh->host,"ssh-dss")){ + sha1(send=2Eb, n, sig, nil); + S =3D strtomp((char *)sig, nil, 10, nil); + } + else { + S =3D pkcs1digest(send=2Eb, n); + } n =3D snprint((char*)send=2Eb, sizeof(send=2Eb), "%B", S); mpfree(S); =20 @@ -778,7 +1218,13 @@ break; =20 S =3D strtomp(rpc->arg, nil, 16, nil); - nsig =3D rsasig2ssh(pub, S, sig, sizeof(sig)); + // TODO branch for ssh-ed25519 + if (!strcmp(agreep->kh->host,"ssh-dss")){ + nsig =3D dsasig2ssh(S, sig, sizeof(sig)); + } + else { + nsig =3D rsasig2ssh(S, sig, sizeof(sig)); + } mpfree(S); =20 /* send final userauth request with the signature */ @@ -787,7 +1233,7 @@ service, strlen(service), authmeth, sizeof(authmeth)-1, 1, - sshrsa, sizeof(sshrsa)-1, + agreep->kh->host, sizeof(agreep->kh->host)-1, pk, npk, sig, nsig); Next2: switch(recvpkt()){ @@ -801,13 +1247,25 @@ case MSG_USERAUTH_SUCCESS: break; } - rsapubfree(pub); + // TODO: branch for ssh-ed25519 + if (!strcmp(agreep->kh->host,"ssh-dss")){ + dsapubfree(pub->dsa); + } + else { + rsapubfree(pub->rsa); + } auth_freerpc(rpc); close(afd); return 0; } Failed: - rsapubfree(pub); + // TODO: branch for ssh-ed25519 + if (!strcmp(agreep->kh->host,"ssh-dss")){ + dsapubfree(pub->dsa); + } + else { + rsapubfree(pub->rsa); + } auth_freerpc(rpc); close(afd); return -1;=09 @@ -1157,7 +1615,7 @@ void usage(void) { - fprint(2, "usage: %s [-dR] [-t thumbfile] [-T tries] [-u user] [-h] [use= r@]host [-W remote!port] [cmd args=2E=2E=2E]\n", argv0); + fprint(2, "usage: %s [-d] [-R] [-r] [-t thumbfile] [-T tries] [-u user] = [-h] [user@]host [-W remote!port] [cmd args=2E=2E=2E]\n", argv0); exits("usage"); } =20 @@ -1258,7 +1716,7 @@ fprint(fd, "%s\r\n", send=2Ev); recv=2Ev =3D readline(); if(debug) - fprint(2, "server verison: %s\n", recv=2Ev); + fprint(2, "server version: %s\n", recv=2Ev); if(strncmp("SSH-2=2E0-", recv=2Ev, 8) !=3D 0) sysfatal("bad server version: %s", recv=2Ev); recv=2Ev =3D strdup(recv=2Ev);