]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Introduce SHA2-256 as a peer certificate hash and make it the default
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Tue, 1 Nov 2016 08:23:48 +0000 (09:23 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Tue, 13 Dec 2016 10:41:55 +0000 (10:41 +0000)
That is, generate and print a SHA256 hash by default, while also
accept the old 'sha1:' type of certificate hashes.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
dtls.c
gnutls.c
library.c
openconnect-internal.h
openssl.c

diff --git a/dtls.c b/dtls.c
index c70ec227502362da5b654f6552f3761120f9196f..6f464fa78bfa07b1c607eacfa41fc85def294eeb 100644 (file)
--- a/dtls.c
+++ b/dtls.c
 #define DTLS_RECV gnutls_record_recv
 #endif
 
+char *openconnect_bin2hex(const char *prefix, const uint8_t *data, unsigned len)
+{
+       char *v;
+       unsigned plen = strlen(prefix);
+       unsigned i;
+
+       v = malloc(len*2+plen+1);
+       if (v) {
+               snprintf(v, plen+1, "%s", prefix);
+               for (i = 0; i < len; i++)
+                       sprintf(&v[i*2 + plen], "%02x", data[i]);
+       }
+       return v;
+}
+
 static int connect_dtls_socket(struct openconnect_info *vpninfo)
 {
        int dtls_fd, ret;
index ecd5cf7fa505eb73b043d01489bf80b39f766a28..b32d497be1b22f8c0911637f8bd0df20f3701a78 100644 (file)
--- a/gnutls.c
+++ b/gnutls.c
@@ -1927,11 +1927,11 @@ int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
 
 static int set_peer_cert_hash(struct openconnect_info *vpninfo)
 {
-       unsigned char sha1[SHA1_SIZE];
+       unsigned char hash[SHA256_SIZE];
        size_t shalen;
        gnutls_pubkey_t pkey;
        gnutls_datum_t d;
-       int i, err;
+       int err;
 
        err = gnutls_pubkey_init(&pkey);
        if (err)
@@ -1969,23 +1969,27 @@ static int set_peer_cert_hash(struct openconnect_info *vpninfo)
        }
 #endif
        gnutls_pubkey_deinit(pkey);
-       shalen = SHA1_SIZE;
+       shalen = SHA256_SIZE;
 
-       err = gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, sha1, &shalen);
+       err = gnutls_fingerprint(GNUTLS_DIG_SHA256, &d, hash, &shalen);
        if (err) {
                gnutls_free(d.data);
                return err;
        }
 
-       gnutls_free(d.data);
+       vpninfo->peer_cert_sha256 = openconnect_bin2hex("sha256:", hash, shalen);
 
-       vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
-       if (vpninfo->peer_cert_hash) {
-               snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
-               for (i = 0; i < shalen; i++)
-                       sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
+       shalen = SHA1_SIZE;
+       err = gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, hash, &shalen);
+       if (err) {
+               gnutls_free(d.data);
+               return err;
        }
 
+       gnutls_free(d.data);
+
+       vpninfo->peer_cert_sha1 = openconnect_bin2hex("sha1:", hash, shalen);
+
        return 0;
 }
 
@@ -2217,8 +2221,10 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
                gnutls_x509_crt_deinit(vpninfo->peer_cert);
                vpninfo->peer_cert = NULL;
        }
-       free(vpninfo->peer_cert_hash);
-       vpninfo->peer_cert_hash = NULL;
+       free(vpninfo->peer_cert_sha1);
+       vpninfo->peer_cert_sha1 = NULL;
+       free(vpninfo->peer_cert_sha256);
+       vpninfo->peer_cert_sha256 = NULL;
        gnutls_free(vpninfo->cstp_cipher);
        vpninfo->cstp_cipher = NULL;
 
@@ -2695,6 +2701,19 @@ int openconnect_sha1(unsigned char *result, void *data, int datalen)
        return 0;
 }
 
+int openconnect_sha256(unsigned char *result, void *data, int datalen)
+{
+       gnutls_datum_t d;
+       size_t shalen = SHA256_SIZE;
+
+       d.data = data;
+       d.size = datalen;
+       if (gnutls_fingerprint(GNUTLS_DIG_SHA256, &d, result, &shalen))
+               return -1;
+
+       return 0;
+}
+
 int openconnect_md5(unsigned char *result, void *data, int datalen)
 {
        gnutls_datum_t d;
index 9cce027e8a185207c09779b381048ef334781470..3fb8cb3319ce3d7f9ecf68bf356ef439817c4e36 100644 (file)
--- a/library.c
+++ b/library.c
@@ -339,7 +339,8 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
                free(cache);
        }
 
-       free(vpninfo->peer_cert_hash);
+       free(vpninfo->peer_cert_sha1);
+       free(vpninfo->peer_cert_sha256);
        free(vpninfo->localname);
        free(vpninfo->useragent);
        free(vpninfo->authgroup);
