summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}