| |
@@ -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 <slapi-private-v4.h>
|
| |
+
|
| |
+ /* Fopen and snprintf */
|
| |
+ #include <stdio.h>
|
| |
+ /* To access errors with files */
|
| |
+ #include <errno.h>
|
| |
+ /* To convert errno to string errors */
|
| |
+ #include <string.h>
|
| |
+ /* To use regex in our config parser */
|
| |
+ #include <pcre.h>
|
| |
+
|
| |
+ #define CERT_ATTR_TYPE "userCertificate;binary"
|
| |
+ #define CERT_FILTER_SIZE 64
|
| |
+
|
| |
+ typedef struct _certmap_config {
|
| |
+ /* There are the fields "certmap <issuername> <issuerdn>" */
|
| |
+ 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<name>\\S*?)\\s+(?P<issuer>.*)$";
|
| |
+ 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<name>[^\\s#]*?):(?P<attr>[^\\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<name>[^\\s#]*?):(?P<attr>[^\\s#]*?)\\s+(?P<values>.*)$";
|
| |
+ 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();
|
| |
+ }
|
| |
+
|
| |