From f8242127a21377941ee582636e006cbaa2ab9433 Mon Sep 17 00:00:00 2001 From: Adrian-Ken Rueegsegger Date: Wed, 23 Sep 2020 18:59:23 +0200 Subject: [PATCH] charon-tkm: Add support for multiple CAs Load CA certificate id mapping from config and pass the correct CA ID to TKM when checking certificate chains. The mapping of CA certificate to CA ID is done via SHA-1 hash of the CA certificates subjectPublicKey. --- src/charon-tkm/src/charon-tkm.c | 7 ++ src/charon-tkm/src/tkm/tkm_listener.c | 145 +++++++++++++++++++++++++- src/charon-tkm/src/tkm/tkm_listener.h | 12 +++ 3 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/charon-tkm/src/charon-tkm.c b/src/charon-tkm/src/charon-tkm.c index b1cd967050..d049596939 100644 --- a/src/charon-tkm/src/charon-tkm.c +++ b/src/charon-tkm/src/charon-tkm.c @@ -323,6 +323,12 @@ int main(int argc, char *argv[]) goto deinit; } + if (!register_ca_mapping()) + { + DBG1(DBG_DMN, "no CA certificate ID mapping defined - aborting %s", dmn_name); + goto deinit; + } + /* register TKM keymat variant */ keymat_register_constructor(IKEV2, (keymat_constructor_t)tkm_keymat_create); @@ -404,6 +410,7 @@ int main(int argc, char *argv[]) deinit: destroy_dh_mapping(); + destroy_ca_mapping(); libcharon_deinit(); tkm_deinit(); unlink_pidfile(); diff --git a/src/charon-tkm/src/tkm/tkm_listener.c b/src/charon-tkm/src/tkm/tkm_listener.c index 36b9771a1b..9e39308f82 100644 --- a/src/charon-tkm/src/tkm/tkm_listener.c +++ b/src/charon-tkm/src/tkm/tkm_listener.c @@ -15,8 +15,10 @@ */ #include +#include #include +#include #include #include #include @@ -30,6 +32,8 @@ typedef struct private_tkm_listener_t private_tkm_listener_t; +static hashtable_t *ca_map = NULL; + /** * Private data of a tkm_listener_t object. */ @@ -155,9 +159,37 @@ static bool build_cert_chain(const ike_sa_t * const ike_sa, cc_id_type cc_id) cert = auth->get(auth, AUTH_RULE_CA_CERT); if (cert) { - const ca_id_type ca_id = 1; + ca_id_type ca_id; + public_key_t *pubkey; certificate_type ca_cert; - chunk_t enc_ca_cert; + chunk_t enc_ca_cert, fp; + uint64_t *raw_id; + + pubkey = cert->get_public_key(cert); + if (!pubkey) + { + DBG1(DBG_IKE, "unable to get CA certificate pubkey"); + rounds->destroy(rounds); + return FALSE; + } + if (!pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &fp)) + { + DBG1(DBG_IKE, "unable to extract CA certificate fingerprint"); + rounds->destroy(rounds); + pubkey->destroy(pubkey); + return FALSE; + } + pubkey->destroy(pubkey); + + raw_id = ca_map->get(ca_map, &fp); + if (!raw_id || *raw_id == 0) + { + DBG1(DBG_IKE, "error mapping CA certificate (fp: %#B) to " + "ID", &fp); + rounds->destroy(rounds); + return FALSE; + } + ca_id = *raw_id; if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_ca_cert)) { @@ -368,3 +400,112 @@ tkm_listener_t *tkm_listener_create() return &this->public; } + +static u_int hash(const chunk_t *key) +{ + return chunk_hash(*key); +} + +static bool equals(const chunk_t *key, const chunk_t *other_key) +{ + return chunk_equals(*key, *other_key); +} + +static u_int id_hash(const uint64_t *key) +{ + return chunk_hash(chunk_create((u_char*)key, sizeof(uint64_t))); +} + +static bool id_equals(const uint64_t *key, const uint64_t *other_key) +{ + return *key == *other_key; +} + +/* + * Described in header. + */ +int register_ca_mapping() +{ + char *section, *tkm_ca_id_str, *key_fp_str; + chunk_t *key_fp; + uint64_t *tkm_ca_id; + hashtable_t *id_map; + enumerator_t *enumerator; + bool err = FALSE; + + ca_map = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8); + id_map = hashtable_create((hashtable_hash_t)id_hash, + (hashtable_equals_t)id_equals, 8); + + enumerator = lib->settings->create_section_enumerator(lib->settings, + "%s.ca_mapping", + lib->ns); + while (enumerator->enumerate(enumerator, §ion)) + { + tkm_ca_id_str = lib->settings->get_str(lib->settings, + "%s.ca_mapping.%s.id", NULL, + lib->ns, section); + tkm_ca_id = malloc_thing(uint64_t); + *tkm_ca_id = settings_value_as_uint64(tkm_ca_id_str, 0); + + key_fp_str = lib->settings->get_str(lib->settings, + "%s.ca_mapping.%s.fingerprint", NULL, + lib->ns, section); + if (key_fp_str) + { + key_fp = malloc_thing(chunk_t); + *key_fp = chunk_from_hex(chunk_from_str(key_fp_str), NULL); + } + + if (!*tkm_ca_id || !key_fp_str || !key_fp->len || + id_map->get(id_map, tkm_ca_id) != NULL) + { + DBG1(DBG_CFG, "error adding CA ID mapping '%s': ID %s, FP '%s'", + section, tkm_ca_id_str, key_fp_str); + free(tkm_ca_id); + if (key_fp_str) + { + chunk_free(key_fp); + free(key_fp); + } + err = TRUE; + } + else + { + DBG2(DBG_CFG, "adding CA ID mapping '%s': ID %" PRIu64 ", FP '%#B'", + section, *tkm_ca_id, key_fp); + ca_map->put(ca_map, key_fp, tkm_ca_id); + /* track CA IDs for uniqueness, set value to not-NULL */ + id_map->put(id_map, tkm_ca_id, id_map); + } + } + enumerator->destroy(enumerator); + id_map->destroy(id_map); + + return err ? 0 : ca_map->get_count(ca_map); +} + +/* + * Described in header. + */ +void destroy_ca_mapping() +{ + enumerator_t *enumerator; + chunk_t *key; + uint64_t *value; + + if (ca_map) + { + enumerator = ca_map->create_enumerator(ca_map); + while (enumerator->enumerate(enumerator, &key, &value)) + { + chunk_free(key); + free(key); + free(value); + } + enumerator->destroy(enumerator); + ca_map->destroy(ca_map); + } + ca_map = NULL; +} diff --git a/src/charon-tkm/src/tkm/tkm_listener.h b/src/charon-tkm/src/tkm/tkm_listener.h index ea8770baaa..fa65cdf392 100644 --- a/src/charon-tkm/src/tkm/tkm_listener.h +++ b/src/charon-tkm/src/tkm/tkm_listener.h @@ -42,6 +42,18 @@ struct tkm_listener_t { void (*destroy)(tkm_listener_t *this); }; +/** + * Loads CA certificate fingerprint to TKM CA id mapping from config. + * + * @return number of registered mappings + */ +int register_ca_mapping(); + +/** + * Destroy CA certificate fingerprint to TKM CA id mapping. + */ +void destroy_ca_mapping(); + /** * Create a tkm_listener instance. * -- 2.47.2