@@ -942,7 +943,15 @@ int openconnect_check_peer_cert_hash(struct openconnect_info *vpninfo,
        const char *fingerprint;
 
        if (strchr(old_hash, ':')) {
-               fingerprint = openconnect_get_peer_cert_hash(vpninfo);
+               if (strncmp(old_hash, "sha1:", 5) == 0) {
+                       fingerprint = vpninfo->peer_cert_sha1;
+               } else if (strncmp(old_hash, "sha256:", 7) == 0) {
+                       fingerprint = vpninfo->peer_cert_sha256;
+               } else {
+                       vpn_progress(vpninfo, PRG_ERR, _("Unknown certificate hash: %s.\n"), old_hash);
+                       return -EIO;
+               }
+
                if (!fingerprint)
                        return -EIO;
        } else {
@@ -976,7 +985,7 @@ const char *openconnect_get_cstp_cipher(struct openconnect_info *vpninfo)
 
 const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo)
 {
-       return vpninfo->peer_cert_hash;
+       return vpninfo->peer_cert_sha256;
 }
 
 int openconnect_set_compression_mode(struct openconnect_info *vpninfo,
index 6c29900484ce195c4abb932471f6c742429f621d..ae901e01fba464a89d83f9f61f3b465f9912de6f 100644 (file)
 
 #include <libxml/tree.h>
 
+#define SHA256_SIZE 32
 #define SHA1_SIZE 20
 #define MD5_SIZE 16
 
@@ -454,7 +455,8 @@ struct openconnect_info {
        void *tok_cbdata;
 
        void *peer_cert;
-       char *peer_cert_hash;
+       char *peer_cert_sha1;
+       char *peer_cert_sha256;
        void *cert_list_handle;
        int cert_list_size;
 
@@ -819,6 +821,7 @@ void append_dtls_ciphers(struct openconnect_info *vpninfo, struct oc_text_buf *b
 void dtls_detect_mtu(struct openconnect_info *vpninfo);
 int openconnect_dtls_read(struct openconnect_info *vpninfo, void *buf, size_t len, unsigned ms);
 int openconnect_dtls_write(struct openconnect_info *vpninfo, void *buf, size_t len);
+char *openconnect_bin2hex(const char *prefix, const uint8_t *data, unsigned len);
 
 /* cstp.c */
 void cstp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
@@ -900,6 +903,7 @@ int cstp_handshake(struct openconnect_info *vpninfo, unsigned init);
 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, void *cert,
                             char *buf);
 int openconnect_sha1(unsigned char *result, void *data, int len);
+int openconnect_sha256(unsigned char *result, void *data, int len);
 int openconnect_md5(unsigned char *result, void *data, int len);
 int openconnect_random(void *bytes, int len);
 int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
index 8ddd862c573134222c15bc7f1582210d1f93710e..20226d551b95530fa3e27e2a8a10ef8beb80b9cf 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -62,6 +62,19 @@ int openconnect_sha1(unsigned char *result, void *data, int len)
        return 0;
 }
 
+int openconnect_sha256(unsigned char *result, void *data, int len)
+{
+       EVP_MD_CTX *c = EVP_MD_CTX_new();
+
+       if (!c)
+               return -ENOMEM;
+
+       EVP_Digest(data, len, result, NULL, EVP_sha256(), NULL);
+       EVP_MD_CTX_free(c);
+
+       return 0;
+}
+
 int openconnect_md5(unsigned char *result, void *data, int len)
 {
        EVP_MD_CTX *c = EVP_MD_CTX_new();
@@ -1086,11 +1099,11 @@ int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
 
 static int set_peer_cert_hash(struct openconnect_info *vpninfo)
 {
-       unsigned char sha1[SHA1_SIZE];
+       unsigned char sha256_hash[SHA256_SIZE];
+       unsigned char sha1_hash[SHA1_SIZE];
        EVP_PKEY *pkey;
        BIO *bp = BIO_new(BIO_s_mem());
        BUF_MEM *keyinfo;
-       int i;
 
        /* We can't use X509_pubkey_digest() because it only hashes the
           subjectPublicKey BIT STRING, and not the whole of the
@@ -1106,16 +1119,13 @@ static int set_peer_cert_hash(struct openconnect_info *vpninfo)
 
        BIO_get_mem_ptr(bp, &keyinfo);
 
-       openconnect_sha1(sha1, keyinfo->data, keyinfo->length);
+       openconnect_sha256(sha256_hash, keyinfo->data, keyinfo->length);
+       openconnect_sha1(sha1_hash, keyinfo->data, keyinfo->length);
 
        BIO_free(bp);
 
-       vpninfo->peer_cert_hash = malloc(SHA1_SIZE * 2 + 6);
-       if (vpninfo->peer_cert_hash) {
-               snprintf(vpninfo->peer_cert_hash, 6, "sha1:");
-               for (i = 0; i < sizeof(sha1); i++)
-                       sprintf(&vpninfo->peer_cert_hash[i*2 + 5], "%02x", sha1[i]);
-       }
+       vpninfo->peer_cert_sha1 = openconnect_bin2hex("sha1:", sha1_hash, sizeof(sha1_hash));
+       vpninfo->peer_cert_sha256 = openconnect_bin2hex("sha256:", sha256_hash, sizeof(sha256_hash));
 
        return 0;
 }
@@ -1648,8 +1658,10 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
                X509_free(vpninfo->peer_cert);
                vpninfo->peer_cert = NULL;
        }
-       free (vpninfo->peer_cert_hash);
-       vpninfo->peer_cert_hash = NULL;
+       free (vpninfo->peer_cert_sha1);
+       vpninfo->peer_cert_sha1 = NULL;
+       free (vpninfo->peer_cert_sha256);
+       vpninfo->peer_cert_sha256 = NULL;
        vpninfo->cstp_cipher = NULL;
 
        ssl_sock = connect_https_socket(vpninfo);