Add SHA256 fingerprint support

Add SHA256 fingerprint support for both the normal exported fingerprints
(tls_digest_n -> tls_digest_sha256_n), as well as for --x509-track.

Also switch to using the SHA256 fingerprint instead of the SHA1 fingerprint
internally, in cert_hash_remember() / cert_hash_compare().  And instead of
updating an #if 0'd code block that has been disabled since 2009, just
remove that.

This should take care of trac #675.

v2: update openvpn.8 accordingly

[ DS: This commit squashes in the clean-up cert_hash_remember scoping patch,
      as it is highly related and tied to this primary patch ]

Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: David Sommerseth <davids@openvpn.net>
Message-Id: 1462479247-21854-1-git-send-email-steffan@karger.me
Message-Id: 1474055635-7427-1-git-send-email-steffan@karger.me
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg11859.html
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12464.html
Signed-off-by: David Sommerseth <davids@openvpn.net>
This commit is contained in:
Steffan Karger 2016-05-05 22:14:07 +02:00 committed by David Sommerseth
parent d13a40a4a4
commit af1e4d26ab
6 changed files with 114 additions and 64 deletions

View file

@ -6443,9 +6443,8 @@ Set prior to execution of the
script.
.\"*********************************************************
.TP
.B tls_digest_{n}
Contains the certificate SHA1 fingerprint/digest hash value,
where
.B tls_digest_{n} / tls_digest_sha256_{n}
Contains the certificate SHA1 / SHA256 fingerprint, where
.B n
is the verification level. Only set for TLS connections. Set prior
to execution of

View file

