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>
| ... | ... |
@@ -6443,9 +6443,8 @@ Set prior to execution of the |
| 6443 | 6443 |
script. |
| 6444 | 6444 |
.\"********************************************************* |
| 6445 | 6445 |
.TP |
| 6446 |
-.B tls_digest_{n}
|
|
| 6447 |
-Contains the certificate SHA1 fingerprint/digest hash value, |
|
| 6448 |
-where |
|
| 6446 |
+.B tls_digest_{n} / tls_digest_sha256_{n}
|
|
| 6447 |
+Contains the certificate SHA1 / SHA256 fingerprint, where |
|
| 6449 | 6448 |
.B n |
| 6450 | 6449 |
is the verification level. Only set for TLS connections. Set prior |
| 6451 | 6450 |
to execution of |
| ... | ... |
@@ -191,40 +191,25 @@ tls_username (const struct tls_multi *multi, const bool null) |
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 | 193 |
void |
| 194 |
-cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash) |
|
| 194 |
+cert_hash_remember (struct tls_session *session, const int error_depth, |
|
| 195 |
+ const struct buffer *cert_hash) |
|
| 195 | 196 |
{
|
| 196 | 197 |
if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH) |
| 197 | 198 |
{
|
| 198 | 199 |
if (!session->cert_hash_set) |
| 199 |
- ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set); |
|
| 200 |
+ {
|
|
| 201 |
+ ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set); |
|
| 202 |
+ } |
|
| 200 | 203 |
if (!session->cert_hash_set->ch[error_depth]) |
| 201 |
- ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash); |
|
| 202 |
- {
|
|
| 203 |
- struct cert_hash *ch = session->cert_hash_set->ch[error_depth]; |
|
| 204 |
- memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH); |
|
| 205 |
- } |
|
| 206 |
- } |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-#if 0 |
|
| 210 |
-static void |
|
| 211 |
-cert_hash_print (const struct cert_hash_set *chs, int msglevel) |
|
| 212 |
-{
|
|
| 213 |
- struct gc_arena gc = gc_new (); |
|
| 214 |
- msg (msglevel, "CERT_HASH"); |
|
| 215 |
- if (chs) |
|
| 216 |
- {
|
|
| 217 |
- int i; |
|
| 218 |
- for (i = 0; i < MAX_CERT_DEPTH; ++i) |
|
| 219 | 204 |
{
|
| 220 |
- const struct cert_hash *ch = chs->ch[i]; |
|
| 221 |
- if (ch) |
|
| 222 |
- msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc)); |
|
| 205 |
+ ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash); |
|
| 223 | 206 |
} |
| 207 |
+ |
|
| 208 |
+ struct cert_hash *ch = session->cert_hash_set->ch[error_depth]; |
|
| 209 |
+ ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash)); |
|
| 210 |
+ memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash)); |
|
| 224 | 211 |
} |
| 225 |
- gc_free (&gc); |
|
| 226 | 212 |
} |
| 227 |
-#endif |
|
| 228 | 213 |
|
| 229 | 214 |
void |
| 230 | 215 |
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 |
| 251 | 251 |
|
| 252 | 252 |
if (!ch1 && !ch2) |
| 253 | 253 |
continue; |
| 254 |
- else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH)) |
|
| 254 |
+ else if (ch1 && ch2 && !memcmp (ch1->sha256_hash, ch2->sha256_hash, |
|
| 255 |
+ sizeof(ch1->sha256_hash))) |
|
| 255 | 256 |
continue; |
| 256 | 257 |
else |
| 257 | 258 |
return false; |
| ... | ... |
@@ -278,7 +264,8 @@ cert_hash_copy (const struct cert_hash_set *chs) |
| 278 | 278 |
if (ch) |
| 279 | 279 |
{
|
| 280 | 280 |
ALLOC_OBJ (dest->ch[i], struct cert_hash); |
| 281 |
- memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH); |
|
| 281 |
+ memcpy (dest->ch[i]->sha256_hash, ch->sha256_hash, |
|
| 282 |
+ sizeof(dest->ch[i]->sha256_hash)); |
|
| 282 | 283 |
} |
| 283 | 284 |
} |
| 284 | 285 |
} |
| ... | ... |
@@ -416,13 +403,19 @@ verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert |
| 416 | 416 |
setenv_str (es, envname, common_name); |
| 417 | 417 |
#endif |
| 418 | 418 |
|
| 419 |
- /* export X509 cert SHA1 fingerprint */ |
|
| 419 |
+ /* export X509 cert fingerprints */ |
|
| 420 | 420 |
{
|
| 421 |
- unsigned char *sha1_hash = x509_get_sha1_hash(peer_cert, &gc); |
|
| 421 |
+ struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc); |
|
| 422 |
+ struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc); |
|
| 422 | 423 |
|
| 423 | 424 |
openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth); |
| 424 |
- setenv_str (es, envname, format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1, |
|
| 425 |
- ":", &gc)); |
|
| 425 |
+ setenv_str (es, envname, |
|
| 426 |
+ format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc)); |
|
| 427 |
+ |
|
| 428 |
+ openvpn_snprintf (envname, sizeof(envname), "tls_digest_sha256_%d", |
|
| 429 |
+ cert_depth); |
|
| 430 |
+ setenv_str (es, envname, |
|
| 431 |
+ format_hex_ex(BPTR(&sha256), BLEN(&sha256), 0, 1, ":", &gc)); |
|
| 426 | 432 |
} |
| 427 | 433 |
|
| 428 | 434 |
/* export serial number as environmental variable */ |
| ... | ... |
@@ -638,8 +631,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep |
| 638 | 638 |
/* verify level 1 cert, i.e. the CA that signed our leaf cert */ |
| 639 | 639 |
if (cert_depth == 1 && opt->verify_hash) |
| 640 | 640 |
{
|
| 641 |
- unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc); |
|
| 642 |
- if (memcmp (sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH)) |
|
| 641 |
+ struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc); |
|
| 642 |
+ if (memcmp (BPTR (&sha1_hash), opt->verify_hash, BLEN(&sha1_hash))) |
|
| 643 | 643 |
{
|
| 644 | 644 |
msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed"); |
| 645 | 645 |
goto cleanup; |
| ... | ... |
@@ -55,7 +55,7 @@ |
| 55 | 55 |
|
| 56 | 56 |
/** Structure containing the hash for a single certificate */ |
| 57 | 57 |
struct cert_hash {
|
| 58 |
- unsigned char sha1_hash[SHA_DIGEST_LENGTH]; /**< The SHA1 hash for a certificate */ |
|
| 58 |
+ unsigned char sha256_hash[256/8]; |
|
| 59 | 59 |
}; |
| 60 | 60 |
|
| 61 | 61 |
/** 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 |
| 66 | 66 |
* |
| 67 | 67 |
* @param session TLS Session associated with this tunnel |
| 68 | 68 |
* @param cert_depth Depth of the current certificate |
| 69 |
- * @param sha1_hash Hash of the current certificate |
|
| 69 |
+ * @param cert_hash Hash of the current certificate |
|
| 70 | 70 |
*/ |
| 71 | 71 |
void cert_hash_remember (struct tls_session *session, const int cert_depth, |
| 72 |
- const unsigned char *sha1_hash); |
|
| 72 |
+ const struct buffer *cert_hash); |
|
| 73 | 73 |
|
| 74 | 74 |
/* |
| 75 | 75 |
* Library-specific functions. |
| ... | ... |
@@ -87,14 +87,27 @@ void cert_hash_remember (struct tls_session *session, const int cert_depth, |
| 87 | 87 |
*/ |
| 88 | 88 |
char *x509_get_subject (openvpn_x509_cert_t *cert, struct gc_arena *gc); |
| 89 | 89 |
|
| 90 |
-/* Retrieve the certificate's SHA1 hash. |
|
| 90 |
+/** |
|
| 91 |
+ * Retrieve the certificate's SHA1 fingerprint. |
|
| 91 | 92 |
* |
| 92 |
- * @param cert Certificate to retrieve the hash from. |
|
| 93 |
+ * @param cert Certificate to retrieve the fingerprint from. |
|
| 93 | 94 |
* @param gc Garbage collection arena to use when allocating string. |
| 94 | 95 |
* |
| 95 |
- * @return a string containing the SHA1 hash of the certificate |
|
| 96 |
+ * @return a string containing the certificate fingerprint |
|
| 96 | 97 |
*/ |
| 97 |
-unsigned char *x509_get_sha1_hash (openvpn_x509_cert_t *cert, struct gc_arena *gc); |
|
| 98 |
+struct buffer x509_get_sha1_fingerprint (openvpn_x509_cert_t *cert, |
|
| 99 |
+ struct gc_arena *gc); |
|
| 100 |
+ |
|
| 101 |
+/** |
|
| 102 |
+ * Retrieve the certificate's SHA256 fingerprint. |
|
| 103 |
+ * |
|
| 104 |
+ * @param cert Certificate to retrieve the fingerprint from. |
|
| 105 |
+ * @param gc Garbage collection arena to use when allocating string. |
|
| 106 |
+ * |
|
| 107 |
+ * @return a string containing the certificate fingerprint |
|
| 108 |
+ */ |
|
| 109 |
+struct buffer x509_get_sha256_fingerprint (openvpn_x509_cert_t *cert, |
|
| 110 |
+ struct gc_arena *gc); |
|
| 98 | 111 |
|
| 99 | 112 |
/* |
| 100 | 113 |
* 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, |
| 60 | 60 |
session->verified = false; |
| 61 | 61 |
|
| 62 | 62 |
/* Remember certificate hash */ |
| 63 |
- cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc)); |
|
| 63 |
+ struct buffer cert_fingerprint = x509_get_sha256_fingerprint (cert, &gc); |
|
| 64 |
+ cert_hash_remember (session, cert_depth, &cert_fingerprint); |
|
| 64 | 65 |
|
| 65 | 66 |
/* did peer present cert which was signed by our root cert? */ |
| 66 | 67 |
if (*flags != 0) |
| ... | ... |
@@ -196,12 +197,29 @@ backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc) |
| 196 | 196 |
return buf; |
| 197 | 197 |
} |
| 198 | 198 |
|
| 199 |
-unsigned char * |
|
| 200 |
-x509_get_sha1_hash (mbedtls_x509_crt *cert, struct gc_arena *gc) |
|
| 199 |
+static struct buffer |
|
| 200 |
+x509_get_fingerprint (const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert, |
|
| 201 |
+ struct gc_arena *gc) |
|
| 201 | 202 |
{
|
| 202 |
- unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); |
|
| 203 |
- mbedtls_sha1(cert->raw.p, cert->tbs.len, sha1_hash); |
|
| 204 |
- return sha1_hash; |
|
| 203 |
+ const size_t md_size = mbedtls_md_get_size (md_info); |
|
| 204 |
+ struct buffer fingerprint = alloc_buf_gc (md_size, gc); |
|
| 205 |
+ mbedtls_md(md_info, cert->raw.p, cert->tbs.len, BPTR (&fingerprint)); |
|
| 206 |
+ ASSERT (buf_inc_len(&fingerprint, md_size)); |
|
| 207 |
+ return fingerprint; |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+struct buffer |
|
| 211 |
+x509_get_sha1_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) |
|
| 212 |
+{
|
|
| 213 |
+ return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), |
|
| 214 |
+ cert, gc); |
|
| 215 |
+} |
|
| 216 |
+ |
|
| 217 |
+struct buffer |
|
| 218 |
+x509_get_sha256_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) |
|
| 219 |
+{
|
|
| 220 |
+ return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), |
|
| 221 |
+ cert, gc); |
|
| 205 | 222 |
} |
| 206 | 223 |
|
| 207 | 224 |
char * |
| ... | ... |
@@ -294,12 +312,20 @@ x509_setenv_track (const struct x509_track *xt, struct env_set *es, |
| 294 | 294 |
{
|
| 295 | 295 |
if (depth == 0 || (xt->flags & XT_FULL_CHAIN)) |
| 296 | 296 |
{
|
| 297 |
- if (0 == strcmp(xt->name, "SHA1")) |
|
| 297 |
+ if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256")) |
|
| 298 | 298 |
{
|
| 299 |
- /* SHA1 fingerprint is not part of X509 structure */ |
|
| 300 |
- unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc); |
|
| 301 |
- char *sha1_fingerprint = format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc); |
|
| 302 |
- do_setenv_x509(es, xt->name, sha1_fingerprint, depth); |
|
| 299 |
+ /* Fingerprint is not part of X509 structure */ |
|
| 300 |
+ struct buffer cert_hash; |
|
| 301 |
+ char *fingerprint; |
|
| 302 |
+ |
|
| 303 |
+ if (0 == strcmp(xt->name, "SHA1")) |
|
| 304 |
+ cert_hash = x509_get_sha1_fingerprint(cert, &gc); |
|
| 305 |
+ else |
|
| 306 |
+ cert_hash = x509_get_sha256_fingerprint(cert, &gc); |
|
| 307 |
+ |
|
| 308 |
+ fingerprint = format_hex_ex(BPTR(&cert_hash), |
|
| 309 |
+ BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc); |
|
| 310 |
+ do_setenv_x509(es, xt->name, fingerprint, depth); |
|
| 303 | 311 |
} |
| 304 | 312 |
else |
| 305 | 313 |
{
|
| ... | ... |
@@ -61,8 +61,8 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) |
| 61 | 61 |
session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index); |
| 62 | 62 |
ASSERT (session); |
| 63 | 63 |
|
| 64 |
- cert_hash_remember (session, ctx->error_depth, |
|
| 65 |
- x509_get_sha1_hash(ctx->current_cert, &gc)); |
|
| 64 |
+ struct buffer cert_hash = x509_get_sha256_fingerprint(ctx->current_cert, &gc); |
|
| 65 |
+ cert_hash_remember (session, ctx->error_depth, &cert_hash); |
|
| 66 | 66 |
|
| 67 | 67 |
/* did peer present cert which was signed by our root cert? */ |
| 68 | 68 |
if (!preverify_ok) |
| ... | ... |
@@ -248,11 +248,21 @@ backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, struct gc_arena *gc) |
| 248 | 248 |
return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc); |
| 249 | 249 |
} |
| 250 | 250 |
|
| 251 |
-unsigned char * |
|
| 252 |
-x509_get_sha1_hash (X509 *cert, struct gc_arena *gc) |
|
| 251 |
+struct buffer |
|
| 252 |
+x509_get_sha1_fingerprint (X509 *cert, struct gc_arena *gc) |
|
| 253 | 253 |
{
|
| 254 |
- unsigned char *hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); |
|
| 255 |
- memcpy(hash, cert->sha1_hash, SHA_DIGEST_LENGTH); |
|
| 254 |
+ struct buffer hash = alloc_buf_gc(sizeof(cert->sha1_hash), gc); |
|
| 255 |
+ memcpy(BPTR(&hash), cert->sha1_hash, sizeof(cert->sha1_hash)); |
|
| 256 |
+ ASSERT (buf_inc_len(&hash, sizeof (cert->sha1_hash))); |
|
| 257 |
+ return hash; |
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+struct buffer |
|
| 261 |
+x509_get_sha256_fingerprint (X509 *cert, struct gc_arena *gc) |
|
| 262 |
+{
|
|
| 263 |
+ struct buffer hash = alloc_buf_gc((EVP_sha256())->md_size, gc); |
|
| 264 |
+ X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL); |
|
| 265 |
+ ASSERT (buf_inc_len(&hash, (EVP_sha256())->md_size)); |
|
| 256 | 266 |
return hash; |
| 257 | 267 |
} |
| 258 | 268 |
|
| ... | ... |
@@ -376,10 +386,19 @@ x509_setenv_track (const struct x509_track *xt, struct env_set *es, const int de |
| 376 | 376 |
switch (xt->nid) |
| 377 | 377 |
{
|
| 378 | 378 |
case NID_sha1: |
| 379 |
+ case NID_sha256: |
|
| 379 | 380 |
{
|
| 380 |
- char *sha1_fingerprint = format_hex_ex(x509->sha1_hash, |
|
| 381 |
- SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc); |
|
| 382 |
- do_setenv_x509(es, xt->name, sha1_fingerprint, depth); |
|
| 381 |
+ struct buffer fp_buf; |
|
| 382 |
+ char *fp_str = NULL; |
|
| 383 |
+ |
|
| 384 |
+ if (xt->nid == NID_sha1) |
|
| 385 |
+ fp_buf = x509_get_sha1_fingerprint(x509, &gc); |
|
| 386 |
+ else |
|
| 387 |
+ fp_buf = x509_get_sha256_fingerprint(x509, &gc); |
|
| 388 |
+ |
|
| 389 |
+ fp_str = format_hex_ex(BPTR(&fp_buf), BLEN(&fp_buf), 0, |
|
| 390 |
+ 1 | FHE_CAPS, ":", &gc); |
|
| 391 |
+ do_setenv_x509(es, xt->name, fp_str, depth); |
|
| 383 | 392 |
} |
| 384 | 393 |
break; |
| 385 | 394 |
default: |