mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
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:
parent
d13a40a4a4
commit
af1e4d26ab
6 changed files with 114 additions and 64 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue