9front - general discussion about 9front
 help / color / mirror / Atom feed
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);


             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).