shithub: riscv

Download patch

ref: 346f5828e0e435d76ef7da8316e77a426c826d19
parent: 2d1fbbdafa37080ddaacb76ac1e4f5a413ef2dc3
author: cinap_lenrek <[email protected]>
date: Sun Apr 23 15:00:08 EDT 2017

libsec: sha256 support for thumbprint files, use it in ssh as well

initThumbprints() now takes an application tag argument
so x509 and ssh can coexist.

the thumbprint entries can now hold both sha1 and sha256
hashes. okThumbprint() now takes a len argument for the
hash length used.

the new function okCertificate() hashes the certificate
with both and checks for any matches.

on failure, okCertificate() returns 0 and sets error string.

we also check for include loops now in thumbfiles, limiting
the number of includes to 8.

--- a/sys/include/ape/libsec.h
+++ b/sys/include/ape/libsec.h
@@ -461,7 +461,8 @@
  */
 typedef struct Thumbprint{
 	struct Thumbprint *next;
-	uchar	sha1[SHA1dlen];
+	uchar	hash[SHA2_256dlen];
+	uchar	len;
 } Thumbprint;
 
 typedef struct TLSconn{
@@ -487,9 +488,10 @@
 int tlsServer(int fd, TLSconn *c);
 
 /* thumb.c */
-Thumbprint* initThumbprints(char *ok, char *crl);
+Thumbprint* initThumbprints(char *ok, char *crl, char *tag);
 void	freeThumbprints(Thumbprint *ok);
-int	okThumbprint(uchar *sha1, Thumbprint *ok);
+int	okThumbprint(uchar *hash, int len, Thumbprint *ok);
+int	okCertificate(uchar *cert, int len, Thumbprint *ok);
 
 /* readcert.c */
 uchar	*readcert(char *filename, int *pcertlen);
--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -453,7 +453,8 @@
  */
 typedef struct Thumbprint{
 	struct Thumbprint *next;
-	uchar	sha1[SHA1dlen];
+	uchar	hash[SHA2_256dlen];
+	uchar	len;
 } Thumbprint;
 
 typedef struct TLSconn{
@@ -479,9 +480,10 @@
 int tlsServer(int fd, TLSconn *c);
 
 /* thumb.c */
-Thumbprint* initThumbprints(char *ok, char *crl);
+Thumbprint* initThumbprints(char *ok, char *crl, char *tag);
 void	freeThumbprints(Thumbprint *ok);
-int	okThumbprint(uchar *sha1, Thumbprint *ok);
+int	okThumbprint(uchar *hash, int len, Thumbprint *ok);
+int	okCertificate(uchar *cert, int len, Thumbprint *ok);
 
 /* readcert.c */
 uchar	*readcert(char *filename, int *pcertlen);
--- a/sys/man/2/pushtls
+++ b/sys/man/2/pushtls
@@ -1,6 +1,6 @@
 .TH PUSHTLS 2
 .SH NAME
-pushtls, tlsClient, tlsServer, initThumbprints, freeThumbprints, okThumbprint, readcert, readcertchain \- attach TLS1 or SSL3 encryption to a communication channel
+pushtls, tlsClient, tlsServer, initThumbprints, freeThumbprints, okThumbprint, okCertificate, readcert, readcertchain \- attach TLS1 or SSL3 encryption to a communication channel
 .SH SYNOPSIS
 .B #include <u.h>
 .br
@@ -29,13 +29,16 @@
 PEMchain *readcertchain(char *filename)
 .PP
 .B
-Thumbprint *initThumbprints(char *ok, char *crl)
+Thumbprint *initThumbprints(char *ok, char *crl, char *tag)
 .PP
 .B
 void	freeThumbprints(Thumbprint *table)
 .PP
 .B
-int	okThumbprint(uchar *hash, Thumbprint *table)
+int	okThumbprint(uchar *hash, int len, Thumbprint *table)
+.PP
+.B
+int	okCertificate(uchar *cert, int len, Thumbprint *table)
 .SH DESCRIPTION
 Transport Layer Security (TLS) comprises a record layer protocol,
 doing message digesting and encrypting in the kernel,
@@ -213,13 +216,10 @@
 Start the client half of TLS and check the remote certificate:
 .IP
 .EX
-uchar hash[SHA1dlen];
-
 conn = (TLSconn*)mallocz(sizeof *conn, 1);
 fd = tlsClient(fd, conn);
-sha1(conn->cert, conn->certlen, hash, nil);
-if(!okThumbprint(hash,table))
-	exits("suspect server");
+if(!okCertificate(conn->cert, conn->certlen, table))
+	sysfatal("suspect server: %r");
 \fI...application begins...\fP
 .EE
 .PP
--- a/sys/man/6/thumbprint
+++ b/sys/man/6/thumbprint
@@ -8,6 +8,8 @@
 .B tlsClient
 and
 .B okThumbprint
+or
+.B okCertificate
 (see
 .IR pushtls (2)),
 check the remote side's public key by comparing against
@@ -25,13 +27,21 @@
 .IB attr = value
 or
 .IR attr .
-The first attribute must be
+The first attribute must be the application tag:
 .B x509
-and the second must be
-.BI sha1= {hex checksum of binary certificate}.
+for tls applications or
+.B ssh
+for ssh server fingerprints.
+The second attribute must be a hash type of
+.B sha1=
+or
+.BI sha256=
+followed by the hex or base64 encoded hash of binary certificate
+or public key.
 All other attributes are treated as comments.
 The file may also contain lines of the form
-.BI #include file
+.B #include
+.I file
 .PP
 For example, a web server might have thumbprint
 .EX
--- a/sys/src/ape/lib/sec/mkfile
+++ b/sys/src/ape/lib/sec/mkfile
@@ -12,6 +12,7 @@
 	}
 
 clean:V:
+	rm -f [$OS].* *.[$OS]
 	for(i in $DIRS)@{
 		echo $i
 		cd $i
@@ -34,5 +35,6 @@
 
 APE=/sys/src/ape
 <$APE/config
+
 $O.tlsclient: tlsclient.c
 	$CC -o $target $CFLAGS -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_NET_EXTENSION tlsclient.c
--- a/sys/src/ape/lib/sec/tlsclient.c
+++ b/sys/src/ape/lib/sec/tlsclient.c
@@ -107,7 +107,7 @@
 		sysfatal("specifying -x without -t is useless");
 
 	if(file){
-		thumb = initThumbprints(file, filex);
+		thumb = initThumbprints(file, filex, "x509");
 		if(thumb == nil)
 			sysfatal("initThumbprints: %r");
 	} else
@@ -144,13 +144,8 @@
 		sysfatal("tlsclient: %r");
 
 	if(thumb){
-		uchar digest[20];
-
-		if(conn->cert==nil || conn->certlen<=0)
-			sysfatal("server did not provide TLS certificate");
-		sha1(conn->cert, conn->certlen, digest, nil);
-		if(!okThumbprint(digest, thumb))
-			sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
+		if(!okCertificate(conn->cert, conn->certlen, thumb))
+			sysfatal("cert for %s not recognized: %r", servername ? servername : addr);
 		freeThumbprints(thumb);
 	}
 
--- a/sys/src/cmd/ssh.c
+++ b/sys/src/cmd/ssh.c
@@ -4,7 +4,6 @@
 #include <libsec.h>
 #include <auth.h>
 #include <authsrv.h>
-#include <bio.h>
 
 enum {
 	MSG_DISCONNECT = 1,
@@ -84,7 +83,6 @@
 
 Oneway recv, send;
 void dispatch(void);
-int checkthumb(char*, uchar*);
 
 void
 shutdown(void)
@@ -580,22 +578,25 @@
 	ds = hashstr(ys, 32, ds);
 
 	if(thumb[0] == 0){
+		Thumbprint *ok;
+
 		sha2_256(ks, nks, h, nil);
-		i = snprint(thumb, sizeof(thumb), "%.*[", sizeof(h), h);
+		i = enc64(thumb, sizeof(thumb), h, sizeof(h));
 		while(i > 0 && thumb[i-1] == '=')
-			thumb[--i] = '\0';
+			i--;
+		thumb[i] = '\0';
+
 		if(debug)
 			fprint(2, "host fingerprint: %s\n", thumb);
-		switch(checkthumb(thumbfile, h)){
-		case 0:
-			werrstr("host unknown");
-		default:
+
+		ok = initThumbprints(thumbfile, nil, "ssh");
+		if(ok == nil || !okThumbprint(h, sizeof(h), ok)){
+			if(ok != nil) werrstr("unknown host");
 			fprint(2, "%s: %r, to add after verification:\n", argv0);
-			fprint(2, "\techo 'ssh sha256=%s # %s' >> %q\n", thumb, host, thumbfile);
+			fprint(2, "\techo 'ssh sha256=%s server=%s' >> %q\n", thumb, host, thumbfile);
 			sysfatal("checking hostkey failed: %r");
-		case 1:
-			break;
 		}
+		freeThumbprints(ok);
 	}
 
 	if((pub = ssh2rsapub(ks, nks)) == nil)
@@ -1074,39 +1075,6 @@
 	}
 }
 
-int
-checkthumb(char *file, uchar hash[SHA2_256dlen])
-{
-	uchar sum[SHA2_256dlen];
-	char *line, *field[50];
-	Biobuf *bin;
-
-	if((bin = Bopen(file, OREAD)) == nil)
-		return -1;
-	for(; (line = Brdstr(bin, '\n', 1)) != nil; free(line)){
-		if(tokenize(line, field, nelem(field)) < 2)
-			continue;
-		if(strcmp(field[0], "ssh") != 0 || strncmp(field[1], "sha256=", 7) != 0)
-			continue;
-		field[1] += 7;
-		if(dec64(sum, SHA2_256dlen, field[1], strlen(field[1])) != SHA2_256dlen){
-			werrstr("malformed ssh entry in %s: %s", file, field[1]);
-			goto err;
-		}
-		if(memcmp(sum, hash, SHA2_256dlen) == 0){
-			free(line);
-			Bterm(bin);
-			return 1;
-		}
-	}
-	Bterm(bin);
-	return 0;
-err:
-	free(line);
-	Bterm(bin);
-	return -1;
-}
-
 void
 usage(void)
 {
@@ -1124,7 +1092,6 @@
 	quotefmtinstall();
 	fmtinstall('B', mpfmt);
 	fmtinstall('H', encodefmt);
-	fmtinstall('[', encodefmt);
 
 	s = getenv("TERM");
 	raw = s != nil && strcmp(s, "dumb") != 0;
--- a/sys/src/cmd/tlsclient.c
+++ b/sys/src/cmd/tlsclient.c
@@ -87,7 +87,7 @@
 		sysfatal("specifying -x without -t is useless");
 
 	if(file){
-		thumb = initThumbprints(file, filex);
+		thumb = initThumbprints(file, filex, "x509");
 		if(thumb == nil)
 			sysfatal("initThumbprints: %r");
 	} else
@@ -123,13 +123,8 @@
 		sysfatal("tlsclient: %r");
 
 	if(thumb){
-		uchar digest[20];
-
-		if(conn->cert==nil || conn->certlen<=0)
-			sysfatal("server did not provide TLS certificate");
-		sha1(conn->cert, conn->certlen, digest, nil);
-		if(!okThumbprint(digest, thumb))
-			sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
+		if(!okCertificate(conn->cert, conn->certlen, thumb))
+			sysfatal("cert for %s not recognized: %r", servername ? servername : addr);
 		freeThumbprints(thumb);
 	}
 
--- a/sys/src/cmd/upas/fs/tls.c
+++ b/sys/src/cmd/upas/fs/tls.c
@@ -6,7 +6,6 @@
 int
 wraptls(int ofd, char *host)
 {
-	uchar digest[SHA1dlen];
 	Thumbprint *thumb;
 	TLSconn conn;
 	int fd;
@@ -18,16 +17,10 @@
 		close(ofd);
 		return -1;
 	}
-	thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
+	thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude", "x509");
 	if(thumb != nil){
-		if(conn.cert == nil || conn.certlen <= 0){
-			werrstr("server did not provide TLS certificate");
-			goto Err;
-		}
-		sha1(conn.cert, conn.certlen, digest, nil);
-		if(!okThumbprint(digest, thumb)){
-			werrstr("server certificate %.*H not recognized",
-				SHA1dlen, digest);
+		if(!okCertificate(conn.cert, conn.certlen, thumb)){
+			werrstr("cert for %s not recognized: %r", host);
 		Err:
 			close(fd);
 			fd = -1;
--- a/sys/src/cmd/upas/smtp/smtp.c
+++ b/sys/src/cmd/upas/smtp/smtp.c
@@ -388,9 +388,8 @@
 {
 	TLSconn *c;
 	Thumbprint *goodcerts;
-	char *h, *err;
+	char *err;
 	int fd;
-	uchar hash[SHA1dlen];
 
 	goodcerts = nil;
 	err = Giveup;
@@ -412,29 +411,19 @@
 	Bterm(&bin);
 	Binit(&bin, fd, OREAD);
 
-	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
+	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs, "x509");
 	if (goodcerts == nil) {
 		syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
 		goto Out;
 	}
-	/* compute sha1 hash of remote's certificate, see if we know it */
-	sha1(c->cert, c->certlen, hash, nil);
-	if (!okThumbprint(hash, goodcerts)) {
-		/* TODO? if not excluded, add hash to thumb list */
-		h = malloc(2*sizeof hash + 1);
-		if (h == nil)
-			goto Out;
-		enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
-		syslog(0, "smtp", "remote cert. has bad thumbprint: x509 sha1=%s server=%q",
-			h, ddomain);
-		free(h);
+	if (!okCertificate(c->cert, c->certlen, goodcerts)) {
+		syslog(0, "smtp", "cert for %s not recognized: %r", ddomain);
 		goto Out;
 	}
 	syslog(0, "smtp", "started TLS to %q", ddomain);
 	err = nil;
 Out:
-	if(goodcerts != nil)
-		freeThumbprints(goodcerts);
+	freeThumbprints(goodcerts);
 	free(c->cert);
 	free(c->sessionID);
 	free(c);
--- a/sys/src/libsec/port/thumb.c
+++ b/sys/src/libsec/port/thumb.c
@@ -5,9 +5,9 @@
 enum{ ThumbTab = 1<<10 };
 
 static Thumbprint*
-tablehead(uchar *sum, Thumbprint *table)
+tablehead(uchar *hash, Thumbprint *table)
 {
-	return &table[((sum[0]<<8) + sum[1]) & (ThumbTab-1)];
+	return &table[((hash[0]<<8) + hash[1]) & (ThumbTab-1)];
 }
 
 void
@@ -27,15 +27,15 @@
 }
 
 int
-okThumbprint(uchar *sum, Thumbprint *table)
+okThumbprint(uchar *hash, int len, Thumbprint *table)
 {
 	Thumbprint *hd, *p;
 
 	if(table == nil)
 		return 0;
-	hd = tablehead(sum, table);
+	hd = tablehead(hash, table);
 	for(p = hd->next; p; p = p->next){
-		if(memcmp(sum, p->sha1, SHA1dlen) == 0)
+		if(p->len == len && memcmp(hash, p->hash, len) == 0)
 			return 1;
 		if(p == hd)
 			break;
@@ -43,14 +43,51 @@
 	return 0;
 }
 
+int
+okCertificate(uchar *cert, int len, Thumbprint *table)
+{
+	uchar hash[SHA2_256dlen];
+	char thumb[2*SHA2_256dlen+1];
+
+	if(table == nil){
+		werrstr("no thumbprints provided");
+		return 0;
+	}
+	if(cert == nil || len <= 0){
+		werrstr("no certificate provided");
+		return 0;
+	}
+
+	sha1(cert, len, hash, nil);
+	if(okThumbprint(hash, SHA1dlen, table))
+		return 1;
+
+	sha2_256(cert, len, hash, nil);
+	if(okThumbprint(hash, SHA2_256dlen, table))
+		return 1;
+
+	len = enc64(thumb, sizeof(thumb), hash, SHA2_256dlen);
+	while(len > 0 && thumb[len-1] == '=')
+		len--;
+	thumb[len] = '\0';
+	werrstr("sha256=%s", thumb);
+
+	return 0;
+}
+
 static int
-loadThumbprints(char *file, Thumbprint *table, Thumbprint *crltab)
+loadThumbprints(char *file, char *tag, Thumbprint *table, Thumbprint *crltab, int depth)
 {
 	Thumbprint *hd, *entry;
 	char *line, *field[50];
-	uchar sum[SHA1dlen];
+	uchar hash[SHA2_256dlen];
 	Biobuf *bin;
+	int len, n;
 
+	if(depth > 8){
+		werrstr("too many includes, last file %s", file);
+		return -1;
+	}
 	if(access(file, AEXIST) < 0)
 		return 0;	/* not an error */
 	if((bin = Bopen(file, OREAD)) == nil)
@@ -59,20 +96,30 @@
 		if(tokenize(line, field, nelem(field)) < 2)
 			continue;
 		if(strcmp(field[0], "#include") == 0){
-			if(loadThumbprints(field[1], table, crltab) < 0)
+			if(loadThumbprints(field[1], tag, table, crltab, depth+1) < 0)
 				goto err;
 			continue;
 		}
-		if(strcmp(field[0], "x509") != 0 || strncmp(field[1], "sha1=", 5) != 0)
+		if(strcmp(field[0], tag) != 0)
 			continue;
-		field[1] += 5;
-		if(dec16(sum, SHA1dlen, field[1], strlen(field[1])) != SHA1dlen){
-			werrstr("malformed x509 entry in %s: %s", file, field[1]);
+		if(strncmp(field[1], "sha1=", 5) == 0){
+			field[1] += 5;
+			len = SHA1dlen;
+		} else if(strncmp(field[1], "sha256=", 7) == 0){
+			field[1] += 7;
+			len = SHA2_256dlen;
+		} else {
+			continue;
+		}
+		n = strlen(field[1]);
+		if((n != len*2 || dec16(hash, len, field[1], n) != len)
+		&& dec64(hash, len, field[1], n) != len){
+			werrstr("malformed %s entry in %s: %s", tag, file, field[1]);
 			goto err;
 		}
-		if(crltab && okThumbprint(sum, crltab))
+		if(crltab && okThumbprint(hash, len, crltab))
 			continue;
-		hd = tablehead(sum, table);
+		hd = tablehead(hash, table);
 		if(hd->next == nil)
 			entry = hd;
 		else {
@@ -81,7 +128,8 @@
 			entry->next = hd->next;
 		}
 		hd->next = entry;
-		memcpy(entry->sha1, sum, SHA1dlen);
+		entry->len = len;
+		memcpy(entry->hash, hash, len);
 	}
 	Bterm(bin);
 	return 0;
@@ -92,7 +140,7 @@
 }
 
 Thumbprint *
-initThumbprints(char *ok, char *crl)
+initThumbprints(char *ok, char *crl, char *tag)
 {
 	Thumbprint *table, *crltab;
 
@@ -101,13 +149,13 @@
 		if((crltab = malloc(ThumbTab * sizeof(*crltab))) == nil)
 			goto err;
 		memset(crltab, 0, ThumbTab * sizeof(*crltab));
-		if(loadThumbprints(crl, crltab, nil) < 0)
+		if(loadThumbprints(crl, tag, crltab, nil, 0) < 0)
 			goto err;
 	}
 	if((table = malloc(ThumbTab * sizeof(*table))) == nil)
 		goto err;
 	memset(table, 0, ThumbTab * sizeof(*table));
-	if(loadThumbprints(ok, table, crltab) < 0){
+	if(loadThumbprints(ok, tag, table, crltab, 0) < 0){
 		freeThumbprints(table);
 		table = nil;
 	}