From: Romano <unobe@cpan.org>
To: 9front@9front.org
Subject: [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
Date: Fri, 12 Jun 2020 20:35:44 +0000 [thread overview]
Message-ID: <8008101F-B2C7-4DB7-BFD9-5BA7E82DBC54@cpan.org> (raw)
All,
Below is a patch (also at http://okturing.com/src/8696/body ) that handles client and server algorithm negotations so that other key exchange, host, cipher, et c. algorithms and implements ssh-dss key exchange. The primary impetus for this was to use rsync.net, which right now allows only ssh-dss and ssh-ed25519 for key exchange. ed25519 is related to curve25519 preferable, and there's an implementation that I'm planning to port to 9front (djb's reference implementation[1] which apparently OpenSSH uses ). However, the first step was to make sure the algorithm negotation bits were all in order. I then noticed that dsa(2) provided the basics for implementation ssh-dss. Ssh-dss was deprecated by OpenSSH too hastily[2], so I've implemented it so that I can use rsync.net. Next, I hope to use ssh-ed25519.
This is my first real foray into C besides the little patch I sent in April. I hope the it's not too terrible and useful for others.
[1] https://ed25519.cr.yp.to/software.html
[2] https://security.stackexchange.com/questions/112802
diff -r 307fbb6fd10a sys/src/cmd/ssh.c
--- a/sys/src/cmd/ssh.c Fri Jun 12 01:36:50 2020 +0200
+++ b/sys/src/cmd/ssh.c 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 separate files, e.g.:
+ ssh/ssh.h <-- structs, enums, et c.
+ ssh/debug.c <-- debugging statement stuff, e.g. printKexinitp()
+ ssh/alloc.c <-- kexinitalloc() et c.
+ ssh/kex.c <-- key exchange and host server algorithm stuff
+ ssh/algs.c <-- cipher, mac, zip, and lang stuff
+ ssh/ssh.c <-- main loop
+*/
+
#include <u.h>
#include <libc.h>
#include <mp.h>
@@ -5,6 +16,7 @@
#include <auth.h>
#include <authsrv.h>
+/* Cf. RFC2450 & RFC8268 */
enum {
MSG_DISCONNECT = 1,
MSG_IGNORE,
@@ -47,7 +59,7 @@
enum {
- Overhead = 256, // enougth for MSG_CHANNEL_DATA header
+ Overhead = 256, // enough for MSG_CHANNEL_DATA header
MaxPacket = 1<<15,
WinPackets = 8, // (1<<15) * 8 = 256K
};
@@ -76,6 +88,280 @@
Rendez;
} Oneway;
+/* 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 = mallocz(sizeof(*nameseq), 1);
+ if(nameseq == nil)
+ sysfatal("nameseqalloc");
+ return nameseq;
+}
+
+void
+nameseqfree(Nameseq *nameseq)
+{
+ if(nameseq == nil)
+ return;
+ nameseqfree(nameseq->next);
+ free(nameseq);
+}
+
+Nameseq *
+getnameseq(char *c)
+{
+ Nameseq *orderp = nameseqalloc();
+ Nameseq *pp = orderp;
+ char *str = strdup(c);
+ while (str = strtok(str, ",")) {
+ pp->val = str;
+ pp->next = nameseqalloc();
+ pp = pp->next;
+ str = nil;
+ }
+ return orderp;
+}
+
+char *
+nameseqagree(char *c, char *s)
+{
+ Nameseq *cp, *sp, *startsp;
+ char *val = nil;
+
+ if (strlen(c) == 0 && strlen(s) == 0 )
+ return "";
+
+ cp = getnameseq(c);
+ startsp = sp = getnameseq(s);
+ while (cp) {
+ if (cp->val && sp->val && !strcmp(cp->val, sp->val)) {
+ val = strdup(cp->val);
+ break;
+ }
+ if(!(sp = sp->next)) {
+ if(!(cp = cp->next))
+ break;
+ sp = 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 = mallocz(sizeof(data), 1);
+ if(data == nil)
+ sysfatal("algsalloc");
+ return data;
+}
+
+Kexhostp
+kexhostalloc(void)
+{
+ Kexhostp data;
+
+ data = mallocz(sizeof(data), 1);
+ if(data == nil)
+ sysfatal("kexhostalloc");
+ return data;
+}
+
+Kexotherp
+kexotheralloc(void)
+{
+ Kexotherp data;
+
+ data = mallocz(sizeof(data), 1);
+ if(data == 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 = mallocz(sizeof(data), 1);
+ if(data == nil)
+ sysfatal("kexinitalloc");
+ data->ko = kexotheralloc();
+ data->kh = kexhostalloc();
+ data->ctos = algsalloc();
+ data->stoc = algsalloc();
+ return data;
+}
+
+void
+defKexotherp(Kexotherp kop) {
+ kop->msg = MSG_KEXINIT;
+ kop->firstkexpkt = 0;
+ kop->reserved = 0L;
+ genrandom(kop->cookie, sizeof(kop->cookie));
+}
+
+void
+defKexhostp(Kexhostp khp) {
+ khp->kex = "curve25519-sha256,curve25519-sha256@libssh.org";
+
+ /*
+ ssh-dss is required per RFC4253. OpenSSH has deprecated its use, but
+ does there's no real basis to support that decision for larger modulo.
+ Support it as a secondary algorithm, most likely until ssh-ed25519 is
+ supported.
+ */
+ khp->host = "ssh-rsa,ssh-dss";
+}
+
+void
+defAlgsp(Algsp algsp) {
+ /*
+ 'At some future time, it is expected that another algorithm, one with better
+ strength, will become so prevalent and ubiquitous that the use of
+ "3des-cbc" will be deprecated by another STANDARDS ACTION.' - RFC4253
+ No standards action has yet deprecated it, but have not seen it supported
+ by default in any server.
+ */
+ algsp->cipher = "chacha20-poly1305@openssh.com";
+
+ /*
+ hmac-sha1 is a required algorithm by RFC4253, and some servers still check
+ for it; it's effectively ignored since chacha20-poly1305 cipher is AEAD.
+ No RFC has deprecated it, only provided other algorithms to be placed
+ before it (e.g., SHA-2).
+ */
+ algsp->mac = "hmac-sha1";
+
+ algsp->zip = "none";
+ algsp->lang = "";
+}
+
+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 = kexhostalloc();
+ c->kex = nameseqagree(a->kex, b->kex);
+ if(!c->kex)
+ sysfatal("Cannot agree on key exchange algorithm");
+
+ c->host = 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 = algsalloc();
+ c->cipher = nameseqagree(a->cipher, b->cipher);
+ if(!c->cipher)
+ sysfatal(smprint("Cannot agree on cipher algorithm for %s", cxt));
+
+ c->mac = nameseqagree(a->mac, b->mac);
+ if(!c->mac)
+ sysfatal(smprint("Cannot agree on mac algorithm for %s", cxt));
+
+ c->zip = nameseqagree(a->zip, b->zip);
+ if(!c->zip)
+ sysfatal(smprint("Cannot agree on zip algorithm for %s", cxt));
+
+ c->lang = 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 = kexinitalloc();
+ agreep->kh = khagree(client->kh, server->kh);
+ agreep->ctos = algsagree(client->ctos, server->ctos, "ctos");
+ agreep->stoc = 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;
Oneway recv, send;
+
void dispatch(void);
void
@@ -163,6 +450,16 @@
memmove(p, s, u);
p += u;
break;
+ /* Why does 's' exist with passing the string length, when it can just be calculated with strlen()? */
+ case 'S':
+ s = va_arg(a, char*);
+ u = strlen(s);
+ if(p+4 > e) goto err;
+ PUT4(p, u), p += 4;
+ if(u > e-p) goto err;
+ memmove(p, s, u);
+ p += u;
+ break;
case 'u':
u = va_arg(a, int);
if(p+4 > e) goto err;
@@ -211,6 +508,14 @@
*va_arg(a, int*) = u;
p += 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 = GET4(p), p += 4;
+ if(u > e-p) goto err;
+ *va_arg(a, char**)= (char*)p;
+ p += u;
+ break;
case '[':
s = va_arg(a, void*);
u = va_arg(a, int);
@@ -280,7 +585,7 @@
send.r = send.b;
send.w = send.b+n;
-if(debug > 1)
+if(debug > 2)
fprint(2, "sendpkt: (%d) %.*H\n", send.r[0], (int)(send.w-send.r), send.r);
if(nsid){
@@ -309,6 +614,26 @@
send.seq++;
}
+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.w = recv.r + n;
recv.seq++;
-if(debug > 1)
+if(debug > 2)
fprint(2, "recvpkt: (%d) %.*H\n", recv.r[0], (int)(recv.w-recv.r), recv.r);
return recv.r[0];
}
-static char sshrsa[] = "ssh-rsa";
+Kexinitp
+recvkexinit(void) {
+
+ Kexinitp spropp = kexinitalloc();
+ if(unpack(recv.r, recv.w-recv.r, "b[SSSSSSSSSSbu", &(spropp->ko->msg), spropp->ko->cookie, sizeof(spropp->ko->cookie), &(spropp->kh->kex), &(spropp->kh->host), &(spropp->ctos->cipher), &(spropp->stoc->cipher), &(spropp->ctos->mac), &(spropp->stoc->mac), &(spropp->ctos->zip), &(spropp->stoc->zip), &(spropp->ctos->lang), &(spropp->stoc->lang), &(spropp->ko->firstkexpkt), &(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;
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);
}
-RSApub*
-ssh2rsapub(uchar *data, int len)
+void
+ssh2dsapub(uchar *data, int len)
{
- RSApub *pub;
char *s;
int n;
- pub = rsapuballoc();
- pub->n = mpnew(0);
- pub->ek = mpnew(0);
- if(unpack(data, len, "smm", &s, &n, pub->ek, pub->n) < 0
- || n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
- rsapubfree(pub);
- return nil;
+ pub->dsa = dsapuballoc();
+ pub->dsa->p = mpnew(0);
+ pub->dsa->q = mpnew(0);
+ pub->dsa->alpha = mpnew(0);
+ pub->dsa->key = mpnew(0);
+ // RFC4253 Section 6.6: 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 != sizeof("ssh-dss")-1 || memcmp(s, "ssh-dss", n) != 0){
+ dsapubfree(pub->dsa);
}
- return pub;
}
int
-rsasig2ssh(RSApub *pub, mpint *S, uchar *data, int len)
+dsasig2ssh(mpint *S, uchar *data, int len)
{
- int l = (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 = dsagen(pub->dsa);
+ DSAsig *dsasig = 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 = betomp(digest, SHA1dlen, nil);
+ ret = V != nil;
+ if(ret){
+ DSAsig *dsasig = dsasigalloc();
+ dsasig->r = mpnew(0);
+ dsasig->s = 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 = !dsaverify(pub->dsa, dsasig, V);
+ dsasigfree(dsasig);
+ mpfree(V);
+ }
+ return ret;
}
mpint*
-ssh2rsasig(uchar *data, int len)
+ssh2sig(uchar *data, int len)
{
mpint *m;
char *s;
@@ -417,32 +791,65 @@
m = mpnew(0);
if(unpack(data, len, "sm", &s, &n, m) < 0
- || n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
+ || n != sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != 0){
mpfree(m);
return nil;
}
return m;
}
+static char sshrsa[] = "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 = rsapuballoc();
+ pub->rsa->n = mpnew(0);
+ pub->rsa->ek = mpnew(0);
+ if(unpack(data, len, "smm", &s, &n, pub->rsa->ek, pub->rsa->n) < 0
+ || n != sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != 0){
+ rsapubfree(pub->rsa);
+ }
+}
+
+int
+rsasig2ssh(mpint *S, uchar *data, int len)
+{
+ int l = (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];
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);
}
int
-pkcs1verify(uchar *data, int len, RSApub *pub, mpint *S)
+pkcs1verify(uchar *data, int len, mpint *S)
{
mpint *V;
int ret;
- V = pkcs1digest(data, len, pub);
+ V = pkcs1digest(data, len);
ret = V != nil;
if(ret){
- rsaencrypt(pub, S, S);
+ rsaencrypt(pub->rsa, S, S);
ret = mpcmp(V, S) == 0;
mpfree(V);
}
@@ -486,37 +893,27 @@
void
kex(int gotkexinit)
{
- static char kexalgs[] = "curve25519-sha256,curve25519-sha256@libssh.org";
- static char cipheralgs[] = "chacha20-poly1305@openssh.com";
- static char zipalgs[] = "none";
- static char macalgs[] = "hmac-sha1"; /* work around for github.com */
- static char langs[] = "";
+ Kexinitp cpropp = kexinitalloc();
+ defKexotherp(cpropp->ko);
+ defKexhostp(cpropp->kh);
+ defAlgsp(cpropp->ctos);
+ defAlgsp(cpropp->stoc);
- 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==nil)
+ pub = mallocz(sizeof(pub), 1);
ds = hashstr(send.v, strlen(send.v), nil);
ds = hashstr(recv.v, strlen(recv.v), ds);
- 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 = hashstr(send.r, send.w-send.r, ds);
if(!gotkexinit){
@@ -530,26 +927,12 @@
}
ds = hashstr(recv.r, recv.w-recv.r, ds);
- if(debug){
- char *tab[] = {
- "kexalgs", "hostalgs",
- "cipher1", "cipher2",
- "mac1", "mac2",
- "zip1", "zip2",
- "lang1", "lang2",
- nil,
- }, **t, *s;
- uchar *p = recv.r+17;
- int n;
- for(t=tab; *t != nil; t++){
- if(unpack(p, recv.w-p, "s.", &s, &n, &p) < 0)
- break;
- fprint(2, "%s: %.*s\n", *t, utfnlen(s, n), s);
- }
- }
-
+ Kexinitp spropp = recvkexinit();
+ authagree(cpropp, spropp);
+ if (debug > 1)
+ printKexinitp("agreement", agreep);
curve25519_dh_new(x, yc);
- yc[31] &= ~0x80;
+ yc[31] &= ~0x80; /* curve25519_dh_new does & 0x80, so why reverse? */
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.r, recv.w-recv.r, "_sss", &ks, &nks, &ys, &nys, &sig, &nsig) < 0)
sysfatal("bad ECDH_REPLY");
break;
}
if(nys != 32)
- sysfatal("bad server ECDH ephermal public key length");
+ sysfatal("bad server ECDH ephemeral public key length");
ds = hashstr(ks, nks, ds);
ds = hashstr(yc, 32, ds);
@@ -587,18 +972,29 @@
if(ok == nil || !okThumbprint(h, sizeof(h), ok)){
if(ok != nil) werrstr("unknown host");
fprint(2, "%s: %r\n", argv0);
- fprint(2, "verify hostkey: %s %.*[\n", sshrsa, nks, ks);
+ fprint(2, "verify hostkey: %s %.*[\n", agreep->kh->host, nks, ks);
fprint(2, "add thumbprint after verification:\n");
- fprint(2, "\techo 'ssh sha256=%s server=%s' >> %q\n", thumb, host, thumbfile);
+ fprint(2, "\techo 'ssh sha256=%s server=%s type=%s' >> %q\n", thumb, host, agreep->kh->host, thumbfile);
sysfatal("checking hostkey failed: %r");
}
freeThumbprints(ok);
}
- if((pub = ssh2rsapub(ks, nks)) == nil)
- sysfatal("bad server public key");
- if((S = ssh2rsasig(sig, nsig)) == nil)
- sysfatal("bad server signature");
+ // TODO: branch for ed25519
+ if(!strcmp(agreep->kh->host,"ssh-dss")) {
+ ssh2dsapub(ks, nks);
+ if(pub->dsa == nil)
+ sysfatal("bad server dss public key");
+ if((S = ssh2sig(sig, nsig)) == nil)
+ sysfatal("no server dss signature");
+ }
+ else {
+ ssh2rsapub(ks, nks);
+ if(pub->rsa == nil)
+ sysfatal("bad server rsa public key");
+ if((S = ssh2sig(sig, nsig)) == nil)
+ sysfatal("no server rsa signature");
+ }
curve25519_dh_finish(x, ys, z);
@@ -609,10 +1005,17 @@
ds = 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);
sendpkt("b", MSG_NEWKEYS);
Next2: switch(recvpkt()){
@@ -705,7 +1108,6 @@
char *s;
mpint *S;
AuthRpc *rpc;
- RSApub *pub;
if(!authok(authmeth))
return -1;
@@ -717,33 +1119,64 @@
return -1;
}
- s = "proto=rsa service=ssh role=client";
+ s = !strcmp(agreep->kh->host,"ssh-dss") ? "proto=dsa service=ssh role=client" : "proto=rsa service=ssh role=client";
if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){
auth_freerpc(rpc);
close(afd);
return -1;
}
- pub = rsapuballoc();
- pub->n = mpnew(0);
- pub->ek = mpnew(0);
+ // TODO: branch for ssh-ed25519
+ if(!strcmp(agreep->kh->host,"ssh-dss")){
+ pub->dsa = dsapuballoc();
+ pub->dsa->p = mpnew(0);
+ pub->dsa->q = mpnew(0);
+ pub->dsa->alpha = mpnew(0);
+ pub->dsa->key = mpnew(0);
+ }
+ else {
+ pub->rsa = rsapuballoc();
+ pub->rsa->n = mpnew(0);
+ pub->rsa->ek = mpnew(0);
+ }
while(auth_rpc(rpc, "read", nil, 0) == ARok){
s = rpc->arg;
- if(strtomp(s, &s, 16, pub->n) == nil)
- break;
- if(*s++ != ' ')
- continue;
- if(strtomp(s, nil, 16, pub->ek) == nil)
- continue;
- npk = 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 factotum
+ if(strtomp(s, &s, 16, pub->dsa->p) == nil)
+ break;
+ if(*s++ != ' ')
+ continue;
+ if(strtomp(s, nil, 16, pub->dsa->q) == nil)
+ continue;
+ if(*s++ != ' ')
+ continue;
+ if(strtomp(s, nil, 16, pub->dsa->alpha) == nil)
+ continue;
+ if(*s++ != ' ')
+ continue;
+ if(strtomp(s, nil, 16, pub->dsa->key) == nil)
+ continue;
+ npk = dsapub2ssh(pk, sizeof(pk));
+ }
+ else {
+ if(strtomp(s, &s, 16, pub->rsa->n) == nil)
+ break;
+ if(*s++ != ' ')
+ continue;
+ if(strtomp(s, nil, 16, pub->rsa->ek) == nil)
+ continue;
+ npk = rsapub2ssh(pk, sizeof(pk));
+ }
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 = pkcs1digest(send.b, n, pub);
+ // TODO branch for ssh-ed25519
+ if (!strcmp(agreep->kh->host,"ssh-dss")){
+ sha1(send.b, n, sig, nil);
+ S = strtomp((char *)sig, nil, 10, nil);
+ }
+ else {
+ S = pkcs1digest(send.b, n);
+ }
n = snprint((char*)send.b, sizeof(send.b), "%B", S);
mpfree(S);
@@ -778,7 +1218,13 @@
break;
S = strtomp(rpc->arg, nil, 16, nil);
- nsig = rsasig2ssh(pub, S, sig, sizeof(sig));
+ // TODO branch for ssh-ed25519
+ if (!strcmp(agreep->kh->host,"ssh-dss")){
+ nsig = dsasig2ssh(S, sig, sizeof(sig));
+ }
+ else {
+ nsig = rsasig2ssh(S, sig, sizeof(sig));
+ }
mpfree(S);
/* 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;
@@ -1157,7 +1615,7 @@
void
usage(void)
{
- fprint(2, "usage: %s [-dR] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
+ fprint(2, "usage: %s [-d] [-R] [-r] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
exits("usage");
}
@@ -1258,7 +1716,7 @@
fprint(fd, "%s\r\n", send.v);
recv.v = readline();
if(debug)
- fprint(2, "server verison: %s\n", recv.v);
+ fprint(2, "server version: %s\n", recv.v);
if(strncmp("SSH-2.0-", recv.v, 8) != 0)
sysfatal("bad server version: %s", recv.v);
recv.v = strdup(recv.v);
next reply other threads:[~2020-06-12 20:35 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-06-12 20:35 Romano [this message]
2020-06-12 22:58 ` [9front] " cinap_lenrek
2020-06-13 0:18 ` Romano
2020-06-13 8:38 ` hiro
2020-06-13 16:32 ` Romano
2020-06-13 16:39 ` ori
2020-06-13 16:46 ` Romano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8008101F-B2C7-4DB7-BFD9-5BA7E82DBC54@cpan.org \
--to=unobe@cpan.org \
--cc=9front@9front.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).