From: Gary Lockyer Date: Mon, 1 Sep 2025 21:59:13 +0000 (+1200) Subject: s4:kdc:sdb Support Windows flexible cert mappings X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14d9a1be89557c3c7bca13be7410a47b7b6bf511;p=thirdparty%2Fsamba.git s4:kdc:sdb Support Windows flexible cert mappings Extract certificate mappings from the altSecurityIdentities attribute and populate the new sdb_certificate_mappings element of sdb Signed-off-by: Gary Lockyer Reviewed-by: Jennifer Sutton --- diff --git a/source4/auth/sam.c b/source4/auth/sam.c index 32d90712aae..55bcd746b7c 100644 --- a/source4/auth/sam.c +++ b/source4/auth/sam.c @@ -69,7 +69,9 @@ "msDS-ManagedPasswordInterval", \ "whenCreated", \ /* Required for Key Trust authentication */\ - "msDS-KeyCredentialLink" + "msDS-KeyCredentialLink", \ + /* Required for certificate mapping */ \ + "altSecurityIdentities" #define AUTHN_POLICY_ATTRS \ /* Required for authentication policies / silos */ \ diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index aecf51f2443..c7dc01fb812 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -47,7 +47,9 @@ #include "lib/messaging/irpc.h" #include "librpc/gen_ndr/ndr_keycredlink.h" #include "talloc.h" +#include "util/data_blob.h" #include "util/debug.h" +#include "util/samba_util.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_KERBEROS @@ -1187,6 +1189,467 @@ static krb5_error_code samba_kdc_get_entry_principal( return code; } + + +/** + * @brief Copy the contents of a data blob to a krb5_data element + * + * @param[in] blob The source data blob + * @param[out] krb5 The target krb5_data element + * + * @return 0 No error + * ENOMEM memory allocation error + * + * @note Memory is allocated, with malloc and needs to be freed + */ +static krb5_error_code data_blob_to_krb5_data( DATA_BLOB *blob, krb5_data *krb5) +{ + krb5->data = malloc(blob->length); + if (krb5->data == NULL) { + return ENOMEM; + } + memcpy(krb5->data, blob->data, blob->length); + krb5->length = blob->length; + return 0; +} + + +/** + * @brief Copy the contents of a hex string data blob to a binary + * krb5_data element + * + * @param[in] blob The source data blob + * @param[out] krb5 The target krb5_data element + * + * @return 0 No error + * ENOMEM memory allocation error + * EINVAL data blob is not a valid hex string encoding + * + * @note Memory is allocated, with malloc and needs to be freed + */ +static krb5_error_code db_hex_str_to_krb5_data( + DATA_BLOB *blob, + krb5_data *krb5) +{ + + size_t size = 0; + + if( (blob->length%2) != 0) { + DBG_ERR( + "Hex string [%*.*s] " + "does not have an even length", + (int) blob->length, + (int) blob->length, + (char *) blob->data); + return EINVAL; + } + krb5->length = (blob->length/2); + krb5->data = malloc(krb5->length); + if (krb5->data == NULL) { + krb5->length = 0; + return ENOMEM; + } + size = strhex_to_str(krb5->data, + krb5->length, + (const char *) blob->data, + blob->length); + if (size != krb5->length) { + krb5->length = 0; + SAFE_FREE(krb5->data); + return EINVAL; + } + return 0; +} + +/* + * Helper macro to populate the data blob constants used by + * populate_certificate_mapping and parse_certificate_mapping + */ +#define DATA_BLOB_STRING(str) {\ + .data = discard_const_p(uint8_t, str), \ + .length = sizeof(str) - 1 \ +} +static const DATA_BLOB ISSUER_NAME = DATA_BLOB_STRING("I"); +static const DATA_BLOB SUBJECT_NAME = DATA_BLOB_STRING("S"); +static const DATA_BLOB SERIAL_NUMBER = DATA_BLOB_STRING("SR"); +static const DATA_BLOB SUBJECT_KEY_IDENTIFIER = DATA_BLOB_STRING("SKI"); +static const DATA_BLOB PUBLIC_KEY = DATA_BLOB_STRING("SHA1-PUKEY"); +static const DATA_BLOB RFC822 = DATA_BLOB_STRING("RFC822"); +static const DATA_BLOB X509_HEADER = DATA_BLOB_STRING("X509:"); +#undef DATA_BLOB_STRING + +/** + * @brief Populate the certificate mapping from the tag and value + * + * @param[in] tag the tag i.e. I, S, SKI, ..... + * @param[in] value the value associated with the tag + * @param[in,out] mapping the mapping to be updated + * + * @return 0 No error + * EINVAL tag or value are invalid + * ENOMEM memory allocation error + * + * @note Memory is allocated, with malloc and needs to be freed with + * sdb_certificate_mapping_free + */ +static krb5_error_code populate_certificate_mapping( + DATA_BLOB *tag, + DATA_BLOB *value, + struct sdb_certificate_mapping *mapping) +{ + krb5_error_code ret = 0; + + if (tag->length == 0) { + DBG_WARNING("altSecurityIdentities empty tag"); + return EINVAL; + } + if (value->length == 0) { + DBG_WARNING("altSecurityIdentities no value for %*.*s", + (int) tag->length, + (int) tag->length, + tag->data); + return EINVAL; + } + + if (data_blob_cmp(&ISSUER_NAME, tag) == 0) { + /* discard any previous value */ + if (mapping->issuer_name.data != NULL) { + SAFE_FREE(mapping->issuer_name.data); + mapping->issuer_name.length = 0; + } + ret = data_blob_to_krb5_data(value, &mapping->issuer_name); + + } else if (data_blob_cmp(&SUBJECT_NAME, tag) == 0) { + /* discard any previous value */ + if (mapping->subject_name.data != NULL) { + SAFE_FREE(mapping->subject_name.data); + mapping->subject_name.length = 0; + } + ret = data_blob_to_krb5_data(value, &mapping->subject_name); + + } else if (data_blob_cmp(&RFC822, tag) == 0) { + /* discard any previous value */ + if (mapping->rfc822.data != NULL) { + SAFE_FREE(mapping->rfc822.data); + mapping->rfc822.length = 0; + } + ret = data_blob_to_krb5_data(value, &mapping->rfc822); + + } else if (data_blob_cmp(&SERIAL_NUMBER, tag ) == 0) { + /* discard any previous value */ + if (mapping->serial_number.data != NULL) { + SAFE_FREE(mapping->serial_number.data); + mapping->serial_number.length = 0; + } + ret = db_hex_str_to_krb5_data(value, &mapping->serial_number); + + } else if (data_blob_cmp(&SUBJECT_KEY_IDENTIFIER, tag) == 0) { + /* discard any previous value */ + if (mapping->ski.data != NULL) { + SAFE_FREE(mapping->ski.data); + mapping->ski.length = 0; + } + ret = db_hex_str_to_krb5_data(value, &mapping->ski); + + } else if (data_blob_cmp(&PUBLIC_KEY, tag) == 0) { + /* discard any previous value */ + if (mapping->public_key.data != NULL) { + SAFE_FREE(mapping->public_key.data); + mapping->public_key.length = 0; + } + ret = db_hex_str_to_krb5_data(value, &mapping->public_key); + + } else { + DBG_WARNING("altSecurityIdentities invalid tag %*.*s", + (int) tag->length, + (int) tag->length, + tag->data); + ret = EINVAL; + } + return ret; +} + + +/** + * @brief does the krb5 element have a value? + * + * @param[in] krb5 The target krb5_data element + * + * @return TRUE krb5 has a value + * FALSE krb5 has no value i.e. it's empty + */ +static krb5_boolean krb5_data_has_value(krb5_data *krb5) +{ + if (krb5->data == NULL || krb5->length == 0) { + return FALSE; + } + return TRUE; +} +/** + * @brief is the certificate mapping a strong mapping? + * + * @param[in] mapping the certificate mapping to examine. + * + * @return TRUE mapping is strong + * FALSE mapping is weak + */ +static krb5_boolean is_strong_certificate_mapping( + struct sdb_certificate_mapping *mapping) +{ + /* Subject Key Identifier */ + if (krb5_data_has_value(&mapping->ski)) { + return TRUE; + } + /* Public Key */ + if (krb5_data_has_value(&mapping->public_key)) { + return TRUE; + } + /* Issuer Serial Number */ + if (krb5_data_has_value(&mapping->issuer_name) && + krb5_data_has_value(&mapping->serial_number) + ) { + return TRUE; + } + return FALSE; +} + + +/** + * @brief Parse a certificate mapping string + * + * The expected format is a header "X509:" and then a series of + * tag value pairs "value" + * where tag is one of: + * Issuer Name + * Subject Name + * Serial Number + * SKI Subject Key Identifier + * SHA1 checksum of the public key + * Email address + * + * + * @param[in] value ldb value containing an altSecurityIdentities entry + * @param[out] mapping data parsed from value + * + * @note it is the callers responsibility to free any memory allocated + * in the mapping with a call to sdb_certificate_mapping_free. + * EVEN if an error is returned, as mapping may have been partially + * updated. + * + * @return 0 No error + * EINVAL altSecurityIdentities entry was invalid + * ENOMEM memory allocation error + */ +static krb5_error_code parse_certificate_mapping( + struct ldb_val *ldb_value, + struct sdb_certificate_mapping *mapping) +{ + krb5_error_code ret = 0; + size_t length = ldb_value->length; + uint8_t *data = ldb_value->data; + DATA_BLOB tag = data_blob_null; + DATA_BLOB value = data_blob_null; + enum { + start_state, + tag_state, + value_state + } state; + size_t i = 0; + + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + /* + * Ensure that there is data, and it starts with X509: + * other wise ignore the entry and return ENOENT + */ + if (data == NULL || length == 0) { + DBG_DEBUG("altSecurityIdentities, is empty"); + ret = ENOENT; + goto out; + } + if (length <= X509_HEADER.length || + memcmp(X509_HEADER.data, data, X509_HEADER.length) != 0) { + DBG_DEBUG("altSecurityIdentities, entry is not X509 ignoring"); + ret = ENOENT; + goto out; + } + + tag = data_blob_talloc(tmp_ctx, NULL, ldb_value->length + 1); + if (tag.data == NULL) { + ret = ENOMEM; + goto out; + } + tag.length = 0; + value = data_blob_talloc(tmp_ctx, NULL, ldb_value->length + 1); + if (value.data == NULL) { + ret = ENOMEM; + goto out; + } + value.length = 0; + + state = start_state; + /* point to the first byte after the header "X509:" */ + for( i = 5; i < length; i++) { + uint8_t c = data[i]; + switch (state) { + case start_state: + /* Ignore characters between the : and the first < */ + if (c == '<') { + state = tag_state; + tag.length = 0; + } + break; + case tag_state: + if (c == '>') { + state = value_state; + tag.data[tag.length] = '\0'; + value.length = 0; + } else { + tag.data[tag.length] = c; + tag.length++; + } + break; + case value_state: + if (c == '<') { + value.data[value.length] = '\0'; + ret = populate_certificate_mapping( + &tag, &value, mapping); + if (ret != 0) { + goto out; + } + state = tag_state; + value.length = 0; + tag.length = 0; + } else { + value.data[value.length] = c; + value.length++; + } + break; + } + } + if (state != value_state) { + DBG_WARNING("altSecurityIdentities, expected a value"); + ret = EINVAL; + goto out; + } + value.data[value.length] = '\0'; + ret = populate_certificate_mapping( + &tag, &value, mapping); + if (ret != 0) { + goto out; + } + mapping->strong_mapping = is_strong_certificate_mapping(mapping); + +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + + +/** + * @brief extract the certificate mappings for PKINIT from the + * ldb message. + * + * Processes the "X509:" certificate mappings in altSecurityIdentities. + * + * @param mem_ctx[in] talloc memory context + * @param lp_ctx[in] parameter context containing the config options + * @param msg[in] ldb message containing the certificate mappings + * @param entry[out] entry will be updated with the certificate mappings + * + * @note Invalid entries will be ignored + * + * @return 0 No error, and there are zero or more certificate mappings + * >0 Errors detected + */ +static krb5_error_code get_certificate_mappings( + TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct ldb_message *msg, + struct sdb_entry *entry) +{ + krb5_error_code ret = 0; + struct ldb_message_element *el = NULL; + size_t i = 0; + struct sdb_certificate_mappings mappings = {}; + unsigned int backdating = 0; + time_t created = 0; + + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + mappings.enforcement_mode = + lpcfg_strong_certificate_binding_enforcement(lp_ctx); + + backdating = lpcfg_certificate_backdating_compensation(lp_ctx); + created = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); + if (created == 0) { + DBG_ERR("No whenCreated entry, unable to continue"); + ret = EINVAL; + goto out; + } + mappings.valid_certificate_start = created - (backdating * 60); + + el = ldb_msg_find_element(msg, "altSecurityIdentities"); + if (el == NULL || el->num_values == 0) { + DBG_DEBUG("No altSecurityIdentities nothing to do"); + ret = 0; + entry->mappings = mappings; + goto out; + } + + for (i = 0; i < el->num_values; i++) { + struct sdb_certificate_mapping mapping = {}; + ret = parse_certificate_mapping(&el->values[i], &mapping); + if (ret != 0) { + DBG_DEBUG("Ignoring invalid altSecurityIdentities" + " entry [%*.*s]", + (int)el->values[i].length, + (int)el->values[i].length, + (char *)el->values[i].data); + sdb_certificate_mapping_free(&mapping); + continue; + } + if (mappings.mappings == NULL) { + mappings.len = 0; + mappings.mappings = calloc(1, sizeof(mapping)); + if (mappings.mappings == NULL) { + sdb_certificate_mapping_free(&mapping); + ret = ENOMEM; + goto out; + } + } else { + struct sdb_certificate_mapping *old_mappings = + mappings.mappings; + mappings.mappings= realloc_p( + mappings.mappings, + struct sdb_certificate_mapping, + mappings.len + 1); + if (mappings.mappings == NULL) { + mappings.mappings = old_mappings; + sdb_certificate_mappings_free(&mappings); + sdb_certificate_mapping_free(&mapping); + ret = ENOMEM; + goto out; + } + } + mappings.mappings[mappings.len] = mapping; + mappings.len++; + } + entry->mappings = mappings; + ret = 0; + +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + + /** * @brief Extract the KeyMaterial from a KEYCREDENTIALLINK_BLOB * as a KeyMaterialInternal structure. @@ -1232,7 +1695,6 @@ static krb5_error_code unpack_key_credential_link_blob( } *pub_key = NULL; - /* Unpack the binary DN */ dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, value, DSDB_SYNTAX_BINARY_DN); if (dsdb_dn == NULL) { DBG_WARNING("Unable to parse KEYCREDENTIALLINK_BLOB, BinaryDn"); @@ -2189,6 +2651,11 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, if (ret != 0) { goto out; } + ret = get_certificate_mappings( + tmp_ctx, kdc_db_ctx->lp_ctx, msg, entry); + if (ret != 0) { + goto out; + } talloc_steal(kdc_db_ctx, p); diff --git a/source4/kdc/sdb.c b/source4/kdc/sdb.c index d9399452eaa..280e7930480 100644 --- a/source4/kdc/sdb.c +++ b/source4/kdc/sdb.c @@ -105,6 +105,48 @@ void sdb_pub_keys_free(struct sdb_pub_keys *keys) ZERO_STRUCTP(keys); } +/** + * @brief free the memory allocated to a sdb certificate mapping structure. + * + * @param[in,out] m mapping to be freed, will be zeroed on return + */ +void sdb_certificate_mapping_free(struct sdb_certificate_mapping *m) +{ + if (m == NULL) { + return; + } + + SAFE_FREE(m->subject_name.data); + SAFE_FREE(m->issuer_name.data); + SAFE_FREE(m->serial_number.data); + SAFE_FREE(m->ski.data); + SAFE_FREE(m->public_key.data); + SAFE_FREE(m->rfc822.data); + + ZERO_STRUCTP(m); +} +/** + * + * @brief free the memory allocated to a sdb certificate mappings structure. + * + * @param[in,out] m mappings to be freed, will be zeroed on return + */ +void sdb_certificate_mappings_free(struct sdb_certificate_mappings *m) +{ + unsigned int i; + + if (m == NULL) { + return; + } + + for (i = 0; i < m->len; i++) { + sdb_certificate_mapping_free(&m->mappings[i]); + } + + SAFE_FREE(m->mappings); + ZERO_STRUCTP(m); +} + void sdb_entry_free(struct sdb_entry *s) { if (s->skdc_entry != NULL) { @@ -121,6 +163,7 @@ void sdb_entry_free(struct sdb_entry *s) sdb_keys_free(&s->keys); sdb_pub_keys_free(&s->pub_keys); + sdb_certificate_mappings_free(&s->mappings); if (s->etypes != NULL) { SAFE_FREE(s->etypes->val); diff --git a/source4/kdc/sdb.h b/source4/kdc/sdb.h index 59676c2842c..f7efc1f304c 100644 --- a/source4/kdc/sdb.h +++ b/source4/kdc/sdb.h @@ -95,6 +95,25 @@ struct sdb_pub_keys { struct sdb_pub_key *keys; }; +struct sdb_certificate_mappings { + int enforcement_mode; + time_t valid_certificate_start; + unsigned int len; + struct sdb_certificate_mapping *mappings; +}; + + +struct sdb_certificate_mapping { + krb5_boolean strong_mapping; + krb5_data issuer_name; + krb5_data subject_name; + krb5_data serial_number; + krb5_data ski; + krb5_data public_key; + krb5_data rfc822; +}; + + struct sdb_entry { struct samba_kdc_entry *skdc_entry; krb5_principal principal; @@ -113,6 +132,7 @@ struct sdb_entry { int *max_renew; struct SDBFlags flags; struct sdb_pub_keys pub_keys; + struct sdb_certificate_mappings mappings; }; #define SDB_ERR_NOENTRY 36150275 @@ -159,6 +179,8 @@ struct sdb_entry { #define SDB_F_FORCE_CANON 0x4000 /* force canonicalization */ #define SDB_F_RODC_NUMBER_SPECIFIED 0x8000 /* we want a particular RODC number */ +void sdb_certificate_mapping_free(struct sdb_certificate_mapping *m); +void sdb_certificate_mappings_free(struct sdb_certificate_mappings *m); void sdb_pub_key_free(struct sdb_pub_key *key); void sdb_pub_keys_free(struct sdb_pub_keys *keys); void sdb_key_free(struct sdb_key *key); diff --git a/source4/kdc/tests/db-glue-test.c b/source4/kdc/tests/db-glue-test.c index caf365d1093..d98b143779d 100644 --- a/source4/kdc/tests/db-glue-test.c +++ b/source4/kdc/tests/db-glue-test.c @@ -43,6 +43,7 @@ #include "krb5-protos.h" #include "ldb.h" #include "samdb/samdb.h" +#include "sdb.h" #include "talloc.h" #include "util/data_blob.h" #include "util/debug.h" @@ -56,34 +57,82 @@ int dsdb_functional_level(struct ldb_context *ldb) return 1; } +int certificate_binding_enforcement = 0; +int lpcfg_strong_certificate_binding_enforcement( + struct loadparm_context *lp_ctx) +{ + return certificate_binding_enforcement; +} + +int certificate_backdating_compensation = 0; +int lpcfg_certificate_backdating_compensation( + struct loadparm_context *lp_ctx) +{ + return certificate_backdating_compensation; +} + /****************************************************************************** * Test helper functions *****************************************************************************/ -static void add_msDS_KeyCredentialLink(TALLOC_CTX *mem_ctx, - struct ldb_message *msg, +static void add_msDS_KeyCredentialLink(struct ldb_message *msg, size_t size, uint8_t *data) { DATA_BLOB key_cred_val = {.length = size, .data = data}; - char *hex_value = data_blob_hex_string_upper(mem_ctx, &key_cred_val); + char *hex_value = data_blob_hex_string_upper(msg, &key_cred_val); size_t hex_len = strlen(hex_value); char *binary_dn = talloc_asprintf( - mem_ctx, "B:%zu:%s:DC=EXAMPLE,DC=COM", hex_len, hex_value); + msg, "B:%zu:%s:DC=EXAMPLE,DC=COM", hex_len, hex_value); TALLOC_FREE(hex_value); /* Add the data to msDS-KeyCredentialLink */ ldb_msg_add_string(msg, "msDS-KeyCredentialLink", binary_dn); } +static struct ldb_val *get_ldb_string(TALLOC_CTX *mem_ctx, const char * str) { + char *string = talloc_asprintf( + mem_ctx, "%s", str); + + size_t len = strlen(string); + + struct ldb_val *value = talloc_zero(mem_ctx, struct ldb_val); + + value->data = (uint8_t *) string; + value->length = len; + return value; +} + +static void add_altSecurityIdentities(struct ldb_message *msg, + const char *str) +{ + /* Add the data to altSecurityIdentities */ + ldb_msg_add_string(msg, "altSecurityIdentities", str); +} + +static void add_whenCreated(struct ldb_message *msg, + time_t created) +{ + char* ts = ldb_timestring(msg, created); + ldb_msg_add_string(msg, "whenCreated", ts); +} + static void add_empty_msDS_KeyCredentialLink_DN(TALLOC_CTX *mem_ctx, struct ldb_message *msg) { - char *binary_dn = talloc_asprintf(mem_ctx, "B:0::DC=EXAMPLE,DC=COM"); + char *binary_dn = talloc_asprintf(msg, "B:0::DC=EXAMPLE,DC=COM"); /* Add the data to msDS-KeyCredentialLink */ ldb_msg_add_string(msg, "msDS-KeyCredentialLink", binary_dn); } +static void add_empty_altSecurities( + TALLOC_CTX *mem_ctx, + struct ldb_message *msg) +{ + /* Add an empty altSecurityIdentities */ + ldb_msg_add_string(msg, "altSecurityIdentifiers", ""); +} + static struct ldb_message *create_ldb_message(TALLOC_CTX *mem_ctx) { DATA_BLOB sid_val = data_blob_null; @@ -473,6 +522,7 @@ static void empty_message2entry(void **state) "atestuser@test.samba.org"); msg = ldb_msg_new(mem_ctx); + err = samba_kdc_message2entry(krb5_ctx, kdc_db_ctx, mem_ctx, @@ -515,6 +565,7 @@ static void minimal_message2entry(void **state) struct sdb_entry entry = {}; krb5_error_code err = 0; + time_t now = time(NULL); /* Set up */ smb_krb5_init_context_common(&krb5_ctx); @@ -525,6 +576,7 @@ static void minimal_message2entry(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); + add_whenCreated(msg, now); err = samba_kdc_message2entry(krb5_ctx, kdc_db_ctx, @@ -570,6 +622,7 @@ static void empty_binary_dn_message2entry(void **state) struct sdb_entry entry = {}; krb5_error_code err = 0; + time_t now = time(NULL); /* Set up */ smb_krb5_init_context_common(&krb5_ctx); @@ -582,6 +635,7 @@ static void empty_binary_dn_message2entry(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); add_empty_msDS_KeyCredentialLink_DN(mem_ctx, msg); + add_whenCreated(msg, now); err = samba_kdc_message2entry(krb5_ctx, kdc_db_ctx, @@ -627,6 +681,7 @@ static void msDS_KeyCredentialLink_message2entry(void **state) struct sdb_entry entry = {}; krb5_error_code err = 0; + time_t now = time(NULL); /* Set up */ smb_krb5_init_context_common(&krb5_ctx); @@ -638,10 +693,10 @@ static void msDS_KeyCredentialLink_message2entry(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(BCRYPT_KEY_CREDENTIAL_LINK), BCRYPT_KEY_CREDENTIAL_LINK); + add_whenCreated(msg, now); err = samba_kdc_message2entry(krb5_ctx, kdc_db_ctx, @@ -706,8 +761,7 @@ static void invalid_version_keycredlink(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(KEY_CREDENTIAL_LINK), KEY_CREDENTIAL_LINK); @@ -755,8 +809,7 @@ static void duplicate_key_material_keycredlink(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(KEY_CREDENTIAL_LINK), KEY_CREDENTIAL_LINK); @@ -800,8 +853,7 @@ static void duplicate_key_usage_keycredlink(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(KEY_CREDENTIAL_LINK), KEY_CREDENTIAL_LINK); @@ -844,8 +896,7 @@ static void invalid_key_usage_keycredlink(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(KEY_CREDENTIAL_LINK), KEY_CREDENTIAL_LINK); @@ -888,8 +939,7 @@ static void invalid_key_material_keycredlink(void **state) /* Create the ldb_message */ msg = ldb_msg_new(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(KEY_CREDENTIAL_LINK), KEY_CREDENTIAL_LINK); @@ -921,8 +971,7 @@ static void keycred_bcrypt_key_material(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(BCRYPT_KEY_CREDENTIAL_LINK), BCRYPT_KEY_CREDENTIAL_LINK); @@ -966,8 +1015,7 @@ static void keycred_tpm_key_material(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(TPM_KEY_CREDENTIAL_LINK), TPM_KEY_CREDENTIAL_LINK); @@ -1011,8 +1059,7 @@ static void keycred_der_key_material(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(DER_KEY_CREDENTIAL_LINK), DER_KEY_CREDENTIAL_LINK); @@ -1056,16 +1103,13 @@ static void keycred_multiple(void **state) /* Create the ldb_message */ msg = create_ldb_message(mem_ctx); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(DER_KEY_CREDENTIAL_LINK), DER_KEY_CREDENTIAL_LINK); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(TPM_KEY_CREDENTIAL_LINK), TPM_KEY_CREDENTIAL_LINK); - add_msDS_KeyCredentialLink(mem_ctx, - msg, + add_msDS_KeyCredentialLink(msg, sizeof(BCRYPT_KEY_CREDENTIAL_LINK), BCRYPT_KEY_CREDENTIAL_LINK); @@ -1124,6 +1168,815 @@ static void keycred_multiple(void **state) TALLOC_FREE(mem_ctx); } + + +/* + * Ensure that parse_certificate_mapping handles an empty ldb string value + */ +static void empty_string_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, ""); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(ENOENT, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an ldb string value containing + * just the X509: tag + */ +static void header_only_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(ENOENT, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an ldb string value containing + * a non X509 mapping + */ +static void not_x509_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "KERBEROS:"); + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(ENOENT, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an ldb string value without + * a tag + */ +static void no_tag_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:No tag here"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(EINVAL, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an ldb string value without + * a tag close character '>' + */ +static void no_tag_close_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:Empty tag"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(EINVAL, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an ldb string value with + * no value + */ +static void no_value_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(EINVAL, err); + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an issuer name + * + */ +static void issuer_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:Issuer"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(6, mapping.issuer_name.length); + assert_memory_equal("Issuer", mapping.issuer_name.data, 6); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles duplicate issuer names + * + */ +static void duplicate_issuer_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value + = get_ldb_string(mem_ctx, "X509:IssuerDuplicate"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + + /* Only use the last value in the event of duplicate values */ + assert_int_equal(9, mapping.issuer_name.length); + assert_memory_equal("Duplicate", mapping.issuer_name.data, 9); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles a subject name + * + */ +static void subject_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string(mem_ctx, "X509:Subject"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(7, mapping.subject_name.length); + assert_memory_equal("Subject", mapping.subject_name.data, 7); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles duplicate subject names + * + */ +static void duplicate_subject_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = + get_ldb_string(mem_ctx, "X509:SubjectA repeat"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + /* Only use the last value in the event of duplicate values */ + assert_int_equal(8, mapping.subject_name.length); + assert_memory_equal("A repeat", mapping.subject_name.data, 8); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an issuer name and subject + * name. + * + */ +static void issuer_and_subject_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:SubjectsNameTheNameOfTheIssuer"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(12, mapping.subject_name.length); + assert_memory_equal("SubjectsName", mapping.subject_name.data, 12); + assert_int_equal(18, mapping.issuer_name.length); + assert_memory_equal("TheNameOfTheIssuer", mapping.issuer_name.data, 18); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles a serial number + * + */ +static void serial_number_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value + = get_ldb_string(mem_ctx, "X509:0123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(sn), mapping.serial_number.length); + assert_memory_equal(sn, mapping.serial_number.data, sizeof(sn)); + /* The Serial number on it's own is not a strong mapping */ + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles multiple serial numbers + * + */ +static void duplicate_serial_number_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value + = get_ldb_string( + mem_ctx, + "X509:fedcba987654100123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(sn), mapping.serial_number.length); + assert_memory_equal(sn, mapping.serial_number.data, sizeof(sn)); + /* The Serial number on it's own is not a strong mapping */ + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles a serial number and + * issuer name + * + */ +static void serial_number_and_issuer_name_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:0123456789abcdefTheIssuer"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(sn), mapping.serial_number.length); + assert_memory_equal(sn, mapping.serial_number.data, sizeof(sn)); + assert_int_equal(9, mapping.issuer_name.length); + assert_memory_equal("TheIssuer", mapping.issuer_name.data, 9); + assert_true(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an SKI (Subject Key Identifier) + */ +static void ski_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t ski[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:0123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(ski), mapping.ski.length); + assert_memory_equal(ski, mapping.ski.data, sizeof(ski)); + assert_true(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles multiple + * SKI (Subject Key Identifier) values + */ +static void duplicate_ski_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t ski[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:0102030405060123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(ski), mapping.ski.length); + assert_memory_equal(ski, mapping.ski.data, sizeof(ski)); + assert_true(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles a public key + */ +static void public_key_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t pubkey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:0123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(pubkey), mapping.public_key.length); + assert_memory_equal(pubkey, mapping.public_key.data, sizeof(pubkey)); + assert_true(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles multiple public keys + */ +static void duplicate_public_key_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + uint8_t pubkey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + struct ldb_val *value = get_ldb_string( + mem_ctx, + "X509:adcdefabcdefabcdef" + "0123456789abcdef"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(sizeof(pubkey), mapping.public_key.length); + assert_memory_equal(pubkey, mapping.public_key.data, sizeof(pubkey)); + assert_true(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that non hex strings are rejected + */ +static void non_hex_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:This is not a hex string"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(EINVAL, err); + assert_int_equal(0, mapping.public_key.length); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that odd length hex strings are rejected + */ +static void odd_length_hex_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:abcde"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(EINVAL, err); + assert_int_equal(0, mapping.public_key.length); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Ensure that parse_certificate_mapping handles an RFC822 identifier + */ +static void RFC822_parse_certificate_mapping(void **state) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + krb5_error_code err = 0; + struct sdb_certificate_mapping mapping = {}; + struct ldb_val *value = get_ldb_string( + mem_ctx, "X509:test@example.com"); + + err = parse_certificate_mapping(value, &mapping); + + assert_int_equal(0, err); + assert_int_equal(16, mapping.rfc822.length); + assert_memory_equal("test@example.com", mapping.rfc822.data, 16); + assert_false(mapping.strong_mapping); + + sdb_certificate_mapping_free(&mapping); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles multiple entries + * + */ +static void multiple_cert_mappings(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + uint8_t ski[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + + time_t now = time(NULL); + const int backdate = 26280000; /* Fifty years */ + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_altSecurityIdentities(msg, + "X509:0123456789abcdef"); + add_altSecurityIdentities(msg, + "X509:test@example.com"); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 1; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(2, entry.mappings.len); + assert_int_equal(1, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + assert_int_equal(sizeof(ski), entry.mappings.mappings[0].ski.length); + assert_memory_equal( + ski, entry.mappings.mappings[0].ski.data, sizeof(ski)); + assert_true(entry.mappings.mappings[0].strong_mapping); + + assert_int_equal(16, entry.mappings.mappings[1].rfc822.length); + assert_memory_equal( + "test@example.com", entry.mappings.mappings[1].rfc822.data, 16); + assert_false(entry.mappings.mappings[1].strong_mapping); + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles a single entry + * + */ +static void single_cert_mapping(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + uint8_t ski[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + + time_t now = time(NULL); + const int backdate = 525600; /* One year */ + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_altSecurityIdentities(msg, + "X509:0123456789abcdef"); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 2; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(1, entry.mappings.len); + assert_int_equal(2, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + assert_int_equal(sizeof(ski), entry.mappings.mappings[0].ski.length); + assert_memory_equal( + ski, entry.mappings.mappings[0].ski.data, sizeof(ski)); + assert_true(entry.mappings.mappings[0].strong_mapping); + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles an ldb message with no + * altSecurityIdentities attribute + * + */ +static void cert_mapping_no_altSecurityIdentities(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + + time_t now = time(NULL); + const int backdate = 10080; /* 1 week */ + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 0; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(0, entry.mappings.len); + assert_int_equal(0, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles an ldb message with an + * altSecurityIdentities attribute containing no X509 entries + * + */ +static void no_X509_altSecurityIdentities(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + + time_t now = time(NULL); + const int backdate = 1440; /* 24 hours */ + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_altSecurityIdentities(msg, + "KERBEROS:0123456789abcdef"); + add_altSecurityIdentities(msg, + "ANOTHER:0123456789abcdef"); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 0; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(0, entry.mappings.len); + assert_int_equal(0, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles an ldb message with an + * altSecurityIdentities attribute containing X509,and KERBEROS + * entries. + * + */ +static void mixed_altSecurityIdentities(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + uint8_t ski[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + + time_t now = time(NULL); + const int backdate = 10; + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_altSecurityIdentities(msg, + "X509:0123456789abcdef"); + add_altSecurityIdentities(msg, + "KERBEROS:0123456789abcdef"); + add_altSecurityIdentities(msg, + "X509:test@example.com"); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 0; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(2, entry.mappings.len); + assert_int_equal(0, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + assert_int_equal(sizeof(ski), entry.mappings.mappings[0].ski.length); + assert_memory_equal( + ski, entry.mappings.mappings[0].ski.data, sizeof(ski)); + assert_true(entry.mappings.mappings[0].strong_mapping); + + assert_int_equal(16, entry.mappings.mappings[1].rfc822.length); + assert_memory_equal( + "test@example.com", entry.mappings.mappings[1].rfc822.data, 16); + assert_false(entry.mappings.mappings[1].strong_mapping); + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + +/* + * Test get_certificate_mapping handles an empty + * altSecurityIdentities attribute + * + */ +static void cert_mapping_empty_altSecurityIdentities(void **state) +{ + + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + struct loadparm_context *lp_ctx = loadparm_init(mem_ctx); + struct ldb_message *msg = NULL; + krb5_error_code err = 0; + struct sdb_entry entry = {}; + + time_t now = time(NULL); + const int backdate = 43800; /* One month */ + const int expected_val_cert_start = now - (backdate * 60); + + /* Create the ldb_message */ + msg = create_ldb_message(mem_ctx); + add_empty_altSecurities(mem_ctx, msg); + add_whenCreated(msg, now); + + certificate_binding_enforcement = 0; + certificate_backdating_compensation = backdate; + err = get_certificate_mappings(mem_ctx, lp_ctx, msg, &entry); + + assert_int_equal(0, err); + + assert_int_equal(0, entry.mappings.len); + assert_int_equal(0, entry.mappings.enforcement_mode); + assert_int_equal( + expected_val_cert_start, + entry.mappings.valid_certificate_start); + + sdb_entry_free(&entry); + TALLOC_FREE(mem_ctx); +} + + int main(int argc, const char **argv) { const struct CMUnitTest tests[] = { @@ -1140,6 +1993,42 @@ int main(int argc, const char **argv) cmocka_unit_test(keycred_tpm_key_material), cmocka_unit_test(keycred_der_key_material), cmocka_unit_test(keycred_multiple), + cmocka_unit_test(empty_string_parse_certificate_mapping), + cmocka_unit_test(header_only_parse_certificate_mapping), + cmocka_unit_test(not_x509_parse_certificate_mapping), + cmocka_unit_test(no_tag_parse_certificate_mapping), + cmocka_unit_test(no_tag_close_parse_certificate_mapping), + cmocka_unit_test(empty_tag_parse_certificate_mapping), + cmocka_unit_test(no_value_parse_certificate_mapping), + cmocka_unit_test(issuer_name_parse_certificate_mapping), + cmocka_unit_test( + duplicate_issuer_name_parse_certificate_mapping), + cmocka_unit_test(subject_name_parse_certificate_mapping), + cmocka_unit_test( + duplicate_subject_name_parse_certificate_mapping), + cmocka_unit_test( + issuer_and_subject_name_parse_certificate_mapping), + cmocka_unit_test(serial_number_parse_certificate_mapping), + cmocka_unit_test( + duplicate_serial_number_parse_certificate_mapping), + cmocka_unit_test( + serial_number_and_issuer_name_parse_certificate_mapping + ), + cmocka_unit_test(ski_parse_certificate_mapping), + cmocka_unit_test(duplicate_ski_parse_certificate_mapping), + cmocka_unit_test(public_key_parse_certificate_mapping), + cmocka_unit_test( + duplicate_public_key_parse_certificate_mapping), + cmocka_unit_test(non_hex_parse_certificate_mapping), + cmocka_unit_test(odd_length_hex_parse_certificate_mapping), + cmocka_unit_test(RFC822_parse_certificate_mapping), + cmocka_unit_test(multiple_cert_mappings), + cmocka_unit_test(single_cert_mapping), + cmocka_unit_test(cert_mapping_no_altSecurityIdentities), + cmocka_unit_test(cert_mapping_empty_altSecurityIdentities), + cmocka_unit_test(no_X509_altSecurityIdentities), + cmocka_unit_test(mixed_altSecurityIdentities), + }; cmocka_set_message_output(CM_OUTPUT_SUBUNIT);