From a33def316851db2f348d122ac5df2f29ec64b15e Mon Sep 17 00:00:00 2001 From: William Brown Date: Tue, 14 Nov 2017 16:51:59 +1000 Subject: [PATCH 6/9] Ticket 49218 - Certmap - certmap plugin Bug Description: This adds support for pluggable certificate mapping libraries. To achieve this, this replaces the existing baked in certificate mapping code. Fix Description: An implementation of the static certmap.conf handler, as a plugin using the new api system. https://pagure.io/389-ds-base/issue/49218 https://pagure.io/lib389/issue/95 https://pagure.io/lib389/issue/84 Author: wibrown Review by: ??? --- ldap/servers/plugins/certmap/certmap.c | 639 +++++++++++++++++++++++++++++++++ lib/ldaputil/certmap.conf | 15 +- 2 files changed, 648 insertions(+), 6 deletions(-) create mode 100644 ldap/servers/plugins/certmap/certmap.c diff --git a/ldap/servers/plugins/certmap/certmap.c b/ldap/servers/plugins/certmap/certmap.c new file mode 100644 index 0000000..dc010d4 --- /dev/null +++ b/ldap/servers/plugins/certmap/certmap.c @@ -0,0 +1,639 @@ +/* BEGIN COPYRIGHT BLOCK + * Copyright (C) 2017 Red Hat, Inc. + * All rights reserved. + * + * License: GPL (version 3 or any later version). + * See LICENSE for details. + * END COPYRIGHT BLOCK */ + +#define SLAPI_PLUGIN_V4_PRERELEASE_ACKNOWLEDGE +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE + +#include + +/* Fopen and snprintf */ +#include +/* To access errors with files */ +#include +/* To convert errno to string errors */ +#include +/* To use regex in our config parser */ +#include + +#define CERT_ATTR_TYPE "userCertificate;binary" +#define CERT_FILTER_SIZE 64 + +typedef struct _certmap_config { + /* There are the fields "certmap " */ + char *issuerdn; + char *issuername; + /* Issuer name is used to associate the remaining fields */ + char *basedn; + char **dncomps; + uint_fast16_t dncomps_set; + char **filtercomps; + uint_fast16_t verifycert; + char *compare_attr; +} certmap_config; + +typedef struct _certmap_config_list { + size_t count; + certmap_config **configs; +} certmap_config_list; + +static certmap_config * +certmap_get_config_by_name(certmap_config_list *cm_config_list, char *name) { + certmap_config *config = NULL; + /* this just does a dumb search */ + for(size_t i = 0; i < cm_config_list->count; i++) { + if (strcmp(name, cm_config_list->configs[i]->issuername) == 0) { + config = cm_config_list->configs[i]; + } + } + return config; +} + +static certmap_config * +certmap_get_config_by_issuer(certmap_config_list *cm_config_list, char *issuer) { + certmap_config *config = NULL; + /* this just does a dumb search */ + for(size_t i = 0; i < cm_config_list->count; i++) { + if (strcmp(issuer, cm_config_list->configs[i]->issuerdn) == 0) { + config = cm_config_list->configs[i]; + } + } + return config; +} + +static void +certmap_add_config(certmap_config_list *cm_config_list, char *name, char *issuer) { + cm_config_list->count += 1; + cm_config_list->configs = (certmap_config **)spal_realloc(cm_config_list->configs, cm_config_list->count * sizeof(certmap_config *)); + + certmap_config *config = (certmap_config *)spal_calloc(sizeof(certmap_config)); + + config->issuername = name; + /* YOU NEED TO NORMALISE THIS */ + config->issuerdn = issuer; + /* This does strdup for us */ + config->basedn = config_get_certmap_basedn(); + /* This gives us a basedn we can search under if none provided */ + if (config->basedn == NULL) { + config->basedn = strdup(""); + } + + cm_config_list->configs[cm_config_list->count - 1] = config; + + return; +} + +static char ** +certmap_str2charray_stripped(unsigned char *value, char *token) { + if (value == NULL) { + return NULL; + } + + size_t number_tokens = 2; + for(unsigned char *s = value; *s != '\0'; s++) { + if (strchr(token, *s) != NULL) { + number_tokens++; + } + } + + char **result = spal_calloc(sizeof(char *) * number_tokens); + char *iter = NULL; + unsigned char *s = (unsigned char *)ldap_utf8strtok_r((char *)value, token, &iter); + for (size_t i = 0; s != NULL; i++) { + /* Strip duplicates the string. */ + unsigned char *stripped = ldap_utf8strip(s); + slapi_log_error(SLAPI_LOG_DEBUG, "certmap_str2charray_stripped", "parsed %s\n", stripped); + if (stripped != NULL) { + result[i] = (char *)stripped; + } + s = (unsigned char *)ldap_utf8strtok_r(NULL, token, &iter); + } + + return result; +} + +static void +certmap_add_option_by_name(certmap_config_list *cm_config_list, char *name, char *option, char *values) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_add_option_by_name", "certmap.conf section name %s option %s values %s\n", name, option, values); + /* Get the config we are modifying */ + certmap_config *config = certmap_get_config_by_name(cm_config_list, name); + if (config == NULL) { + /* Skip the invalid option */ + slapi_log_error(SLAPI_LOG_WARNING, "certmap_add_option_by_name", "certmap.conf section name %s does not exist\n", name); + return; + } + + char *option_lower = (char *)slapi_utf8StrToLower((unsigned char *)option); + + /* Which option is it? */ + if (strcmp(option_lower, "basedn") == 0) { + spal_free(config->basedn); + if (values == NULL) { + config->basedn = config_get_certmap_basedn(); + } else { + /* Values is already alloc by caller */ + config->basedn = values; + } + } else if (strcmp(option_lower, "dncomps") == 0) { + slapi_v4_charray_free(config->dncomps); + /* Distinguish the commented / uncommented case */ + config->dncomps_set = 1; + if (values == NULL) { + config->dncomps = NULL; + } else { + /* Needs to trim whitespaces. */ + config->dncomps = certmap_str2charray_stripped((unsigned char *)values, ","); + } + } else if (strcmp(option_lower, "filtercomps") == 0) { + /* Parse these to arrays? */ + slapi_v4_charray_free(config->filtercomps); + if (values == NULL) { + config->filtercomps = NULL; + } else { + /* Needs to trim whitespaces. */ + config->filtercomps = certmap_str2charray_stripped((unsigned char *)values, ","); + } + } else if (strcmp(option_lower, "verifycert") == 0) { + if (values == NULL) { + config->verifycert = 0; + } else { + if (strcmp(values, "on") == 0) { + config->verifycert = 1; + } else { + config->verifycert = 0; + } + spal_free(values); + } + } else if (strcmp(option_lower, "cmapldapattr") == 0) { + /* It's valid for this to reset to NULL */ + spal_free(config->compare_attr); + config->compare_attr = values; + } else { + slapi_log_error(SLAPI_LOG_WARNING, "certmap_add_option_by_name", "certmap.conf option %s not recognised\n", option); + } + + spal_free(option_lower); + + return; +} + +static void +certmap_config_free(certmap_config *config) { + slapi_v4_charray_free(config->dncomps); + slapi_v4_charray_free(config->filtercomps); + spal_free(config->issuername); + spal_free(config->issuerdn); + spal_free(config->basedn); + spal_free(config); +} + +slapi_v4_plugin_result * +certmap_start_fn(void **ctx) { + /* Get the config dir */ + char *configdir = config_get_configdir(); + char *configname = "/certmap.conf"; + + size_t config_path_size = strlen(configdir) + strlen(configname) + 1; + char *config_path = (char *)spal_calloc(config_path_size); + snprintf(config_path, config_path_size, "%s%s", configdir, configname); + + spal_free(configdir); + + /* Open the certmap file */ + /* Handle all those nasty errors that can happen .... */ + FILE *f = fopen(config_path, "r"); + if (f == NULL) { + int cerrno = errno; + char serror[40] = {0}; + strerror_r(cerrno, serror, 39); + char *msg = (char *)spal_calloc(64 * sizeof(char)); + /* Failed to open */ + snprintf(msg, 63, "certmap_start_fn - Failed to open file %s - %s", serror, config_path); + spal_free(config_path); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, msg); + } + + /* Create the config list. We may need to realloc as we go ... */ + certmap_config_list *cm_config_list = (certmap_config_list *)spal_calloc(sizeof(certmap_config_list)); + + /* Prepare our regex */ + char *reerr; + int reerroffset = 0; + + char *cmap_regex = "^certmap\\s+(?P\\S*?)\\s+(?P.*)$"; + pcre *cmap_regex_re = pcre_compile(cmap_regex, 0, (const char **)&reerr, &reerroffset, NULL); + if (cmap_regex_re == NULL) { + char *msg = spal_calloc(128 * sizeof(char)); + snprintf(msg, 127, "certmap_start_fn - Failed to compile %s - %s", reerr, cmap_regex); + spal_free(config_path); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, msg); + } + + char *cmap_attr_regex = "^(?P[^\\s#]*?):(?P[^\\s#]*?)$"; + pcre *cmap_attr_regex_re = pcre_compile(cmap_attr_regex, 0, (const char **)&reerr, &reerroffset, NULL); + if (cmap_attr_regex_re == NULL) { + char *msg = spal_calloc(128 * sizeof(char)); + snprintf(msg, 127, "certmap_start_fn - Failed to compile %s - %s", reerr, cmap_attr_regex); + pcre_free(cmap_regex_re); + spal_free(config_path); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, msg); + } + + char *cmap_option_regex = "^(?P[^\\s#]*?):(?P[^\\s#]*?)\\s+(?P.*)$"; + pcre *cmap_option_regex_re = pcre_compile(cmap_option_regex, 0, (const char **)&reerr, &reerroffset, NULL); + if (cmap_option_regex_re == NULL) { + char *msg = spal_calloc(128 * sizeof(char)); + snprintf(msg, 127, "certmap_start_fn - Failed to compile %s - %s", reerr, cmap_option_regex); + pcre_free(cmap_regex_re); + pcre_free(cmap_attr_regex_re); + spal_free(config_path); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, msg); + } + + /* For each line, match it to a config ... */ + char buffer[256] = {0}; + while (fgets(buffer, 255, f) != NULL) { + /* Setup the call to pcre */ + /* Trim the \n */ + size_t line_size = strlen(buffer); + if (buffer[line_size - 1] == '\n') { + buffer[line_size - 1] = '\0'; + line_size -= 1; + } + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_start_fn", "parsing: %s\n", buffer); + /* + * Pcre is odd: it only uses the first 2/3rd of this as a multiple of 3 + * for storing capture groups. So we have 3 groups, thus 6 indexes, so we + * need 9 in ovector. But the first 2 are the size of the match, so we need + # 12. + */ + int ovector[12] = {0}; + int rc = pcre_exec(cmap_regex_re, NULL, buffer, line_size, 0, 0, ovector, 12); + /* Is the line a new definition? */ + if (rc >= 0) { + char *name = strndup(buffer + ovector[2], ovector[3] - ovector[2]); + char *issuer = strndup(buffer + ovector[4], ovector[5] - ovector[4]); + certmap_add_config(cm_config_list, name, issuer); + } + + /* Is the line adding to a definition */ + rc = pcre_exec(cmap_attr_regex_re, NULL, buffer, line_size, 0, 0, ovector, 12); + if (rc >= 0) { + char *name = strndup(buffer + ovector[2], ovector[3] - ovector[2]); + char *attr = strndup(buffer + ovector[4], ovector[5] - ovector[4]); + certmap_add_option_by_name(cm_config_list, name, attr, NULL); + } + + rc = pcre_exec(cmap_option_regex_re, NULL, buffer, line_size, 0, 0, ovector, 12); + if (rc >= 0) { + char *name = strndup(buffer + ovector[2], ovector[3] - ovector[2]); + char *attr = strndup(buffer + ovector[4], ovector[5] - ovector[4]); + char *values = strndup(buffer + ovector[6], ovector[7] - ovector[6]); + certmap_add_option_by_name(cm_config_list, name, attr, values); + } + + /* If it's none of these, we skip it .... */ + } + + /* Close the file */ + fclose(f); + + pcre_free(cmap_regex_re); + pcre_free(cmap_attr_regex_re); + pcre_free(cmap_option_regex_re); + + /* Finally, hand this to our private data holder. */ + *ctx = (void *)cm_config_list; + + spal_free(config_path); + return slapi_v4_plugin_result_ok(); +} + +slapi_v4_plugin_result * +certmap_close_fn(void **ctx) { + /* Destroy the certmap configuration */ + certmap_config_list *cm_config_list = (certmap_config_list *)*ctx; + + if (cm_config_list != NULL) { + for (size_t i = 0; i < cm_config_list->count; i++) { + certmap_config_free(cm_config_list->configs[i]); + } + + spal_free(cm_config_list->configs); + spal_free(cm_config_list); + } + + return slapi_v4_plugin_result_ok(); +} + +static void +certmap_verifycert_fn(struct berval *bv, void *acc, void *arg) { + int64_t *status = (int64_t *)acc; + struct berval *bp_cert = (struct berval *)arg; + + if (*status != 0) { + return; + } + + /* Are the sizes the same? */ + if (bv->bv_len != bp_cert->bv_len) { + return; + } + + /* What about the actual data? */ + if (memcmp(bv->bv_val, bp_cert->bv_val, bp_cert->bv_len) == 0) { + /*If all good, set status to 1! */ + *status = 1; + } + +} + +static slapi_v4_dn * +certmap_search_int(certmap_config *config, slapi_v4_cert *cert, char *basedn, char *filter, int scope) { + /* + * Search for the entry. + */ + slapi_v4_dn *base_sdn = slapi_v4_sdn_new_from_char_dn(basedn); + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_search_int", "Searching basedn: %s\n", basedn); + + int32_t attrsonly = 0; + /* WE ONLY NEED userCertificate;binary */ + char **attrs = slapi_v4_charray_append(NULL, strdup(CERT_ATTR_TYPE)); + + /* execute search */ + /* If basedn == "", but scope == SUBTREE, we need a diff search handler. */ + slapi_v4_search_pblock *pb_search_result = NULL; + if (strcmp(basedn, "") == 0 && scope == LDAP_SCOPE_SUBTREE) { + pb_search_result = slapi_v4_search_internal_all_contexts(scope, filter, attrs, attrsonly, NULL, NULL, 0); + } else { + pb_search_result = slapi_v4_search_internal(base_sdn, scope, filter, attrs, attrsonly, NULL, NULL, 0); + } + + /* Search is over, don't need the basedn anymore. */ + slapi_v4_charray_free(attrs); + slapi_v4_sdn_free(base_sdn); + + /* get the number of entries */ + uint64_t num_results = slapi_v4_search_pblock_get_num_results(pb_search_result); + + if (num_results == 0) { + slapi_log_error(SLAPI_LOG_WARNING, "certmap_search_int", "No entries matched filter: %s basedn: %s\n", filter, basedn); + slapi_v4_search_pblock_destroy(pb_search_result); + return NULL; + } else if (num_results >= 2) { + slapi_log_error(SLAPI_LOG_WARNING, "certmap_search_int", "Multiple entries matched filter: %s basedn: %s\n", filter, basedn); + slapi_v4_search_pblock_destroy(pb_search_result); + return NULL; + } + + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_search_int", "Query filter: %s basedn: %s number of entries: %"PRIu64"\n", filter, basedn, num_results); + + Slapi_Entry **op_entries = slapi_v4_search_pblock_get_entries(pb_search_result); + Slapi_Entry *bind_entry = op_entries[0]; + + /* + * Now verify the cert if requested. + * This involves checking the certificate attribute contains the DER of our certificate. + * only matters if verifyCert == on + */ + + if (config->verifycert == 1) { + + struct berval **barray = slapi_v4_entry_attr_get_bervals(bind_entry, CERT_ATTR_TYPE); + /* + * Now we have to memcmp the certs. We can do this by turning the cert + * into a berval. + */ + struct berval *bp_cert = slapi_v4_cert_get_der_berval(cert); + int64_t status = 0; + + slapi_v4_bvarray_fold(barray, certmap_verifycert_fn, (void *)&status, (void *)bp_cert); + + slapi_v4_bv_free(bp_cert); + slapi_v4_bvarray_destroy(barray); + + if (status == 0) { + slapi_log_error(SLAPI_LOG_WARNING, "certmap_search_int", "Certificate verification failed, no matching %s attribute\n", CERT_ATTR_TYPE); + slapi_v4_search_pblock_destroy(pb_search_result); + return NULL; + } + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_search_int", "Certificate verification was successful\n"); + } + + slapi_v4_dn *entry_dn = slapi_v4_sdn_dup(slapi_v4_entry_get_sdn(bind_entry)); + + slapi_v4_search_pblock_destroy(pb_search_result); + return entry_dn; +} + +slapi_v4_plugin_result * +certmap_certmap_fn(void *ctx, slapi_v4_certmap_pblock *pbc) { + /* + * At this point pbc contains a populated struct of cert info. We need to extract + * this and determine an sdn (or entry?) that we should bind to. + */ + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Calling bind_certmap plugin.\n"); + certmap_config_list *cm_config_list = (certmap_config_list *)ctx; + /* + * Find the relevant certmap config in the list. + */ + slapi_v4_cert *cert = slapi_v4_certmap_pblock_get_clientcert(pbc); + + char *subject_dn = slapi_v4_cert_get_subjectdn(cert); + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Attempting to bind subjectdn: %s\n", subject_dn); + char *issuer_dn = slapi_v4_cert_get_issuerdn(cert); + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Attempting to bind issuerdn: %s\n", issuer_dn); + + /* Check if subject and issuer are valid. */ + + certmap_config *config = certmap_get_config_by_issuer(cm_config_list, issuer_dn); + if (config == NULL) { + /* No config found, use default! */ + config = certmap_get_config_by_name(cm_config_list, "default"); + if (config == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "No default config nor issuer config could be found.\n"); + /* Free some stuff and bail here */ + spal_free(subject_dn); + spal_free(issuer_dn); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, strdup("Certificate can not be mapped to an identity.")); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "No issuer config could be found, falling back to default..\n"); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Found configuration for %s\n", issuer_dn); + } + + slapi_v4_dn *mapped_dn = NULL; + + if (config->dncomps_set == 0) { + if (config->compare_attr == NULL) { + /* + * If dncomps == NONE, cmapattr == None, bind to subject DN + */ + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "dncomps == NULL, cmap == NULL,\n"); + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Binding to to subjectdn: %s\n", subject_dn); + + /* Do a base search for this object */ + mapped_dn = certmap_search_int(config, cert, subject_dn, "(objectClass=*)", LDAP_SCOPE_BASE); + + } else { + /* + * If dncomps == NONE, cmapattr == Some(dn), search under basedn for cmapattr = subject_dn + */ + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "dncomps == NULL, cmap == %s,\n", config->compare_attr); + + /* Filter escape the subject_dn */ + char *subject_dn_escaped = slapi_v4_escape_filter_value(subject_dn, strlen(subject_dn)); + + /* Turn the cmap attr into a filter. */ + size_t filterlen = strlen(config->compare_attr) + strlen(subject_dn_escaped) + 4; + char *filter = spal_calloc(filterlen); + snprintf(filter, filterlen, "(%s=%s)", config->compare_attr, subject_dn_escaped); + spal_free(subject_dn_escaped); + + /* What abotu NULL basedn? */ + mapped_dn = certmap_search_int(config, cert, config->basedn, filter, LDAP_SCOPE_SUBTREE); + spal_free(filter); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "dncomps != NULL\n"); + if (config->dncomps == NULL) { + /* + * if dncomps == "", use filter comps to search for an entry below basedn. + */ + + /* We have a filtercomps (I hope) */ + if (config->filtercomps == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "dncomps != NULL and filtercomps == NULL, can not map certificate!\n"); + /* Error! */ + } else { + + size_t filter_max_len = CERT_FILTER_SIZE; + size_t filter_len = 2; + char *filter = spal_calloc(sizeof(char) * filter_max_len); + + /* Open the filter */ + sprintf(filter, "(&"); + + /* Take a pointer into the filter at some point range: */ + + for (size_t j = 0; config->filtercomps[j] != NULL; j++) { + char *filtercomp = config->filtercomps[j]; + /* For each value, extract the ava from the cert */ + /* Construct these to a filter. */ + char **avas = slapi_v4_cert_get_subject_ava_val(cert, filtercomp); + if (avas == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Failed to parse certificate filtercomp %s\n", filtercomp); + spal_free(subject_dn); + spal_free(issuer_dn); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, strdup("Certificate can not be mapped to an identity.")); + } + for (size_t i = 0; avas[i] != NULL; i++) { + char *ava = avas[i]; + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "%s avas %s\n", filtercomp, ava); + + /* Work out how large our new filter element will be */ + /* Remember it is: (%s=%s) so at least 3 extra chars. */ + size_t ava_len = 3 + strlen(ava) + strlen(filtercomp); + while ((ava_len + filter_len) >= filter_max_len) { + filter_max_len = filter_max_len + CERT_FILTER_SIZE; + filter = spal_realloc(filter, sizeof(char) * filter_max_len); + } + char *filter_ptr = filter + filter_len; + + sprintf(filter_ptr, "(%s=%s)", filtercomp, ava); + filter_len = filter_len + ava_len; + + } + + slapi_v4_charray_free(avas); + } + + sprintf(filter + filter_len, ")"); + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "filter %s\n", filter); + + /* Submit the search! */ + mapped_dn = certmap_search_int(config, cert, config->basedn, filter, LDAP_SCOPE_SUBTREE); + spal_free(filter); + } + } else { + /* + * if dncomps == "attr list", construct a DN to bind to. + */ + size_t binddn_max_len = CERT_FILTER_SIZE; + size_t binddn_len = 0; + char *binddn = spal_calloc(sizeof(char) * binddn_max_len); + + for (size_t i = 0; config->dncomps[i] != NULL; i++) { + char *dncomp = config->dncomps[i]; + + char **avas = slapi_v4_cert_get_subject_ava_val(cert, dncomp); + if (avas == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "Failed to parse certificate dncomp %s\n", dncomp); + spal_free(subject_dn); + spal_free(issuer_dn); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, strdup("Certificate can not be mapped to an identity.")); + } + for (size_t j = 0; avas[j] != NULL; j++) { + char *ava = avas[j]; + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "%s avas %s\n", dncomp, ava); + + /* Work out how large our new filter element will be */ + /* Remember it is: %s=%s, so at least 2 extra chars. */ + size_t ava_len = 2 + strlen(ava) + strlen(dncomp); + while ((ava_len + binddn_len) >= binddn_max_len) { + binddn_max_len = binddn_max_len + CERT_FILTER_SIZE; + binddn = spal_realloc(binddn, sizeof(char) * binddn_max_len); + } + char *binddn_ptr = binddn + binddn_len; + + sprintf(binddn_ptr, "%s=%s,", dncomp, ava); + binddn_len = binddn_len + ava_len; + } + + slapi_v4_charray_free(avas); + } + /* Replace the final trailing , with a \0 */ + binddn[binddn_len - 1] = '\0'; + + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "binddn %s\n", binddn); + + /* Submit the search! */ + mapped_dn = certmap_search_int(config, cert, binddn, "(objectClass=*)", LDAP_SCOPE_BASE); + spal_free(binddn); + } + } + + spal_free(subject_dn); + spal_free(issuer_dn); + + if (mapped_dn == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "bind_certmap plugin failed to map client certficate.\n"); + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_INVALID_CREDENTIALS, strdup("Failed to map client certificate to LDAP entry.")); + } + + /* If it exists, attach the SDN to the pbc */ + slapi_v4_certmap_pblock_set_clientdn(pbc, mapped_dn); + + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_certmap_fn", "bind_certmap plugin complete.\n"); + return slapi_v4_plugin_result_ok(); +} + +slapi_v4_plugin_result * +certmap_init_fn(slapi_v4_plugin_registration *p_register) { + slapi_log_error(SLAPI_LOG_PLUGIN, "certmap_init_fn", "Initialising certmap plugin.\n"); + + /* register name will dup this for us. */ + slapi_v4_plugin_register_name(p_register, "certmap plugin"); + slapi_v4_plugin_register_precedence(p_register, SV4_PLUGIN_PRECEDENCE_DEFAULT); + slapi_v4_plugin_register_start_fn(p_register, certmap_start_fn); + slapi_v4_plugin_register_close_fn(p_register, certmap_close_fn); + slapi_v4_plugin_register_bind_certmap_fn(p_register, certmap_certmap_fn); + + return slapi_v4_plugin_result_ok(); +} + diff --git a/lib/ldaputil/certmap.conf b/lib/ldaputil/certmap.conf index b490787..9e7eccc 100644 --- a/lib/ldaputil/certmap.conf +++ b/lib/ldaputil/certmap.conf @@ -41,11 +41,14 @@ # empty - set the filter to "objectclass=*" # attr names - a comma separated list of attributes to form the filter # +# 7. basedn is the basedn to search rather than using dncomps. If dncomps +# is specified, basedn is ignored. If basedn is not specified, we use +# nsslapd-certmap-basedn +# -certmap default default +certmap default default +#default:basedn ou=People,dc=example,dc=com #default:DNComps -#default:FilterComps e, uid -#default:verifycert on -#default:CmapLdapAttr certSubjectDN -#default:library -#default:InitFn +#default:FilterComps e, uid +#default:verifycert on +#default:CmapLdapAttr nsCertSubjectDN -- 1.8.3.1