summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2017-04-23 19:00:08 +0200
committercinap_lenrek <cinap_lenrek@felloff.net>2017-04-23 19:00:08 +0200
commit346f5828e0e435d76ef7da8316e77a426c826d19 (patch)
tree316f4fb2d2c4d08e51a32efa6206b814362393dc
parent2d1fbbdafa37080ddaacb76ac1e4f5a413ef2dc3 (diff)
downloadplan9front-346f5828e0e435d76ef7da8316e77a426c826d19.tar.xz
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.
-rw-r--r--sys/include/ape/libsec.h8
-rw-r--r--sys/include/libsec.h8
-rw-r--r--sys/man/2/pushtls16
-rw-r--r--sys/man/6/thumbprint18
-rw-r--r--sys/src/ape/lib/sec/mkfile2
-rw-r--r--sys/src/ape/lib/sec/tlsclient.c11
-rw-r--r--sys/src/cmd/ssh.c57
-rw-r--r--sys/src/cmd/tlsclient.c11
-rw-r--r--sys/src/cmd/upas/fs/tls.c13
-rw-r--r--sys/src/cmd/upas/smtp/smtp.c21
-rw-r--r--sys/src/libsec/port/thumb.c84
11 files changed, 126 insertions, 123 deletions
diff --git a/sys/include/ape/libsec.h b/sys/include/ape/libsec.h
index d7b3f9a39..954d3641d 100644
--- a/sys/include/ape/libsec.h
+++ b/sys/include/ape/libsec.h
@@ -461,7 +461,8 @@ DSApriv* asn1toDSApriv(uchar*, int);
*/
typedef struct Thumbprint{
struct Thumbprint *next;
- uchar sha1[SHA1dlen];
+ uchar hash[SHA2_256dlen];
+ uchar len;
} Thumbprint;
typedef struct TLSconn{
@@ -487,9 +488,10 @@ int tlsClient(int fd, TLSconn *c);
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);
diff --git a/sys/include/libsec.h b/sys/include/libsec.h
index 0067302e8..12f4d4be4 100644
--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -453,7 +453,8 @@ DSApriv* asn1toDSApriv(uchar*, int);
*/
typedef struct Thumbprint{
struct Thumbprint *next;
- uchar sha1[SHA1dlen];
+ uchar hash[SHA2_256dlen];
+ uchar len;
} Thumbprint;
typedef struct TLSconn{
@@ -479,9 +480,10 @@ int tlsClient(int fd, TLSconn *c);
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);
diff --git a/sys/man/2/pushtls b/sys/man/2/pushtls
index ed3f293dd..72a2605bf 100644
--- 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 @@ uchar *readcert(char *filename, int *pcertlen)
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 @@ be freed by the application whenever convenient.
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
diff --git a/sys/man/6/thumbprint b/sys/man/6/thumbprint
index 63be911af..196108e1f 100644
--- a/sys/man/6/thumbprint
+++ b/sys/man/6/thumbprint
@@ -8,6 +8,8 @@ for example by calling
.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 @@ attribute/value pairs of the form
.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
diff --git a/sys/src/ape/lib/sec/mkfile b/sys/src/ape/lib/sec/mkfile
index 89d464885..abd5813c9 100644
--- a/sys/src/ape/lib/sec/mkfile
+++ b/sys/src/ape/lib/sec/mkfile
@@ -12,6 +12,7 @@ install all:V:
}
clean:V:
+ rm -f [$OS].* *.[$OS]
for(i in $DIRS)@{
echo $i
cd $i
@@ -34,5 +35,6 @@ everything:V:
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
diff --git a/sys/src/ape/lib/sec/tlsclient.c b/sys/src/ape/lib/sec/tlsclient.c
index 37095a6ab..3fd412486 100644
--- a/sys/src/ape/lib/sec/tlsclient.c
+++ b/sys/src/ape/lib/sec/tlsclient.c
@@ -107,7 +107,7 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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);
}
diff --git a/sys/src/cmd/ssh.c b/sys/src/cmd/ssh.c
index a635dd546..e4e1f3e89 100644
--- 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 @@ char *user, *service, *status, *host, *cmd;
Oneway recv, send;
void dispatch(void);
-int checkthumb(char*, uchar*);
void
shutdown(void)
@@ -580,22 +578,25 @@ Next1: switch(recvpkt()){
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 @@ rawon(void)
}
}
-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 @@ main(int argc, char *argv[])
quotefmtinstall();
fmtinstall('B', mpfmt);
fmtinstall('H', encodefmt);
- fmtinstall('[', encodefmt);
s = getenv("TERM");
raw = s != nil && strcmp(s, "dumb") != 0;
diff --git a/sys/src/cmd/tlsclient.c b/sys/src/cmd/tlsclient.c
index a90eabf96..1b79fb739 100644
--- a/sys/src/cmd/tlsclient.c
+++ b/sys/src/cmd/tlsclient.c
@@ -87,7 +87,7 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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);
}
diff --git a/sys/src/cmd/upas/fs/tls.c b/sys/src/cmd/upas/fs/tls.c
index 07906ee38..dd1ca22cb 100644
--- 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 @@ wraptls(int ofd, char *host)
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;
diff --git a/sys/src/cmd/upas/smtp/smtp.c b/sys/src/cmd/upas/smtp/smtp.c
index b20d4df7b..4fb437992 100644
--- a/sys/src/cmd/upas/smtp/smtp.c
+++ b/sys/src/cmd/upas/smtp/smtp.c
@@ -388,9 +388,8 @@ wraptls(void)
{
TLSconn *c;
Thumbprint *goodcerts;
- char *h, *err;
+ char *err;
int fd;
- uchar hash[SHA1dlen];
goodcerts = nil;
err = Giveup;
@@ -412,29 +411,19 @@ wraptls(void)
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);
diff --git a/sys/src/libsec/port/thumb.c b/sys/src/libsec/port/thumb.c
index d8ded8305..39757939b 100644
--- 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 @@ freeThumbprints(Thumbprint *table)
}
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 @@ okThumbprint(uchar *sum, Thumbprint *table)
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 @@ loadThumbprints(char *file, Thumbprint *table, Thumbprint *crltab)
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 @@ loadThumbprints(char *file, Thumbprint *table, Thumbprint *crltab)
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 @@ err:
}
Thumbprint *
-initThumbprints(char *ok, char *crl)
+initThumbprints(char *ok, char *crl, char *tag)
{
Thumbprint *table, *crltab;
@@ -101,13 +149,13 @@ initThumbprints(char *ok, char *crl)
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;
}