diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2009-10-25 15:51:04 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2009-10-25 15:51:04 +0000 |
commit | ec4a500bb4f0c642fb4e013387d97aab3c516372 (patch) | |
tree | c56bc3a8991d0afe025e946f8a6b61296487dde7 | |
parent | Change to doval valgrind script. The openvpn command parameter is now (diff) | |
download | openvpn-ec4a500bb4f0c642fb4e013387d97aab3c516372.tar.xz |
On server, lock client-provided certs against mid-session TLS
renegotiations -- this is similer to how the common name is also
locked.
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5105 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r-- | multi.c | 3 | ||||
-rw-r--r-- | ssl.c | 137 | ||||
-rw-r--r-- | ssl.h | 21 |
3 files changed, 156 insertions, 5 deletions
@@ -1458,8 +1458,9 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi ASSERT (mi->context.c1.tuntap); - /* lock down the common name so it can't change during future TLS renegotiations */ + /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */ tls_lock_common_name (mi->context.c2.tls_multi); + tls_lock_cert_hash_set (mi->context.c2.tls_multi); /* generate a msg() prefix for this client instance */ generate_prefix (mi); @@ -340,6 +340,104 @@ tmp_rsa_cb (SSL * s, int is_export, int keylength) } /* + * Cert hash functions + */ +static void +cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_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)); + } + } + gc_free (&gc); +} +#endif + +static void +cert_hash_free (struct cert_hash_set *chs) +{ + if (chs) + { + int i; + for (i = 0; i < MAX_CERT_DEPTH; ++i) + free (chs->ch[i]); + free (chs); + } +} + +static bool +cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2) +{ + if (chs1 && chs2) + { + int i; + for (i = 0; i < MAX_CERT_DEPTH; ++i) + { + const struct cert_hash *ch1 = chs1->ch[i]; + const struct cert_hash *ch2 = chs2->ch[i]; + + if (!ch1 && !ch2) + continue; + else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH)) + continue; + else + return false; + } + return true; + } + else if (!chs1 && !chs2) + return true; + else + return false; +} + +static struct cert_hash_set * +cert_hash_copy (const struct cert_hash_set *chs) +{ + struct cert_hash_set *dest = NULL; + if (chs) + { + int i; + ALLOC_OBJ_CLEAR (dest, struct cert_hash_set); + for (i = 0; i < MAX_CERT_DEPTH; ++i) + { + const struct cert_hash *ch = chs->ch[i]; + if (ch) + { + ALLOC_OBJ (dest->ch[i], struct cert_hash); + memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH); + } + } + } + return dest; +} + +/* * Extract a field from an X509 subject name. * * Example: @@ -603,7 +701,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) SSL *ssl; struct tls_session *session; const struct tls_options *opt; - const int max_depth = 8; + const int max_depth = MAX_CERT_DEPTH; struct argv argv = argv_new (); /* get the tls_session pointer */ @@ -645,9 +743,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); + cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash); + #if 0 /* print some debugging info */ - msg (D_LOW, "LOCAL OPT: %s", opt->local_options); - msg (D_LOW, "X509: %s", subject); + { + struct gc_arena gc = gc_new (); + msg (M_INFO, "LOCAL OPT[%d]: %s", ctx->error_depth, opt->local_options); + msg (M_INFO, "X509[%d]: %s", ctx->error_depth, subject); + msg (M_INFO, "SHA1[%d]: %s", ctx->error_depth, format_hex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc)); + gc_free (&gc); + } #endif /* did peer present cert which was signed our root cert? */ @@ -898,6 +1003,14 @@ tls_lock_common_name (struct tls_multi *multi) multi->locked_cn = string_alloc (cn, NULL); } +void +tls_lock_cert_hash_set (struct tls_multi *multi) +{ + const struct cert_hash_set *chs = multi->session[TM_ACTIVE].cert_hash_set; + if (chs && !multi->locked_cert_hash_set) + multi->locked_cert_hash_set = cert_hash_copy (chs); +} + static bool tls_lock_username (struct tls_multi *multi, const char *username) { @@ -2251,6 +2364,8 @@ tls_session_free (struct tls_session *session, bool clear) if (session->common_name) free (session->common_name); + cert_hash_free (session->cert_hash_set); + if (clear) CLEAR (*session); } @@ -2444,6 +2559,8 @@ tls_multi_free (struct tls_multi *multi, bool clear) if (multi->locked_username) free (multi->locked_username); + cert_hash_free (multi->locked_cert_hash_set); + for (i = 0; i < TM_SIZE; ++i) tls_session_free (&multi->session[i], false); @@ -3492,6 +3609,20 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi } } + /* Don't allow the cert hashes to change once they have been locked */ + if (ks->authenticated && multi->locked_cert_hash_set) + { + const struct cert_hash_set *chs = session->cert_hash_set; + if (chs && !cert_hash_compare (chs, multi->locked_cert_hash_set)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN=%s client-provided SSL certs unexpectedly changed during mid-session reauth", + session->common_name); + + /* disable the tunnel */ + tls_deauthenticate (multi); + } + } + /* verify --client-config-dir based authentication */ if (ks->authenticated && session->opt->client_config_dir_exclusive) { @@ -303,6 +303,21 @@ /* #define MEASURE_TLS_HANDSHAKE_STATS */ /* + * Keep track of certificate hashes at various depths + */ + +/* Maximum certificate depth we will allow */ +#define MAX_CERT_DEPTH 8 + +struct cert_hash { + unsigned char sha1_hash[SHA_DIGEST_LENGTH]; +}; + +struct cert_hash_set { + struct cert_hash *ch[MAX_CERT_DEPTH]; +}; + +/* * Key material, used as source for PRF-based * key expansion. */ @@ -518,6 +533,8 @@ struct tls_session char *common_name; + struct cert_hash_set *cert_hash_set; + #ifdef ENABLE_PF uint32_t common_name_hashval; #endif @@ -589,10 +606,11 @@ struct tls_multi int n_soft_errors; /* errors due to unrecognized or failed-to-authenticate incoming packets */ /* - * Our locked common name and username (cannot change during the life of this tls_multi object) + * Our locked common name, username, and cert hashes (cannot change during the life of this tls_multi object) */ char *locked_cn; char *locked_username; + struct cert_hash_set *locked_cert_hash_set; #ifdef ENABLE_DEF_AUTH /* @@ -692,6 +710,7 @@ bool tls_rec_payload (struct tls_multi *multi, const char *tls_common_name (const struct tls_multi* multi, const bool null); void tls_set_common_name (struct tls_multi *multi, const char *common_name); void tls_lock_common_name (struct tls_multi *multi); +void tls_lock_cert_hash_set (struct tls_multi *multi); #define TLS_AUTHENTICATION_SUCCEEDED 0 #define TLS_AUTHENTICATION_FAILED 1 |