@ -191,40 +191,25 @@ tls_username (const struct tls_multi *multi, const bool null)
}
void
cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash)
cert_hash_remember (struct tls_session *session, const int error_depth,
const struct buffer *cert_hash)
{
if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
{
if (!session->cert_hash_set)
ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
if (!session->cert_hash_set->ch[error_depth])
ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
{
struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH);
}
}
}
#if 0
static void
cert_hash_print (const struct cert_hash_set *chs, int msglevel)
{
struct gc_arena gc = gc_new ();
msg (msglevel, "CERT_HASH");
if (chs)
{
int i;
for (i = 0; i < MAX_CERT_DEPTH; ++i)
{
const struct cert_hash *ch = chs->ch[i];
if (ch)
msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
}
if (!session->cert_hash_set->ch[error_depth])
{
ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
}
struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash));
memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash));
}
gc_free (&gc);
}
#endif
void
cert_hash_free (struct cert_hash_set *chs)
@ -251,7 +236,8 @@ cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set
if (!ch1 && !ch2)
continue;
else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH))
else if (ch1 && ch2 && !memcmp (ch1->sha256_hash, ch2->sha256_hash,
sizeof(ch1->sha256_hash)))
continue;
else
return false;
@ -278,7 +264,8 @@ cert_hash_copy (const struct cert_hash_set *chs)
if (ch)
{
ALLOC_OBJ (dest->ch[i], struct cert_hash);
memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH);
memcpy (dest->ch[i]->sha256_hash, ch->sha256_hash,
sizeof(dest->ch[i]->sha256_hash));
}
}
}
@ -416,13 +403,19 @@ verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert
setenv_str (es, envname, common_name);
#endif
/* export X509 cert SHA1 fingerprint */
/* export X509 cert fingerprints */
{
unsigned char *sha1_hash = x509_get_sha1_hash(peer_cert, &gc);
struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc);
struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc);
openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth);
setenv_str (es, envname, format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1,
":", &gc));
setenv_str (es, envname,
format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc));
openvpn_snprintf (envname, sizeof(envname), "tls_digest_sha256_%d",
cert_depth);
setenv_str (es, envname,
format_hex_ex(BPTR(&sha256), BLEN(&sha256), 0, 1, ":", &gc));
}
/* export serial number as environmental variable */
@ -638,8 +631,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
/* verify level 1 cert, i.e. the CA that signed our leaf cert */
if (cert_depth == 1 && opt->verify_hash)
{
unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
if (memcmp (sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc);
if (memcmp (BPTR (&sha1_hash), opt->verify_hash, BLEN(&sha1_hash)))
{
msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
goto cleanup;

View file

@ -55,7 +55,7 @@
/** Structure containing the hash for a single certificate */
struct cert_hash {
unsigned char sha1_hash[SHA_DIGEST_LENGTH]; /**< The SHA1 hash for a certificate */
unsigned char sha256_hash[256/8];
};
/** Structure containing the hashes for a full certificate chain */

View file

@ -66,10 +66,10 @@ result_t verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int
*
* @param session TLS Session associated with this tunnel
* @param cert_depth Depth of the current certificate
* @param sha1_hash Hash of the current certificate
* @param cert_hash Hash of the current certificate
*/
void cert_hash_remember (struct tls_session *session, const int cert_depth,
const unsigned char *sha1_hash);
const struct buffer *cert_hash);
/*
* Library-specific functions.
@ -87,14 +87,27 @@ void cert_hash_remember (struct tls_session *session, const int cert_depth,
*/
char *x509_get_subject (openvpn_x509_cert_t *cert, struct gc_arena *gc);
/* Retrieve the certificate's SHA1 hash.
/**
* Retrieve the certificate's SHA1 fingerprint.
*
* @param cert Certificate to retrieve the hash from.
* @param cert Certificate to retrieve the fingerprint from.
* @param gc Garbage collection arena to use when allocating string.
*
* @return a string containing the SHA1 hash of the certificate
* @return a string containing the certificate fingerprint
*/
unsigned char *x509_get_sha1_hash (openvpn_x509_cert_t *cert, struct gc_arena *gc);
struct buffer x509_get_sha1_fingerprint (openvpn_x509_cert_t *cert,
struct gc_arena *gc);
/**
* Retrieve the certificate's SHA256 fingerprint.
*
* @param cert Certificate to retrieve the fingerprint from.
* @param gc Garbage collection arena to use when allocating string.
*
* @return a string containing the certificate fingerprint
*/
struct buffer x509_get_sha256_fingerprint (openvpn_x509_cert_t *cert,
struct gc_arena *gc);
/*
* Retrieve the certificate's username from the specified field.

View file

@ -60,7 +60,8 @@ verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth,
session->verified = false;
/* Remember certificate hash */
cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc));
struct buffer cert_fingerprint = x509_get_sha256_fingerprint (cert, &gc);
cert_hash_remember (session, cert_depth, &cert_fingerprint);
/* did peer present cert which was signed by our root cert? */
if (*flags != 0)
@ -196,12 +197,29 @@ backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc)
return buf;
}
unsigned char *
x509_get_sha1_hash (mbedtls_x509_crt *cert, struct gc_arena *gc)
static struct buffer
x509_get_fingerprint (const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert,
struct gc_arena *gc)
{
unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc);
mbedtls_sha1(cert->raw.p, cert->tbs.len, sha1_hash);
return sha1_hash;
const size_t md_size = mbedtls_md_get_size (md_info);
struct buffer fingerprint = alloc_buf_gc (md_size, gc);
mbedtls_md(md_info, cert->raw.p, cert->tbs.len, BPTR (&fingerprint));
ASSERT (buf_inc_len(&fingerprint, md_size));
return fingerprint;
}
struct buffer
x509_get_sha1_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc)
{
return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
cert, gc);
}
struct buffer
x509_get_sha256_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc)
{
return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
cert, gc);
}
char *
@ -294,12 +312,20 @@ x509_setenv_track (const struct x509_track *xt, struct env_set *es,
{
if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
{
if (0 == strcmp(xt->name, "SHA1"))
if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256"))
{
/* SHA1 fingerprint is not part of X509 structure */
unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
char *sha1_fingerprint = format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
/* Fingerprint is not part of X509 structure */
struct buffer cert_hash;
char *fingerprint;
if (0 == strcmp(xt->name, "SHA1"))
cert_hash = x509_get_sha1_fingerprint(cert, &gc);
else
cert_hash = x509_get_sha256_fingerprint(cert, &gc);
fingerprint = format_hex_ex(BPTR(&cert_hash),
BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, fingerprint, depth);
}
else
{

View file

@ -61,8 +61,8 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index);
ASSERT (session);
cert_hash_remember (session, ctx->error_depth,
x509_get_sha1_hash(ctx->current_cert, &gc));
struct buffer cert_hash = x509_get_sha256_fingerprint(ctx->current_cert, &gc);
cert_hash_remember (session, ctx->error_depth, &cert_hash);
/* did peer present cert which was signed by our root cert? */
if (!preverify_ok)
@ -248,11 +248,21 @@ backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, struct gc_arena *gc)
return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc);
}
unsigned char *
x509_get_sha1_hash (X509 *cert, struct gc_arena *gc)
struct buffer
x509_get_sha1_fingerprint (X509 *cert, struct gc_arena *gc)
{
unsigned char *hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc);
memcpy(hash, cert->sha1_hash, SHA_DIGEST_LENGTH);
struct buffer hash = alloc_buf_gc(sizeof(cert->sha1_hash), gc);
memcpy(BPTR(&hash), cert->sha1_hash, sizeof(cert->sha1_hash));
ASSERT (buf_inc_len(&hash, sizeof (cert->sha1_hash)));
return hash;
}
struct buffer
x509_get_sha256_fingerprint (X509 *cert, struct gc_arena *gc)
{
struct buffer hash = alloc_buf_gc((EVP_sha256())->md_size, gc);
X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL);
ASSERT (buf_inc_len(&hash, (EVP_sha256())->md_size));
return hash;
}
@ -376,10 +386,19 @@ x509_setenv_track (const struct x509_track *xt, struct env_set *es, const int de
switch (xt->nid)
{
case NID_sha1:
case NID_sha256:
{
char *sha1_fingerprint = format_hex_ex(x509->sha1_hash,
SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
struct buffer fp_buf;
char *fp_str = NULL;
if (xt->nid == NID_sha1)
fp_buf = x509_get_sha1_fingerprint(x509, &gc);
else
fp_buf = x509_get_sha256_fingerprint(x509, &gc);
fp_str = format_hex_ex(BPTR(&fp_buf), BLEN(&fp_buf), 0,
1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, fp_str, depth);
}
break;
default: