diff --git a/dtls.c b/dtls.c index c70ec227..6f464fa7 100644 --- a/dtls.c +++ b/dtls.c @@ -66,6 +66,21 @@ #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; diff --git a/gnutls.c b/gnutls.c index ecd5cf7f..b32d497b 100644 --- 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; diff --git a/library.c b/library.c index 9cce027e..3fb8cb33 100644 --- 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, diff --git a/openconnect-internal.h b/openconnect-internal.h index 6c299004..ae901e01 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -114,6 +114,7 @@ #include +#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, diff --git a/openssl.c b/openssl.c index 8ddd862c..20226d55 100644 --- 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);