From a91f12319c18d25246a0c4f95c15f1f2f06d35b8 Mon Sep 17 00:00:00 2001 From: William Brown Date: Tue, 14 Nov 2017 16:47:45 +1000 Subject: [PATCH 3/9] Ticket 49218 - Certmap - plugin v4 api Bug Description: This adds support for pluggable certificate mapping libraries. To achieve this, this replaces the existing baked in certificate mapping code. Fix Description: Add the suppport for the v4 api. This includes a more efficent search interface, some type wrappers for important escape and filter types, as well as improvements to the memory allocation routines. 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/slapd/add.c | 34 +++++++ ldap/servers/slapd/bvarray_v4.c | 60 +++++++++++ ldap/servers/slapd/cert_v4.c | 128 +++++++++++++++++++++++ ldap/servers/slapd/ch_malloc.c | 3 +- ldap/servers/slapd/charray.c | 16 ++- ldap/servers/slapd/dn.c | 27 +++-- ldap/servers/slapd/entry.c | 29 ++++++ ldap/servers/slapd/log.c | 69 +++++-------- ldap/servers/slapd/pblock_v4.c | 173 ++++++++++++++++++++++++++++++++ ldap/servers/slapd/plugin.c | 3 + ldap/servers/slapd/plugin_internal_op.c | 4 +- ldap/servers/slapd/slapi-plugin-v4.c | 70 +++++++++++++ ldap/servers/slapd/slapi-private-v4.c | 131 ++++++++++++++++++++++++ ldap/servers/slapd/slapi_pal.c | 78 ++++++++++++++ ldap/servers/slapd/utf8.c | 26 +++++ ldap/servers/slapd/util.c | 3 + 16 files changed, 798 insertions(+), 56 deletions(-) create mode 100644 ldap/servers/slapd/bvarray_v4.c create mode 100644 ldap/servers/slapd/cert_v4.c create mode 100644 ldap/servers/slapd/pblock_v4.c create mode 100644 ldap/servers/slapd/slapi-plugin-v4.c create mode 100644 ldap/servers/slapd/slapi-private-v4.c diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c index 0a4a5d7..5691cbf 100644 --- a/ldap/servers/slapd/add.c +++ b/ldap/servers/slapd/add.c @@ -38,6 +38,8 @@ #include "pratom.h" #include "csngen.h" +#include + /* Forward declarations */ static int add_internal_pb(Slapi_PBlock *pb); static void op_shared_add(Slapi_PBlock *pb); @@ -324,6 +326,38 @@ slapi_add_internal_pb(Slapi_PBlock *pb) return add_internal_pb(pb); } +/* + * v4 add api. We discard the allow_operation check because plugins just have + * total access to the server memory - they can do what they please. Instead + * we just wrap the existing add code in a nicer api, and we forced the + * modifier name to come from the caller rather than the convoluted plugin_id + * mechanism. If a plugin wans to lie, we can't stop it, so just go with simple! + */ + +slapi_v4_search_pblock * +slapi_v4_add_internal_entry(Slapi_Entry *entry, const char *modifier_name) { + if (modifier_name == NULL) { + /* Should we create a pblock and return it with a failure code? */ + return NULL; + } + /* Create a new pblock */ + Slapi_PBlock *int_pbs = slapi_pblock_new(); + /* Allocate a new operation */ + /* There are NO cases I can find of these flags being used, so don't bother! */ + Operation *op = internal_operation_new(SLAPI_OPERATION_ADD, 0); + slapi_pblock_set(int_pbs, SLAPI_OPERATION, op); + slapi_pblock_set(int_pbs, SLAPI_CONTROLS_ARG, NULL); + /* Inject the modifier into the entry */ + slapi_entry_attr_set_charptr(entry, "internalModifiersname", modifier_name); + slapi_pblock_set(int_pbs, SLAPI_ADD_ENTRY, entry); + /* Add the entry */ + add_internal_pb(int_pbs); + /* Extract and return the result */ + slapi_v4_search_pblock *pbs = slapi_v4_search_pblock_extract(int_pbs); + slapi_pblock_destroy(int_pbs); + return pbs; +} + int slapi_add_internal_set_pb(Slapi_PBlock *pb, const char *dn, LDAPMod **attrs, LDAPControl **controls, Slapi_ComponentId *plugin_identity, int operation_flags) { diff --git a/ldap/servers/slapd/bvarray_v4.c b/ldap/servers/slapd/bvarray_v4.c new file mode 100644 index 0000000..f277feb --- /dev/null +++ b/ldap/servers/slapd/bvarray_v4.c @@ -0,0 +1,60 @@ +/** 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 **/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +void +slapi_v4_bv_free(struct berval *v) { + if (v != NULL) { + spal_free(v->bv_val); + spal_free(v); + } +} + +/* Create a bvarray */ +struct berval ** +slapi_v4_bvarray_create() { + /* Allocate a default size. */ + return (struct berval **)spal_calloc(sizeof(struct berval *)); +} + +/* Destroy it */ +void +slapi_v4_bvarray_destroy(struct berval **array) { + for(size_t i = 0; array[i] != NULL; i++) { + slapi_ch_bvfree(&(array[i])); + } + spal_free(array); +} + +/* Push back */ +struct berval ** +slapi_v4_bvarray_append(struct berval **array, struct berval *bv) { + size_t i= 0; + for(; array[i] != NULL; i++) { + } + + array = (struct berval **)spal_realloc((char *)array, sizeof(struct berval *) * (i + 2)); + + array[i] = bv; + array[i + 1] = NULL; + + return array; +} + +/* fold */ +void +slapi_v4_bvarray_fold(struct berval **array, void (*fn)(struct berval *bv, void *acc, void *arg), void *acc, void *arg){ + for(size_t i = 0; array[i] != NULL; i++) { + fn(array[i], acc, arg); + } +} diff --git a/ldap/servers/slapd/cert_v4.c b/ldap/servers/slapd/cert_v4.c new file mode 100644 index 0000000..bfbbdba --- /dev/null +++ b/ldap/servers/slapd/cert_v4.c @@ -0,0 +1,128 @@ +/* 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 */ + +/* For NSS certificate parsing */ +#include + +/* + * To fix a compiler warning here: cert.h pulls + * in prcpucfg.h which defines LINUX as empty. + * However, config.h defines it to 1, so we need + * to undefined it after cert.h to let config.h + * do it's job. + */ + +#undef LINUX + +#include + +static const int SEC_OID_AVA_UNKNOWN = 0; /* unknown OID */ + +char * +slapi_v4_cert_get_subjectdn(slapi_v4_cert *cert) { + return CERT_NameToAscii(&cert->subject); +} + +char * +slapi_v4_cert_get_issuerdn(slapi_v4_cert *cert) { + return CERT_NameToAscii(&cert->issuer); +} + +struct berval * +slapi_v4_cert_get_der_berval(slapi_v4_cert *cert) { + /* Construct a berval and return it. */ + struct berval *bp = (struct berval *)spal_calloc(sizeof(struct berval)); + + SECItem derCert = cert->derCert; + + bp->bv_len = derCert.len; + bp->bv_val = spal_calloc(derCert.len); + + memcpy(bp->bv_val, derCert.data, derCert.len); + return bp; +} + +static int +slapi_v4_cert_name_to_secoid(const char *attr) { + if (!strcasecmp(attr, "c")) { + return SEC_OID_AVA_COUNTRY_NAME; + } else if (!strcasecmp(attr, "o")) { + return SEC_OID_AVA_ORGANIZATION_NAME; + } else if (!strcasecmp(attr, "cn")) { + return SEC_OID_AVA_COMMON_NAME; + } else if (!strcasecmp(attr, "l")) { + return SEC_OID_AVA_LOCALITY; + } else if (!strcasecmp(attr, "st")) { + return SEC_OID_AVA_STATE_OR_PROVINCE; + } else if (!strcasecmp(attr, "ou")) { + return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME; + } else if (!strcasecmp(attr, "uid")) { + return SEC_OID_RFC1274_UID; + } else if (!strcasecmp(attr, "e")) { + return SEC_OID_PKCS9_EMAIL_ADDRESS; + } else if (!strcasecmp(attr, "mail")) { + return SEC_OID_RFC1274_MAIL; + } else if (!strcasecmp(attr, "dc")) { + return SEC_OID_AVA_DC; + } + + return SEC_OID_AVA_UNKNOWN; +} + +static char ** +slapi_v4_cert_get_ava_val(CERTName *dn, const char *attr) { + int attr_tag = slapi_v4_cert_name_to_secoid(attr); + char **result = NULL; + + if (attr_tag == SEC_OID_AVA_UNKNOWN) { + return NULL; + } + + CERTRDN **rdns = dn->rdns; + if (rdns == NULL) { + return NULL; + } + + for (size_t i = 0; rdns[i] != NULL; i++) { + CERTRDN *rdn = rdns[i]; + CERTAVA **avas = rdn->avas; + + for (size_t j = 0; avas[j] != NULL; j++) { + CERTAVA *ava = avas[j]; + if (attr_tag == CERT_GetAVATag(ava)) { + /* + * In the original implementation we need to trim + * the ava value here else it will fail to be parsed. + */ + size_t datalen = ava->value.len + 4; + char *data = spal_calloc(datalen); + + int offset = 2; + if (ava->value.len >= 128) { + offset = 3; + } + + int rv = CERT_RFC1485_EscapeAndQuote(data, datalen, (char *)(ava->value.data + offset), ava->value.len - offset); + if (rv == SECSuccess) { + slapi_ch_array_add(&result, data); + } + break; + } + } + + } + + return result; +} + +char ** +slapi_v4_cert_get_subject_ava_val(slapi_v4_cert *cert, const char *attr) { + CERTName *dn = &(cert->subject); + return slapi_v4_cert_get_ava_val(dn, attr); +} + diff --git a/ldap/servers/slapd/ch_malloc.c b/ldap/servers/slapd/ch_malloc.c index ef436b3..05f8f43 100644 --- a/ldap/servers/slapd/ch_malloc.c +++ b/ldap/servers/slapd/ch_malloc.c @@ -163,7 +163,8 @@ slapi_ch_realloc( char * slapi_ch_calloc( unsigned long nelem, - unsigned long size) + unsigned long size +) { char *newmem; diff --git a/ldap/servers/slapd/charray.c b/ldap/servers/slapd/charray.c index 6175491..5c3ed86 100644 --- a/ldap/servers/slapd/charray.c +++ b/ldap/servers/slapd/charray.c @@ -62,6 +62,13 @@ slapi_ch_array_add_ext(char ***a, char *s) return n; } +char ** +slapi_v4_charray_append(char **a, char *s) { + char **ptr = a; + charray_add(&ptr, s); + return ptr; +} + void charray_merge( char ***a, @@ -201,11 +208,13 @@ charray_free(char **array) for (a = array; *a != NULL; a++) { char *tmp = *a; - slapi_ch_free((void **)&tmp); + spal_free(tmp); } - slapi_ch_free((void **)&array); + spal_free(array); } +void slapi_v4_charray_free(char **array) __attribute__((weak, alias("charray_free"))); + /* * charray_free version for plugins: there is a need for plugins to free * the ch_arrays returned by functions like: @@ -322,6 +331,9 @@ slapi_str2charray(char *str, char *brkstr) return (slapi_str2charray_ext(str, brkstr, 1)); } +char ** +slapi_v4_charray_from_str(char *str, char *brkstr) __attribute__((weak, alias("slapi_str2charray"))); + /* * extended version of str2charray lets you disallow * duplicate values into the array. diff --git a/ldap/servers/slapd/dn.c b/ldap/servers/slapd/dn.c index afca372..19c3ba7 100644 --- a/ldap/servers/slapd/dn.c +++ b/ldap/servers/slapd/dn.c @@ -2011,6 +2011,9 @@ slapi_sdn_new_dn_byval(const char *dn) } Slapi_DN * +slapi_v4_sdn_new_from_char_dn(const char *dn) __attribute__((weak, alias("slapi_sdn_new_dn_byval"))); + +Slapi_DN * slapi_sdn_new_ndn_byval(const char *ndn) { Slapi_DN *sdn = slapi_sdn_new(); @@ -2323,21 +2326,28 @@ slapi_sdn_done(Slapi_DN *sdn) } void -slapi_sdn_free(Slapi_DN **sdn) -{ - if (sdn != NULL && *sdn != NULL) { +slapi_v4_sdn_free(Slapi_DN *sdn) { + if (sdn != NULL) { int is_allocated = 0; - SDN_DUMP(*sdn, "slapi_sdn_free"); - is_allocated = slapi_isbitset_uchar((*sdn)->flag, FLAG_ALLOCATED); - slapi_sdn_done(*sdn); + SDN_DUMP(sdn, "slapi_sdn_free"); + is_allocated = slapi_isbitset_uchar(sdn->flag, FLAG_ALLOCATED); + slapi_sdn_done(sdn); if (is_allocated) { - slapi_ch_free((void **)sdn); + spal_free(sdn); PR_INCREMENT_COUNTER(slapi_sdn_counter_deleted); PR_DECREMENT_COUNTER(slapi_sdn_counter_exist); } } } +void +slapi_sdn_free(Slapi_DN **sdn) +{ + if (sdn != NULL) { + slapi_v4_sdn_free(*sdn); + } +} + const char * slapi_sdn_get_dn(const Slapi_DN *sdn) { @@ -2472,6 +2482,9 @@ slapi_sdn_dup(const Slapi_DN *sdn) return tmp; } +Slapi_DN * +slapi_v4_sdn_dup(const Slapi_DN *sdn) __attribute__((weak, alias("slapi_sdn_dup"))); + void slapi_sdn_copy(const Slapi_DN *from, Slapi_DN *to) { diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c index fbbc8fa..c38c8db 100644 --- a/ldap/servers/slapd/entry.c +++ b/ldap/servers/slapd/entry.c @@ -22,6 +22,8 @@ #include #include "slap.h" +#include + #undef ENTRY_DEBUG #define DELETED_ATTR_STRING ";deletedattribute" @@ -2081,6 +2083,9 @@ slapi_entry_get_sdn_const(const Slapi_Entry *e) return &e->e_sdn; } +const Slapi_DN * +slapi_v4_entry_get_sdn(Slapi_Entry *e) __attribute__((weak, alias("slapi_entry_get_sdn_const"))); + Slapi_DN * slapi_entry_get_sdn(Slapi_Entry *e) { @@ -2694,6 +2699,9 @@ slapi_entry_attr_get_charray(const Slapi_Entry *e, const char *type) return slapi_entry_attr_get_charray_ext(e, type, &ignore); } +char ** +slapi_v4_entry_attr_get_charray(const Slapi_Entry *e, const char *type) __attribute__((weak, alias("slapi_entry_attr_get_charray"))); + /* * The extension also gathers the number of values. * The caller must free with slapi_ch_array_free @@ -2751,6 +2759,27 @@ slapi_entry_attr_get_charptr(const Slapi_Entry *e, const char *type) return p; } +struct berval ** +slapi_v4_entry_attr_get_bervals(const Slapi_Entry *e, const char *type) { + + struct berval **bervals = {0}; + Slapi_Attr *attr = NULL; + slapi_entry_attr_find(e, type, &attr); + + if (attr != NULL) { + bervals = slapi_v4_bvarray_create(); + int hint; + Slapi_Value *v = NULL; + + for (hint = slapi_attr_first_value(attr, &v); hint != -1; hint = slapi_attr_next_value(attr, hint, &v)) { + struct berval *bvp = slapi_ch_bvdup(slapi_value_get_berval(v)); + bervals = slapi_v4_bvarray_append(bervals, bvp); + } + } + + return bervals; +} + /* returned value: attribute value as an integer type */ int slapi_entry_attr_get_int(const Slapi_Entry *e, const char *type) diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c index 998efae..6afa508 100644 --- a/ldap/servers/slapd/log.c +++ b/ldap/servers/slapd/log.c @@ -29,6 +29,10 @@ #include "log.h" #include "fe.h" #include /* getpwnam */ + +/* Access the log levels enum. */ +#include + #define _PSEP '/' /************************************************************************** @@ -2397,17 +2401,9 @@ vslapd_log_error( return (0); } -/* - * Log a message to the errors log - * - * loglevel - The logging level: replication, plugin, etc - * severity - LOG_ERR, LOG_WARNING, LOG_INFO, etc - */ -int -slapi_log_error(int loglevel, char *subsystem, char *fmt, ...) +int32_t +slapi_log_error_ext(slapi_log_level loglevel, char *subsystem, char *fmt, va_list ap_err, va_list ap_file) { - va_list ap_err; - va_list ap_file; int rc = LDAP_SUCCESS; int lbackend = loginfo.log_backend; /* We copy this to make these next checks atomic */ @@ -2420,33 +2416,19 @@ slapi_log_error(int loglevel, char *subsystem, char *fmt, ...) if (slapd_ldap_debug & slapi_log_map[loglevel]) { if (lbackend & LOGGING_BACKEND_INTERNAL) { - va_start(ap_err, fmt); - va_start(ap_file, fmt); rc = slapd_log_error_proc_internal(loglevel, subsystem, fmt, ap_err, ap_file); - va_end(ap_file); - va_end(ap_err); } if (rc != LDAP_SUCCESS) { return (rc); } if (lbackend & LOGGING_BACKEND_SYSLOG) { - va_start(ap_err, fmt); - /* va_start( ap_file, fmt ); */ /* This returns void, so we hope it worked */ vsyslog(get_syslog_loglevel(loglevel), fmt, ap_err); - /* vsyslog(LOG_ERROR, fmt, ap_file); */ - /* va_end(ap_file); */ - va_end(ap_err); } #ifdef HAVE_JOURNALD if (lbackend & LOGGING_BACKEND_JOURNALD) { - va_start(ap_err, fmt); - /* va_start( ap_file, fmt ); */ /* This isn't handling RC nicely ... */ rc = sd_journal_printv(get_syslog_loglevel(loglevel), fmt, ap_err); - /* rc = sd_journal_printv(LOG_ERROR, fmt, ap_file); */ - /* va_end(ap_file); */ - va_end(ap_err); } #endif } else { @@ -2456,28 +2438,27 @@ slapi_log_error(int loglevel, char *subsystem, char *fmt, ...) return (rc); } -int -slapi_log_error_ext(int loglevel, char *subsystem, char *fmt, va_list varg1, va_list varg2) -{ - int rc = 0; - - if (loglevel < SLAPI_LOG_MIN || loglevel > SLAPI_LOG_MAX) { - (void)slapd_log_error_proc(loglevel, subsystem, "slapi_log_error: invalid severity %d (message %s)\n", - loglevel, fmt); - return (-1); - } - - if (slapd_ldap_debug & slapi_log_map[loglevel]) { - rc = slapd_log_error_proc_internal(loglevel, subsystem, fmt, varg1, varg2); - } else { - rc = 0; /* nothing to be logged --> always return success */ - } - - return (rc); +/* + * Log a message to the errors log + * + * loglevel - The logging level: replication, plugin, etc + * severity - LOG_ERR, LOG_WARNING, LOG_INFO, etc + */ +int32_t +slapi_log_error(slapi_log_level loglevel, char *subsystem, char *fmt, ...) { + int32_t rc = 0; + va_list ap_err; + va_list ap_file; + va_start(ap_err, fmt); + va_start(ap_file, fmt); + rc = slapi_log_error_ext(loglevel, subsystem, fmt, ap_err, ap_file); + va_end(ap_file); + va_end(ap_err); + return rc; } -int -slapi_is_loglevel_set(const int loglevel) +int32_t +slapi_is_loglevel_set(const slapi_log_level loglevel) { return (slapd_ldap_debug & slapi_log_map[loglevel] ? 1 : 0); } diff --git a/ldap/servers/slapd/pblock_v4.c b/ldap/servers/slapd/pblock_v4.c new file mode 100644 index 0000000..56eec93 --- /dev/null +++ b/ldap/servers/slapd/pblock_v4.c @@ -0,0 +1,173 @@ +/* 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 */ + +/* + * Replacement of the monolithic pblock into smaller focused + * versions. + * + * Basically the extension of the pblock_v3 breakup and hiding work, but + * without needing a super structure wrapper in front. + */ + +#define SLAPI_PLUGIN_V4_PRERELEASE_ACKNOWLEDGE +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE + +#include "pblock_v3.h" +#include + +/* NSS cert includes for certcertificate */ +#include + +typedef struct _slapi_v4_certmap_pblock { + slapi_v4_dn *sdn; + slapi_v4_cert *clientcert; +} slapi_v4_certmap_pblock; + +slapi_v4_certmap_pblock * +slapi_v4_certmap_pblock_init(void) { + slapi_v4_certmap_pblock *pbc = (slapi_v4_certmap_pblock *)spal_calloc(sizeof(slapi_v4_certmap_pblock)); + return pbc; +} + +slapi_v4_dn * +slapi_v4_certmap_pblock_get_clientdn(slapi_v4_certmap_pblock *pbc) { + return pbc->sdn; +} + +void +slapi_v4_certmap_pblock_set_clientdn(slapi_v4_certmap_pblock *pbc, slapi_v4_dn *sdn) { + pbc->sdn = sdn; +} + +slapi_v4_cert * +slapi_v4_certmap_pblock_get_clientcert(slapi_v4_certmap_pblock *pbc) { + return pbc->clientcert; +} + +void +slapi_v4_certmap_pblock_set_clientcert(slapi_v4_certmap_pblock *pbc, slapi_v4_cert *clientcert) { + pbc->clientcert = clientcert; +} + +void +slapi_v4_certmap_pblock_destroy(slapi_v4_certmap_pblock *pbc) { + slapi_sdn_free(&(pbc->sdn)); + spal_free(pbc); +} + + +/* + * Search pblock functions. + */ + +/* + * To the external world we present a simple api of get/set same as pblock + * + * But when we go to commit a search, we actually alloc a v3 pblock, and push + * this as the intop pblock into it. + * + * This abstraction means that later we can later remove the v3 layer from the middle + * and just go full v4 for search pblock access. + */ + +slapi_v4_search_pblock * +slapi_v4_search_pblock_create(void) { + slapi_v4_search_pblock *pbs = (slapi_v4_search_pblock *)spal_calloc(sizeof(slapi_v4_search_pblock)); + return pbs; +} + +slapi_v4_search_pblock * +slapi_v4_search_pblock_extract(Slapi_PBlock *pb) { + slapi_v4_search_pblock *pbs = pb->pb_intop; + pb->pb_intop = NULL; + return pbs; +} + +void +slapi_v4_search_pblock_destroy(slapi_v4_search_pblock *pbs) { + + Slapi_Entry **op_entries = pbs->pb_plugin_internal_search_op_entries; + + if (op_entries != NULL) { + for (size_t i = 0; op_entries[i] != NULL; i++) { + slapi_entry_free(op_entries[i]); + } + spal_free(op_entries); + } + + char **op_referrals = pbs->pb_plugin_internal_search_op_referrals; + + if (op_referrals != NULL) { + for (size_t i = 0; op_referrals[i] != NULL; i++) { + spal_free(op_referrals[i]); + } + spal_free(op_referrals); + } + + if (pbs != NULL) { + delete_passwdPolicy(&pbs->pwdpolicy); + spal_free(pbs->pb_result_text); + } + spal_free(pbs); +} + +uint64_t +slapi_v4_search_pblock_get_num_results(slapi_v4_search_pblock *pbs) { + return pbs->pb_plugin_internal_search_num_entries; +} + +Slapi_Entry ** +slapi_v4_search_pblock_get_entries(slapi_v4_search_pblock *pbs) { + return pbs->pb_plugin_internal_search_op_entries; +} + +char ** +slapi_v4_search_pblock_get_referrals(slapi_v4_search_pblock *pbs) { + return pbs->pb_plugin_internal_search_op_referrals; +} + +int +slapi_v4_search_pblock_get_ldapresult(slapi_v4_search_pblock *pbs) { + return pbs->pb_internal_op_result; +} + +void +slapi_v4_search_pblock_merge(slapi_v4_search_pblock *pbs_a, slapi_v4_search_pblock *pbs_b) { + /* Check the a and b are valid */ + if (pbs_a == NULL || pbs_b == NULL) { + return; + } + /* Check the search results of b exist. */ + + if (pbs_b->pb_plugin_internal_search_num_entries > 0) { + /* Merge the content of B into A if any */ + uint64_t total_entries = pbs_a->pb_plugin_internal_search_num_entries + pbs_b->pb_plugin_internal_search_num_entries; + /* Realloc the entry array space. */ + pbs_a->pb_plugin_internal_search_op_entries = (Slapi_Entry **)spal_realloc((char *)pbs_a->pb_plugin_internal_search_op_entries, + sizeof(Slapi_Entry *) * (total_entries + 1)); + + for(size_t i = 0; i < pbs_b->pb_plugin_internal_search_num_entries; i++) { + /* + * This works as if pbs_a->num_ent == 0, we get 0 with i== 0. If we have 1 + * we get 1, and the entry is at 0. Etc. + */ + size_t offset_a = pbs_a->pb_plugin_internal_search_num_entries + i; + pbs_a->pb_plugin_internal_search_op_entries[offset_a] = pbs_b->pb_plugin_internal_search_op_entries[i]; + pbs_b->pb_plugin_internal_search_op_entries[i] = NULL; + } + /* Ensure the last slot is null. */ + pbs_a->pb_plugin_internal_search_op_entries[total_entries] = NULL; + + pbs_a->pb_plugin_internal_search_num_entries = total_entries; + } + + /* Finally, destroy b. */ + slapi_v4_search_pblock_destroy(pbs_b); +} + + diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c index e02133a..33bd699 100644 --- a/ldap/servers/slapd/plugin.c +++ b/ldap/servers/slapd/plugin.c @@ -18,6 +18,9 @@ #include #include "slap.h" +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE +#include + /* this defines are used for plugin configuration */ #define LOCAL_DATA "local data" #define REMOTE_DATA "remote data" diff --git a/ldap/servers/slapd/plugin_internal_op.c b/ldap/servers/slapd/plugin_internal_op.c index 52b8df8..5aea76c 100644 --- a/ldap/servers/slapd/plugin_internal_op.c +++ b/ldap/servers/slapd/plugin_internal_op.c @@ -55,8 +55,8 @@ typedef struct callback_fn_ptrs /* forward declarations */ static int seq_internal_callback_pb(Slapi_PBlock *pb, void *callback_data, plugin_result_callback prc, plugin_search_entry_callback psec, plugin_referral_entry_callback prec); -static int search_internal_pb(Slapi_PBlock *pb); static int search_internal_callback_pb(Slapi_PBlock *pb, void *callback_data, plugin_result_callback prc, plugin_search_entry_callback psec, plugin_referral_entry_callback prec); +int search_internal_pb(Slapi_PBlock *pb); void internal_getresult_callback(struct conn *unused1 __attribute__((unused)), @@ -561,7 +561,7 @@ internal_plugin_result_callback(int rc, void *callback_data) ((plugin_search_internal_data *)callback_data)->rc = rc; } -static int +int search_internal_pb(Slapi_PBlock *pb) { plugin_search_internal_data psid; diff --git a/ldap/servers/slapd/slapi-plugin-v4.c b/ldap/servers/slapd/slapi-plugin-v4.c new file mode 100644 index 0000000..2ace85f --- /dev/null +++ b/ldap/servers/slapd/slapi-plugin-v4.c @@ -0,0 +1,70 @@ +/* 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 +#include + +slapi_v4_plugin_result * +slapi_v4_plugin_result_ok(void) { + /* + * A potential (and premature, and probably silly) + * optimisation here is OK is == NULL, to avoid an alloc / free. + * + * However, this may interfere with FFI which may expect a value. + * provided we *don't* offer an api that allows modification of a + * result post creation, it's a possibility. + */ + slapi_v4_plugin_result *res = (slapi_v4_plugin_result *)spal_calloc(sizeof(slapi_v4_plugin_result)); + res->result = SLAPI_V4_PLUGIN_SUCCESS; + return res; +} + +slapi_v4_plugin_result * +slapi_v4_plugin_result_err(SV4_PLUGIN_RESULT_CODE result, int ldap_code, char *msg) { + slapi_v4_plugin_result *res = (slapi_v4_plugin_result *)spal_calloc(sizeof(slapi_v4_plugin_result)); + res->result = result; + res->ldap_code = ldap_code; + res->msg = msg; + return res; +} + +void +slapi_v4_plugin_result_destroy(slapi_v4_plugin_result *r) { + if (r != NULL) { + spal_free(r->msg); + spal_free(r); + } +} + +void +slapi_v4_plugin_register_name(slapi_v4_plugin_registration *pr, char *name) { + pr->sv4_name = strdup(name); +} + +void +slapi_v4_plugin_register_precedence(slapi_v4_plugin_registration *pr, uint64_t order) { + pr->precedence = order; +} + +void +slapi_v4_plugin_register_start_fn(slapi_v4_plugin_registration *pr, slapi_v4_plugin_result *(*sv4_start_fn)(void **ctx)) { + pr->sv4_start_fn = sv4_start_fn; +} + +void +slapi_v4_plugin_register_close_fn(slapi_v4_plugin_registration *pr, slapi_v4_plugin_result *(*sv4_close_fn)(void **ctx)) { + pr->sv4_close_fn = sv4_close_fn; +} + +void +slapi_v4_plugin_register_bind_certmap_fn(slapi_v4_plugin_registration *pr, slapi_v4_plugin_result *(*sv4_bind_certmap_fn)(void *ctx, slapi_v4_certmap_pblock *pbc)) { + pr->sv4_bind_certmap_fn = sv4_bind_certmap_fn; +} diff --git a/ldap/servers/slapd/slapi-private-v4.c b/ldap/servers/slapd/slapi-private-v4.c new file mode 100644 index 0000000..ac505d1 --- /dev/null +++ b/ldap/servers/slapd/slapi-private-v4.c @@ -0,0 +1,131 @@ +/* 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 */ + +#include + +slapi_v4_plugin_registration * +slapi_v4_plugin_registration_create() { + slapi_v4_plugin_registration *pr = (slapi_v4_plugin_registration *)spal_calloc(sizeof(slapi_v4_plugin_registration)); + return pr; +} + +void +slapi_v4_plugin_registration_destroy(slapi_v4_plugin_registration *pr) { + spal_free(pr->sv4_name); + spal_free(pr); +} + + +/* Internal search helper functions. */ + +slapi_v4_search_pblock * +slapi_v4_search_internal(Slapi_DN *basedn, + int32_t scope, + const char *filter, + char **attrs, + int32_t attrsonly, + LDAPControl **controls, + const char *uniqueid, + int32_t operation_flags) +{ + /* + * This setups the search pblock similar to slapi_search_internal_set_pb_ext + * followed by calling search_internal_pb + */ + + Slapi_PBlock *int_pbs = slapi_pblock_new(); + /* Allocate a new operation */ + Operation *op = internal_operation_new(SLAPI_OPERATION_SEARCH, operation_flags); + slapi_pblock_set(int_pbs, SLAPI_OPERATION, op); + + /* Setup the search pb */ + slapi_pblock_set(int_pbs, SLAPI_ORIGINAL_TARGET_DN, (void *)slapi_sdn_get_udn(basedn)); + slapi_pblock_set(int_pbs, SLAPI_TARGET_SDN, (void *)basedn); + slapi_pblock_set(int_pbs, SLAPI_SEARCH_SCOPE, &scope); + slapi_pblock_set(int_pbs, SLAPI_SEARCH_STRFILTER, (void *)filter); + slapi_pblock_set(int_pbs, SLAPI_CONTROLS_ARG, controls); + /* forbidden attrs could be removed in slapi_pblock_set. */ + char **tmp_attrs = slapi_ch_array_dup(attrs); + slapi_pblock_set(int_pbs, SLAPI_SEARCH_ATTRS, tmp_attrs); + slapi_pblock_set(int_pbs, SLAPI_SEARCH_ATTRSONLY, &attrsonly); + slapi_pblock_set(int_pbs, SLAPI_TARGET_UNIQUEID, (void *)uniqueid); + + /* Do the search */ + search_internal_pb(int_pbs); + /* Check the result. */ + + /* Now undefined the pb_search from pb so we can free pb*/ + slapi_v4_search_pblock *pbs = slapi_v4_search_pblock_extract(int_pbs); + slapi_pblock_destroy(int_pbs); + return pbs; +} + +slapi_v4_search_pblock * +slapi_v4_search_internal_all_contexts(int32_t scope, + const char *filter, + char **attrs, + int32_t attrsonly, + LDAPControl **controls, + const char *uniqueid, + int32_t operation_flags) +{ + /* We can use mapping tree to iterate over the suffixes. */ + void *mt_node = NULL; + /* Create a new result */ + slapi_v4_search_pblock *pbs = slapi_v4_search_pblock_create(); + /* Search each of them, building a result set, */ + + slapi_v4_dn *basedn = slapi_get_first_suffix(&mt_node, 0); + while (basedn != NULL) { + /* Search it! */ + slapi_log_error(SLAPI_LOG_DEBUG, "slapi_v4_search_internal_all_contexts", "Searching %s ...\n", slapi_sdn_get_dn(basedn)); + slapi_v4_search_pblock *pbs_inner = slapi_v4_search_internal(basedn, scope, filter, attrs, attrsonly, controls, uniqueid, operation_flags); + slapi_v4_search_pblock_merge(pbs, pbs_inner); + + basedn = slapi_get_next_suffix(&mt_node, 0); + } + /* Return it! */ + return pbs; +} + +slapi_v4_plugin_result * +slapi_v4_entry_exists_or_create(Slapi_DN *dn, const char *filter, const char *entry, const char *modifier_name) { + + /* Search for the entry */ + int32_t attrsonly = 0; + char **attrs = NULL; + + slapi_v4_search_pblock *pb_search_result = slapi_v4_search_internal(dn, LDAP_SCOPE_BASE, filter, attrs, attrsonly, NULL, NULL, 0); + + uint64_t num_results = slapi_v4_search_pblock_get_num_results(pb_search_result); + slapi_v4_search_pblock_destroy(pb_search_result); + + if (num_results == 0) { + slapi_log_error(SLAPI_LOG_DEBUG, "slapi_v4_entry_exists_or_create", "creating entry:\n%s\n", entry); + /* Create the entry */ + + Slapi_Entry *certmap_entry = slapi_str2entry((char *)entry, 0); + if (certmap_entry == NULL) { + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, strdup("Failed to parse entry")); + } + slapi_v4_search_pblock *pb_add_result = slapi_v4_add_internal_entry(certmap_entry, modifier_name); + + if (pb_add_result == NULL) { + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, LDAP_OPERATIONS_ERROR, strdup("Failed to create entry, missing modifier name?")); + } + + int opresult = slapi_v4_search_pblock_get_ldapresult(pb_add_result); + slapi_v4_search_pblock_destroy(pb_add_result); + + if (opresult != LDAP_SUCCESS) { + return slapi_v4_plugin_result_err(SLAPI_V4_PLUGIN_FAILURE, opresult, strdup("Failed to create entry")); + } + } + return slapi_v4_plugin_result_ok(); +} + diff --git a/ldap/servers/slapd/slapi_pal.c b/ldap/servers/slapd/slapi_pal.c index 600d03d..dd3427c 100644 --- a/ldap/servers/slapd/slapi_pal.c +++ b/ldap/servers/slapd/slapi_pal.c @@ -35,6 +35,84 @@ #include #endif +/* + * Memory allocation wrappers + * + * Why do we use these instead of slapi_ch_* types? + * + * The problems that existed that slapi_ch_* were trying to solve don't + * exist anymore. + * + * First, we don't have our memory allocator - no matter what, we can't write + * something better than jemalloc, so don't. + * + * Second, linux lies. while we tried to check for null on ptr return + * and then handle the situation, the situation handlers were broken and + * didn't work, and the check doesn't matter anyway, because linux *always* + * returns a valid pointer to a zero page and it's only on write do we actually + * discover OOM. OOPs! + * + * Third, we tried to wrap calloc to have a number * size, but this means + * that the compiler can't pre-compute values, so we should just use size. + * + * Fourth, even in an OOM scenario, all past experiences and examples show + * we *never* get to this scenario where we can safely handle this, and infact + * we are OOM killed by the kernel first. + * + * Fifth, the slapi_ch types use ulongs not size_t. + * + * Finally, We spent so much time with free (&type) and then checking nulls + * and setting nulls that doesn't matter. free literally handles ptr == null as + * a no-op, and the setting of a pointer to null *does not* help us beside a tiny + * set of datastructure scenarios that we *don't use*. + * + * Thus, we should stop messing about and make these interfaces much lighter + * and simpler. + */ + +void * +spal_calloc(size_t size) { +#ifdef LINUX + return calloc(1, size); +#else + void *ptr = calloc(1, size); + if (ptr == NULL) { + /* + * Just abort - the core will tell us where and why, and there + * is no safe way to shutdown in OOM anyway. + */ + abort(); + } + return ptr; +#endif +} + +void * +spal_realloc(void *ptr, size_t size) { +#ifdef LINUX + return realloc(ptr, size); +#else + void *ptr = realloc(ptr, size); + if (ptr == NULL) { + /* + * Just abort - the core will tell us where and why, and there + * is no safe way to shutdown in OOM anyway. + */ + abort(); + } + return ptr; +#endif +} + +void +spal_free(void *ptr) { + free(ptr); +} + +/* + * Limit management. + */ + static int_fast32_t _spal_rlimit_get(int resource, uint64_t *soft_limit, uint64_t *hard_limit) { diff --git a/ldap/servers/slapd/utf8.c b/ldap/servers/slapd/utf8.c index b0667c6..20d6f9a 100644 --- a/ldap/servers/slapd/utf8.c +++ b/ldap/servers/slapd/utf8.c @@ -411,4 +411,30 @@ ldap_utf8isspace(char *s) return 0; } +unsigned char * +ldap_utf8strip(unsigned char *s) { + /* First find the first non-whitespace char */ + size_t first = 0; + size_t last = strlen((char *)s); + + for(; ldap_utf8isspace((char *)(s + first)); first++) { + } + for(; ldap_utf8isspace((char *)(s + last)); last--) { + } + + /* You are so screwed if this is true. */ + if (last <= first) { + return NULL; + } + size_t size = last - first + 1; + if (size == 0) { + return NULL; + } + + unsigned char *out = spal_calloc(size * sizeof(unsigned char)); + snprintf(out, size, "%s", (char *)(s + first)); + + return out; +} + #endif /* USE_OPENLDAP */ diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c index a0f3268..29a47dc 100644 --- a/ldap/servers/slapd/util.c +++ b/ldap/servers/slapd/util.c @@ -494,6 +494,9 @@ slapi_escape_filter_value(char *filter_str, int len) #endif } +char * +slapi_v4_escape_filter_value(char *filter_str, int len) __attribute__((weak, alias("slapi_escape_filter_value"))); + /* ** This function takes a quoted attribute value of the form "abc", ** and strips off the enclosing quotes. It also deals with quoted -- 1.8.3.1