From f2e4e2b0b7b3393ac62023b94033707be1ef3924 Mon Sep 17 00:00:00 2001 From: William Brown Date: Thu, 20 Jul 2017 09:35:13 +1000 Subject: [PATCH] Ticket 49218 - Certmap plugin capabalities for freeipa Bug Description: This adds support for pluggable certificate mapping libraries. To achieve this, this replaces the existing baked in certificate mapping code. Fix Description: * The slapi v4 plugin api, which is a new start with the aim to be cleaner and ffi friendly. See: http://www.port389.org/docs/389ds/design/plugin-v4.html * A new plugin management kit. It's based on the b+tree cow from sds to provide thread safety. It's based on the design: http://www.port389.org/docs/389ds/design/transactional-operations.html importantly, this is 100% thread safe with dynamic plugins designed from the start. It's far more efficent using less atomics and locks than the existing plugin system * An upgrade mechanism so that on update the server can self-migrate new settings. Rather than each part of the code with their own system, we should use a centralised one. This is also an important step to docker integration * A certificate mapping plugin that uses the v4 api, the new plugin code and is enabled on upgrade. It is designed to be config compatible with the existing certmap.conf and is intended to be a compatibilty step for existing users, and an api example for new api consumers. Note that many of the apis are still in the "private" header. This is because until we confirm the stabilisation of these, we want to have the freedom to change these. There is an "agreement" trap set in the header so a consumer can not "accidentally" include these without acknowledgement. https://pagure.io/389-ds-base/issue/49218 Author: wibrown Review by: ??? --- Makefile.am | 59 +- ldap/schema/01core389.ldif | 1 + ldap/schema/30ns-common.ldif | 3 +- ldap/servers/plugins/certmap/certmap.c | 639 ++++++++++++ ldap/servers/plugins/rever/rever.h | 5 + ldap/servers/slapd/add.c | 34 + ldap/servers/slapd/auth.c | 466 ++------- 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/config.c | 27 +- ldap/servers/slapd/configdse.c | 21 + ldap/servers/slapd/connection.c | 18 + ldap/servers/slapd/daemon.c | 10 + ldap/servers/slapd/dn.c | 27 +- ldap/servers/slapd/dse.c | 37 +- ldap/servers/slapd/entry.c | 29 + ldap/servers/slapd/fedse.c | 36 + ldap/servers/slapd/libglobs.c | 6 +- ldap/servers/slapd/log.c | 74 +- ldap/servers/slapd/main.c | 33 +- ldap/servers/slapd/operation.c | 1 + ldap/servers/slapd/pblock.c | 4 + ldap/servers/slapd/pblock_v3.h | 1 + ldap/servers/slapd/pblock_v4.c | 173 ++++ ldap/servers/slapd/plugin.c | 3 + ldap/servers/slapd/plugin_internal_op.c | 4 +- ldap/servers/slapd/plugin_v4.c | 1171 +++++++++++++++++++++ ldap/servers/slapd/proto-slap.h | 6 +- ldap/servers/slapd/slapi-internal-v4.h | 127 +++ ldap/servers/slapd/slapi-plugin-v4.c | 70 ++ ldap/servers/slapd/slapi-plugin-v4.h | 57 + ldap/servers/slapd/slapi-private-v4.c | 131 +++ ldap/servers/slapd/slapi-private-v4.h | 203 ++++ ldap/servers/slapd/slapi_pal.c | 78 ++ ldap/servers/slapd/slapi_pal.h | 6 + ldap/servers/slapd/snmp_collator.c | 3 + ldap/servers/slapd/upgrade.c | 60 ++ ldap/servers/slapd/utf8.c | 26 + ldap/servers/slapd/util.c | 3 + lib/ldaputil/.cvsignore | 1 - lib/ldaputil/cert.c | 489 --------- lib/ldaputil/certmap.c | 1721 ------------------------------- lib/ldaputil/certmap.conf | 15 +- lib/ldaputil/dbconf.c | 686 ------------ lib/ldaputil/encode.c | 151 --- lib/ldaputil/errors.c | 210 ---- lib/ldaputil/examples/Makefile | 89 -- lib/ldaputil/examples/README | 100 -- lib/ldaputil/examples/init.c | 43 - lib/ldaputil/examples/plugin.c | 240 ----- lib/ldaputil/examples/plugin.h | 35 - lib/ldaputil/init.c | 138 --- lib/ldaputil/ldapauth.c | 214 ---- lib/ldaputil/ldapu-changes.html | 406 -------- lib/ldaputil/ldaputili.h | 68 -- lib/ldaputil/vtable.c | 210 ---- src/libsds/include/sds.h | 24 +- src/libsds/sds/bpt/bpt.c | 3 + src/libsds/sds/bpt/common.c | 2 +- src/libsds/sds/bpt/set.c | 4 +- src/libsds/sds/bpt_cow/bpt_cow.c | 45 + src/libsds/sds/bpt_cow/txn.c | 27 +- src/libsds/test/test_sds_cow.c | 37 + src/libsds/test/test_sds_set.c | 4 +- 66 files changed, 3479 insertions(+), 5342 deletions(-) create mode 100644 ldap/servers/plugins/certmap/certmap.c 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/plugin_v4.c create mode 100644 ldap/servers/slapd/slapi-internal-v4.h create mode 100644 ldap/servers/slapd/slapi-plugin-v4.c create mode 100644 ldap/servers/slapd/slapi-plugin-v4.h create mode 100644 ldap/servers/slapd/slapi-private-v4.c create mode 100644 ldap/servers/slapd/slapi-private-v4.h create mode 100644 ldap/servers/slapd/upgrade.c delete mode 100644 lib/ldaputil/.cvsignore delete mode 100644 lib/ldaputil/cert.c delete mode 100644 lib/ldaputil/certmap.c delete mode 100644 lib/ldaputil/dbconf.c delete mode 100644 lib/ldaputil/encode.c delete mode 100644 lib/ldaputil/errors.c delete mode 100644 lib/ldaputil/examples/Makefile delete mode 100644 lib/ldaputil/examples/README delete mode 100644 lib/ldaputil/examples/init.c delete mode 100644 lib/ldaputil/examples/plugin.c delete mode 100644 lib/ldaputil/examples/plugin.h delete mode 100644 lib/ldaputil/init.c delete mode 100644 lib/ldaputil/ldapauth.c delete mode 100644 lib/ldaputil/ldapu-changes.html delete mode 100644 lib/ldaputil/ldaputili.h delete mode 100644 lib/ldaputil/vtable.c diff --git a/Makefile.am b/Makefile.am index 89b16ea..a82c002 100644 --- a/Makefile.am +++ b/Makefile.am @@ -84,7 +84,8 @@ AM_CPPFLAGS = $(DEBUG_DEFINES) $(GCCSEC_DEFINES) $(ASAN_DEFINES) $(PROFILING_DEF # WARNING: This needs a clean up, because slap.h is a horrible mess and is publically exposed! DSPLUGIN_CPPFLAGS = $(DS_DEFINES) $(DS_INCLUDES) $(PATH_DEFINES) $(SYSTEMD_DEFINES) $(NUNCSTANS_INCLUDES) @openldap_inc@ @ldapsdk_inc@ @nss_inc@ $(NSPR_INCLUDES) @systemd_inc@ # This should give access to internal headers only for tests!!! -DSINTERNAL_CPPFLAGS = -I$(srcdir)/include/ldaputil +# DSINTERNAL_CPPFLAGS = -I$(srcdir)/include/ldaputil +DSINTERNAL_CPPFLAGS = # Flags for Datastructure Library SDS_CPPFLAGS = $(SDS_INCLUDES) $(NSPR_INCLUDES) # Flags for nunc-stans @@ -289,7 +290,7 @@ bin_PROGRAMS = dbscan \ pwdhash \ rsearch -server_LTLIBRARIES = libsds.la libnunc-stans.la libldaputil.la libslapd.la libns-dshttpd.la +server_LTLIBRARIES = libsds.la libnunc-stans.la libslapd.la libns-dshttpd.la # this is how to add optional plugins if enable_pam_passthru @@ -322,6 +323,7 @@ endif serverplugin_LTLIBRARIES = libacl-plugin.la \ libaddn-plugin.la \ + libcertmap-plugin.la \ libattr-unique-plugin.la \ libautomember-plugin.la libback-ldbm.la libchainingdb-plugin.la \ libcollation-plugin.la libcos-plugin.la libderef-plugin.la \ @@ -353,14 +355,6 @@ dist_noinst_HEADERS = \ include/base/systems.h \ include/base/systhr.h \ include/base/util.h \ - include/ldaputil/cert.h \ - include/ldaputil/certmap.h \ - include/ldaputil/dbconf.h \ - include/ldaputil/encode.h \ - include/ldaputil/errors.h \ - include/ldaputil/init.h \ - include/ldaputil/ldapauth.h \ - include/ldaputil/ldaputil.h \ include/libaccess/aclerror.h \ include/libaccess/acleval.h \ include/libaccess/aclglobal.h \ @@ -473,7 +467,10 @@ dist_noinst_HEADERS = \ ldap/servers/slapd/slapi_pal.h \ ldap/servers/slapd/slapi-plugin-compat4.h \ ldap/servers/slapd/slapi-plugin.h \ + ldap/servers/slapd/slapi-plugin-v4.h \ ldap/servers/slapd/slapi-private.h \ + ldap/servers/slapd/slapi-private-v4.h \ + ldap/servers/slapd/slapi-internal-v4.h \ ldap/servers/slapd/snmp_collator.h \ ldap/servers/slapd/sslerrstrs.h \ ldap/servers/slapd/statechange.h \ @@ -508,7 +505,6 @@ dist_noinst_HEADERS = \ ldap/systools/pio.h \ lib/base/lexer_pvt.h \ lib/base/plist_pvt.h \ - lib/ldaputil/ldaputili.h \ lib/libaccess/access_plhash.h \ lib/libaccess/aclcache.h \ lib/libaccess/aclpriv.h \ @@ -857,6 +853,7 @@ pkgconfig_DATA = src/pkgconfig/dirsrv.pc \ serverinc_HEADERS = ldap/servers/plugins/replication/repl-session-plugin.h \ ldap/servers/slapd/slapi_pal.h \ ldap/servers/slapd/slapi-plugin.h \ + ldap/servers/slapd/slapi-plugin-v4.h \ ldap/servers/plugins/replication/winsync-plugin.h \ src/nunc-stans/include/nunc-stans.h \ src/libsds/include/sds.h @@ -1012,21 +1009,6 @@ endif libavl_a_SOURCES = ldap/libraries/libavl/avl.c libavl_a_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) -#------------------------ -# libldaputil -#------------------------ -libldaputil_la_SOURCES = lib/ldaputil/cert.c \ - lib/ldaputil/certmap.c \ - lib/ldaputil/dbconf.c \ - lib/ldaputil/encode.c \ - lib/ldaputil/errors.c \ - lib/ldaputil/init.c \ - lib/ldaputil/ldapauth.c \ - lib/ldaputil/vtable.c - -libldaputil_la_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(DSINTERNAL_CPPFLAGS) -I$(srcdir)/lib/ldaputil - - #//////////////////////////////////////////////////////////////// # # Dynamic Server Libraries @@ -1139,8 +1121,8 @@ libns_dshttpd_la_SOURCES = lib/libaccess/access_plhash.cpp \ lib/libsi18n/reshash.c \ lib/libsi18n/txtfile.c -libns_dshttpd_la_CPPFLAGS = -I$(srcdir)/include/base $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) -I$(srcdir)/lib/ldaputil -libns_dshttpd_la_LIBADD = libslapd.la libldaputil.la $(LDAPSDK_LINK) $(SASL_LINK) $(NSS_LINK) $(NSPR_LINK) +libns_dshttpd_la_CPPFLAGS = -I$(srcdir)/include/base $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) +libns_dshttpd_la_LIBADD = libslapd.la $(LDAPSDK_LINK) $(SASL_LINK) $(NSS_LINK) $(NSPR_LINK) # Mark that this is a per version library. libns_dshttpd_la_LDFLAGS = -release @PACKAGE_VERSION@ @@ -1159,6 +1141,8 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/backend_manager.c \ ldap/servers/slapd/bitset.c \ ldap/servers/slapd/bulk_import.c \ + ldap/servers/slapd/bvarray_v4.c \ + ldap/servers/slapd/cert_v4.c \ ldap/servers/slapd/charray.c \ ldap/servers/slapd/ch_malloc.c \ ldap/servers/slapd/computed.c \ @@ -1202,7 +1186,9 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/opshared.c \ ldap/servers/slapd/pagedresults.c \ ldap/servers/slapd/pblock.c \ + ldap/servers/slapd/pblock_v4.c \ ldap/servers/slapd/plugin.c \ + ldap/servers/slapd/plugin_v4.c \ ldap/servers/slapd/plugin_acl.c \ ldap/servers/slapd/plugin_internal_op.c \ ldap/servers/slapd/plugin_mr.c \ @@ -1223,6 +1209,8 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/security_wrappers.c \ ldap/servers/slapd/slapd_plhash.c \ ldap/servers/slapd/slapi_counter.c \ + ldap/servers/slapd/slapi-plugin-v4.c \ + ldap/servers/slapd/slapi-private-v4.c \ ldap/servers/slapd/slapi2nspr.c \ ldap/servers/slapd/snmp_collator.c \ ldap/servers/slapd/sort.c \ @@ -1237,6 +1225,7 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/utf8.c \ ldap/servers/slapd/utf8compare.c \ ldap/servers/slapd/util.c \ + ldap/servers/slapd/upgrade.c \ ldap/servers/slapd/uuid.c \ ldap/servers/slapd/value.c \ ldap/servers/slapd/valueset.c \ @@ -1244,7 +1233,7 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/slapi_pal.c \ $(libavl_a_SOURCES) -libslapd_la_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(SASL_INCLUDES) @db_inc@ $(SVRCORE_INCLUDES) @kerberos_inc@ @pcre_inc@ $(SDS_CPPFLAGS) +libslapd_la_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(SDS_CPPFLAGS) $(SASL_INCLUDES) @db_inc@ $(SVRCORE_INCLUDES) @kerberos_inc@ @pcre_inc@ libslapd_la_LIBADD = $(LDAPSDK_LINK) $(SASL_LINK) $(SVRCORE_LINK) $(NSS_LINK) $(NSPR_LINK) $(KERBEROS_LINK) $(PCRE_LINK) $(THREADLIB) $(SYSTEMD_LINK) libsds.la libslapd_la_LDFLAGS = $(AM_LDFLAGS) $(SLAPD_LDFLAGS) @@ -1379,6 +1368,16 @@ libaddn_plugin_la_DEPENDENCIES = libslapd.la libaddn_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libcertmap-plugin +#------------------------ +libcertmap_plugin_la_SOURCES = ldap/servers/plugins/certmap/certmap.c + +libcertmap_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) @pcre_inc@ +libcertmap_plugin_la_LIBADD = libslapd.la $(NSPR_LINK) $(PCRE_LINK) +libcertmap_plugin_la_DEPENDENCIES = libslapd.la +libcertmap_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # librootdn-access-plugin #------------------------ # @@ -1954,7 +1953,7 @@ ns_slapd_SOURCES = ldap/servers/slapd/abandon.c \ $(GETSOCKETPEER) ns_slapd_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(SASL_INCLUDES) $(SVRCORE_INCLUDES) -ns_slapd_LDADD = libnunc-stans.la libslapd.la libldaputil.la $(LDAPSDK_LINK) $(NSS_LINK) $(LIBADD_DL) \ +ns_slapd_LDADD = libnunc-stans.la libslapd.la $(LDAPSDK_LINK) $(NSS_LINK) $(LIBADD_DL) \ $(NSPR_LINK) $(SASL_LINK) $(SVRCORE_LINK) $(LIBNSL) $(LIBSOCKET) $(THREADLIB) $(SYSTEMD_LINK) $(EVENT_LINK) ns_slapd_DEPENDENCIES = libslapd.la libnunc-stans.la # We need to link ns-slapd with the C++ compiler on HP-UX since we load diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif index 2464952..31c181a 100644 --- a/ldap/schema/01core389.ldif +++ b/ldap/schema/01core389.ldif @@ -322,4 +322,5 @@ objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation $ ServerKeyExtractFile $ ServerCertExtractFile ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.327 NAME 'rootDNPluginConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( rootdn-open-time $ rootdn-close-time $ rootdn-days-allowed $ rootdn-allow-host $ rootdn-deny-host $ rootdn-allow-ip $ rootdn-deny-ip ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.328 NAME 'nsSchemaPolicy' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ schemaUpdateObjectclassAccept $ schemaUpdateObjectclassReject $ schemaUpdateAttributeAccept $ schemaUpdateAttributeReject) X-ORIGIN 'Netscape Directory Server' ) +objectClasses: ( 2.16.840.1.113730.3.2.330 NAME 'nsDylibPlugin4' DESC 'A version 4 slapi plugin that is loaded from a platform dylib' SUP top MUST ( cn $ nsslapd-pluginpath $ nsslapd-plugininitfunc $ nsslapd-pluginenabled ) X-ORIGIN '389 Directory Server Project' ) diff --git a/ldap/schema/30ns-common.ldif b/ldap/schema/30ns-common.ldif index b095909..216e6ca 100644 --- a/ldap/schema/30ns-common.ldif +++ b/ldap/schema/30ns-common.ldif @@ -53,6 +53,7 @@ attributeTypes: ( nsExecRef-oid NAME 'nsExecRef' DESC 'Netscape defined attribut attributeTypes: ( nsLogSuppress-oid NAME 'nsLogSuppress' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( nsJarfilename-oid NAME 'nsJarfilename' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( nsClassname-oid NAME 'nsClassname' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) +attributeTypes: ( 2.16.840.1.113730.3.1.2336 NAME 'nsCertSubjectDN' DESC 'An x509 DN from a certificate used to map during a TLS bind process' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN '389 Directory Server Project' ) objectClasses: ( nsAdminDomain-oid NAME 'nsAdminDomain' DESC 'Netscape defined objectclass' SUP organizationalUnit MAY ( nsAdminDomainName ) X-ORIGIN 'Netscape' ) objectClasses: ( nsHost-oid NAME 'nsHost' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( serverHostName $ description $ l $ nsHostLocation $ nsHardwarePlatform $ nsOsVersion ) X-ORIGIN 'Netscape' ) objectClasses: ( nsAdminGroup-oid NAME 'nsAdminGroup' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsAdminGroupName $ description $ nsConfigRoot $ nsAdminSIEDN ) X-ORIGIN 'Netscape' ) @@ -64,4 +65,4 @@ objectClasses: ( nsAdminObject-oid NAME 'nsAdminObject' DESC 'Netscape defined o objectClasses: ( nsConfig-oid NAME 'nsConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( description $ nsServerPort $ nsServerAddress $ nsSuiteSpotUser $ nsErrorLog $ nsPidLog $ nsAccessLog $ nsDefaultAcceptLanguage $ nsServerSecurity ) X-ORIGIN 'Netscape' ) objectClasses: ( nsDirectoryInfo-oid NAME 'nsDirectoryInfo' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsBindDN $ nsBindPassword $ nsDirectoryURL $ nsDirectoryFailoverList $ nsDirectoryInfoRef ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.329 NAME 'nsMemberOf' DESC 'Allow memberOf assignment on groups for nesting and users' SUP top AUXILIARY MAY ( memberOf ) X-ORIGIN '389 Directory Server Project' ) - +objectClasses: ( 2.16.840.1.113730.3.2.331 NAME 'nsAccount' DESC 'A representation of a user in a directory server' SUP top AUXILIARY MAY ( userCertificate $ nsCertSubjectDN ) X-ORIGIN '389 Directory Server Project' ) diff --git a/ldap/servers/plugins/certmap/certmap.c b/ldap/servers/plugins/certmap/certmap.c new file mode 100644 index 0000000..0649779 --- /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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_LOG_PLUGIN, "certmap_certmap_fn", "Attempting to bind subjectdn: %s\n", subject_dn); + char *issuer_dn = slapi_v4_cert_get_issuerdn(cert); + slapi_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_LOG_PLUGIN, "certmap_certmap_fn", "No issuer config could be found, falling back to default..\n"); + } + } else { + slapi_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_LOG_PLUGIN, "certmap_certmap_fn", "dncomps == NULL, cmap == NULL,\n"); + slapi_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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/ldap/servers/plugins/rever/rever.h b/ldap/servers/plugins/rever/rever.h index 5c7484a..09b45f5 100644 --- a/ldap/servers/plugins/rever/rever.h +++ b/ldap/servers/plugins/rever/rever.h @@ -20,6 +20,11 @@ #include "slap.h" #include +/* We pull in the internal headers for config */ + +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE +#include + #define AES_MECH 1 #define DES_MECH 2 #define AES_REVER_SCHEME_NAME "AES" 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/auth.c b/ldap/servers/slapd/auth.c index b8e171b..12e3b7c 100644 --- a/ldap/servers/slapd/auth.c +++ b/ldap/servers/slapd/auth.c @@ -20,334 +20,7 @@ #include /* MAXPATHLEN */ #include "slap.h" /* slapi_ch_malloc */ #include "fe.h" - -char *client_auth_config_file = NULL; - -/* forward declarations */ - -static void generate_id(void); -static Slapi_ComponentId *auth_get_component_id(void); - -#define internal_ld NULL - -static int LDAP_CALL LDAP_CALLBACK -slapu_msgfree(LDAP *ld, LDAPMessage *msg) -{ - Slapi_PBlock *pb = (Slapi_PBlock *)msg; - if (ld != internal_ld) { - return ldap_msgfree(msg); - } - if (pb) { - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - } - return LDAP_SUCCESS; -} - -static int LDAP_CALL LDAP_CALLBACK -slapu_search_s(LDAP *ld, const char *rawbaseDN, int scope, const char *filter, char **attrs, int attrsonly, LDAPMessage **result) -{ - int err = LDAP_NO_SUCH_OBJECT; - Slapi_PBlock *pb = NULL; - LDAPControl **ctrls; - Slapi_DN *sdn = slapi_sdn_new_dn_byval(rawbaseDN); - const char *baseDN = slapi_sdn_get_dn(sdn); - - if (ld != internal_ld) { - err = ldap_search_ext_s(ld, baseDN, scope, filter, attrs, attrsonly, - NULL, NULL, NULL, -1, result); - slapi_sdn_free(&sdn); - return err; - } - slapi_log_err(SLAPI_LOG_TRACE, "slapu_search_s", "=> (\"%s\", %i, %s)\n", - baseDN, scope, filter); - if (filter == NULL) - filter = "objectclass=*"; - - /* use new internal search API */ - pb = slapi_pblock_new(); - /* we need to provide managedsait control to avoid returning continuation references */ - ctrls = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *)); - ctrls[0] = (LDAPControl *)slapi_ch_malloc(sizeof(LDAPControl)); - ctrls[0]->ldctl_oid = slapi_ch_strdup(LDAP_CONTROL_MANAGEDSAIT); - ctrls[0]->ldctl_value.bv_val = NULL; - ctrls[0]->ldctl_value.bv_len = 0; - ctrls[0]->ldctl_iscritical = '\0'; - slapi_search_internal_set_pb(pb, baseDN, scope, (char *)filter, attrs, - attrsonly, ctrls, NULL, - auth_get_component_id(), 0 /* actions */); - slapi_search_internal_pb(pb); - - if (pb != NULL) { - if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &err)) { - err = LDAP_LOCAL_ERROR; - } - if (err != LDAP_SUCCESS) { - slapu_msgfree(ld, (LDAPMessage *)pb); - pb = NULL; - if (scope == LDAP_SCOPE_SUBTREE) { - char fbuf[BUFSIZ]; - slapi_log_err(SLAPI_LOG_ERR, "slapu_search_s", "(\"%s\", subtree, %s) err %i\n", - baseDN, escape_string((char *)filter, fbuf), err); - } - } - } else { - char fbuf[BUFSIZ]; - slapi_log_err(SLAPI_LOG_ERR, "slapu_search_s", "(\"%s\", %i, %s) NULL\n", - baseDN, scope, escape_string((char *)filter, fbuf)); - } - slapi_sdn_free(&sdn); - *result = (LDAPMessage *)pb; - slapi_log_err(SLAPI_LOG_TRACE, "<= slapu_search_s", "%i\n", err); - return err; -} - -static int LDAP_CALL LDAP_CALLBACK -slapu_count_entries(LDAP *ld, LDAPMessage *msg) -{ - Slapi_Entry **entry = NULL; - int count = 0; - if (ld != internal_ld) { - return ldap_count_entries(ld, msg); - } - if (!slapi_pblock_get((Slapi_PBlock *)msg, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry) && entry) { - for (; *entry; ++entry) - ++count; - } - return count; -} - -/* slapu_search_s() returns a Slapi_PBlock*, but slapu_first_entry() and - * slapu_next_entry() return a Slapi_Entry** pointing into the same array - * as the PBlock. If one of the iteration (Slapi_Entry**) pointers was - * passed to slapu_msgfree(), havoc would ensue. ldaputil never does this. - * But ldap_msgfree() would support it (no?); so a plugin function might. - * Yet another way this doesn't support plugin functions. - */ - -static LDAPMessage *LDAP_CALL LDAP_CALLBACK -slapu_first_entry(LDAP *ld, LDAPMessage *msg) -{ - Slapi_Entry **entry = NULL; - if (ld != internal_ld) { - return ldap_first_entry(ld, msg); - } - if (!slapi_pblock_get((Slapi_PBlock *)msg, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry) && entry && *entry) { - return (LDAPMessage *)entry; - } - return NULL; -} - -static LDAPMessage *LDAP_CALL LDAP_CALLBACK -slapu_next_entry(LDAP *ld, LDAPMessage *msg) -{ - Slapi_Entry **entry = (Slapi_Entry **)msg; - if (ld != internal_ld) { - if (msg) { - return ldap_next_entry(ld, msg); - } else { - return NULL; - } - } - if (entry && *entry && *++entry) { - return (LDAPMessage *)entry; - } - return NULL; -} - -static char *LDAP_CALL LDAP_CALLBACK -slapu_get_dn(LDAP *ld, LDAPMessage *entry) -{ - if (ld != internal_ld) { - return ldap_get_dn(ld, entry); - } - return slapi_ch_strdup(slapi_entry_get_dn(*(Slapi_Entry **)entry)); -} - -static void LDAP_CALL LDAP_CALLBACK -slapu_memfree(LDAP *ld, void *dn) -{ - if (ld != internal_ld) { - ldap_memfree(dn); - } else { - free(dn); - } -} - -static char * -slapu_attr_get_desc(Slapi_Attr *attr) -{ - char *desc = NULL; - if (slapi_attr_get_type(attr, &desc) == LDAP_SUCCESS && desc) { - return slapi_ch_strdup(desc); - } - return NULL; -} - -/* slapu_first_attribute and slapu_next_attribute use a Slapi_Attr* - * as an iterator. It is malloc'd by first() and free'd by ber_free(). - */ - -static char *LDAP_CALL LDAP_CALLBACK -slapu_first_attribute(LDAP *ld, LDAPMessage *entry, BerElement **iter) -{ - if (ld != internal_ld) { - return ldap_first_attribute(ld, entry, iter); - } else { - Slapi_Attr **attr = (Slapi_Attr **)slapi_ch_malloc(sizeof(Slapi_Attr *)); - *iter = (BerElement *)attr; - if (attr && slapi_entry_first_attr(*(Slapi_Entry **)entry, attr) == LDAP_SUCCESS) { - return slapu_attr_get_desc(*attr); - } - } - return NULL; -} - -static char *LDAP_CALL LDAP_CALLBACK -slapu_next_attribute(LDAP *ld, LDAPMessage *entry, BerElement *iter) -{ - Slapi_Attr **attr = (Slapi_Attr **)iter; - if (ld != internal_ld) { - return ldap_next_attribute(ld, entry, iter); - } - if (attr && slapi_entry_next_attr(*(Slapi_Entry **)entry, *attr, attr) == LDAP_SUCCESS) { - return slapu_attr_get_desc(*attr); - } - return NULL; -} - -static void LDAP_CALL LDAP_CALLBACK -slapu_ber_free(LDAP *ld, BerElement *iter, int freebuf) -{ - if (ld != internal_ld) { - ber_free(iter, freebuf); - } else { - free((Slapi_Attr **)iter); - } -} - -static struct berval **LDAP_CALL LDAP_CALLBACK -slapu_get_values_len(LDAP *ld, LDAPMessage *entry, const char *desc) -{ - Slapi_Attr *attr = NULL; - if (ld != internal_ld) { - return ldap_get_values_len(ld, entry, desc); - } - if (slapi_entry_attr_find(*(Slapi_Entry **)entry, desc, &attr) == LDAP_SUCCESS && attr) { - struct berval **values = NULL; - if (slapi_attr_get_bervals_copy(attr, &values) == 0) { - return (values); - } - } - return NULL; -} - -static void LDAP_CALL LDAP_CALLBACK -slapu_value_free_len(LDAP *ld, struct berval **values) -{ - if (ld != internal_ld) { - ldap_value_free_len(values); - } else { - ber_bvecfree(values); - } -} - -void -client_auth_init() -{ - int err; - if (client_auth_config_file == NULL) { - char *confdir = config_get_configdir(); - if (NULL == confdir) { - slapi_log_err(SLAPI_LOG_ERR, "client_auth_init", "Failed to get configdir\n"); - return; - } - client_auth_config_file = PR_smprintf("%s/certmap.conf", confdir); - if (NULL == client_auth_config_file) { - slapi_log_err(SLAPI_LOG_ERR, "client_auth_init", "Failed to duplicate \"%s/certmap\"\n", confdir); - slapi_ch_free_string(&confdir); - return; - } - slapi_ch_free_string(&confdir); - } - err = ldaputil_init(client_auth_config_file, "", NULL, "slapd", NULL); - if (err != LDAPU_SUCCESS) { - slapi_log_err(SLAPI_LOG_TRACE, "client_auth_init", "ldaputil_init(%s,...) %i\n", client_auth_config_file, err); - } else { - LDAPUVTable_t vtable = { - NULL /* ssl_init */, - NULL /* set_option */, - NULL /* simple_bind_s */, - NULL /* unbind */, - slapu_search_s, - slapu_count_entries, - slapu_first_entry, - slapu_next_entry, - slapu_msgfree, - slapu_get_dn, - slapu_memfree, - slapu_first_attribute, - slapu_next_attribute, - slapu_ber_free, - NULL /* get_values */, - NULL /* value_free */, - slapu_get_values_len, - slapu_value_free_len}; - ldapu_VTable_set(&vtable); - } - - /* Generate a component id for cert-based authentication */ - generate_id(); -} - -#include -#include "slapi-plugin.h" /* SLAPI_BERVAL_EQ */ -#include "slapi-private.h" /* COMPONENT_CERT_AUTH */ - -static Slapi_ComponentId *auth_component_id = NULL; - -static void -generate_id(void) -{ - if (auth_component_id == NULL) { - auth_component_id = generate_componentid(NULL /* Not a plugin */, COMPONENT_CERT_AUTH); - } -} - -static Slapi_ComponentId * -auth_get_component_id(void) -{ - return auth_component_id; -} - - -static char * -subject_of(CERTCertificate *cert) -{ - char *dn = NULL; - if (cert != NULL) { - int err = ldapu_get_cert_subject_dn(cert, &dn); - if (err != LDAPU_SUCCESS) { - slapi_log_err(SLAPI_LOG_ERR, "subject_of", "ldapu_get_cert_subject_dn(%p) %i (%s)\n", - (void *)cert, err, ldapu_err2string(err)); - } - } - return dn; -} - -static char * -issuer_of(CERTCertificate *cert) -{ - char *dn = NULL; - if (cert != NULL) { - int err = ldapu_get_cert_issuer_dn(cert, &dn); - if (err != LDAPU_SUCCESS) { - slapi_log_err(SLAPI_LOG_ERR, "issuer_of", "ldapu_get_cert_issuer_dn(%p) %i (%s)\n", - (void *)cert, err, ldapu_err2string(err)); - } - } - return dn; -} +#include /* * Log a certificate that was rejected because the client didn't @@ -365,19 +38,22 @@ handle_bad_certificate(void *clientData, PRFileDesc *prfd) CERTCertificate *clientCert = slapd_ssl_peerCertificate(prfd); PRErrorCode errorCode = PR_GetError(); - char *subject = subject_of(clientCert); - char *issuer = issuer_of(clientCert); + char *subject = slapi_v4_cert_get_subjectdn(clientCert); + char *issuer = slapi_v4_cert_get_issuerdn(clientCert); slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64 " " SLAPI_COMPONENT_NAME_NSPR " error %i (%s); unauthenticated client %s; issuer %s\n", conn->c_connid, errorCode, slapd_pr_strerror(errorCode), subject ? escape_string(subject, sbuf) : "NULL", issuer ? escape_string(issuer, ibuf) : "NULL"); - if (issuer) + if (issuer) { free(issuer); - if (subject) + } + if (subject) { free(subject); - if (clientCert) + } + if (clientCert) { CERT_DestroyCertificate(clientCert); + } return -1; /* non-zero means reject this certificate */ } @@ -395,7 +71,6 @@ handle_handshake_done(PRFileDesc *prfd, void *clientData) Connection *conn = (Connection *)clientData; CERTCertificate *clientCert = slapd_ssl_peerCertificate(prfd); - char *clientDN = NULL; int keySize = 0; char *cipher = NULL; char *extraErrorMsg = ""; @@ -420,7 +95,7 @@ handle_handshake_done(PRFileDesc *prfd, void *clientData) } keySize = cipherInfo.effectiveKeyBits; - cipher = slapi_ch_strdup(cipherInfo.symCipherName); + cipher = strdup(cipherInfo.symCipherName); /* If inside an Start TLS operation, perform the privacy level discovery * and if the security degree achieved after the handshake is not reckoned @@ -445,85 +120,66 @@ handle_handshake_done(PRFileDesc *prfd, void *clientData) conn->c_connid, sslversion, keySize, cipher ? cipher : "NULL"); } else { - subject = subject_of(clientCert); - if (!subject) { - (void)slapi_getSSLVersion_str(channelInfo.protocolVersion, - sslversion, sizeof(sslversion)); - slapi_log_access(LDAP_DEBUG_STATS, - "conn=%" PRIu64 " %s %i-bit %s; missing subject\n", - conn->c_connid, - sslversion, keySize, cipher ? cipher : "NULL"); + + slapi_v4_certmap_pblock *pbc = slapi_v4_certmap_pblock_init(); + /* + * Populate all the parameters to pbc that are needed. + * + * This is where we actually extract all the certificate details we'll need for + * the certmap bind process to operate. + * + * We need: issuer + * : subject dn + * : raw DER? + * What if we just attach the cert to the pbc, then we can call as needed? + */ + slapi_v4_certmap_pblock_set_clientcert(pbc, clientCert); + + plugin_v4_result bind_certmap_result = plugin_v4_call_bind_certmap(pbc); + if (bind_certmap_result != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, "handle_handshake_done", "Failed to execute bind_certmap plugins\n"); + slapi_v4_certmap_pblock_destroy(pbc); goto done; } - { - char *issuer = issuer_of(clientCert); - char sbuf[BUFSIZ], ibuf[BUFSIZ]; - (void)slapi_getSSLVersion_str(channelInfo.protocolVersion, - sslversion, sizeof(sslversion)); - slapi_log_access(LDAP_DEBUG_STATS, - "conn=%" PRIu64 " %s %i-bit %s; client %s; issuer %s\n", - conn->c_connid, - sslversion, keySize, cipher ? cipher : "NULL", - subject ? escape_string(subject, sbuf) : "NULL", - issuer ? escape_string(issuer, ibuf) : "NULL"); - if (issuer) - free(issuer); + + Slapi_DN *client_dn = slapi_v4_certmap_pblock_get_clientdn(pbc); + + if (client_dn == NULL) { + /* + * Given we got a SUCCESS from the plugin, this shouldn't happen, but + * who knows what kind of junk people write. + */ + slapi_v4_certmap_pblock_destroy(pbc); + slapi_log_err(SLAPI_LOG_ERR, "handle_handshake_done", "bind_certmap plugins return NULL result, this is a mistake!!!!\n"); + goto done; } - slapi_dn_normalize(subject); - { - LDAPMessage *chain = NULL; - char *basedn = config_get_basedn(); - int err; + /* As pointless as this seems, I want to wait to change connection->c_dn to sdn */ + char *binddn = strdup(slapi_sdn_get_dn(client_dn)); - err = ldapu_cert_to_ldap_entry(clientCert, internal_ld, basedn ? basedn : "" /*baseDN*/, &chain); - if (err == LDAPU_SUCCESS && chain) { - LDAPMessage *entry = slapu_first_entry(internal_ld, chain); - if (entry) { - /* clientDN is duplicated in slapu_get_dn */ - clientDN = slapu_get_dn(internal_ld, entry); - } else { + (void)slapi_getSSLVersion_str(channelInfo.protocolVersion, sslversion, sizeof(sslversion)); - extraErrorMsg = "no entry"; - slapi_log_err(SLAPI_LOG_TRACE, "handle_handshake_done", "<= ldapu_cert_to_ldap_entry() %s\n", - extraErrorMsg); - } - } else { - extraErrorMsg = ldapu_err2string(err); - slapi_log_err(SLAPI_LOG_TRACE, "handle_handshake_done", "<= ldapu_cert_to_ldap_entry() %i (%s)%s\n", - err, extraErrorMsg, chain ? "" : " NULL"); - } - slapi_ch_free_string(&basedn); - slapu_msgfree(internal_ld, chain); + if (client_dn != NULL) { + slapi_log_access(LDAP_DEBUG_STATS, + "conn=%" PRIu64 " %s client bound as %s\n", + conn->c_connid, + sslversion, binddn); + } else if (clientCert != NULL) { + slapi_log_access(LDAP_DEBUG_STATS, + "conn=%" PRIu64 " %s failed to map client " + "certificate to LDAP DN (%s)\n", + conn->c_connid, + sslversion, extraErrorMsg); } - } - if (clientDN != NULL) { - Slapi_DN *sdn = NULL; - sdn = slapi_sdn_new_dn_passin(clientDN); - clientDN = slapi_ch_strdup(slapi_sdn_get_dn(sdn)); - slapi_sdn_free(&sdn); - (void)slapi_getSSLVersion_str(channelInfo.protocolVersion, - sslversion, sizeof(sslversion)); - slapi_log_access(LDAP_DEBUG_STATS, - "conn=%" PRIu64 " %s client bound as %s\n", - conn->c_connid, - sslversion, clientDN); - } else if (clientCert != NULL) { - (void)slapi_getSSLVersion_str(channelInfo.protocolVersion, - sslversion, sizeof(sslversion)); - slapi_log_access(LDAP_DEBUG_STATS, - "conn=%" PRIu64 " %s failed to map client " - "certificate to LDAP DN (%s)\n", - conn->c_connid, - sslversion, extraErrorMsg); + /* + * Associate the new credentials with the connection. Note that + * clientDN and clientCert may be NULL. + */ + bind_credentials_set_nolock(conn, SLAPD_AUTH_SSL, binddn, SLAPD_AUTH_SSL, binddn, clientCert, NULL); + + slapi_v4_certmap_pblock_destroy(pbc); } - /* - * Associate the new credentials with the connection. Note that - * clientDN and clientCert may be NULL. - */ - bind_credentials_set_nolock(conn, SLAPD_AUTH_SSL, clientDN, - SLAPD_AUTH_SSL, clientDN, clientCert, NULL); done: slapi_ch_free_string(&subject); slapi_ch_free_string(&cipher); 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/config.c b/ldap/servers/slapd/config.c index afe07df..821ca73 100644 --- a/ldap/servers/slapd/config.c +++ b/ldap/servers/slapd/config.c @@ -27,7 +27,7 @@ #include #include -#define MAXARGS 1000 +#include extern int should_detach; extern Slapi_PBlock *repl_pb; @@ -303,14 +303,10 @@ slapd_bootstrap_config(const char *configdir) } #endif /* see if the entry is a child of the plugin base dn */ - if (slapi_sdn_isparent(&plug_dn, - slapi_entry_get_sdn_const(e))) { - if (entry_has_attr_and_value(e, "objectclass", - "nsSlapdPlugin", 0) && - (entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE, - "syntax", 0) || - entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE, - "matchingrule", 0))) { + if (slapi_sdn_isparent(&plug_dn, slapi_entry_get_sdn_const(e))) { + if (entry_has_attr_and_value(e, "objectclass", "nsSlapdPlugin", 0) && + (entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE, "syntax", 0) || + entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE, "matchingrule", 0))) { /* add the syntax/matching scheme rule plugin */ if (plugin_setup(e, 0, 0, 1, returntext)) { slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", @@ -320,6 +316,16 @@ slapd_bootstrap_config(const char *configdir) slapi_sdn_done(&plug_dn); goto bail; } + } else if (entry_has_attr_and_value(e, "objectclass", "nsDylibPlugin4", 0)) { + plugin_v4_result p_result = plugin_v4_setup(e); + if (p_result != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", + "The plugin entry [%s] in the configfile %s was invalid. %d\n", + slapi_entry_get_dn(e), configfile, p_result); + rc = 0; + slapi_sdn_done(&plug_dn); + goto bail; + } } } @@ -495,8 +501,9 @@ slapd_bootstrap_config(const char *configdir) val[0] = 0; } - if (e) + if (e) { slapi_entry_free(e); + } } /* kexcoff: initialize rootpwstoragescheme and pw_storagescheme * if not explicilty set in the config file diff --git a/ldap/servers/slapd/configdse.c b/ldap/servers/slapd/configdse.c index 28160d3..f885545 100644 --- a/ldap/servers/slapd/configdse.c +++ b/ldap/servers/slapd/configdse.c @@ -22,6 +22,7 @@ #include "log.h" #include "slap.h" #include "pw.h" +#include static int check_all_maxdiskspace_and_mlogsize(Slapi_PBlock *pb, LDAPMod **mod, char *returntext); static int is_delete_a_replace(LDAPMod **mods, int mod_count); @@ -369,6 +370,26 @@ load_plugin_entry(Slapi_PBlock *pb __attribute__((unused)), } int +load_plugin_v4_entry(Slapi_PBlock *pb __attribute__((unused)), + Slapi_Entry* e, + Slapi_Entry* ignored __attribute__((unused)), + int *returncode, + char *returntext __attribute__((unused)), + void *arg __attribute__((unused))) +{ + plugin_v4_result p_result = plugin_v4_setup(e); + if (p_result != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, "load_plugin_v4_entry", + "The plugin entry [%s] is invalid. %d\n", + slapi_entry_get_dn(e), p_result); + exit(1); + } + + *returncode = (int)p_result; + return SLAPI_DSE_CALLBACK_OK; +} + +int modify_config_dse(Slapi_PBlock *pb, Slapi_Entry *entryBefore __attribute__((unused)), Slapi_Entry *e, diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c index 24a7a1c..a52af68 100644 --- a/ldap/servers/slapd/connection.c +++ b/ldap/servers/slapd/connection.c @@ -27,6 +27,8 @@ #include /* for TCP_CORK */ #endif +#include + typedef Connection work_q_item; static void connection_threadmain(void); static void connection_add_operation(Connection *conn, Operation *op); @@ -1598,6 +1600,13 @@ connection_threadmain() /* Once we're here we have a pb */ slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); slapi_pblock_get(pb, SLAPI_OPERATION, &op); + + /* Get our plugins ready. */ + if (plugin_v4_start_operation() != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_CRIT, "connection_threadmain", "Failed to init plugins for operation\n"); + goto done; + } + maxthreads = config_get_maxthreadsperconn(); more_data = 0; ret = connection_read_operation(conn, op, &tag, &more_data); @@ -1761,6 +1770,15 @@ connection_threadmain() connection_dispatch_operation(conn, op, pb); done: + /* + * Operation is complete, yield the plugin list. + * Future william: Should plugin tls be part of operation instead? + */ + if (plugin_v4_close_operation() != PLUGIN_V4_SUCCESS) { + /* This is a recoverable error, but may leak memory. */ + slapi_log_err(SLAPI_LOG_CRIT, "connection_threadmain", "closing plugin after operation failed!\n"); + } + if (doshutdown) { PR_EnterMonitor(conn->c_mutex); connection_remove_operation_ext(pb, conn, op); diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c index 4e0466a..890c736 100644 --- a/ldap/servers/slapd/daemon.c +++ b/ldap/servers/slapd/daemon.c @@ -57,6 +57,8 @@ #include #include "fe.h" +#include + #if defined(ENABLE_LDAPI) #include "getsocketpeer.h" #endif /* ENABLE_LDAPI */ @@ -1236,6 +1238,14 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) plugin_closeall(1 /* Close Backends */, 1 /* Close Globals */); + /* + * Shutdown all plugin_v4 components. + */ + if (plugin_v4_shutdown() != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_WARNING, "slapd_daemon", + "Unable to cleanly stop plugin v4 subsystem. Some resources may leak ...\n"); + } + if (!in_referral_mode) { /* Close SNMP collator after the plugins closed... * Replication plugin still performs internal ops that 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/dse.c b/ldap/servers/slapd/dse.c index 420248c..0ce2f43 100644 --- a/ldap/servers/slapd/dse.c +++ b/ldap/servers/slapd/dse.c @@ -42,6 +42,8 @@ #include /* provides fsync/close */ +#include + /* #define SLAPI_DSE_DEBUG */ /* define this to force trace log */ /* messages to always be logged */ @@ -1819,6 +1821,10 @@ dse_modify(Slapi_PBlock *pb) /* JCM There should only be one exit point from thi goto done; } } + } else if (slapi_entry_attr_hasvalue(ecc, SLAPI_ATTR_OBJECTCLASS, "nsDylibPlugin4")) { + /* Let the plugin know it's being modified. */ + /* It will work out what actions need to be taken. */ + plugin_v4_modify(ecc); } } } @@ -2029,14 +2035,15 @@ dse_add_plugin(Slapi_Entry *entry, char *returntext) { int rc = LDAP_SUCCESS; - if (!slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsSlapdPlugin") || - !config_get_dynamic_plugins()) { - /* - * This is not a plugin, or we are not allowing dynamic updates. - */ - return rc; + if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsSlapdPlugin") && + config_get_dynamic_plugins()) { + rc = plugin_add(entry, returntext, 0 /* not locked */); + } else if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsDylibPlugin4")) { + plugin_v4_result result = plugin_v4_add(entry); + if (result != PLUGIN_V4_SUCCESS) { + rc = LDAP_UNWILLING_TO_PERFORM; + } } - rc = plugin_add(entry, returntext, 0 /* not locked */); return rc; } @@ -2049,16 +2056,16 @@ dse_delete_plugin(Slapi_Entry *entry, char *returntext) { int rc = LDAP_SUCCESS; - if (!slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsSlapdPlugin") || - slapi_entry_attr_hasvalue(entry, "nsslapd-PluginEnabled", "off") || + if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsSlapdPlugin") && + slapi_entry_attr_hasvalue(entry, "nsslapd-PluginEnabled", "off") && !config_get_dynamic_plugins()) { - /* - * This is not a plugin, this plugin was not enabled to begin with, or we - * are not allowing dynamic updates . - */ - return rc; + rc = plugin_delete(entry, returntext, 0 /* not locked */); + } else if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "nsDylibPlugin4")) { + plugin_v4_result result = plugin_v4_delete(entry); + if (result != PLUGIN_V4_SUCCESS) { + rc = LDAP_UNWILLING_TO_PERFORM; + } } - rc = plugin_delete(entry, returntext, 0 /* not locked */); return rc; } diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c index 62d10c2..66e4704 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) { @@ -2693,6 +2698,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 @@ -2750,6 +2758,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/fedse.c b/ldap/servers/slapd/fedse.c index f225c40..25146bd1 100644 --- a/ldap/servers/slapd/fedse.c +++ b/ldap/servers/slapd/fedse.c @@ -42,6 +42,7 @@ #include "slap.h" #include "fe.h" #include +#include extern char **getSupportedCiphers(void); extern char **getEnabledCiphers(void); @@ -1522,6 +1523,7 @@ static const char *easter_egg_photos[NUM_EASTER_EGG_PHOTOS + 1]; static struct dse *pfedse = NULL; static int check_plugin_path(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e, int *returncode, char *returntext, void *arg); +static int check_plugin_v4(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e, int *returncode, char *returntext, void *arg); static void internal_add_helper(Slapi_Entry *e, int dont_write_file) @@ -1569,6 +1571,9 @@ init_dse_file(const char *configdir, Slapi_DN *config) dse_register_callback(pfedse, DSE_OPERATION_READ, DSE_FLAG_PREOP, config, LDAP_SCOPE_BASE, "(objectclass=*)", load_config_dse, NULL, NULL); + dse_register_callback(pfedse, DSE_OPERATION_READ, DSE_FLAG_PREOP, config, + LDAP_SCOPE_SUBTREE, "(objectclass=nsDylibPlugin4)", + load_plugin_v4_entry, NULL, NULL); slapi_pblock_set(pb, SLAPI_CONFIG_DIRECTORY, (void *)configdir); /* don't write out the file when reading */ @@ -1848,6 +1853,7 @@ setup_internal_backends(char *configdir) dse_register_callback(pfedse, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, &root, LDAP_SCOPE_BASE, "(objectclass=*)", modify_root_dse, NULL, NULL); dse_register_callback(pfedse, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, &saslmapping, LDAP_SCOPE_SUBTREE, "(objectclass=nsSaslMapping)", sasl_map_config_modify, NULL, NULL); dse_register_callback(pfedse, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsSlapdPlugin)", check_plugin_path, NULL, NULL); + dse_register_callback(pfedse, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsDylibPlugin4)", check_plugin_v4, NULL, NULL); /* Delete */ dse_register_callback(pfedse, SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, &config, LDAP_SCOPE_BASE, "(objectclass=*)", dont_allow_that, NULL, NULL); @@ -1864,6 +1870,7 @@ setup_internal_backends(char *configdir) /* Add */ dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &saslmapping, LDAP_SCOPE_SUBTREE, "(objectclass=nsSaslMapping)", sasl_map_config_add, NULL, NULL); dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsSlapdPlugin)", check_plugin_path, NULL, NULL); + dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsDylibPlugin4)", check_plugin_v4, NULL, NULL); be = be_new_internal(pfedse, "DSE", DSE_BACKEND, &fedse_plugin); be_addsuffix(be, &root); @@ -2009,3 +2016,32 @@ check_plugin_path(Slapi_PBlock *pb __attribute__((unused)), return rc; } + +static int +check_plugin_v4(Slapi_PBlock *pb __attribute__((unused)), + Slapi_Entry* entryBefore, + Slapi_Entry* e, + int *returncode, + char *returntext, + void *arg __attribute__((unused))) +{ + int rc = SLAPI_DSE_CALLBACK_OK; + + plugin_v4_result result = {0}; + + if (e == NULL) { + /* The add case uses entryBefore */ + result = plugin_v4_validate(entryBefore); + } else { + result = plugin_v4_validate(e); + } + /* Validate logs it's own error message for admin. */ + + if (result != PLUGIN_V4_SUCCESS) { + *returncode = LDAP_UNWILLING_TO_PERFORM; + PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Invalid plugin configuration"); + rc = SLAPI_DSE_CALLBACK_ERROR; + } + return rc; +} + diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c index 6a8ab15..c883aa1 100644 --- a/ldap/servers/slapd/libglobs.c +++ b/ldap/servers/slapd/libglobs.c @@ -628,7 +628,7 @@ static struct config_get_and_set NULL, 0, (void **)&global_slapdFrontendConfig.secureport, CONFIG_INT, NULL, NULL}, - {CONFIG_BASEDN_ATTRIBUTE, config_set_basedn, + {CONFIG_BASEDN_ATTRIBUTE, config_set_certmap_basedn, NULL, 0, (void **)&global_slapdFrontendConfig.certmap_basedn, CONFIG_STRING, NULL, NULL /* deletion is not allowed */}, @@ -6088,7 +6088,7 @@ config_get_return_orig_type_switch() } char * -config_get_basedn(void) +config_get_certmap_basedn(void) { slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); char *retVal; @@ -6101,7 +6101,7 @@ config_get_basedn(void) } int -config_set_basedn(const char *attrname, char *value, char *errorbuf, int apply) +config_set_certmap_basedn(const char *attrname, char *value, char *errorbuf, int apply) { int retVal = LDAP_SUCCESS; slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c index 41b5c99..ad36ef8 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 '/' /************************************************************************** @@ -2427,17 +2431,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, ...) +slapi_log_error_ext(int 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 */ @@ -2450,33 +2446,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 { @@ -2486,24 +2468,40 @@ 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); - } +/* + * Logs to the errors log - this call is wrapped to provide support + * to the new v4 api + */ - 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 */ - } +void +slapi_v4_log_error(slapi_v4_log_level loglevel, char *subsystem, char *fmt, ...) { + va_list ap_err; + va_list ap_file; + va_start(ap_err, fmt); + va_start(ap_file, fmt); + (void)slapi_log_error_ext((int)loglevel, subsystem, fmt, ap_err, ap_file); + va_end(ap_file); + va_end(ap_err); +} - return (rc); +/* + * 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, ...) { + int 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 diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c index ddaceff..12c5f8a 100644 --- a/ldap/servers/slapd/main.c +++ b/ldap/servers/slapd/main.c @@ -53,12 +53,14 @@ union semun #include "slapi-plugin.h" #include "prinit.h" #include "snmp_collator.h" -#include "fe.h" /* client_auth_init() */ +#include "fe.h" #include "protect_db.h" #include "getopt_ext.h" #include "fe.h" #include +#include + #ifdef LINUX /* For mallopt. Should be removed soon. */ #include @@ -800,6 +802,15 @@ main(int argc, char **argv) global_plugin_init(); /* + * Initialise the plugin v4 structures and values. + */ + if (plugin_v4_init() != PLUGIN_V4_SUCCESS) { + slapi_log_err(SLAPI_LOG_EMERG, "main", + "Unable to initialise plugin v4 subsystem\n"); + exit(1); + } + + /* * Process the config files. */ if (0 == slapd_bootstrap_config(slapdFrontendConfig->configdir)) { @@ -1109,6 +1120,17 @@ main(int argc, char **argv) */ task_cleanup(); + /* + * This step checks for any updates and changes on upgrade + * specifically, it manages assumptions about what plugins should exist, and their + * configurations, and potentially even the state of configurations on the server + * and their removal and deprecation. + */ + if (upgrade_server() != UPGRADE_SUCCESS) { + return_value = 1; + goto cleanup; + } + /* init the thread data indexes */ slapi_td_dn_init(); slapi_td_plugin_lock_init(); @@ -1122,6 +1144,10 @@ main(int argc, char **argv) plugin_print_lists(); plugin_startall(argc, argv, NULL /* specific plugin list */); + if (plugin_v4_startall() != PLUGIN_V4_SUCCESS) { + return_value = 1; + goto cleanup; + } compute_plugins_started(); if (housekeeping_start((time_t)0, NULL) == NULL) { return_value = 1; @@ -1190,6 +1216,7 @@ main(int argc, char **argv) reslimit_cleanup(); vattr_cleanup(); sasl_map_done(); + plugin_v4_destroy(); cleanup: compute_terminate(); SSL_ShutdownServerSessionIDCache(); @@ -2981,10 +3008,6 @@ slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt, int s_por } } - if (slapd_exemode == SLAPD_EXEMODE_SLAPD) { - client_auth_init(); - } - if (init_ssl && slapd_ssl_init()) { if (force_to_disable_security("SSL", &init_ssl, ports_info)) { return 1; diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c index 4a05e0a..e2f836f 100644 --- a/ldap/servers/slapd/operation.c +++ b/ldap/servers/slapd/operation.c @@ -211,6 +211,7 @@ operation_done(Slapi_Operation **op, Connection *conn) factory_destroy_extension(get_operation_object_type(), *op, conn, &((*op)->o_extension)); slapi_sdn_done(&(*op)->o_sdn); slapi_sdn_free(&(*op)->o_target_spec); + (*op)->o_target_spec = NULL; slapi_ch_free_string(&(*op)->o_authtype); if ((*op)->o_searchattrs != NULL) { charray_free((*op)->o_searchattrs); diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c index 9a9f84e..fc196f5 100644 --- a/ldap/servers/slapd/pblock.c +++ b/ldap/servers/slapd/pblock.c @@ -3757,6 +3757,9 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value) if (pblock->pb_op != NULL) { pblock->pb_op->o_results.r.r_search.nentries = *((int *)value); } + _pblock_assert_pb_intop(pblock); + /* We are given an int, need to extend it */ + pblock->pb_intop->pb_plugin_internal_search_num_entries = (uint64_t)(*((int *)value)); break; /* Referrals encountered while iterating over the result set */ case SLAPI_SEARCH_REFERRALS: @@ -4139,6 +4142,7 @@ slapi_pblock_set_op_stack_elem(Slapi_PBlock *pb, void *stack_elem) pb->pb_intop->op_stack_elem = stack_elem; } + /* * Clear and then set the bind DN and related credentials for the * connection `conn'. diff --git a/ldap/servers/slapd/pblock_v3.h b/ldap/servers/slapd/pblock_v3.h index 5ccfd6d..2464a60 100644 --- a/ldap/servers/slapd/pblock_v3.h +++ b/ldap/servers/slapd/pblock_v3.h @@ -134,6 +134,7 @@ typedef struct _slapi_pblock_intop /* pointer to array of results returned on search */ Slapi_Entry **pb_plugin_internal_search_op_entries; + uint64_t pb_plugin_internal_search_num_entries; char **pb_plugin_internal_search_op_referrals; LDAPControl **pb_search_ctrls; /* for search operations, allows plugins to provide controls to pass for each entry or referral returned */ 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 d181059..b03ffb5 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/plugin_v4.c b/ldap/servers/slapd/plugin_v4.c new file mode 100644 index 0000000..83fa7e6 --- /dev/null +++ b/ldap/servers/slapd/plugin_v4.c @@ -0,0 +1,1171 @@ +/* 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 */ + +/* See also https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html */ + +/* ========================= WARNING ============================ + * UNLESS YOU HAVE READ: + * https://www.kernel.org/doc/Documentation/memory-barriers.txt + * and SERIOUSLY understand it, and how it works you *MUST* not + * edit this file. This section of the code relies on a deep + * understanding of locking and memory barriers. + * ============================================================== + */ + +/* + * A majority of the safety here is provided by the bptree_cow + * which is already well tested and proven safe. We still need to + * understand it's safety properties, so really, read the above + * before you start making wild changes here. + */ + +#include + +/* + * Provides dlopen and related components. + */ +#include + +/* + * Provides SDS datastructures + */ +#include + +/* For realpath */ +#include +#include + +/** + * Plugin v4 types are *always* dynamic, so there is no need for expensive checks + * in this space. + */ + +typedef enum _plugin_v4_type { + PLUGIN_V4_INTERNAL, + PLUGIN_V4_DYLIB, +} plugin_v4_type; + +typedef enum _plugin_v4_state { + /* Says we are running and enabled. */ + PLUGIN_V4_ENABLED, + /* We are enabled, but not yet running, so wait ... */ + PLUGIN_V4_ENABLED_PRESTART, + /* Not running at all. */ + PLUGIN_V4_DISABLE, +} plugin_v4_state; + +typedef struct _dylib_v4 { + char *path; + void *handle; + uint64_t refcount; +} dylib_v4; + +typedef struct _plugin_v4 { + uint64_t precedence; + char *name; + plugin_v4_state state; + plugin_v4_type type; + dylib_v4 *dl_handle; + void *plugin_private; + uint64_t refcount; + slapi_v4_plugin_result *(*start_fn)(void **ctx); + slapi_v4_plugin_result *(*close_fn)(void **ctx); + slapi_v4_plugin_result *(*bind_certmap_fn)(void *ctx, slapi_v4_certmap_pblock *pbc); +} plugin_v4; + +typedef struct _plugin_key_v4 { + uint64_t precedence; + char *name; +} plugin_key_v4; + +typedef enum _plugin_v4_op_proceed { + OP_PROCEED, + OP_TERMINATE, +} plugin_v4_op_proceed; + +typedef struct _plugin_v4_op_ctx { + plugin_v4_op_proceed proceed; + slapi_v4_plugin_result *res; + void *pb; +} plugin_v4_op_ctx; + +typedef slapi_v4_plugin_result *(*slapi_v4_init_ptr)(slapi_v4_plugin_registration *p_register); + +/* + * We store: + * * tree of dlopened values. These last for the LIFETIME of the server + * because we open them with NODELETE. This means if you delete/add a plugin + * we actually use the same in memory library. This solves *real* consistency + * issues. + * * tree of plugin configs. These are *all* the currently loaded plugin configs + * of the server regardless of enable/disable status. + * * + */ + +static sds_bptree_cow_instance *dylib_tree = NULL; +static sds_bptree_cow_instance *plugin_tree = NULL; + +/* + * plugin thread local storage components + * This is used to keep a thread local map of the active plugins making look up + * and access much faster for a txn. + */ + +typedef enum _plugin_v4_op_state { + PLUGIN_V4_OP_INACTIVE, + PLUGIN_V4_OP_RUNNING, +} plugin_v4_op_state; + +static pthread_key_t plugin_v4_tls_key; + +typedef struct _plugin_v4_tls_map { + plugin_v4_op_state state; + uint64_t txnid; + sds_bptree_transaction *rotxn; + sds_bptree_instance *bind_certmap_map; +} plugin_v4_tls_map; + +/* + * These functions manage the thread local storage. Specifically, the are used + * to destroy the trees and used to + */ + +static void +plugin_v4_tls_destroy(void *tls) { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_tls_destroy", "cleaning thread local storage\n"); + plugin_v4_tls_map *tls_map = (plugin_v4_tls_map *)tls; + if (tls_map != NULL) { + /* Now destroy all the maps that exist */ + sds_bptree_destroy(tls_map->bind_certmap_map); + /* Finally free the value */ + spal_free(tls_map); + } +} + +/* + * Check registration worked. + */ + +static plugin_v4_result +slapi_v4_plugin_registration_validate(slapi_v4_plugin_registration *pr, plugin_v4 *plugin_inst) { + /* + * For a plugin registration to be valid we require: + * * name + * * precedence + * * start + * * close + * otherwise, we can have as many or few hooks after that. + */ + if (pr->sv4_name == NULL || + pr->precedence == 0 || + pr->sv4_start_fn == NULL || + pr->sv4_close_fn == NULL) { + return PLUGIN_V4_INVALID_PLUGIN; + slapi_v4_log_error(SLAPI_LOG_CRIT, "slapi_v4_plugin_registration_validate", "Plugin returned NULL on a critical pointer type.\n"); + } + /* + * Assert our Cn and name match *exactly* + * + * This is how we force single instance (to a point ...) + */ + if (strcmp(plugin_inst->name, pr->sv4_name) != 0) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "slapi_v4_plugin_registration_validate", "Plugin cn differed from config cn. Plugin 'cn=%s' must be named 'cn=%s'.\n", pr->sv4_name, plugin_inst->name); + return PLUGIN_V4_INVALID_PLUGIN; + } + + return PLUGIN_V4_SUCCESS; +} + +/* Plugin key management. */ + +plugin_key_v4 * +plugin_key_extract(plugin_v4 *p) { + plugin_key_v4 *pk = (plugin_key_v4 *)spal_calloc(sizeof(plugin_key_v4)); + pk->precedence = p->precedence; + pk->name = strdup(p->name); + return pk; +} + +char * +plugin_key_name_extract(plugin_v4 *p) { + return strdup(p->name); +} + +static int64_t +plugin_key_cmp_fn(void *a, void *b) { + /* Convert the types. */ + plugin_key_v4 *pka = (plugin_key_v4 *)a; + plugin_key_v4 *pkb = (plugin_key_v4 *)b; + if (pka->precedence != pkb->precedence) { + if (pka->precedence < pkb->precedence) { + return -1; + } else { + return 1; + } + } + /* Same precedence, strcmp them. */ + return (int64_t)strcmp((const char *)pka->name, (const char *)pkb->name); +} + +void * +plugin_key_dup_fn(void *a) { + plugin_key_v4 *pka = (plugin_key_v4 *)a; + plugin_key_v4 *pkn = (plugin_key_v4 *)spal_calloc(sizeof(plugin_key_v4)); + pkn->precedence = pka->precedence; + pkn->name = strdup(pka->name); + return (void *)pkn; +} + +void +plugin_key_free_fn(void *a) { + plugin_key_v4 *pk = (plugin_key_v4 *)a; + spal_free(pk->name); + spal_free(pk); +} + +/* Plugin instance management */ + +/* ========================= WARNING ============================ + * UNLESS YOU HAVE READ: + * https://www.kernel.org/doc/Documentation/memory-barriers.txt + * and SERIOUSLY understand it, and how it works you *MUST* not + * edit this file. This section of the code relies on a deep + * understanding of locking and memory barriers. + * ============================================================== + */ + +static plugin_v4 * +plugin_create() { + plugin_v4 *p = (plugin_v4 *)spal_calloc(sizeof(plugin_v4)); + __atomic_add_fetch_8(&(p->refcount), 1, __ATOMIC_RELAXED); + return p; +} + +static void * +plugin_dup_fn(void *a) { + /* + * During a dup, we need to rc by 1 + * this is thread safe inside of SDS because in a rotxn /wrtxn + * we already hold at least 1 rc for the node to be alive, so + * this can't fail. + */ + plugin_v4 *p = (plugin_v4 *)a; + __atomic_add_fetch_8(&(p->refcount), 1, __ATOMIC_RELAXED); + return a; +} + +static plugin_v4 * +plugin_clone_fn(plugin_v4 *p1) { + plugin_v4 *p_new = plugin_create(); + p_new->precedence = p1->precedence; + p_new->type = p1->type; + p_new->dl_handle = p1->dl_handle; + p_new->start_fn = p1->start_fn; + p_new->close_fn = p1->close_fn; + p_new->bind_certmap_fn = p1->bind_certmap_fn; + return p_new; +} + +static plugin_v4 * +plugin_from_entry(struct slapi_entry *plugin_entry) { + plugin_v4 *plugin_inst = plugin_create(); + plugin_inst->name = slapi_entry_attr_get_charptr(plugin_entry, "cn"); + + char *state = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-pluginEnabled"); + if (strcmp(state, "on") == 0) { + plugin_inst->state = PLUGIN_V4_ENABLED_PRESTART; + } else { + plugin_inst->state = PLUGIN_V4_DISABLE; + } + spal_free(state); + plugin_inst->type = PLUGIN_V4_DYLIB; + + return plugin_inst; +} + +static plugin_v4 * +plugin_lookup_from_entry(sds_bptree_transaction *plugin_wrtxn, struct slapi_entry *plugin_entry) { + char *plugin_key = slapi_entry_attr_get_charptr(plugin_entry, "cn"); + if (plugin_key == NULL) { + return NULL; + } + plugin_v4 *plugin_inst = NULL; + sds_bptree_cow_retrieve(plugin_wrtxn, plugin_key, (void **)&plugin_inst); + /* + * If this fails, plugin_inst is already NULL so we have a course of action .... + */ + + spal_free(plugin_key); + + /* return the entry or NULL */ + return plugin_inst; +} + +static void +plugin_free_fn(void *a) { + /* + * Dec the ref count. If we get to 0, there can be no situation + * in which another dup of us occurs, so we can free here. + */ + plugin_v4 *p = (plugin_v4 *)a; + /* + * this can be relaxed, because to have this pointer the barrier + * must have already been issued, so we don't need another. + */ + uint64_t rc = __atomic_sub_fetch_8(&(p->refcount), 1, __ATOMIC_RELAXED); + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_free_fn", "%s %p refcount %"PRId64" \n", p->name, (void *)p, rc); + if (rc == 0) { + /* + * Now we can free it. + */ + slapi_v4_plugin_result *close_result = p->close_fn(&p->plugin_private); + if (close_result == NULL || close_result->result != SLAPI_V4_PLUGIN_SUCCESS) { + /* What was the error? better handle it .... */ + slapi_v4_log_error(SLAPI_LOG_WARNING, "plugin_free_fn", "recoverable - plugin close failed: %"PRId32" '%s'\n", close_result->ldap_code, close_result->msg); + } + slapi_v4_plugin_result_destroy(close_result); + /* + * IF the plugin state is enabled, call the close fn. + */ + spal_free(p->name); + spal_free(p); + } +} + +/* dylib key management */ + +char * +dylib_key_extract(dylib_v4 *d) { + return strdup(d->path); +} + +/* Shared string comparisons */ + +static int64_t +string_key_cmp_fn(void *a, void *b) { + return (int64_t)strcmp((const char *)a, (const char *)b); +} + +static void * +string_key_dup_fn(void *a) { + return (void *)strdup((char *)a); +} + +static void +string_key_free_fn(void *a) { + spal_free(a); +} + +/* dylib helpers */ + +/* ========================= WARNING ============================ + * UNLESS YOU HAVE READ: + * https://www.kernel.org/doc/Documentation/memory-barriers.txt + * and SERIOUSLY understand it, and how it works you *MUST* not + * edit this file. This section of the code relies on a deep + * understanding of locking and memory barriers. + * ============================================================== + */ + +dylib_v4 * +dylib_create() { + dylib_v4 *d = (dylib_v4 *)spal_calloc(sizeof(dylib_v4)); + __atomic_add_fetch_8(&(d->refcount), 1, __ATOMIC_RELAXED); + return d; +} + +void * +dylib_dup_fn(void *a) { + dylib_v4 *d = (dylib_v4 *)a; + __atomic_add_fetch_8(&(d->refcount), 1, __ATOMIC_RELAXED); + return a; +} + +void +dylib_free_fn(void *a) { + dylib_v4 *d = (dylib_v4 *)a; + uint64_t rc = __atomic_sub_fetch_8(&(d->refcount), 1, __ATOMIC_RELAXED); + if (rc == 0) { + slapi_v4_log_error(SLAPI_V4_LOG_DEBUG, "dylib_free_fn", "freeing dylib at rc 0 - %s\n", d->path); + /* Close the library. */ + dlclose(d->handle); + /* Remember, path is from real path which uses malloc, not slapi_ch* - not that this matters too much ... */ + free(d->path); + spal_free(d); + } +} + +/* + * We then get the txn id, and compare to thread local storage. If they match, + * use the TLS cache. If not, we map the fn types, and build TLS of the event + * types. + * + * This means very little inter-thread contention on the lists, they are protected + * by the same txn as we hold from the tree, fast access etc. + */ + +plugin_v4_result +plugin_v4_init() { + /* + * This pattern leaks if we fail to alloc dylib tree, but if we can't + * alloc it, you are so screwed anyway. + */ + if (sds_bptree_cow_init(&plugin_tree, 0, string_key_cmp_fn, plugin_free_fn, plugin_dup_fn, string_key_free_fn, string_key_dup_fn) != SDS_SUCCESS) { + return PLUGIN_V4_RESOURCE; + } + if (sds_bptree_cow_init(&dylib_tree, 0, string_key_cmp_fn, dylib_free_fn, dylib_dup_fn, string_key_free_fn, string_key_dup_fn) != SDS_SUCCESS) { + return PLUGIN_V4_RESOURCE; + } + /* Create the TLS key for threads to store caches of plugin maps */ + if (pthread_key_create(&plugin_v4_tls_key, plugin_v4_tls_destroy) != 0) { + return PLUGIN_V4_RESOURCE; + } + + return PLUGIN_V4_SUCCESS; +} + +/* + * Finally we free and destroy the tree. This is done after the thread + * pools have stopped. + */ +void +plugin_v4_destroy() { + sds_bptree_cow_destroy(plugin_tree); + sds_bptree_cow_destroy(dylib_tree); +} + +static char * +_plugin_v4_normalise_libpath(char *path) { + char *abs_path = NULL; + char *real_path = NULL; + + if (path[0] != '/') { + /* It's not an abs path already */ + /* + 2 for \0 and / */ + size_t total_len = strlen(path) + strlen(PLUGINDIR) + 2; + abs_path = spal_calloc(total_len); + snprintf(abs_path, total_len, "%s/%s", PLUGINDIR, path); + } else { + /* Already abs, just copy it. */ + abs_path = strdup(path); + } + + /* Do we end in .so (or other platform lib nams we could add here) */ + char *suffix = "-plugin.so"; + size_t suffix_len = strlen(suffix); + size_t abs_path_len = strlen(abs_path); + if (strncmp(abs_path + (abs_path_len - suffix_len), suffix, suffix_len) != 0) { + /* Need to add .so.*/ + size_t total_len = abs_path_len + suffix_len + 1; + char *tmp_abs_path = spal_calloc(total_len); + snprintf(tmp_abs_path, total_len, "%s%s", abs_path, suffix); + spal_free(abs_path); + abs_path = tmp_abs_path; + } + + real_path = realpath(abs_path, NULL); + spal_free(abs_path); + + return real_path; +} + +static plugin_v4_result +_plugin_v4_validate_dylib(struct slapi_entry *plugin_entry, dylib_v4 **d) { + /* Normalise the dl path */ + char *path = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-pluginpath"); + if (path == NULL) { + return PLUGIN_V4_NULL_POINTER; + } + + char *dylib_path = _plugin_v4_normalise_libpath(path); + + slapi_v4_log_error(SLAPI_V4_LOG_DEBUG, "plugin_v4_validate", "normalised to %s\n", dylib_path); + + spal_free(path); + + /* + * IF dylib_path == NULL, then there is NO REAL item in the fs + * that we can access. + */ + if (dylib_path == NULL) { + slapi_v4_log_error(SLAPI_V4_LOG_FATAL, "plugin_v4_validate", "nsslapd-pluginpath: %s does not exist or in inaccesible\n", path); + return PLUGIN_V4_INVALID_CONFIG; + } + + /* + * Does this dylib exist already in our DB? + * We need to do this in a wrtxn because we don't want someone else to try + * and add it while we are looking. + */ + plugin_v4_result final_result = PLUGIN_V4_SUCCESS; + sds_bptree_transaction *dylib_wrtxn; + dylib_v4 *d_tree = NULL; + sds_bptree_cow_wrtxn_begin(dylib_tree, &dylib_wrtxn); + + if (sds_bptree_cow_retrieve(dylib_wrtxn, dylib_path, (void **)&d_tree) == SDS_KEY_PRESENT) { + /* + * It already exists. We don't need to do anything else, we'll + * reuse the handle when it comes to it. + */ + if (d != NULL) { + /* + * provide the handle to our caller. + * normally it's *unsafe* to move out of the txn scope + * but the design of this module dylib tree is additive only + * and we ditch all our refs as we shutdown the plugin trees + * which means moving this out of scope is safe here. + * + * a potential option is to do a dylib dup / free with the plugin? + */ + *d = d_tree; + } + /* AT this point we have succeeded in the check. */ + free(dylib_path); + sds_bptree_cow_wrtxn_abort(&dylib_wrtxn); + } else { + /* It's not in the tree yet. Can we open it? */ + /* Can we open the dylib? */ + void *handle = dlopen(dylib_path, RTLD_NOW|RTLD_LOCAL); + if (handle == NULL) { + sds_bptree_cow_wrtxn_abort(&dylib_wrtxn); + slapi_v4_log_error(SLAPI_V4_LOG_FATAL, "plugin_v4_validate", "Unable to dlopen(%s)\n", dylib_path); + free(dylib_path); + /* okay, we failed here. */ + final_result = PLUGIN_V4_RESOURCE; + } else { + /* We opened the library. Let's put it in the tree. */ + d_tree = dylib_create(); + d_tree->path = dylib_path; + d_tree->handle = handle; + + sds_bptree_cow_insert(dylib_wrtxn, dylib_path, d_tree); + + if (d != NULL) { + *d = d_tree; + } + + sds_bptree_cow_wrtxn_commit(&dylib_wrtxn); + } + } + + return final_result; +} + +plugin_v4_result +plugin_v4_validate(struct slapi_entry *plugin_entry) { + /* Given a plugin entry, validate the configuration. */ + /* + * v3 only checks the dylib works at this point, not the + * actual config. The config is up to the plugin arguably + */ + return _plugin_v4_validate_dylib(plugin_entry, NULL); +} + +/* + * _plugin_v4_create takes a plugin DN and entry, and processes + * it into our plugin tree. + */ +static plugin_v4_result +_plugin_v4_create(sds_bptree_transaction *plugin_wrtxn, struct slapi_entry *plugin_entry, plugin_v4 **plugin_out) { + /* + * Name comes from from_entry. + * validate already confirmed it matches our expectations. + * plugin_inst->name = strdup(p_register->sv4_name); + */ + plugin_v4 *plugin_inst = plugin_from_entry(plugin_entry); + + slapi_v4_init_ptr init_fn_ptr = NULL; + + if (plugin_inst->type == PLUGIN_V4_DYLIB) { + /* First validate the entry */ + dylib_v4 *dl = NULL; + plugin_v4_result result = _plugin_v4_validate_dylib(plugin_entry, &dl); + if (result != PLUGIN_V4_SUCCESS) { + plugin_free_fn((void *)plugin_inst); + return result; + } + /* + * We have a handle to the dl now. Lets check our init function is + * present. + */ + char *init_fn = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugininitfunc"); + if (init_fn == NULL) { + /* Can't init with no fn! */ + plugin_free_fn((void *)plugin_inst); + return PLUGIN_V4_NULL_POINTER; + } + + init_fn_ptr = (slapi_v4_init_ptr)dlsym(dl->handle, init_fn); + spal_free(init_fn); + /* Now stash the dylib lookup */ + plugin_inst->dl_handle = dl; + + } + + if (init_fn_ptr == NULL) { + plugin_free_fn((void *)plugin_inst); + return PLUGIN_V4_INVALID_CONFIG; + } + + /* + * Call the init fn. This is needed to get the fns and precedence. + * this is SAFE to run mulitple times. The wrtxn prevents it being + * run in parallel though. + */ + slapi_v4_plugin_registration *p_register = slapi_v4_plugin_registration_create(); + + slapi_v4_plugin_result *res = init_fn_ptr(p_register); + + if (res->result != SLAPI_V4_PLUGIN_SUCCESS) { + plugin_free_fn((void *)plugin_inst); + slapi_v4_plugin_registration_destroy(p_register); + slapi_v4_log_error(SLAPI_LOG_ERR, "_plugin_v4_create", "Plugin init function failed - %s\n", res->msg); + slapi_v4_plugin_result_destroy(res); + return PLUGIN_V4_INVALID_CONFIG; + } else { + slapi_v4_plugin_result_destroy(res); + } + + + /* + * Validate our plugin registration has enough information. + */ + plugin_v4_result reg_result = slapi_v4_plugin_registration_validate(p_register, plugin_inst); + if (reg_result != PLUGIN_V4_SUCCESS) { + plugin_free_fn((void *)plugin_inst); + slapi_v4_plugin_registration_destroy(p_register); + slapi_v4_log_error(SLAPI_LOG_CRIT, "_plugin_v4_create", "Plugin is registration is invalid, and will not function\n"); + return reg_result; + } + + /* + * Finally, we have enough information now. Lets create the plugin + * object and commit, because we are ready to *rock*. \m/ + */ + plugin_inst->precedence = p_register->precedence; + /* + * For now we only support dylib. + */ + plugin_inst->start_fn = p_register->sv4_start_fn; + plugin_inst->close_fn = p_register->sv4_close_fn; + plugin_inst->bind_certmap_fn = p_register->sv4_bind_certmap_fn; + + /* + * We are done with registration. + */ + slapi_v4_plugin_registration_destroy(p_register); + + /* + * Does this plugin already exist? + * Generate a plugin key, and check. + */ + void *plugin_key = (void*)plugin_key_name_extract(plugin_inst); + sds_result insert_result = sds_bptree_cow_insert(plugin_wrtxn, plugin_key, plugin_inst); + spal_free(plugin_key); + if (insert_result != SDS_SUCCESS) { + plugin_free_fn((void *)plugin_inst); + if (insert_result == SDS_DUPLICATE_KEY) { + /* + * Plugin with this name exists. Need to bail now. Setup *may* try to call this multiple times + * so we just have to ignore it :( + */ + slapi_v4_log_error(SLAPI_LOG_DEBUG, "_plugin_v4_create", "Plugin cn already exists in tree, ignoring ...\n"); + return PLUGIN_V4_DUPLICATE; + } else { + slapi_v4_log_error(SLAPI_LOG_CRIT, "_plugin_v4_create", "Plugin cn failed to be added for an unknown reason.\n"); + return PLUGIN_V4_UNKNOWN; + } + } + + slapi_v4_log_error(SLAPI_V4_LOG_DEBUG, "_plugin_v4_create", "Plugin %s registered correctly\n", plugin_inst->name ); + *plugin_out = plugin_inst; + + return PLUGIN_V4_SUCCESS; +} + +/* + * INTERNAL start and stop. These actually perform the STATE transitions on plugins to move + * them from the "just added" prestart state to enabled or disabled. + */ + +static plugin_v4_result +_plugin_v4_start_trans(plugin_v4 *plugin_inst) { + + /* + * When we get here we should be in PRESTART. Create builds in PRESTART, and add + * does too. If we build "off" we are DISABLE, so we *don't* want to start no + * matter what. + */ + if (plugin_inst->state == PLUGIN_V4_ENABLED || plugin_inst->state == PLUGIN_V4_DISABLE) { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "_plugin_v4_start_trans", "Plugin already enabled or disabled\n"); + return PLUGIN_V4_SUCCESS; + } + + slapi_v4_plugin_result *start_result = plugin_inst->start_fn(&plugin_inst->plugin_private); + + if (start_result == NULL || start_result->result != SLAPI_V4_PLUGIN_SUCCESS) { + /* What was the error? better handle it .... */ + slapi_v4_log_error(SLAPI_LOG_CRIT, "_plugin_v4_start_trans", "plugin start failed: %"PRId32" '%s'\n", start_result->ldap_code, start_result->msg); + return PLUGIN_V4_DUPLICATE; + } else { + plugin_inst->state = PLUGIN_V4_ENABLED; + slapi_v4_log_error(SLAPI_LOG_DEBUG, "_plugin_v4_start_trans", "started %s state %"PRIu32"\n", plugin_inst->name, plugin_inst->state); + } + + slapi_v4_plugin_result_destroy(start_result); + + return PLUGIN_V4_SUCCESS; +} + +static plugin_v4_result +_plugin_v4_close_trans(sds_bptree_transaction *plugin_wrtxn, plugin_v4 *plugin_inst) { + /* + * How does this blackmagic work! Why are you updating and deleting! + * + * So we know that some set of past operations may still be open in the + * tree consuming plugin_inst - so we can *NOT* change it. We need them + * to finish before we call close too. + * + * We exploit the properties of plugin_free_fn, that it calls close + * when the ref count hits 0. So by calling update, this reduces the in + * wrtxn refcount by 1 - so all other references to plugin inst *must* be + * from past generations now. + * + * So either we are the last ref and update will "close" the plugin, or + * we are waiting on older txns - when they close, because we cowed this node, + * the older tree node will be fred, and that will trigger the refcount to drop to + * to 0 as well, which then causes the plugin inst to fall to 0, and the close + * to run. + * + * Lockless thread safety all up in this code mf!!!!! + */ + + plugin_v4_result op_result = PLUGIN_V4_SUCCESS; + + plugin_v4 *plugin_clone = plugin_clone_fn(plugin_inst); + plugin_clone->state = PLUGIN_V4_DISABLE; + + void *plugin_key = (void*)plugin_key_name_extract(plugin_inst); + sds_result update_result = sds_bptree_cow_update(plugin_wrtxn, plugin_key, plugin_clone); + if (update_result != SDS_SUCCESS) { + op_result = PLUGIN_V4_UNKNOWN; + slapi_v4_log_error(SLAPI_LOG_ERR, "_plugin_v4_close_trans", "Failed to disable %s, reverting to enabled.\n", plugin_clone->name); + plugin_free_fn(plugin_clone); + } + string_key_free_fn(plugin_key); + return op_result; +} + +static plugin_v4_result +_plugin_v4_delete_trans(sds_bptree_transaction *plugin_wrtxn, plugin_v4 *plugin_inst) { + /* + * This works on the same principal as close, but uses delete instead. + */ + void *plugin_key = (void*)plugin_key_name_extract(plugin_inst); + sds_result update_result = sds_bptree_cow_delete(plugin_wrtxn, plugin_key); + string_key_free_fn(plugin_key); + if (update_result != SDS_KEY_PRESENT) { + return PLUGIN_V4_UNKNOWN; + } + return PLUGIN_V4_SUCCESS; +} + +static void +plugin_v4_start_cb(void *plugin_key __attribute__((unused)), void *plugin, void *arg __attribute__((unused))) { + plugin_v4 *plugin_inst = (plugin_v4 *)plugin; + _plugin_v4_start_trans(plugin_inst); +} + +/* + * This is the point where everything that's enabled gets moved into the + * actually event proccessing lists. + */ +plugin_v4_result +plugin_v4_startall() { + /* + * For everything in the tree, move the state to enabled from prestart + */ + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_startall", "starting ....\n"); + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + sds_bptree_cow_map(plugin_wrtxn, NULL, plugin_v4_start_cb); + + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_startall", "started\n"); + return PLUGIN_V4_SUCCESS; +} + +static void +plugin_v4_stop_cb(void *plugin_key __attribute__((unused)), void *plugin, void *arg) { + plugin_v4 *plugin_inst = (plugin_v4 *)plugin; + sds_bptree_transaction *plugin_wrtxn = (sds_bptree_transaction *)arg; + /* + * We *can not* change value or key here, but we can use them to UPDATE + * in the write txn! + */ + _plugin_v4_close_trans(plugin_wrtxn, plugin_inst); +} +/* + * This has mulitple stages. First, we need to let every current user drain + * (hopefully should already be the case). We then clean all the processing lists + * next we ditch all the open plugins configs + * finally, we close everything in the dlopen tree. + */ +plugin_v4_result +plugin_v4_shutdown() { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_shutdown", "stopping ....\n"); + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_transaction *plugin_rotxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + /* + * Okay, now that we have the wrtxn, we can GUARANTEE no new + * txns will commit after us. So we open a READ txn for the map. + */ + sds_bptree_cow_rotxn_begin(plugin_tree, &plugin_rotxn); + + /* + * Now, we use the rotxn to map the tree because this WILL NOT change + * but within that we pass the wrtxn to the CB to update each key! + */ + sds_bptree_cow_map(plugin_rotxn, plugin_wrtxn, plugin_v4_stop_cb); + + /* + * Now undo in reverse order. + */ + sds_bptree_cow_rotxn_close(&plugin_rotxn); + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_shutdown", "stopped\n"); + return PLUGIN_V4_SUCCESS; +} + +/* + * When a change to dse is made via cn=config, this picks it up. We know that + * the entry was *modified* only, so we go from there. + */ +plugin_v4_result +plugin_v4_modify(struct slapi_entry *plugin_entry) { + /* + * Update the plugin value, IE replace it. This will effectively call delete then + * add on the plugin. Why? Because this lets the INIT FUNCTION change. + */ + + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + plugin_v4 *plugin_ref = plugin_lookup_from_entry(plugin_wrtxn, plugin_entry); + /* + * IN THEORY this state is impossible, becuase it means we are modify a plugin + * that *is not in the dse*. This *can not happen* but lets be defensive anyway. + */ + if (plugin_ref == NULL) { + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + return PLUGIN_V4_UNKNOWN; + } + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_modify", "modifying %s\n", plugin_ref->name); + + plugin_v4_result delete_result = _plugin_v4_delete_trans(plugin_wrtxn, plugin_ref); + if (delete_result != PLUGIN_V4_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_modify", "resetting failed, aborting %s\n", plugin_ref->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + return delete_result; + } + /* + * May not access plugin ref from here .... + */ + plugin_v4 *plugin_ref_new = NULL; + + plugin_v4_result create_result = _plugin_v4_create(plugin_wrtxn, plugin_entry, &plugin_ref_new); + if (create_result != PLUGIN_V4_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_modify", "cow failed, aborting %s\n", plugin_ref_new->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + return create_result; + } + + plugin_v4_result enable_result = _plugin_v4_start_trans(plugin_ref_new); + if (enable_result != PLUGIN_V4_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_modify", "start failed, aborting %s\n", plugin_ref_new->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + } else { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_modify", "started, commiting %s state : %"PRIu32"\n", plugin_ref->name, plugin_ref_new->state); + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + } + + return enable_result; +} + +/* + * This creates the plugin but *does not* start it. + */ +plugin_v4_result +plugin_v4_setup(struct slapi_entry *plugin_entry) { + /* Pelrhaps do this in a txn? */ + /* + * create the txn + */ + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + plugin_v4 *plugin_ref = NULL; + plugin_v4_result create_result = _plugin_v4_create(plugin_wrtxn, plugin_entry, &plugin_ref); + + /* + * Commit or abort. + */ + if (create_result == PLUGIN_V4_SUCCESS) { + /* + * Finally, commit. My plugin is ready. + */ + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_setup", "create commiting %s state: %"PRIu32" \n", plugin_ref->name, plugin_ref->state); + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + } else if (create_result == PLUGIN_V4_DUPLICATE) { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_setup", "create, already exists\n"); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + create_result = PLUGIN_V4_SUCCESS; + } else { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_setup", "create aborting %s \n", plugin_ref->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + } + + return create_result; +} + +/* + * This creates the plugin and *does* start it. + */ +plugin_v4_result +plugin_v4_add(struct slapi_entry *plugin_entry) { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_add", "begin\n"); + /* + * Create the txn + */ + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + plugin_v4 *plugin_ref = NULL; + + plugin_v4_result create_result = _plugin_v4_create(plugin_wrtxn, plugin_entry, &plugin_ref); + if (create_result != PLUGIN_V4_SUCCESS) { + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + return create_result; + } + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_add", "adding %s\n", plugin_ref->name); + + plugin_v4_result enable_result = _plugin_v4_start_trans(plugin_ref); + if (enable_result != PLUGIN_V4_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_add", "failed to add, aborting %s\n", plugin_ref->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + } else { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_add", "added, commiting %s\n", plugin_ref->name); + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + } + return enable_result; +} + +/* + * A plugin was deleted. This means we need to disable it, and then + * let the COW gc do it's job. + */ +plugin_v4_result +plugin_v4_delete(struct slapi_entry *plugin_entry) { + + sds_bptree_transaction *plugin_wrtxn = NULL; + sds_bptree_cow_wrtxn_begin(plugin_tree, &plugin_wrtxn); + + plugin_v4 *plugin_ref = plugin_lookup_from_entry(plugin_wrtxn, plugin_entry); + /* + * IN THEORY this state is impossible, becuase it means we are deleting a plugin + * that *is not in the dse*. This *can not happen* but lets be defensive anyway. + */ + if (plugin_ref == NULL) { + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + return PLUGIN_V4_UNKNOWN; + } + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_delete", "removing %s\n", plugin_ref->name); + + plugin_v4_result delete_result = _plugin_v4_delete_trans(plugin_wrtxn, plugin_ref); + if (delete_result != PLUGIN_V4_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_delete", "failed to remove, aborting %s\n", plugin_ref->name); + sds_bptree_cow_wrtxn_abort(&plugin_wrtxn); + } else { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_delete", "removed, commiting %s\n", plugin_ref->name); + sds_bptree_cow_wrtxn_commit(&plugin_wrtxn); + } + /* + * DANGER from here you MUST NOT touch plugin_inst again as it MAY BE FREED. + */ + return delete_result; +} + + +/* + * ===================================================================== + * PLUGIN CALLS HERE + * ===================================================================== + * + * Below this line is where we actually execute plugin callbacks. + * + * The behaviour is that we have a thread local linked list (created by the b+tree) + * of the plugins that are valid for this transaction. This is because our *main* plugin + * tree stores all plugins regardless of state, but the tls bpt stores many smaller + * lists of plugins sorted by PRECEDENCE and NAME, and only adds them if they + * are enabled *and* relevant to the call type. This is why we have many smaller + * maps of plugins here, and we extract them at the start of the operation. + * + * To provide a performant solution, we check if the transaction id we hold + * is the same as the txn id that created the map - if they are we are still valid. + * because txnid is 64bit int, this is never going to conflict. If this valued changes + * we ditch the lists and we build new ones. + * + * The benefit of this is *zero* locking, cache locality, and memory saftey protected + * by the main plugin tree, and we get ordered lists. This makes operation execution + * very fast. + */ + +/* ========================= WARNING ============================ + * UNLESS YOU HAVE READ: + * https://www.kernel.org/doc/Documentation/memory-barriers.txt + * and SERIOUSLY understand it, and how it works you *MUST* not + * edit this file. This section of the code relies on a deep + * understanding of locking and memory barriers. + * ============================================================== + */ + +static void +plugin_v4_bind_certmap_populate_cb(void *key __attribute__((unused)), void *value, void *arg) { + sds_bptree_instance *map = (sds_bptree_instance *)arg; + plugin_v4 *plugin = (plugin_v4 *)value; + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_bind_certmap_populate_cb", "Checking '%s' for state\n", plugin->name); + /* Test if the plugin matches our criteria */ + if (plugin->bind_certmap_fn != NULL && plugin->state == PLUGIN_V4_ENABLED) { + plugin_key_v4 *pkey = plugin_key_extract(plugin); + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_bind_certmap_populate_cb", "Adding '%s' to active map\n", plugin->name); + sds_bptree_insert(map, pkey, plugin); + /* Now free the pkey, bptree clones this. */ + plugin_key_free_fn(pkey); + } else { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_bind_certmap_populate_cb", "Skipping '%s', either disabled or does not support operation type\n", plugin->name); + } +} + + +plugin_v4_result +plugin_v4_start_operation() { + /* + * This will check the presence of our tls. If it exists + * and is valid, we return it. If it exists and is invalid, we rebuild it. + * if it doesn't existi, we build it. + */ + /* Begin a read transaction */ + sds_bptree_transaction *plugin_rotxn = NULL; + sds_bptree_cow_rotxn_begin(plugin_tree, &plugin_rotxn); + /* extract the txnid */ + uint64_t txnid = sds_bptree_txn_get_id(plugin_rotxn); + /* Get the key */ + plugin_v4_tls_map *tls_map = (plugin_v4_tls_map *)pthread_getspecific(plugin_v4_tls_key); + /* If doesn't exist, create it */ + if (tls_map == NULL) { + tls_map = (plugin_v4_tls_map *)spal_calloc(sizeof(plugin_v4_tls_map)); + tls_map->state = PLUGIN_V4_OP_INACTIVE; + /* + * This is a trick to trigger our update below - since we don't + * match, we'll recreate these lists. + */ + tls_map->txnid = txnid - 1; + pthread_setspecific(plugin_v4_tls_key, (void *)tls_map); + } + if (tls_map->state != PLUGIN_V4_OP_INACTIVE) { + slapi_v4_log_error(SLAPI_LOG_CRIT, "plugin_v4_start_operation", "Invalid state, plugin operation already running\n"); + sds_bptree_cow_rotxn_close(&plugin_rotxn); + return PLUGIN_V4_INVALID_STATE; + } + /* What txn id is it? did we just create? */ + if (tls_map->txnid != txnid) { + /* We differ, so we need to rebuild the txn maps */ + /* Free the existing maps */ + sds_bptree_destroy(tls_map->bind_certmap_map); + /* Now init a new map, and add our plugins that match. */ + sds_bptree_init(&(tls_map->bind_certmap_map), 1, plugin_key_cmp_fn, NULL, plugin_key_free_fn, plugin_key_dup_fn); + /* Now tell our rotxn to map over the values, inserting them to the new bind_certmap_map */ + sds_bptree_cow_map(plugin_rotxn, (void *)tls_map->bind_certmap_map, plugin_v4_bind_certmap_populate_cb); + + /* Now update our txnid */ + tls_map->txnid = txnid; + } + /* Finally stash the rotxn in the tls map for close. */ + tls_map->rotxn = plugin_rotxn; + /* And mark the state as ready. */ + tls_map->state = PLUGIN_V4_OP_RUNNING; + return PLUGIN_V4_SUCCESS; +} + +plugin_v4_result +plugin_v4_close_operation() { + /* Just move the state to done */ + plugin_v4_tls_map *tls_map = (plugin_v4_tls_map *)pthread_getspecific(plugin_v4_tls_key); + if (tls_map == NULL) { + return PLUGIN_V4_INVALID_STATE; + } + tls_map->state = PLUGIN_V4_OP_INACTIVE; + if (sds_bptree_cow_rotxn_close(&(tls_map->rotxn)) != SDS_SUCCESS) { + return PLUGIN_V4_INVALID_STATE; + } + return PLUGIN_V4_SUCCESS; +} + +static void +plugin_v4_call_bind_certmap_cb(void *key __attribute__((unused)), void *value, void *arg) { + plugin_v4_op_ctx *op_ctx = (plugin_v4_op_ctx *)arg; + if (op_ctx->proceed != OP_PROCEED) { + /* A previous operation failed, bail! */ + return; + } + /* Free the previous iterations result */ + slapi_v4_plugin_result_destroy(op_ctx->res); + + plugin_v4 *plugin = (plugin_v4 *)value; + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_call_bind_certmap_cb", "calling bind_certmap function on plugin %s\n", plugin->name); + op_ctx->res = plugin->bind_certmap_fn(plugin->plugin_private, (slapi_v4_certmap_pblock *)op_ctx->pb); + /* Now act on the result. Probably need to hand it to our operation context */ + if (op_ctx->res->result != SLAPI_V4_PLUGIN_SUCCESS) { + slapi_v4_log_error(SLAPI_LOG_WARNING, "plugin_v4_call_bind_certmap_cb", "Plugin %s operation failed reason %s\n", plugin->name, op_ctx->res->msg); + /* In some cases we could send back an ldap result here. */ + op_ctx->proceed = OP_TERMINATE; + } +} + +plugin_v4_result +plugin_v4_call_bind_certmap(slapi_v4_certmap_pblock *pbc) { + slapi_v4_log_error(SLAPI_LOG_DEBUG, "plugin_v4_call_bind_certmap", "calling bind_certmap functions\n"); + /* + * First check the tls + */ + plugin_v4_tls_map *tls_map = (plugin_v4_tls_map *)pthread_getspecific(plugin_v4_tls_key); + if (pbc == NULL || tls_map == NULL || tls_map->state != PLUGIN_V4_OP_RUNNING) { + return PLUGIN_V4_INVALID_STATE; + } + plugin_v4_op_ctx op_ctx = {0}; + op_ctx.proceed = OP_PROCEED; + op_ctx.pb = (void *)pbc; + /* + * Now that we know the TLS is good, access it, and iterate over the bind_certmap_fn types + * We rely on sds map for this + */ + sds_bptree_map(tls_map->bind_certmap_map, &op_ctx, plugin_v4_call_bind_certmap_cb); + /* op_ctx now has a result here */ + /* How can we get a plugin response back out here? Give a ctx? */ + if (op_ctx.res->result == SLAPI_V4_PLUGIN_SUCCESS) { + slapi_v4_plugin_result_destroy(op_ctx.res); + return PLUGIN_V4_SUCCESS; + } else { + slapi_v4_plugin_result_destroy(op_ctx.res); + return PLUGIN_V4_UNKNOWN; + } +} + diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index 2661ca8..64d597f 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -335,7 +335,7 @@ int config_set_maxbersize(const char *attrname, char *value, char *errorbuf, int int config_set_maxsasliosize(const char *attrname, char *value, char *errorbuf, int apply); int config_set_versionstring(const char *attrname, char *versionstring, char *errorbuf, int apply); int config_set_enquote_sup_oc(const char *attrname, char *value, char *errorbuf, int apply); -int config_set_basedn(const char *attrname, char *value, char *errorbuf, int apply); +int config_set_certmap_basedn(const char *attrname, char *value, char *errorbuf, int apply); int config_set_configdir(const char *attrname, char *value, char *errorbuf, int apply); int config_set_instancedir(const char *attrname, char *value, char *errorbuf, int apply); int config_set_schemadir(const char *attrname, char *value, char *errorbuf, int apply); @@ -493,8 +493,7 @@ size_t config_get_maxsasliosize(void); char *config_get_versionstring(void); char *config_get_buildnum(void); int config_get_enquote_sup_oc(void); -char *config_get_basedn(void); -char *config_get_configdir(void); +char *config_get_certmap_basedn(void); char *config_get_schemadir(void); char *config_get_lockdir(void); char *config_get_tmpdir(void); @@ -589,6 +588,7 @@ int modify_config_dse(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int postop_modify_config_dse(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg); int add_root_dse(Slapi_PBlock *pb); int load_plugin_entry(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg); +int load_plugin_v4_entry(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg); /* diff --git a/ldap/servers/slapd/slapi-internal-v4.h b/ldap/servers/slapd/slapi-internal-v4.h new file mode 100644 index 0000000..df478b7 --- /dev/null +++ b/ldap/servers/slapd/slapi-internal-v4.h @@ -0,0 +1,127 @@ +/* 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 */ + +#pragma once + +#include + +#define SLAPI_PLUGIN_V4_PRERELEASE_ACKNOWLEDGE +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE + +#include + +/** + * Internal implementation details of the slapi-plugin v4 call system + * + * This is how the server INTERNALLY manages plugins. Unless you are working + * inside of ns-slapd, libslapd, you will *never* need to include this file. + */ + +typedef enum _plugin_v4_result { + PLUGIN_V4_SUCCESS, + PLUGIN_V4_INVALID_CONFIG, + PLUGIN_V4_NULL_POINTER, + PLUGIN_V4_DUPLICATE, + PLUGIN_V4_RESOURCE, + PLUGIN_V4_INVALID_PLUGIN, + PLUGIN_V4_UNKNOWN, + PLUGIN_V4_INVALID_STATE, +} plugin_v4_result; + +/* + * I think we need a result -> str table. + */ + +plugin_v4_result plugin_v4_init(); +plugin_v4_result plugin_v4_shutdown(); +void plugin_v4_destroy(); + +plugin_v4_result plugin_v4_validate(struct slapi_entry *plugin_entry); + +/** + * This is called with an entry to setup a plugin + */ +plugin_v4_result plugin_v4_startall(); +plugin_v4_result plugin_v4_stopall(); + +plugin_v4_result plugin_v4_modify(struct slapi_entry *plugin_entry); +plugin_v4_result plugin_v4_add(struct slapi_entry *plugin_entry); +plugin_v4_result plugin_v4_setup(struct slapi_entry *plugin_entry); +plugin_v4_result plugin_v4_delete(struct slapi_entry *plugin_entry); + +typedef struct _slapi_v4_plugin_registration { + /* + * The name of the plugin. We will enforce this matches + * the cn. + */ + char *sv4_name; + /* + * Plugin order of operation precedence. + */ + uint64_t precedence; + /* + * This function is called *before* the plugin starts + * and guarantees no operations will start until + * this function is completed. It's guaranteed that + * close will not run until start is complete. + */ + slapi_v4_plugin_result *(*sv4_start_fn)(void **ctx); + /* + * This function is called *after* the plugin has stopped + * processing any operations. This allows you to free and + * stop and resources from your operation. It's guaranteed + * that no other plugin fn will run at the same time as this. + */ + slapi_v4_plugin_result *(*sv4_close_fn)(void **ctx); + /* + * Map certificate details to an entry for bind. + */ + slapi_v4_plugin_result *(*sv4_bind_certmap_fn)(void *ctx, slapi_v4_certmap_pblock *pbc); + +} slapi_v4_plugin_registration; + +slapi_v4_plugin_registration * slapi_v4_plugin_registration_create(); +void slapi_v4_plugin_registration_destroy(slapi_v4_plugin_registration *pr); + +/* + * Internal result handling from operations. + */ +int64_t slapi_v4_plugin_result_is_ok(slapi_v4_plugin_result *result); + +/* + * PLUGIN CALLBACK HOOKS + * + * These are the functions that we call to trigger plugin operations. + */ +plugin_v4_result plugin_v4_start_operation(); +plugin_v4_result plugin_v4_close_operation(); +plugin_v4_result plugin_v4_call_bind_certmap(); + +/* + * Internal pblock operations + */ + +slapi_v4_search_pblock * slapi_v4_search_pblock_extract(Slapi_PBlock *pb); +slapi_v4_search_pblock * slapi_v4_search_pblock_create(); +/* Merge the results of B into A */ +void slapi_v4_search_pblock_merge(slapi_v4_search_pblock *pbs_a, slapi_v4_search_pblock *pbs_b); + +/* from plugin-internal-op */ +int search_internal_pb(Slapi_PBlock *pb); + + +/* + * SERVER UPGRADE INTERNALS + */ +typedef enum _upgrade_status { + UPGRADE_SUCCESS = 0, + UPGRADE_FAILURE = 1, +} upgrade_status; + +upgrade_status upgrade_server(void); + 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-plugin-v4.h b/ldap/servers/slapd/slapi-plugin-v4.h new file mode 100644 index 0000000..7c3b989 --- /dev/null +++ b/ldap/servers/slapd/slapi-plugin-v4.h @@ -0,0 +1,57 @@ +/* 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 */ + +#pragma once + +#ifndef SLAPI_PLUGIN_V4_PRERELEASE_ACKNOWLEDGE +#error slapi plugin v4 is *not* stabilised for release. DO NOT USE IT. +#endif + +#include + +#include +#include + + +/* + * Logging + */ + +/* allowed values for the "severity" parameter */ +/* THESE MUST MATCH SLAPI-PLUGIN.h */ +typedef enum _slapi_log_level { + SLAPI_V4_LOG_FATAL = 0, + SLAPI_V4_LOG_TRACE = 1, + SLAPI_V4_LOG_PACKETS = 2, + SLAPI_V4_LOG_ARGS = 3, + SLAPI_V4_LOG_CONNS = 4, + SLAPI_V4_LOG_BER = 5, + SLAPI_V4_LOG_FILTER = 6, + SLAPI_V4_LOG_CONFIG = 7, + SLAPI_V4_LOG_ACL = 8, + SLAPI_V4_LOG_SHELL = 9, + SLAPI_V4_LOG_PARSE = 10, + SLAPI_V4_LOG_HOUSE = 11, + SLAPI_V4_LOG_REPL = 12, + SLAPI_V4_LOG_CACHE = 13, + SLAPI_V4_LOG_PLUGIN = 14, + SLAPI_V4_LOG_TIMING = 15, + SLAPI_V4_LOG_BACKLDBM = 16, + SLAPI_V4_LOG_ACLSUMMARY = 17, + SLAPI_V4_LOG_NUNCSTANS = 18, + SLAPI_V4_LOG_EMERG = 19, + SLAPI_V4_LOG_ALERT = 20, + SLAPI_V4_LOG_CRIT = 21, + SLAPI_V4_LOG_ERR = 22, + SLAPI_V4_LOG_WARNING = 23, + SLAPI_V4_LOG_NOTICE = 24, + SLAPI_V4_LOG_INFO = 25, + SLAPI_V4_LOG_DEBUG = 26, +} slapi_v4_log_level; + + diff --git a/ldap/servers/slapd/slapi-private-v4.c b/ldap/servers/slapd/slapi-private-v4.c new file mode 100644 index 0000000..1dc45a1 --- /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_v4_log_error(SLAPI_V4_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_v4_log_error(SLAPI_V4_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-private-v4.h b/ldap/servers/slapd/slapi-private-v4.h new file mode 100644 index 0000000..e265052 --- /dev/null +++ b/ldap/servers/slapd/slapi-private-v4.h @@ -0,0 +1,203 @@ +/* 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 */ + +#pragma once + +#ifndef SLAPI_PRIVATE_V4_ACKNOWLEDGE +#error slapi private v4 is *not* a guaranteed api. It may change between minor releases. +#endif + +/* + * If you're using the internal private header, you know + * what you're getting into. + */ +#define SLAPI_PLUGIN_V4_PRERELEASE_ACKNOWLEDGE +#include + +/* Could be SLAPI_V4_PLUGIN_RESULT_CODE? */ +/* We *must* define these values for FFI */ +typedef enum { + SLAPI_V4_PLUGIN_SUCCESS = 0, + SLAPI_V4_PLUGIN_FAILURE = 1 +} SV4_PLUGIN_RESULT_CODE; + +typedef struct _slapi_v4_plugin_result { + SV4_PLUGIN_RESULT_CODE result; + /* Should we have an LDAP response code here? */ + /* Will allow sending back responses easily. */ + int ldap_code; + char *msg; +} slapi_v4_plugin_result; + +/* + * Low level berval manipulation + */ + +void slapi_v4_bv_free(struct berval *v); + +struct berval **slapi_v4_bvarray_create(); +void slapi_v4_bvarray_destroy(struct berval **array); +struct berval **slapi_v4_bvarray_append(struct berval **array, struct berval *bv); +void slapi_v4_bvarray_fold(struct berval **array, void (*fn)(struct berval *bv, void *acc, void *arg), void *acc, void *arg); + +/* + * Slapi_DN types + */ + +/** + * Represents a distinguished name in a directory entry. + * + * #Slapi_DN is the data type for an opaque structure that represents a distinguished + * name in the server plug-in. + */ +typedef struct slapi_dn slapi_v4_dn; +void slapi_v4_sdn_free(slapi_v4_dn *sdn); +slapi_v4_dn * slapi_v4_sdn_new_from_char_dn(const char *dn); +slapi_v4_dn * slapi_v4_sdn_dup(const slapi_v4_dn *sdn); + +/* + * TLS and certificate handling. + */ + +typedef struct CERTCertificateStr slapi_v4_cert; +char * slapi_v4_cert_get_subjectdn(slapi_v4_cert *cert); +char * slapi_v4_cert_get_issuerdn(slapi_v4_cert *cert); +struct berval *slapi_v4_cert_get_der_berval(slapi_v4_cert *cert); +char ** slapi_v4_cert_get_subject_ava_val(slapi_v4_cert *cert, const char *attr); + +/* + * PBLOCKS and related option passing code + */ + +/* We only have empty pblock declarations */ + +typedef struct _slapi_v4_certmap_pblock slapi_v4_certmap_pblock; + +slapi_v4_certmap_pblock * slapi_v4_certmap_pblock_init(void); +void slapi_v4_certmap_pblock_destroy(slapi_v4_certmap_pblock *pbc); + +void slapi_v4_certmap_pblock_set_clientdn(slapi_v4_certmap_pblock *pbc, slapi_v4_dn *sdn); +slapi_v4_dn * slapi_v4_certmap_pblock_get_clientdn(slapi_v4_certmap_pblock *pbc); + +slapi_v4_cert * slapi_v4_certmap_pblock_get_clientcert(slapi_v4_certmap_pblock *pbc); +void slapi_v4_certmap_pblock_set_clientcert(slapi_v4_certmap_pblock *pbc, slapi_v4_cert *clientcert); + +/* + * Entry interaction code. + */ +typedef struct slapi_entry Slapi_Entry; + +const slapi_v4_dn * slapi_v4_entry_get_sdn(Slapi_Entry *e); + +char **slapi_v4_entry_attr_get_charray(const Slapi_Entry *e, const char *type); + +struct berval **slapi_v4_entry_attr_get_bervals(const Slapi_Entry *e, const char *type); + +/* + * Search pblock. This is based on the v3 search pblock. + */ +typedef struct _slapi_pblock_intop slapi_v4_search_pblock; + +void slapi_v4_search_pblock_destroy(slapi_v4_search_pblock *pbs); +/* execute a search, and result the result pb */ +slapi_v4_search_pblock * slapi_v4_search_internal(slapi_v4_dn *basedn, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **controls, + const char *uniqueid, + int operation_flags); + +slapi_v4_search_pblock * slapi_v4_search_internal_all_contexts(int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **controls, + const char *uniqueid, + int operation_flags); + +slapi_v4_search_pblock * slapi_v4_add_internal_entry(Slapi_Entry *entry, const char *modifier_name); + +slapi_v4_plugin_result * slapi_v4_entry_exists_or_create(slapi_v4_dn *dn, const char *filter, const char *entry, const char *modifier_name); + +uint64_t slapi_v4_search_pblock_get_num_results(slapi_v4_search_pblock *pbs); +Slapi_Entry **slapi_v4_search_pblock_get_entries(slapi_v4_search_pblock *pbs); +char **slapi_v4_search_pblogk_get_referrals(slapi_v4_search_pblock *pbs); + +int slapi_v4_search_pblock_get_ldapresult(slapi_v4_search_pblock *pbs); + + + +/* + * PLUGIN REGISTRATION COMPONENTS + */ + +/* Forward declare this empty type */ +typedef struct _slapi_v4_plugin_registration slapi_v4_plugin_registration; + +#define SV4_PLUGIN_PRECEDENCE_DEFAULT 50 + +void slapi_v4_plugin_register_name(slapi_v4_plugin_registration *pr, char *name); +void slapi_v4_plugin_register_precedence(slapi_v4_plugin_registration *pr, uint64_t order); +void slapi_v4_plugin_register_start_fn(slapi_v4_plugin_registration *pr, slapi_v4_plugin_result *(*sv4_start_fn)(void **ctx)); +void slapi_v4_plugin_register_close_fn(slapi_v4_plugin_registration *pr, slapi_v4_plugin_result *(*sv4_close_fn)(void **ctx)); +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)); + +/* + * RESULT MANAGEMENT + */ + +slapi_v4_plugin_result *slapi_v4_plugin_result_ok(void); +slapi_v4_plugin_result *slapi_v4_plugin_result_err(SV4_PLUGIN_RESULT_CODE result, int ldap_code, char *msg); +void slapi_v4_plugin_result_destroy(slapi_v4_plugin_result *r); + +/* + * CONFIGURATION + * Access to configurations. These should always stay private as plugins should + * never need to access this content. + */ + +char *config_get_configdir(void); + +char *config_get_certmap_basedn(void); + +/* + * Filter management + */ + +char * slapi_v4_escape_filter_value(char *filter_str, int len); + +/* + * Logging + */ +void slapi_v4_log_error(slapi_v4_log_level loglevel, char *subsystem, char *fmt, ...) __attribute__((format(printf, 3, 4))); + +/* + * Char arrays and manipulation. + */ + +char ** slapi_v4_charray_from_str(char *str, char *brkstr, int allow_dups); +char ** slapi_v4_charray_append(char **a, char *s); +void slapi_v4_charray_free(char **array); + +/* + * BORROW FROM V3 + * + * YOU MUST NOT CHANGE THESE LINES, AND THEY MAY NOT REMAIN STABLE + */ + +/* + * UTF8 string handling + */ + +unsigned char * slapi_utf8StrToLower(unsigned char *s); +unsigned char * ldap_utf8strip(unsigned char *s); +char * ldap_utf8strtok_r(char *sp, const char *brk, char **next); + + diff --git a/ldap/servers/slapd/slapi_pal.c b/ldap/servers/slapd/slapi_pal.c index 38c178c..a48edb6 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/slapi_pal.h b/ldap/servers/slapd/slapi_pal.h index a6d9453..301e13b 100644 --- a/ldap/servers/slapd/slapi_pal.h +++ b/ldap/servers/slapd/slapi_pal.h @@ -20,6 +20,12 @@ #pragma once #include +/* malloc and size_t */ +#include + +void * spal_calloc(size_t size); +void * spal_realloc(void *ptr, size_t size); +void spal_free(void *ptr); /** * Structure that contains our system memory information in bytes and pages. diff --git a/ldap/servers/slapd/snmp_collator.c b/ldap/servers/slapd/snmp_collator.c index 5d25597..87e1625 100644 --- a/ldap/servers/slapd/snmp_collator.c +++ b/ldap/servers/slapd/snmp_collator.c @@ -31,6 +31,9 @@ #include "prcvar.h" #include "plstr.h" +#define SLAPI_PRIVATE_V4_ACKNOWLEDGE +#include + #ifdef HPUX /* HP-UX doesn't define SEM_FAILED like other platforms, so * we define it ourselves. */ diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c new file mode 100644 index 0000000..2be8c13 --- /dev/null +++ b/ldap/servers/slapd/upgrade.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 */ + +#include + +/* + * This is called on server startup *before* plugins start + * but after config dse is read for operations. This allows + * us to make internal assertions about the state of the configuration + * at start up, enable plugins, and more. + */ + +static char *modifier_name = "cn=upgrade internal,cn=config"; + +/* + * Ensure that the certmap plugin object exists. If not, + * create and enable it. If it exists and is disabled, + * ignore it. + */ +static upgrade_status +upgrade_137_certmap_exists(void) { + /* Does it exist? */ + char *certmap = strdup("dn: cn=certmap plugin,cn=plugins,cn=config\n" + "objectClass: top\n" + "objectClass: nsDylibPlugin4\n" + "cn: certmap plugin\n" + "nsslapd-pluginpath: libcertmap\n" + "nsslapd-plugininitfunc: certmap_init_fn\n" + "nsslapd-pluginenabled: on"); + + slapi_v4_dn *base_sdn = slapi_v4_sdn_new_from_char_dn("cn=certmap plugin,cn=plugins,cn=config"); + /* If not, create it. */ + slapi_v4_plugin_result *result = slapi_v4_entry_exists_or_create(base_sdn, "(cn=certmap plugin)", certmap, modifier_name); + upgrade_status uresult = UPGRADE_SUCCESS; + + if (result->result != SLAPI_V4_PLUGIN_SUCCESS) { + slapi_v4_log_error(SLAPI_V4_LOG_FATAL, "upgrade_137_certmap_exists", "Failed to create certmap entry: %"PRId32": %s\n", result->ldap_code, result->msg); + uresult = UPGRADE_FAILURE; + } + spal_free(certmap); + slapi_v4_sdn_free(base_sdn); + slapi_v4_plugin_result_destroy(result); + return uresult; +} + +upgrade_status +upgrade_server(void) { + /* Could this iterate over an array of function pointers? */ + if (upgrade_137_certmap_exists() != UPGRADE_SUCCESS) { + return UPGRADE_FAILURE; + } + return UPGRADE_SUCCESS; +} + + 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 diff --git a/lib/ldaputil/.cvsignore b/lib/ldaputil/.cvsignore deleted file mode 100644 index e7b97c5..0000000 --- a/lib/ldaputil/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.depends diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c deleted file mode 100644 index 65a4815..0000000 --- a/lib/ldaputil/cert.c +++ /dev/null @@ -1,489 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -/* This was malloc.h - but it's moved to stdlib.h on most platforms, and FBSD is strict */ -/* Make it stdlib.h, and revert to malloc.h with ifdefs if we have issues here. WB 2016 */ -#include - -/* removed for ns security integration -#include -*/ -#include "prmem.h" -#include -#include -#include -#include -#include -#include -#include "ldaputili.h" - -#include "slapi-plugin.h" - -NSAPI_PUBLIC int -ldapu_get_cert(void *SSLendpoint __attribute__((unused)), void **cert __attribute__((unused))) -{ - /* TEMPORARY -- not implemented yet*/ - return LDAPU_FAILED; -} - - -NSAPI_PUBLIC int -ldapu_get_cert_subject_dn(void *cert_in, char **subjectDN) -{ - CERTCertificate *cert = (CERTCertificate *)cert_in; - char *cert_subject = CERT_NameToAscii(&cert->subject); - - if (cert_subject != NULL) - *subjectDN = strdup(cert_subject); - else - *subjectDN = NULL; - - PR_Free(cert_subject); - return *subjectDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_SUBJECTDN_FAILED; -} - -NSAPI_PUBLIC int -ldapu_get_cert_issuer_dn(void *cert_in, char **issuerDN) -{ - CERTCertificate *cert = (CERTCertificate *)cert_in; - char *cert_issuer = CERT_NameToAscii(&cert->issuer); - - *issuerDN = strdup(cert_issuer); - PR_Free(cert_issuer); - - return *issuerDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_ISSUERDN_FAILED; -} - -NSAPI_PUBLIC int -ldapu_get_cert_der(void *cert_in, unsigned char **der, unsigned int *len) -{ - CERTCertificate *cert = (CERTCertificate *)cert_in; - SECItem derCert = ((CERTCertificate *)cert)->derCert; - unsigned char *data = derCert.data; - - *len = derCert.len; - *der = (unsigned char *)malloc(*len); - - if (!*der) - return LDAPU_ERR_OUT_OF_MEMORY; - - memcpy(*der, data, *len); - - return *len ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_DERCERT_FAILED; -} - -static int -certmap_name_to_secoid(const char *str) -{ - if (!ldapu_strcasecmp(str, "c")) - return SEC_OID_AVA_COUNTRY_NAME; - if (!ldapu_strcasecmp(str, "o")) - return SEC_OID_AVA_ORGANIZATION_NAME; - if (!ldapu_strcasecmp(str, "cn")) - return SEC_OID_AVA_COMMON_NAME; - if (!ldapu_strcasecmp(str, "l")) - return SEC_OID_AVA_LOCALITY; - if (!ldapu_strcasecmp(str, "st")) - return SEC_OID_AVA_STATE_OR_PROVINCE; - if (!ldapu_strcasecmp(str, "ou")) - return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME; - if (!ldapu_strcasecmp(str, "uid")) - return SEC_OID_RFC1274_UID; - if (!ldapu_strcasecmp(str, "e")) - return SEC_OID_PKCS9_EMAIL_ADDRESS; - if (!ldapu_strcasecmp(str, "mail")) - return SEC_OID_RFC1274_MAIL; - if (!ldapu_strcasecmp(str, "dc")) - return SEC_OID_AVA_DC; - - return SEC_OID_AVA_UNKNOWN; /* return invalid OID */ -} - -NSAPI_PUBLIC int -ldapu_get_cert_ava_val(void *cert_in, int which_dn, const char *attr, char ***val_out) -{ - CERTCertificate *cert = (CERTCertificate *)cert_in; - CERTName *cert_dn; - CERTRDN **rdns; - CERTRDN **rdn; - CERTAVA **avas; - CERTAVA *ava; - int attr_tag = certmap_name_to_secoid(attr); - char **val; - char **ptr; - int rv; - - *val_out = 0; - - if (attr_tag == SEC_OID_AVA_UNKNOWN) { - return LDAPU_ERR_INVALID_ARGUMENT; - } - - if (which_dn == LDAPU_SUBJECT_DN) - cert_dn = &cert->subject; - else if (which_dn == LDAPU_ISSUER_DN) - cert_dn = &cert->issuer; - else - return LDAPU_ERR_INVALID_ARGUMENT; - - val = (char **)malloc(32 * sizeof(char *)); - - if (!val) - return LDAPU_ERR_OUT_OF_MEMORY; - - ptr = val; - - rdns = cert_dn->rdns; - - if (rdns) { - for (rdn = rdns; *rdn; rdn++) { - avas = (*rdn)->avas; - while ((ava = *avas++) != NULL) { - int tag = CERT_GetAVATag(ava); - - if (tag == attr_tag) { - char buf[BIG_LINE]; - int lenLen; - int vallen; - /* Found it */ - - /* Copied from ns/lib/libsec ... - * XXX this code is incorrect in general - * -- should use a DER template. - */ - lenLen = 2; - if (ava->value.len >= 128) - lenLen = 3; - vallen = ava->value.len - lenLen; - - rv = CERT_RFC1485_EscapeAndQuote(buf, - BIG_LINE, - (char *)ava->value.data + lenLen, - vallen); - - if (rv == SECSuccess) { - *ptr++ = strdup(buf); - } - break; - } - } - } - } - - *ptr = 0; - - if (*val) { - /* At least one value found */ - *val_out = val; - rv = LDAPU_SUCCESS; - } else { - free(val); - rv = LDAPU_FAILED; - } - - return rv; -} - -static void -_rdns_free(char ***rdns) -{ - auto char ***rdn; - for (rdn = rdns; *rdn; ++rdn) { - slapi_ldap_value_free(*rdn); - } - free(rdns); -} - -static char *** -_explode_dn(const char *dn) -{ - auto char ***exp = NULL; - if (dn && *dn) { - auto char **rdns = slapi_ldap_explode_dn(dn, 0); - if (rdns) { - auto size_t expLen = 0; - auto char **rdn; - for (rdn = rdns; *rdn; ++rdn) { - auto char **avas = slapi_ldap_explode_rdn(*rdn, 0); - if (avas && *avas) { - exp = (char ***)ldapu_realloc(exp, sizeof(char **) * (expLen + 2)); - if (exp) { - exp[expLen++] = avas; - } else { - slapi_ldap_value_free(avas); - break; - } - } else { /* parse error */ - if (avas) { - slapi_ldap_value_free(avas); - } - if (exp) { - exp[expLen] = NULL; - _rdns_free(exp); - exp = NULL; - } - break; - } - } - if (exp) { - exp[expLen] = NULL; - } - slapi_ldap_value_free(rdns); - } - } - return exp; -} - -static size_t -_rdns_count(char ***rdns) -{ - auto size_t count = 0; - auto char ***rdn; - for (rdn = rdns; *rdn; ++rdn) { - auto char **ava; - for (ava = *rdn; *ava; ++ava) { - ++count; - } - } - return count; -} - -static int -_replaceAVA(char *attr, char **avas) -{ - if (attr && avas) { - for (; *avas; ++avas) { - if (!ldapu_strcasecmp(*avas, attr)) { - *avas = attr; - return 1; - } - } - } - return 0; -} - -struct _attr_getter_pair -{ -#if NSS_VMAJOR < 3 || (NSS_VMAJOR == 3 && NSS_VMINOR < 15) - char *(*getter)(CERTName *dn); -#else - /* in 3.15.x "const" was added to the declarations */ - char *(*getter)(const CERTName *dn); -#endif - const char *name1; - const char *name2; -} _attr_getter_table[] = - { - {NULL, "OU", "organizationalUnitName"}, - {CERT_GetOrgName, "O", "organizationName"}, - {CERT_GetCommonName, "CN", "commonName"}, - {CERT_GetCertEmailAddress, "E", NULL}, - {CERT_GetCertEmailAddress, "MAIL", "rfc822mailbox"}, - {CERT_GetCertUid, "uid", NULL}, - {CERT_GetCountryName, "C", "country"}, - {CERT_GetStateName, "ST", "state"}, - {CERT_GetLocalityName, "L", "localityName"}, - {CERT_GetDomainComponentName, "DC", "dc"}, - {NULL, NULL, NULL}}; - -static int -_is_OU(const char *attr) -{ - auto struct _attr_getter_pair *descAttr; - for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) { - if (descAttr->getter == NULL) { /* OU attribute */ - if (!ldapu_strcasecmp(attr, descAttr->name1) || (descAttr->name2 && - !ldapu_strcasecmp(attr, descAttr->name2))) { - return 1; - } - break; - } - } - return 0; -} - -static char ** -_previous_OU(char **ava, char **avas) -{ - while (ava != avas) { - --ava; - if (_is_OU(*ava)) { - return ava; - } - } - return NULL; -} - -static char * -_value_normalize(char *value) -/* Remove leading and trailing spaces, and - change consecutive spaces to a single space. - */ -{ - auto char *t; - auto char *f; - t = f = value; - while (*f == ' ') - ++f; /* ignore leading spaces */ - for (; *f; ++f) { - if (*f != ' ' || t[-1] != ' ') { - *t++ = *f; /* no consecutive spaces */ - } - } - if (t > value && t[-1] == ' ') { - --t; /* ignore trailing space */ - } - *t = '\0'; - return value; -} - -static int -_explode_AVA(char *AVA) -/* Change an attributeTypeAndValue a la , - to the type name, followed immediately by the attribute value, - both normalized. - */ -{ - auto char *value = strchr(AVA, '='); - if (!value) - return LDAPU_FAILED; - *value++ = '\0'; - _value_normalize(AVA); - _value_normalize(value); - { - auto char *typeEnd = AVA + strlen(AVA); - if ((typeEnd + 1) != value) { - memmove(typeEnd + 1, value, strlen(value) + 1); - } - } - return LDAPU_SUCCESS; -} - -static char * -_AVA_value(char *AVA) -{ - return (AVA + strlen(AVA) + 1); -} - -static int -_value_match(char *value, char *desc) -{ - auto const int result = - !ldapu_strcasecmp(_value_normalize(value), desc); - return result; -} - -int -ldapu_member_certificate_match(void *cert, const char *desc) -/* - * Return Values: (same as ldapu_find) - * LDAPU_SUCCESS cert matches desc - * LDAPU_FAILED cert doesn't match desc - * Something went wrong. - */ -{ - auto int err = LDAPU_FAILED; - auto char ***descRDNs; - if (!cert || !desc || desc[0] != '{') - return LDAPU_FAILED; - if (desc[1] == '\0') - return LDAPU_SUCCESS; /* no AVAs */ - descRDNs = _explode_dn(desc + 1); - if (descRDNs) { - auto char **descAVAs = (char **)ldapu_malloc(sizeof(char *) * (_rdns_count(descRDNs) + 1)); - if (!descAVAs) { - err = LDAPU_ERR_OUT_OF_MEMORY; - } else { - auto CERTName *subject = &(((CERTCertificate *)cert)->subject); - auto char **descAVA; - - err = LDAPU_SUCCESS; - { /* extract all the AVAs, but not duplicate types, except OU */ - auto size_t descAVAsLen = 0; - auto char ***descRDN; - descAVAs[0] = NULL; - for (descRDN = descRDNs; err == LDAPU_SUCCESS && *descRDN; ++descRDN) { - for (descAVA = *descRDN; err == LDAPU_SUCCESS && *descAVA; ++descAVA) { - err = _explode_AVA(*descAVA); - if (err == LDAPU_SUCCESS) { - if (_is_OU(*descAVA) || - !_replaceAVA(*descAVA, descAVAs)) { - descAVAs[descAVAsLen++] = *descAVA; - descAVAs[descAVAsLen] = NULL; - } - } - } - } - } - - /* match all the attributes except OU */ - for (descAVA = descAVAs; err == LDAPU_SUCCESS && *descAVA; ++descAVA) { - auto struct _attr_getter_pair *descAttr; - err = LDAPU_FAILED; /* if no match */ - for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) { - if (!ldapu_strcasecmp(*descAVA, descAttr->name1) || (descAttr->name2 && - !ldapu_strcasecmp(*descAVA, descAttr->name2))) { - if (descAttr->getter == NULL) { /* OU attribute */ - err = LDAPU_SUCCESS; /* for now */ - } else { - auto char *certVal = (*(descAttr->getter))(subject); - if (certVal && _value_match(certVal, _AVA_value(*descAVA))) { - err = LDAPU_SUCCESS; - } - PR_Free(certVal); - } - break; - } - } - } - - /* match the OU attributes */ - if (err == LDAPU_SUCCESS && descAVA != descAVAs) { - /* Iterate over the OUs in the certificate subject */ - auto CERTRDN **certRDN = subject->rdns; - descAVA = _previous_OU(descAVA, descAVAs); - for (; descAVA && *certRDN; ++certRDN) { - auto CERTAVA **certAVA = (*certRDN)->avas; - for (; descAVA && *certAVA; ++certAVA) { - auto const int tag = CERT_GetAVATag(*certAVA); - if (tag == SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME) { - auto const size_t certValLen = (*certAVA)->value.len; - auto const size_t lenLen = (certValLen < 128) ? 2 : 3; - auto const size_t buflen = certValLen - lenLen; - auto char *buf = (char *)ldapu_malloc(buflen + 1); - if (!buf) { - err = LDAPU_ERR_OUT_OF_MEMORY; - descAVA = NULL; - } else { - memcpy(buf, (*certAVA)->value.data + lenLen, buflen); - buf[buflen] = 0; - if (_value_match(buf, _AVA_value(*descAVA))) { - descAVA = _previous_OU(descAVA, descAVAs); - } - free(buf); - } - } - } - } - if (descAVA) { - err = LDAPU_FAILED; /* no match for descAVA in subject */ - } - } - free(descAVAs); - } - _rdns_free(descRDNs); - } - return err; -} diff --git a/lib/ldaputil/certmap.c b/lib/ldaputil/certmap.c deleted file mode 100644 index 78bb363..0000000 --- a/lib/ldaputil/certmap.c +++ /dev/null @@ -1,1721 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -#include -#include -/* This was malloc.h - but it's moved to stdlib.h on most platforms, and FBSD is strict */ -/* Make it stdlib.h, and revert to malloc.h with ifdefs if we have issues here. WB 2016 */ -#include - -/* removed for ns security integration -#include -*/ -#include -#include -#include - -#include -#include -#define DEFINE_LDAPU_STRINGS 1 -#include -#include -#include -#include -#include "ldaputili.h" - -#ifndef BIG_LINE -#define BIG_LINE 1024 -#endif - -/* This is hack, the function is defined in cert/alg1485.c */ -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -static char this_dllname[256]; -static const char *LIB_DIRECTIVE = "certmap"; -static const int LIB_DIRECTIVE_LEN = 7; /* strlen("LIB_DIRECTIVE") */ - -static char *ldapu_dn_normalize(char *dn); -static void *ldapu_propval_free(void *propval_in, void *arg); - -typedef struct -{ - FILE *fp; - void *arg; -} LDAPUPrintInfo_t; - -static LDAPUCertMapListInfo_t *certmap_listinfo = 0; -static LDAPUCertMapInfo_t *default_certmap_info = 0; - -static const char *certmap_attrs[] = { - 0, - 0, - 0, - 0}; - -const long CERTMAP_BIT_POS_UNKNOWN = 0; /* unknown OID */ -const long CERTMAP_BIT_POS_CN = 1L << 1; /* Common Name */ -const long CERTMAP_BIT_POS_OU = 1L << 2; /* Organization unit */ -const long CERTMAP_BIT_POS_O = 1L << 3; /* Organization */ -const long CERTMAP_BIT_POS_C = 1L << 4; /* Country */ -const long CERTMAP_BIT_POS_L = 1L << 5; /* Locality */ -const long CERTMAP_BIT_POS_ST = 1L << 6; /* State or Province */ -const long CERTMAP_BIT_POS_MAIL = 1L << 7; /* E-mail Address */ -const long CERTMAP_BIT_POS_UID = 1L << 8; /* UID */ -const long CERTMAP_BIT_POS_DC = 1L << 9; /* DC */ - -const int SEC_OID_AVA_UNKNOWN = 0; /* unknown OID */ - -static long -certmap_secoid_to_bit_pos(int oid) -{ - switch (oid) { - case SEC_OID_AVA_COUNTRY_NAME: - return CERTMAP_BIT_POS_C; - case SEC_OID_AVA_ORGANIZATION_NAME: - return CERTMAP_BIT_POS_O; - case SEC_OID_AVA_COMMON_NAME: - return CERTMAP_BIT_POS_CN; - case SEC_OID_AVA_LOCALITY: - return CERTMAP_BIT_POS_L; - case SEC_OID_AVA_STATE_OR_PROVINCE: - return CERTMAP_BIT_POS_ST; - case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: - return CERTMAP_BIT_POS_OU; - case SEC_OID_RFC1274_UID: - return CERTMAP_BIT_POS_UID; - /* Map "E" and "MAIL" to the same bit position */ - case SEC_OID_PKCS9_EMAIL_ADDRESS: - return CERTMAP_BIT_POS_MAIL; - case SEC_OID_RFC1274_MAIL: - return CERTMAP_BIT_POS_MAIL; - case SEC_OID_AVA_DC: - return CERTMAP_BIT_POS_DC; - default: - return CERTMAP_BIT_POS_UNKNOWN; - } -} - -static const char * -certmap_secoid_to_name(int oid) -{ - switch (oid) { - case SEC_OID_AVA_COUNTRY_NAME: - return "C"; - case SEC_OID_AVA_ORGANIZATION_NAME: - return "O"; - case SEC_OID_AVA_COMMON_NAME: - return "CN"; - case SEC_OID_AVA_LOCALITY: - return "L"; - case SEC_OID_AVA_STATE_OR_PROVINCE: - return "ST"; - case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: - return "OU"; - case SEC_OID_RFC1274_UID: - return "UID"; - /* Map both 'e' and 'mail' to 'mail' in LDAP */ - case SEC_OID_PKCS9_EMAIL_ADDRESS: - return "MAIL"; - case SEC_OID_RFC1274_MAIL: - return "MAIL"; - case SEC_OID_AVA_DC: - return "DC"; - default: - return 0; - } -} - -static void -tolower_string(char *str) -{ - if (str) { - while (*str) { - *str = tolower(*str); - str++; - } - } -} - -static long -certmap_name_to_bit_pos(const char *str) -{ - if (!ldapu_strcasecmp(str, "c")) - return CERTMAP_BIT_POS_C; - if (!ldapu_strcasecmp(str, "o")) - return CERTMAP_BIT_POS_O; - if (!ldapu_strcasecmp(str, "cn")) - return CERTMAP_BIT_POS_CN; - if (!ldapu_strcasecmp(str, "l")) - return CERTMAP_BIT_POS_L; - if (!ldapu_strcasecmp(str, "st")) - return CERTMAP_BIT_POS_ST; - if (!ldapu_strcasecmp(str, "ou")) - return CERTMAP_BIT_POS_OU; - if (!ldapu_strcasecmp(str, "uid")) - return CERTMAP_BIT_POS_UID; - /* Map "E" and "MAIL" to the same bit position */ - if (!ldapu_strcasecmp(str, "e")) - return CERTMAP_BIT_POS_MAIL; - if (!ldapu_strcasecmp(str, "mail")) - return CERTMAP_BIT_POS_MAIL; - if (!ldapu_strcasecmp(str, "dc")) - return CERTMAP_BIT_POS_DC; - - return CERTMAP_BIT_POS_UNKNOWN; -} - -#if 0 /* may need this in the future */ -static int certmap_name_to_secoid (const char *str) -{ - if (!ldapu_strcasecmp(str, "c")) return SEC_OID_AVA_COUNTRY_NAME; - if (!ldapu_strcasecmp(str, "o")) return SEC_OID_AVA_ORGANIZATION_NAME; - if (!ldapu_strcasecmp(str, "cn")) return SEC_OID_AVA_COMMON_NAME; - if (!ldapu_strcasecmp(str, "l")) return SEC_OID_AVA_LOCALITY; - if (!ldapu_strcasecmp(str, "st")) return SEC_OID_AVA_STATE_OR_PROVINCE; - if (!ldapu_strcasecmp(str, "ou")) return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME; - if (!ldapu_strcasecmp(str, "uid")) return SEC_OID_RFC1274_UID; - if (!ldapu_strcasecmp(str, "e")) return SEC_OID_PKCS9_EMAIL_ADDRESS; - if (!ldapu_strcasecmp(str, "mail")) return SEC_OID_RFC1274_MAIL; - if (!ldapu_strcasecmp(str, "dc")) return SEC_OID_AVA_DC; - - return SEC_OID_AVA_UNKNOWN; /* return invalid OID */ -} -#endif - -NSAPI_PUBLIC int -ldapu_list_alloc(LDAPUList_t **list) -{ - *list = (LDAPUList_t *)calloc(1, sizeof(LDAPUList_t)); - - if (!*list) - return LDAPU_ERR_OUT_OF_MEMORY; - - return LDAPU_SUCCESS; -} - -static int -ldapu_list_add_node(LDAPUList_t *list, LDAPUListNode_t *node) -{ - if (list->head) { - node->prev = list->tail; - list->tail->next = node; - } else { - node->prev = 0; - list->head = node; - } - - node->next = 0; - list->tail = node; - return LDAPU_SUCCESS; -} - -NSAPI_PUBLIC int -ldapu_list_add_info(LDAPUList_t *list, void *info) -{ - LDAPUListNode_t *node; - - /* Allocate the list node and set info in the node. */ - node = (LDAPUListNode_t *)calloc(1, sizeof(LDAPUListNode_t)); - - if (!node) { - return LDAPU_ERR_OUT_OF_MEMORY; - } - - node->info = info; - - return ldapu_list_add_node(list, node); -} - - -static void -ldapu_list_free(LDAPUList_t *list, LDAPUListNodeFn_t free_fn) -{ - if (list) { - auto LDAPUListNode_t *node = list->head; - while (node) { - auto LDAPUListNode_t *next = node->next; - if (free_fn) { - (*free_fn)(node->info, 0); - node->info = 0; - } - node->info = 0; - free(node); - node = next; - } - list->head = 0; - list->tail = 0; - } - return; -} - -NSAPI_PUBLIC int -ldapu_propval_alloc(const char *prop, const char *val, LDAPUPropVal_t **propval) -{ - *propval = (LDAPUPropVal_t *)malloc(sizeof(LDAPUPropVal_t)); - - if (!*propval) - return LDAPU_ERR_OUT_OF_MEMORY; - - (*propval)->prop = prop ? strdup(prop) : 0; - (*propval)->val = val ? strdup(val) : 0; - - if ((!prop || (*propval)->prop) && (!val || (*propval)->val)) { - /* strdup worked */ - return LDAPU_SUCCESS; - } else { - return LDAPU_ERR_OUT_OF_MEMORY; - } -} - - -static int -PresentInComps(long comps_bitmask, int tag) -{ - long bit = certmap_secoid_to_bit_pos(tag); - - if (comps_bitmask & bit) - return 1; - else - return 0; -} - - -static int -dbconf_to_certmap_err(int err) -{ - switch (err) { - case LDAPU_ERR_DBNAME_IS_MISSING: - return LDAPU_ERR_CANAME_IS_MISSING; - case LDAPU_ERR_PROP_IS_MISSING: - return LDAPU_ERR_CAPROP_IS_MISSING; - default: - return err; - } -} - -/* CAUTION: this function hijacks some substructures from db_info and make - * the pointers to it NULL in the db_info. It is safe to deallocate db_info. - */ -static int -dbinfo_to_certinfo(DBConfDBInfo_t *db_info, - LDAPUCertMapInfo_t **certinfo_out) -{ - LDAPUCertMapInfo_t *certinfo = NULL; - LDAPUPropValList_t *propval_list = NULL; - int rv = LDAPU_SUCCESS; - - *certinfo_out = 0; - - certinfo = (LDAPUCertMapInfo_t *)calloc(1, sizeof(LDAPUCertMapInfo_t)); - - if (!certinfo) { - rv = LDAPU_ERR_OUT_OF_MEMORY; - goto error; - } - - /* hijack few structures rather then copy. Make the pointers to the - structures NULL in the original structure so that they don't freed up - when db_info is freed. */ - certinfo->issuerName = db_info->dbname; - db_info->dbname = 0; - - certinfo->issuerDN = ldapu_dn_normalize(db_info->url); - db_info->url = 0; - - /* hijack actual prop-vals from dbinfo -- to avoid strdup calls */ - if (db_info->firstprop) { - LDAPUPropVal_t *propval; - DBPropVal_t *dbpropval; - - dbpropval = db_info->firstprop; - - rv = ldapu_list_alloc(&propval_list); - - if (rv != LDAPU_SUCCESS) { - goto error; - } - - while (dbpropval) { - propval = (LDAPUPropVal_t *)malloc(sizeof(LDAPUPropVal_t)); - - if (!propval) { - rv = LDAPU_ERR_OUT_OF_MEMORY; - goto error; - } - - propval->prop = dbpropval->prop; - dbpropval->prop = 0; - - propval->val = dbpropval->val; - dbpropval->val = 0; - - rv = ldapu_list_add_info(propval_list, propval); - - if (rv != LDAPU_SUCCESS) { - goto error; - } - - dbpropval = dbpropval->next; - } - - certinfo->propval = propval_list; - } - - *certinfo_out = certinfo; - goto done; - -error: - if (propval_list) - ldapu_propval_list_free(propval_list); - if (certinfo) - free(certinfo); - -done: - return rv; -} - -static int -ldapu_binary_cmp_certs(void *subject_cert, - void *entry_cert_binary, - unsigned long entry_cert_len) -{ - SECItem derCert = ((CERTCertificate *)subject_cert)->derCert; - int rv; - - /* binary compare the two certs */ - if (derCert.len == entry_cert_len && - !memcmp(derCert.data, entry_cert_binary, entry_cert_len)) { - rv = LDAPU_SUCCESS; - } else { - rv = LDAPU_ERR_CERT_VERIFY_FAILED; - } - - return rv; -} - - -static int -ldapu_cert_verifyfn_default(void *subject_cert, LDAP *ld, void *certmap_info __attribute__((unused)), LDAPMessage *res, LDAPMessage **entry_out) -{ - LDAPMessage *entry; - struct berval **bvals; - int i; - int rv = LDAPU_ERR_CERT_VERIFY_FAILED; - char *cert_attr = ldapu_strings[LDAPU_STR_ATTR_CERT]; - char *cert_attr_nosubtype = ldapu_strings[LDAPU_STR_ATTR_CERT_NOSUBTYPE]; - - *entry_out = 0; - - for (entry = ldapu_first_entry(ld, res); entry != NULL; - entry = ldapu_next_entry(ld, entry)) { - if (((bvals = ldapu_get_values_len(ld, entry, cert_attr)) == NULL) && - ((bvals = ldapu_get_values_len(ld, entry, cert_attr_nosubtype)) == NULL)) { - rv = LDAPU_ERR_CERT_VERIFY_NO_CERTS; - continue; - } - - for (i = 0; bvals[i] != NULL; i++) { - rv = ldapu_binary_cmp_certs(subject_cert, - bvals[i]->bv_val, - bvals[i]->bv_len); - - if (rv == LDAPU_SUCCESS) { - break; - } - } - - ldapu_value_free_len(ld, bvals); - - if (rv == LDAPU_SUCCESS) { - *entry_out = entry; - break; - } - } - - return rv; -} - -static int -parse_into_bitmask(const char *comps_in, long *bitmask_out, long default_val) -{ - long bitmask; - char *comps = comps_in ? strdup(comps_in) : 0; - - if (!comps) { - /* Not present in the config file */ - bitmask = default_val; - } else if (!*comps) { - /* present but empty */ - bitmask = 0; - } else { - char *ptr = comps; - char *name = comps; - long bit; - int break_loop = 0; - - bitmask = 0; - - while (*name) { - /* advance ptr to delimeter */ - while (*ptr && !isspace(*ptr) && *ptr != ',') - ptr++; - - if (!*ptr) - break_loop = 1; - else - *ptr++ = 0; - - bit = certmap_name_to_bit_pos(name); - bitmask |= bit; - - if (break_loop) - break; - /* skip delimeters */ - while (*ptr && (isspace(*ptr) || *ptr == ',')) - ptr++; - name = ptr; - } - } - - if (comps) - free(comps); - *bitmask_out = bitmask; - /* print_oid_bitmask(bitmask); */ - return LDAPU_SUCCESS; -} - -static int -process_certinfo(LDAPUCertMapInfo_t *certinfo) -{ - int rv = LDAPU_SUCCESS; - char *dncomps = 0; - char *filtercomps = 0; - char *libname = 0; - char *verify = 0; - char *fname = 0; - char *searchAttr = 0; - - if (!ldapu_strcasecmp(certinfo->issuerName, "default")) { - default_certmap_info = certinfo; - } else if (!certinfo->issuerDN) { - return LDAPU_ERR_NO_ISSUERDN_IN_CONFIG_FILE; - } else { - rv = ldapu_list_add_info(certmap_listinfo, certinfo); - } - - if (rv != LDAPU_SUCCESS) - return rv; - - /* look for dncomps property and parse it into the dncomps bitmask */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_DNCOMPS, &dncomps); - - if (rv == LDAPU_SUCCESS && dncomps) { - certinfo->dncompsState = COMPS_HAS_ATTRS; - tolower_string(dncomps); - } else if (rv == LDAPU_FAILED) { - certinfo->dncompsState = COMPS_COMMENTED_OUT; - rv = LDAPU_SUCCESS; - } else if (rv == LDAPU_SUCCESS && !dncomps) { - certinfo->dncompsState = COMPS_EMPTY; - dncomps = strdup(""); /* present but empty */ - } - - rv = parse_into_bitmask(dncomps, &certinfo->dncomps, -1); - - free(dncomps); - dncomps = NULL; - - if (rv != LDAPU_SUCCESS) - return rv; - - /* look for filtercomps property and parse it into the filtercomps bitmask */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_FILTERCOMPS, - &filtercomps); - - if (rv == LDAPU_SUCCESS && filtercomps) { - certinfo->filtercompsState = COMPS_HAS_ATTRS; - tolower_string(filtercomps); - } else if (rv == LDAPU_FAILED) { - certinfo->filtercompsState = COMPS_COMMENTED_OUT; - rv = LDAPU_SUCCESS; - } else if (rv == LDAPU_SUCCESS && !filtercomps) { - certinfo->filtercompsState = COMPS_EMPTY; - filtercomps = strdup(""); /* present but empty */ - } - - rv = parse_into_bitmask(filtercomps, &certinfo->filtercomps, 0); - - free(filtercomps); - filtercomps = NULL; - - if (rv != LDAPU_SUCCESS) - return rv; - - /* look for "CmapLdapAttr" property and store it into searchAttr */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_CERTMAP_LDAP_ATTR, - &searchAttr); - - if (rv == LDAPU_FAILED || !searchAttr) { - rv = LDAPU_SUCCESS; - } else { - certinfo->searchAttr = searchAttr; - - if (searchAttr && !certinfo->searchAttr) - rv = LDAPU_ERR_OUT_OF_MEMORY; - else - rv = LDAPU_SUCCESS; - } - - if (rv != LDAPU_SUCCESS) - return rv; - - /* look for verifycert property and set the default verify function */ - /* The value of the verifycert property is ignored */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_VERIFYCERT, &verify); - - if (rv == LDAPU_SUCCESS) { - if (!ldapu_strcasecmp(verify, "on")) - certinfo->verifyCert = 1; - else if (!ldapu_strcasecmp(verify, "off")) - certinfo->verifyCert = 0; - else if (!verify || !*verify) /* for mail/news backward compatibilty */ - certinfo->verifyCert = 1; /* otherwise, this should be an error */ - else - rv = LDAPU_ERR_MISSING_VERIFYCERT_VAL; - } else if (rv == LDAPU_FAILED) - rv = LDAPU_SUCCESS; - - if (verify) - free(verify); - - if (rv != LDAPU_SUCCESS) - return rv; - - { - PRLibrary *lib = 0; - - /* look for the library property and load it */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_LIBRARY, &libname); - - if (rv == LDAPU_SUCCESS) { - if (libname && *libname) { - lib = PR_LoadLibrary(libname); - if (!lib) - rv = LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN; - } else { - rv = LDAPU_ERR_MISSING_LIBNAME; - } - } else if (rv == LDAPU_FAILED) - rv = LDAPU_SUCCESS; - - if (libname) - free(libname); - if (rv != LDAPU_SUCCESS) - return rv; - - /* look for the InitFn property, find it in the libray and call it */ - rv = ldapu_certmap_info_attrval(certinfo, LDAPU_ATTR_INITFN, &fname); - - if (rv == LDAPU_SUCCESS) { - if (fname && *fname) { - /* If lib is NULL, PR_FindSymbol will search all libs loaded - * through PR_LoadLibrary. - */ - CertMapInitFn_t fn = (CertMapInitFn_t)PR_FindSymbol(lib, fname); - - if (!fn) { - rv = LDAPU_ERR_MISSING_INIT_FN_IN_LIB; - } else { - rv = (*fn)(certinfo, certinfo->issuerName, - certinfo->issuerDN, this_dllname); - } - } else { - rv = LDAPU_ERR_MISSING_INIT_FN_NAME; - } - } else if (lib) { - /* If library is specified, init function must be specified */ - /* If init fn is specified, library may not be specified */ - rv = LDAPU_ERR_MISSING_INIT_FN_IN_CONFIG; - } else if (rv == LDAPU_FAILED) { - rv = LDAPU_SUCCESS; - } - - if (fname) - free(fname); - - if (rv != LDAPU_SUCCESS) - return rv; - } - - return rv; -} - -/* This function will read multiple certmap directives and set the information - * in the global certmap_listinfo structure. - */ -int -certmap_read_certconfig_file(const char *file) -{ - DBConfInfo_t *conf_info = 0; - int rv; - - /* Read the config file */ - rv = dbconf_read_config_file_sub(file, LIB_DIRECTIVE, LIB_DIRECTIVE_LEN, - &conf_info); - - /* Convert the conf_info into certmap_listinfo. Some of the - * sub-structures are simply hijacked rather than copied since we are - * going to (carefully) free the conf_info anyway. - */ - - if (rv == LDAPU_SUCCESS && conf_info) { - DBConfDBInfo_t *nextdb; - DBConfDBInfo_t *curdb; - LDAPUCertMapInfo_t *certinfo; - - curdb = conf_info->firstdb; - - while (curdb) { - nextdb = curdb->next; - rv = dbinfo_to_certinfo(curdb, &certinfo); - - if (rv != LDAPU_SUCCESS) { - dbconf_free_confinfo(conf_info); - return rv; - } - - rv = process_certinfo(certinfo); - - if (rv != LDAPU_SUCCESS) { - dbconf_free_confinfo(conf_info); - return rv; - } - - curdb = nextdb; - } - - dbconf_free_confinfo(conf_info); - } else { - rv = dbconf_to_certmap_err(rv); - } - - return rv; -} - -/* This function will read the "certmap default" directive from the config - * file and set the information in the global certmap_info. - */ -int -certmap_read_default_certinfo(const char *file) -{ - DBConfDBInfo_t *db_info = 0; - int rv; - - rv = dbconf_read_default_dbinfo_sub(file, LIB_DIRECTIVE, LIB_DIRECTIVE_LEN, - &db_info); - - if (rv != LDAPU_SUCCESS) - return rv; - - rv = dbinfo_to_certinfo(db_info, &default_certmap_info); - - dbconf_free_dbinfo(db_info); - return rv; -} - -static int -ldapu_cert_searchfn_default(void *cert, LDAP *ld, void *certmap_info_in, const char *basedn, const char *dn, const char *filter, const char **attrs, LDAPMessage ***res) -{ - int rv = LDAPU_FAILED; - const char *ldapdn; - LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)certmap_info_in; - LDAPMessage *single_res = NULL; - LDAPMessage **multiple_res = NULL; - - - if (certmap_info && certmap_info->searchAttr) { - char *subjectDN = 0; - char *certFilter = 0; - int len; - - rv = ldapu_get_cert_subject_dn(cert, &subjectDN); - - if (rv != LDAPU_SUCCESS || !subjectDN) { - return rv; - } - len = strlen(certmap_info->searchAttr) + strlen(subjectDN) + - strlen("=") + 1; - certFilter = (char *)ldapu_malloc(len * sizeof(char)); - if (!certFilter) { - free(subjectDN); - return LDAPU_ERR_OUT_OF_MEMORY; - } - sprintf(certFilter, "%s=%s", certmap_info->searchAttr, subjectDN); - free(subjectDN); - if (ldapu_strcasecmp(basedn, "")) { - rv = ldapu_find(ld, basedn, LDAP_SCOPE_SUBTREE, certFilter, attrs, 0, &single_res); - ldapu_free((void *)certFilter); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = (LDAPMessage **)ldapu_malloc(2 * sizeof(LDAPMessage *)); - (*res)[0] = single_res; - (*res)[1] = NULL; - return rv; - } else if (single_res) { - ldapu_msgfree(ld, single_res); - single_res = 0; - } - } else { - rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, certFilter, attrs, 0, &multiple_res); - ldapu_free((void *)certFilter); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = multiple_res; - return rv; - } else if (multiple_res) { - int n; - for (n = 0; multiple_res[n] != NULL; n++) - ldapu_msgfree(ld, multiple_res[n]); - ldapu_memfree(ld, multiple_res); - } - } - } - - if (dn && *dn) { - /* First do the base level search --- NOT ANY MORE!! */ - /* We actually do the search on the whole subtree hanging from "ldapdn" since we want to - * find all the entries that match the filter. - * If we get more than one matching entry in return, it'll be at verify time that we'll - * choose the correct one among them all. - * However, if certificate verify is not active, certificate mapping will fail and will - * consequently display an error message (something done at 'handle_handshake_done' level, - * for instance). */ - ldapdn = dn; - - if (ldapu_strcasecmp(ldapdn, "")) { - rv = ldapu_find(ld, ldapdn, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &single_res); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = (LDAPMessage **)ldapu_malloc(2 * sizeof(LDAPMessage *)); - (*res)[0] = single_res; - (*res)[1] = NULL; - return rv; - } else if (single_res) { - ldapu_msgfree(ld, single_res); - single_res = 0; - } - } else { - rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &multiple_res); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = multiple_res; - return rv; - } else if (multiple_res) { - int n; - for (n = 0; multiple_res[n] != NULL; n++) - ldapu_msgfree(ld, multiple_res[n]); - ldapu_memfree(ld, multiple_res); - } - } - } else { - /* default the dn and filter for subtree search */ - ldapdn = basedn; - if (!filter || !*filter) { - if (certmap_info && certmap_info->searchAttr) { - /* dn & filter returned by the mapping function are both NULL - and 'searchAttr' based search has failed. Don't do brute - force search if 'searchAttr' is being used. Otherwise, - this search will result in all LDAP entries being - returned. - */ - } else { - filter = "objectclass=*"; - } - } - } - - /* For local LDAP DB, the LDAP_SCOPE_BASE search may fail for dn == basedn - * since that object doesn't actually exists. - */ - if ((rv == LDAPU_FAILED || rv == LDAP_NO_SUCH_OBJECT) && filter && (!dn || !*dn)) { - - /* Try the subtree search only if the filter is non-NULL */ - if (ldapu_strcasecmp(ldapdn, "")) { - rv = ldapu_find(ld, ldapdn, LDAP_SCOPE_SUBTREE, filter, 0, 0, &single_res); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = (LDAPMessage **)ldapu_malloc(2 * sizeof(LDAPMessage *)); - (*res)[0] = single_res; - (*res)[1] = NULL; - return rv; - } else if (single_res) { - ldapu_msgfree(ld, single_res); - single_res = 0; - } - } else { - rv = ldapu_find_entire_tree(ld, LDAP_SCOPE_SUBTREE, filter, 0, 0, &multiple_res); - if (rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) { - *res = multiple_res; - return rv; - } else if (multiple_res) { - int n; - for (n = 0; multiple_res[n] != NULL; n++) - ldapu_msgfree(ld, multiple_res[n]); - ldapu_memfree(ld, multiple_res); - } - } - } - - if (rv == LDAPU_FAILED) { - /* Not an error but couldn't map the cert */ - rv = LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND; - } else if ((!dn || !*dn) && (rv == LDAP_NO_SUCH_OBJECT)) { - rv = LDAPU_ERR_INVALID_SUFFIX; - } - - return rv; -} - -NSAPI_PUBLIC int -ldapu_issuer_certinfo(const char *issuerDN, void **certmap_info) -{ - *certmap_info = 0; - - if (!issuerDN || !*issuerDN || !ldapu_strcasecmp(issuerDN, "default")) { - *certmap_info = default_certmap_info; - } else if (certmap_listinfo) { - char *n_issuerDN = ldapu_dn_normalize(ldapu_strdup(issuerDN)); - LDAPUListNode_t *cur = certmap_listinfo->head; - while (cur) { - if (!ldapu_strcasecmp(n_issuerDN, ((LDAPUCertMapInfo_t *)cur->info)->issuerDN)) { - *certmap_info = cur->info; - break; - } - cur = cur->next; - } - if (n_issuerDN) - ldapu_free(n_issuerDN); - } - return *certmap_info ? LDAPU_SUCCESS : LDAPU_FAILED; -} - -NSAPI_PUBLIC int -ldapu_certmap_info_attrval(void *certmap_info_in, - const char *attr, - char **val) -{ - /* Look for given attr in the certmap_info and return its value */ - LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)certmap_info_in; - LDAPUListNode_t *curprop = certmap_info->propval ? certmap_info->propval->head : 0; - LDAPUPropVal_t *propval; - int rv = LDAPU_FAILED; - - *val = 0; - while (curprop) { - propval = (LDAPUPropVal_t *)curprop->info; - if (!ldapu_strcasecmp(propval->prop, attr)) { - *val = propval->val ? strdup(propval->val) : 0; - rv = LDAPU_SUCCESS; - break; - } - curprop = curprop->next; - } - - return rv; -} - -static int -AddAVAToBuf(char *buf, int size, int *len, const char *tagName, CERTAVA *ava) -{ - int lenLen; - int taglen; - SECStatus rv; - - buf += *len; - - /* Copied from ns/lib/libsec ... - * XXX this code is incorrect in general - * -- should use a DER template. - */ - lenLen = 2; - if (ava->value.len >= 128) - lenLen = 3; - - taglen = PL_strlen(tagName); - memcpy(buf, tagName, taglen); - buf[taglen++] = '='; - - rv = CERT_RFC1485_EscapeAndQuote(buf + taglen, - size - taglen, - (char *)ava->value.data + lenLen, - ava->value.len - lenLen); - - *len += strlen(buf); - - return (rv == SECSuccess ? LDAPU_SUCCESS : LDAPU_FAILED); -} - -static int -AddToLdapDN(char *ldapdn, int size, int *dnlen, const char *tagName, CERTAVA *ava) -{ - char *dn = ldapdn + *dnlen; - - if (*dnlen) { - strcat(dn, ", "); - dn += 2; - *dnlen += 2; - } - return AddAVAToBuf(ldapdn, size, dnlen, tagName, ava); -} - -static int -AddToFilter(char *filter, int size, int *flen, const char *tagName, CERTAVA *ava) -{ - int rv; - - /* Append opening parenthesis */ - strcat(filter + *flen, " ("); - *flen += 2; - rv = AddAVAToBuf(filter, size, flen, tagName, ava); - - if (rv != LDAPU_SUCCESS) - return rv; - - /* Append closing parenthesis */ - strcat(filter + *flen, ")"); - (*flen)++; - - return rv; -} - -NSAPI_PUBLIC int -ldapu_free_cert_ava_val(char **val) -{ - char **ptr = val; - - if (!val) - return LDAPU_SUCCESS; - - while (*ptr) - free(*ptr++); - free(val); - - return LDAPU_SUCCESS; -} - -static int -ldapu_cert_mapfn_default(void *cert_in, LDAP *ld __attribute__((unused)), void *certmap_info_in, char **ldapDN_out, char **filter_out) -{ - CERTCertificate *cert = (CERTCertificate *)cert_in; - LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)certmap_info_in; - int rv = LDAPU_SUCCESS; - - *ldapDN_out = *filter_out = 0; - - if (!certmap_info) { - /* Use subject DN as is -- identity mapping function */ - rv = ldapu_get_cert_subject_dn(cert, ldapDN_out); - - return rv; - } else { - /* - * Iterate over rdns from the subject and collect AVAs depending on - * dnComps and filtercomps to form ldapDN and filter respectively. - * certmap_info->dncomps - */ - CERTName *subject = &cert->subject; - CERTRDN **rdns = subject->rdns; - CERTRDN **lastRdn; - CERTRDN **rdn; - CERTAVA **avas; - CERTAVA *ava; - char ldapdn[BIG_LINE]; - char filter[BIG_LINE]; - int dnlen = 0; /* ldap DN length */ - int flen = 0; /* filter length */ - int numfavas = 0; /* no of avas added to filter */ - - if (rdns == NULL) { - /* error */ - return LDAPU_ERR_INTERNAL; - } - - /* find last RDN */ - lastRdn = rdns; - while (*lastRdn) - lastRdn++; - lastRdn--; - - /* Initialize filter to "(&" */ - strcpy(filter, "(&"); - flen = 2; - - /* - * Loop over subject rdns in the _reverse_ order while forming ldapDN - * and filter. - */ - for (rdn = lastRdn; rdn >= rdns; rdn--) { - avas = (*rdn)->avas; - while ((ava = *avas++) != NULL) { - int tag = CERT_GetAVATag(ava); - const char *tagName = certmap_secoid_to_name(tag); - - if (PresentInComps(certmap_info->dncomps, tag)) { - rv = AddToLdapDN(ldapdn, BIG_LINE, &dnlen, tagName, ava); - if (rv != LDAPU_SUCCESS) - return rv; - } - - if (PresentInComps(certmap_info->filtercomps, tag)) { - rv = AddToFilter(filter, BIG_LINE, &flen, tagName, ava); - if (rv != LDAPU_SUCCESS) - return rv; - numfavas++; - } - } - } - - if (numfavas == 0) { - /* nothing added to filter */ - *filter = 0; - } else if (numfavas == 1) { - /* one ava added to filter -- remove "(& (" from the front and ")" - * from the end. - */ - *filter_out = strdup(filter + 4); - if (!*filter_out) - return LDAPU_ERR_OUT_OF_MEMORY; - (*filter_out)[strlen(*filter_out) - 1] = 0; - } else { - /* Add the closing parenthesis to filter */ - strcat(filter + flen, ")"); - *filter_out = strdup(filter); - } - - if (dnlen >= BIG_LINE) - return LDAPU_FAILED; - ldapdn[dnlen] = 0; - *ldapDN_out = *ldapdn ? strdup(ldapdn) : 0; - - if ((numfavas && !*filter_out) || (dnlen && !*ldapDN_out)) { - /* strdup failed */ - return LDAPU_ERR_OUT_OF_MEMORY; - } - - if ((certmap_info->dncompsState == COMPS_HAS_ATTRS && dnlen == 0) || - (certmap_info->filtercompsState == COMPS_HAS_ATTRS && - numfavas == 0)) { - /* At least one attr in DNComps should be present in the cert */ - /* Same is true for FilterComps */ - rv = LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND; - } - } - - return rv; -} - -NSAPI_PUBLIC int -ldapu_set_cert_mapfn(const char *issuerDN, - CertMapFn_t mapfn) -{ - LDAPUCertMapInfo_t *certmap_info; - int rv; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - - /* Don't set the mapping function if certmap_info doesen't exist */ - if (rv != LDAPU_SUCCESS) - return rv; - - certmap_info->mapfn = mapfn; - return LDAPU_SUCCESS; -} - -static CertMapFn_t -ldapu_get_cert_mapfn_sub(LDAPUCertMapInfo_t *certmap_info) -{ - CertMapFn_t mapfn; - - if (certmap_info && certmap_info->mapfn) - mapfn = certmap_info->mapfn; - else if (default_certmap_info && default_certmap_info->mapfn) - mapfn = default_certmap_info->mapfn; - else - mapfn = ldapu_cert_mapfn_default; - - return mapfn; -} - -NSAPI_PUBLIC CertMapFn_t -ldapu_get_cert_mapfn(const char *issuerDN) -{ - LDAPUCertMapInfo_t *certmap_info = 0; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - /* certmap_info may be NULL -- use the default */ - - return ldapu_get_cert_mapfn_sub(certmap_info); -} - -NSAPI_PUBLIC int -ldapu_set_cert_searchfn(const char *issuerDN, - CertSearchFn_t searchfn) -{ - LDAPUCertMapInfo_t *certmap_info; - int rv; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - - /* Don't set the mapping function if certmap_info doesen't exist */ - if (rv != LDAPU_SUCCESS) - return rv; - - certmap_info->searchfn = searchfn; - return LDAPU_SUCCESS; -} - -static CertSearchFn_t -ldapu_get_cert_searchfn_sub(LDAPUCertMapInfo_t *certmap_info) -{ - CertSearchFn_t searchfn; - - if (certmap_info && certmap_info->searchfn) - searchfn = certmap_info->searchfn; - else if (default_certmap_info && default_certmap_info->searchfn) - searchfn = default_certmap_info->searchfn; - else - searchfn = ldapu_cert_searchfn_default; - - return searchfn; -} - -NSAPI_PUBLIC CertSearchFn_t -ldapu_get_cert_searchfn(const char *issuerDN) -{ - LDAPUCertMapInfo_t *certmap_info = 0; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - /* certmap_info may be NULL -- use the default */ - - return ldapu_get_cert_searchfn_sub(certmap_info); -} - -NSAPI_PUBLIC int -ldapu_set_cert_verifyfn(const char *issuerDN, - CertVerifyFn_t verifyfn) -{ - LDAPUCertMapInfo_t *certmap_info; - int rv; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - - /* Don't set the verify function if certmap_info doesen't exist */ - if (rv != LDAPU_SUCCESS) - return rv; - - certmap_info->verifyfn = verifyfn; - return LDAPU_SUCCESS; -} - -static CertVerifyFn_t -ldapu_get_cert_verifyfn_sub(LDAPUCertMapInfo_t *certmap_info) -{ - CertVerifyFn_t verifyfn; - - if (certmap_info && certmap_info->verifyfn) - verifyfn = certmap_info->verifyfn; - else if (default_certmap_info && default_certmap_info->verifyfn) - verifyfn = default_certmap_info->verifyfn; - else - verifyfn = ldapu_cert_verifyfn_default; - - return verifyfn; -} - -NSAPI_PUBLIC CertVerifyFn_t -ldapu_get_cert_verifyfn(const char *issuerDN) -{ - LDAPUCertMapInfo_t *certmap_info = 0; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - /* certmap_info may be NULL -- use the default */ - - return ldapu_get_cert_verifyfn_sub(certmap_info); -} - -#if 0 /* may need this in the future */ -static int ldapu_certinfo_copy (const LDAPUCertMapInfo_t *from, - const char *newIssuerName, - const char *newIssuerDN, - LDAPUCertMapInfo_t *to) -{ - /* This function is not tested and is not used */ - int rv; - - to->issuerName = newIssuerName ? strdup(newIssuerName) : 0; - to->issuerDN = newIssuerDN ? strdup(newIssuerDN) : 0; - if (from->propval) { - rv = ldapu_list_copy(from->propval, &to->propval, ldapu_propval_copy); - if (rv != LDAPU_SUCCESS) return rv; - } - else { - to->propval = 0; - } - - return process_certinfo(to); -} -#endif - -NSAPI_PUBLIC int -ldapu_cert_to_ldap_entry(void *cert, LDAP *ld, const char *basedn, LDAPMessage **res) -{ - char *issuerDN = 0; - char *ldapDN = 0; - char *filter = 0; - LDAPUCertMapInfo_t *certmap_info; - LDAPMessage **res_array = NULL; - CertMapFn_t mapfn; - CertVerifyFn_t verifyfn; - CertSearchFn_t searchfn; - int rv, i, j; - - *res = 0; - - if (!certmap_attrs[0]) { - /* Initialize certmap_attrs */ - certmap_attrs[0] = ldapu_strings[LDAPU_STR_ATTR_USER]; - certmap_attrs[1] = ldapu_strings[LDAPU_STR_ATTR_CERT]; - certmap_attrs[2] = ldapu_strings[LDAPU_STR_ATTR_CERT_NOSUBTYPE]; - certmap_attrs[3] = 0; - } - - rv = ldapu_get_cert_issuer_dn(cert, &issuerDN); - - if (rv != LDAPU_SUCCESS) - return LDAPU_ERR_NO_ISSUERDN_IN_CERT; - - /* don't free the certmap_info -- its a pointer to an internal structure */ - rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info); - free(issuerDN); - - if (!certmap_info) - certmap_info = default_certmap_info; - - /* Get the mapping function from the certmap_info */ - mapfn = ldapu_get_cert_mapfn_sub(certmap_info); - - rv = (*mapfn)(cert, ld, certmap_info, &ldapDN, &filter); - - if (rv != LDAPU_SUCCESS) - return rv; - - /* Get the search function from the certmap_info - certinfo maybe NULL */ - searchfn = ldapu_get_cert_searchfn_sub(certmap_info); - - rv = (*searchfn)(cert, ld, certmap_info, basedn, ldapDN, filter, - certmap_attrs, &res_array); - - if (ldapDN) - free(ldapDN); - if (filter) - free(filter); - - /* - * Get the verify cert function & call it. - */ - j = 0; - if ((rv == LDAPU_SUCCESS || rv == LDAPU_ERR_MULTIPLE_MATCHES) && - (certmap_info ? certmap_info->verifyCert : 0)) { - verifyfn = ldapu_get_cert_verifyfn_sub(certmap_info); - - if (verifyfn) { - int verify_rv; - - i = 0; - do { - LDAPMessage *entry; - verify_rv = (*verifyfn)(cert, ld, certmap_info, res_array[i], &entry); - - if (rv == LDAPU_ERR_MULTIPLE_MATCHES) { - if (verify_rv == LDAPU_SUCCESS) { - /* 'entry' points to the matched entry */ - /* Get the 'res' which only contains this entry */ - char *dn = ldapu_get_dn(ld, entry); - if (*res) - ldapu_msgfree(ld, *res); - rv = ldapu_find(ld, dn, LDAP_SCOPE_BASE, 0, certmap_attrs, 0, res); - ldapu_memfree(ld, dn); - } else { - /* Verify failed for multiple matches -- keep rv */ - /* multiple matches err is probably more interesting to - the caller then any other error returned by the verify - fn */ - } - } else /* rv == LDAPU_SUCCESS */ { - if (verify_rv == LDAPU_SUCCESS) { - *res = res_array[0]; - j = 1; - } else { - rv = verify_rv; - } - } - } while ((verify_rv != LDAPU_SUCCESS) && (res_array[++i] != NULL)); - } - } else { - if (rv == LDAPU_SUCCESS) { - *res = res_array[0]; - j = 1; - } - } - - - if (rv != LDAPU_SUCCESS) { - if (*res) { - ldapu_msgfree(ld, *res); - *res = 0; - } - } - - i = j; /* ugaston - if the search had been successful, despite verifyCert being "off", - * mapping is considered successful, so we keep the first (and only) response message. - * If, on the other hand, the search had returned multiple matches, the fact - * of having verifyCert "off" automatically turns the mapping faulty, so we - * don't need to care about keeping any response at all. - */ - - if (res_array) { - while (res_array[i] != NULL) { - ldapu_msgfree(ld, res_array[i]); - res_array[i++] = 0; - } - ldapu_memfree(ld, res_array); - } - return rv; -} - -/* The caller shouldn't free the entry */ -NSAPI_PUBLIC int -ldapu_cert_to_user(void *cert, LDAP *ld, const char *basedn, LDAPMessage **res_out, char **user) -{ - int rv; - LDAPMessage *res; - LDAPMessage *entry; - int numEntries; - char **attrVals = NULL; - - *res_out = 0; - *user = 0; - - rv = ldapu_cert_to_ldap_entry(cert, ld, basedn, &res); - - if (rv != LDAPU_SUCCESS) { - goto done; - } - - if (!res) { - rv = LDAPU_ERR_EMPTY_LDAP_RESULT; - goto done; - } - - /* Extract user login (the 'uid' attr) from 'res' */ - numEntries = ldapu_count_entries(ld, res); - - if (numEntries != 1) { - rv = LDAPU_ERR_MULTIPLE_MATCHES; - goto done; - } - - entry = ldapu_first_entry(ld, res); - - if (!entry) { - rv = LDAPU_ERR_MISSING_RES_ENTRY; - goto done; - } - - attrVals = ldapu_get_values(ld, entry, - ldapu_strings[LDAPU_STR_ATTR_USER]); - - if (!attrVals || !attrVals[0]) { - rv = LDAPU_ERR_MISSING_UID_ATTR; - goto done; - } - - *user = strdup(attrVals[0]); - - /* ldapu_msgfree(res); */ - - if (!*user) { - rv = LDAPU_ERR_OUT_OF_MEMORY; - goto done; - } - - *res_out = res; - -done: - if (attrVals) { - ldapu_value_free(ld, attrVals); - } - - return rv; -} - -static void * -ldapu_propval_free(void *propval_in, void *arg __attribute__((unused))) -{ - LDAPUPropVal_t *propval = (LDAPUPropVal_t *)propval_in; - - if (propval->prop) - free(propval->prop); - if (propval->val) - free(propval->val); - memset((void *)propval, 0, sizeof(LDAPUPropVal_t)); - free(propval); - return 0; -} - -void -ldapu_certinfo_free(void *info_in) -{ - LDAPUCertMapInfo_t *certmap_info = (LDAPUCertMapInfo_t *)info_in; - - if (certmap_info->issuerName) - free(certmap_info->issuerName); - if (certmap_info->issuerDN) - free(certmap_info->issuerDN); - if (certmap_info->propval) - ldapu_list_free(certmap_info->propval, ldapu_propval_free); - if (certmap_info->searchAttr) - free(certmap_info->searchAttr); - memset((void *)certmap_info, 0, sizeof(LDAPUCertMapInfo_t)); - free(certmap_info); -} - -static void * -ldapu_certinfo_free_helper(void *info, void *arg __attribute__((unused))) -{ - ldapu_certinfo_free(info); - return (void *)LDAPU_SUCCESS; -} - -void -ldapu_certmap_listinfo_free(void *_certmap_listinfo) -{ - LDAPUCertMapListInfo_t *list = (LDAPUCertMapListInfo_t *)_certmap_listinfo; - ldapu_list_free(list, ldapu_certinfo_free_helper); -} - -void -ldapu_propval_list_free(void *propval_list) -{ - LDAPUPropValList_t *list = (LDAPUPropValList_t *)propval_list; - ldapu_list_free(list, ldapu_propval_free); - free(list); -} - -int -ldapu_certmap_init(const char *config_file, - const char *dllname, - LDAPUCertMapListInfo_t **certmap_list, - LDAPUCertMapInfo_t **certmap_default) -{ - int rv; - certmap_listinfo = (LDAPUCertMapListInfo_t *)calloc(1, sizeof(LDAPUCertMapListInfo_t)); - - *certmap_list = 0; - *certmap_default = 0; - PR_snprintf(this_dllname, sizeof(this_dllname), "%s", dllname); - - if (!certmap_listinfo) - return LDAPU_ERR_OUT_OF_MEMORY; - - rv = certmap_read_certconfig_file(config_file); - - if (rv == LDAPU_SUCCESS) { - *certmap_list = certmap_listinfo; - *certmap_default = default_certmap_info; - } - - return rv; -} - -NSAPI_PUBLIC int -ldaputil_exit() -{ - if (default_certmap_info) { - ldapu_certinfo_free(default_certmap_info); - default_certmap_info = 0; - } - - if (certmap_listinfo) { - ldapu_certmap_listinfo_free(certmap_listinfo); - certmap_listinfo = 0; - } - - return LDAPU_SUCCESS; -} - - -NSAPI_PUBLIC void -ldapu_free(void *ptr) -{ - if (ptr) - free(ptr); -} - -NSAPI_PUBLIC void -ldapu_free_old(char *ptr) -{ - free((void *)ptr); -} - -NSAPI_PUBLIC void * -ldapu_malloc(int size) -{ - return malloc(size); -} - -NSAPI_PUBLIC char * -ldapu_strdup(const char *ptr) -{ - return strdup(ptr); -} - -NSAPI_PUBLIC void * -ldapu_realloc(void *ptr, int size) -{ - return realloc(ptr, size); -} - -#define DNSEPARATOR(c) (c == ',' || c == ';') -#define SEPARATOR(c) (c == ',' || c == ';' || c == '+') -#define SPACE(c) (c == ' ' || c == '\n') -#define NEEDSESCAPE(c) (c == '\\' || c == '"') -#define B4TYPE 0 -#define INTYPE 1 -#define B4EQUAL 2 -#define B4VALUE 3 -#define INVALUE 4 -#define INQUOTEDVALUE 5 -#define B4SEPARATOR 6 - -static char * -ldapu_dn_normalize(char *dn) -{ - char *d, *s; - int state, gotesc; - - gotesc = 0; - state = B4TYPE; - for (d = s = dn; *s; s++) { - switch (state) { - case B4TYPE: - if (!SPACE(*s)) { - state = INTYPE; - *d++ = *s; - } - break; - case INTYPE: - if (*s == '=') { - state = B4VALUE; - *d++ = *s; - } else if (SPACE(*s)) { - state = B4EQUAL; - } else { - *d++ = *s; - } - break; - case B4EQUAL: - if (*s == '=') { - state = B4VALUE; - *d++ = *s; - } else if (!SPACE(*s)) { - /* not a valid dn - but what can we do here? */ - *d++ = *s; - } - break; - case B4VALUE: - if (*s == '"') { - state = INQUOTEDVALUE; - *d++ = *s; - } else if (!SPACE(*s)) { - state = INVALUE; - *d++ = *s; - } - break; - case INVALUE: - if (!gotesc && SEPARATOR(*s)) { - while (SPACE(*(d - 1))) - d--; - state = B4TYPE; - if (*s == '+') { - *d++ = *s; - } else { - *d++ = ','; - } - } else if (gotesc && !NEEDSESCAPE(*s) && - !SEPARATOR(*s)) { - *--d = *s; - d++; - } else { - *d++ = *s; - } - break; - case INQUOTEDVALUE: - if (!gotesc && *s == '"') { - state = B4SEPARATOR; - *d++ = *s; - } else if (gotesc && !NEEDSESCAPE(*s)) { - *--d = *s; - d++; - } else { - *d++ = *s; - } - break; - case B4SEPARATOR: - if (SEPARATOR(*s)) { - state = B4TYPE; - if (*s == '+') { - *d++ = *s; - } else { - *d++ = ','; - } - } - break; - default: - break; - } - if (*s == '\\') { - gotesc = 1; - } else { - gotesc = 0; - } - } - *d = '\0'; - - /* Trim trailing spaces */ - d--; - while (d >= dn && *d == ' ') { - *d-- = '\0'; - } - - return (dn); -} 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 diff --git a/lib/ldaputil/dbconf.c b/lib/ldaputil/dbconf.c deleted file mode 100644 index 76654e7..0000000 --- a/lib/ldaputil/dbconf.c +++ /dev/null @@ -1,686 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -/* This was malloc.h - but it's moved to stdlib.h on most platforms, and FBSD is strict */ -/* Make it stdlib.h, and revert to malloc.h with ifdefs if we have issues here. WB 2016 */ -#include -#include - -#include -#include -#include -#include - -#define BIG_LINE 1024 - -static const char *DB_DIRECTIVE = "directory"; -static const int DB_DIRECTIVE_LEN = 9; /* strlen("DB_DIRECTIVE") */ - -static const char *ENCODED = "encoded"; - -static void -insert_dbinfo_propval(DBConfDBInfo_t *db_info, - DBPropVal_t *propval) -{ - if (db_info->lastprop) { - db_info->lastprop->next = propval; - } else { - db_info->firstprop = propval; - } - - db_info->lastprop = propval; -} - -static void -insert_dbconf_dbinfo(DBConfInfo_t *conf_info, - DBConfDBInfo_t *db_info) -{ - if (conf_info->lastdb) { - conf_info->lastdb->next = db_info; - } else { - conf_info->firstdb = db_info; - } - - conf_info->lastdb = db_info; -} - -void -dbconf_free_propval(DBPropVal_t *propval) -{ - if (propval) { - if (propval->prop) - free(propval->prop); - if (propval->val) - free(propval->val); - memset((void *)propval, 0, sizeof(DBPropVal_t)); - free(propval); - } -} - -NSAPI_PUBLIC void -dbconf_free_dbinfo(DBConfDBInfo_t *db_info) -{ - if (db_info) { - DBPropVal_t *next; - DBPropVal_t *cur; - - if (db_info->dbname) - free(db_info->dbname); - if (db_info->url) - free(db_info->url); - - cur = db_info->firstprop; - - while (cur) { - next = cur->next; - dbconf_free_propval(cur); - cur = next; - } - - memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); - free(db_info); - } -} - -NSAPI_PUBLIC void -dbconf_free_confinfo(DBConfInfo_t *conf_info) -{ - DBConfDBInfo_t *next; - DBConfDBInfo_t *cur; - - if (conf_info) { - cur = conf_info->firstdb; - - while (cur) { - next = cur->next; - dbconf_free_dbinfo(cur); - cur = next; - } - - memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); - free(conf_info); - } -} - -static int -skip_blank_lines_and_spaces(FILE *fp, char *buf, char **ptr_out, int *eof) -{ - char *ptr = buf; - char *end; - - while (buf && (*buf || fgets(buf, BIG_LINE, fp))) { - ptr = buf; - - /* skip leading whitespace */ - while (*ptr && isspace(*ptr)) - ++ptr; - - /* skip blank line or comment */ - if (!*ptr || *ptr == '#') { - *buf = 0; /* to force reading of next line */ - continue; - } - - /* Non-blank line found */ - break; - } - - *ptr_out = ptr; - if (!*ptr) { - *eof = 1; - } else { - /* skip trailing whitespace */ - end = ptr + strlen(ptr) - 1; - while (isspace(*end)) - *end-- = 0; - } - - return LDAPU_SUCCESS; -} - -static int -dbconf_parse_propval(char *buf, char *ptr, DBConfDBInfo_t *db_info) -{ - char *dbname = db_info->dbname; - int dbname_len = strlen(dbname); - char *prop; - char *val; - DBPropVal_t *propval; - char *delimeter_chars = " \t"; - char *lastchar; - int end_of_prop; - char *encval = 0; /* encoded value */ - char *origprop = 0; - - if ((ptr - buf + dbname_len > BIG_LINE) || - strncmp(ptr, dbname, dbname_len) || - !(ptr[dbname_len] == ':' || isspace(ptr[dbname_len]))) { - /* Not a prop-val for the current db but not an error */ - return LDAPU_ERR_NOT_PROPVAL; - } - - /* remove the last char if it is newline */ - lastchar = strrchr(buf, '\n'); - if (lastchar) - *lastchar = '\0'; - - prop = ptr + dbname_len + 1; - - while (*prop && (isspace(*prop) || *prop == ':')) - ++prop; - - if (!*prop) { - return LDAPU_ERR_PROP_IS_MISSING; - } - - end_of_prop = strcspn(prop, delimeter_chars); - - if (prop[end_of_prop] != '\0') { - /* buf doesn't end here -- val is present */ - prop[end_of_prop] = '\0'; - val = &prop[end_of_prop + 1]; - - while (*val && isspace(*val)) - ++val; - if (*val == '\0') - val = 0; - } else { - val = 0; - } - - /* - * The prop-val line could be one of the following: - * ":prop val" OR ":encoded prop encval" - * If (prop == "encoded") then the val has "prop encval". - * Get the actual prop from val and get encval (i.e. encoded value) - * and decode it. If it is encoded then the val part must be non-NULL. - */ - if (val && *val && !strcmp(prop, ENCODED)) { - /* val has the actual prop followed by the encoded value */ - origprop = prop; - prop = val; - while (*prop && (isspace(*prop) || *prop == ':')) - ++prop; - - if (!*prop) { - return LDAPU_ERR_PROP_IS_MISSING; - } - - end_of_prop = strcspn(prop, delimeter_chars); - - if (prop[end_of_prop] != '\0') { - /* buf doesn't end here -- encval is present */ - prop[end_of_prop] = '\0'; - encval = &prop[end_of_prop + 1]; - - while (*encval && isspace(*encval)) - ++encval; - if (*encval == '\0') - encval = 0; - } else { - encval = 0; - } - - if (!encval) { - /* special case - if encval is null, "encoded" itself is a - * property and what we have in prop now is the value. */ - val = prop; - prop = origprop; - } else { - /* decode the value */ - val = dbconf_decodeval(encval); - } - } - - /* Success - we have prop & val */ - propval = (DBPropVal_t *)malloc(sizeof(DBPropVal_t)); - - if (!propval) { - if (encval && val) - free(val); - return LDAPU_ERR_OUT_OF_MEMORY; - } - memset((void *)propval, 0, sizeof(DBPropVal_t)); - propval->prop = strdup(prop); - propval->val = val ? strdup(val) : 0; - - if (encval && val) - free(val); /* val was allocated by dbconf_decodeval */ - - if (!propval->prop || (val && !propval->val)) { - dbconf_free_propval(propval); - return LDAPU_ERR_OUT_OF_MEMORY; - } - - insert_dbinfo_propval(db_info, propval); - return LDAPU_SUCCESS; -} - -static int -dbconf_read_propval(FILE *fp, char *buf, DBConfDBInfo_t *db_info, int *eof) -{ - int rv; - char *ptr = buf; - - while (buf && (*buf || fgets(buf, BIG_LINE, fp))) { - ptr = buf; - - rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); - - if (rv != LDAPU_SUCCESS || *eof) - return rv; - - /* We have a non-blank line which could be prop-val pair for the - * dbname in the db_info. parse the prop-val pair and continue. - */ - rv = dbconf_parse_propval(buf, ptr, db_info); - - if (rv == LDAPU_ERR_NOT_PROPVAL) - return LDAPU_SUCCESS; - if (rv != LDAPU_SUCCESS) - return rv; - - *buf = 0; /* to force reading of next line */ - } - - if (!*buf) - *eof = 1; - - return LDAPU_SUCCESS; -} - -static int -parse_directive(char *buf, const char *directive, const int directive_len, DBConfDBInfo_t **db_info_out) -{ - DBConfDBInfo_t *db_info; - char *dbname; - char *url; - int end_of_dbname; - char *delimeter_chars = " \t"; - char *lastchar; - - /* remove the last char if it is newline */ - lastchar = strrchr(buf, '\n'); - if (lastchar) - *lastchar = '\0'; - - if (strncmp(buf, directive, directive_len) || - !isspace(buf[directive_len])) { - return LDAPU_ERR_DIRECTIVE_IS_MISSING; - } - - dbname = buf + directive_len + 1; - - while (*dbname && isspace(*dbname)) - ++dbname; - - if (!*dbname) { - return LDAPU_ERR_DBNAME_IS_MISSING; - } - - end_of_dbname = strcspn(dbname, delimeter_chars); - - if (dbname[end_of_dbname] != '\0') { - /* buf doesn't end here -- url is present */ - dbname[end_of_dbname] = '\0'; - url = &dbname[end_of_dbname + 1]; - - while (*url && isspace(*url)) - ++url; - - if (*url == '\0') - url = 0; - } else { - url = 0; - } - - /* Success - we have dbname & url */ - db_info = (DBConfDBInfo_t *)malloc(sizeof(DBConfDBInfo_t)); - - if (!db_info) - return LDAPU_ERR_OUT_OF_MEMORY; - memset((void *)db_info, 0, sizeof(DBConfDBInfo_t)); - db_info->dbname = strdup(dbname); - db_info->url = url ? strdup(url) : 0; - - if (!db_info->dbname || (url && !db_info->url)) { - dbconf_free_dbinfo(db_info); - return LDAPU_ERR_OUT_OF_MEMORY; - } - - *db_info_out = db_info; - return LDAPU_SUCCESS; -} - -/* Read the next database info from the file and put it in db_info_out. The - * buf may contain first line of the database info. When this function - * finishes, the buf may contain unprocessed information (which should be - * passed to the next call to read_db_info). - */ -static int -read_db_info(FILE *fp, char *buf, DBConfDBInfo_t **db_info_out, const char *directive, const int directive_len, int *eof) -{ - char *ptr; - DBConfDBInfo_t *db_info; - int rv; - - *db_info_out = 0; - - rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof); - - if (rv != LDAPU_SUCCESS || *eof) - return rv; - - /* We possibly have a directive of the form "directory " */ - rv = parse_directive(ptr, directive, directive_len, &db_info); - if (rv != LDAPU_SUCCESS) - return rv; - - /* We have parsed the directive successfully -- lets look for additional - * property-value pairs for the database. - */ - if (!fgets(buf, BIG_LINE, fp)) { - *eof = 1; - rv = LDAPU_SUCCESS; - } else { - rv = dbconf_read_propval(fp, buf, db_info, eof); - } - - if (rv != LDAPU_SUCCESS) { - dbconf_free_dbinfo(db_info); - *db_info_out = 0; - } else { - *db_info_out = db_info; - } - - return rv; -} - -int -dbconf_read_config_file_sub(const char *file, - const char *directive, - const int directive_len, - DBConfInfo_t **conf_info_out) -{ - FILE *fp; - DBConfInfo_t *conf_info; - DBConfDBInfo_t *db_info; - char buf[BIG_LINE]; - int rv; - int eof; - - buf[0] = 0; - - if ((fp = fopen(file, "r")) == NULL) { - return LDAPU_ERR_CANNOT_OPEN_FILE; - } - - /* Allocate DBConfInfo_t */ - conf_info = (DBConfInfo_t *)malloc(sizeof(DBConfInfo_t)); - - if (!conf_info) { - fclose(fp); - return LDAPU_ERR_OUT_OF_MEMORY; - } - - memset((void *)conf_info, 0, sizeof(DBConfInfo_t)); - - /* Read each db info */ - eof = 0; - while (!eof && - ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) { - insert_dbconf_dbinfo(conf_info, db_info); - } - - if (rv != LDAPU_SUCCESS) { - dbconf_free_confinfo(conf_info); - *conf_info_out = 0; - } else { - *conf_info_out = conf_info; - } - - fclose(fp); - return rv; -} - -NSAPI_PUBLIC int -dbconf_read_config_file(const char *file, DBConfInfo_t **conf_info_out) -{ - return dbconf_read_config_file_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, - conf_info_out); -} - -int -dbconf_read_default_dbinfo_sub(const char *file, - const char *directive, - const int directive_len, - DBConfDBInfo_t **db_info_out) -{ - FILE *fp; - DBConfDBInfo_t *db_info; - char buf[BIG_LINE]; - int rv; - int eof; - - buf[0] = 0; - - if ((fp = fopen(file, "r")) == NULL) { - return LDAPU_ERR_CANNOT_OPEN_FILE; - } - - /* Read each db info until eof or dbname == default*/ - eof = 0; - - while (!eof && - ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS)) { - if (!strcmp(db_info->dbname, DBCONF_DEFAULT_DBNAME)) - break; - dbconf_free_dbinfo(db_info); - db_info = NULL; - } - - if (rv != LDAPU_SUCCESS) { - *db_info_out = 0; - } else { - *db_info_out = db_info; - } - - fclose(fp); - return rv; -} - - -NSAPI_PUBLIC int -dbconf_read_default_dbinfo(const char *file, - DBConfDBInfo_t **db_info_out) -{ - return dbconf_read_default_dbinfo_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN, - db_info_out); -} - -/* - * ldapu_strcasecmp - is like strcasecmp on UNIX but also accepts null strings. - */ -int -ldapu_strcasecmp(const char *s1, const char *s2) -{ - - if (!s1) - return !s2 ? 0 : 0 - tolower(*s2); - else if (!s2) - return tolower(*s1); - - return strcasecmp(s1, s2); -} - -NSAPI_PUBLIC int -ldapu_dbinfo_attrval(DBConfDBInfo_t *db_info, - const char *attr, - char **val) -{ - /* Look for given attr in the db_info and return its value */ - int rv = LDAPU_ATTR_NOT_FOUND; - DBPropVal_t *next; - - *val = 0; - - if (db_info) { - next = db_info->firstprop; - while (next) { - rv = ldapu_strcasecmp(attr, next->prop); - if (!rv) { - /* Found the property */ - *val = next->val ? strdup(next->val) : 0; - - if (next->val && !*val) { - rv = LDAPU_ERR_OUT_OF_MEMORY; - } else { - rv = LDAPU_SUCCESS; - } - break; - } - next = next->next; - } - } - - return rv; -} - -void -dbconf_print_propval(DBPropVal_t *propval) -{ - if (propval) { - fprintf(stderr, "\tprop: \"%s\"\tval: \"%s\"\n", propval->prop, - propval->val ? propval->val : ""); - } else { - fprintf(stderr, "Null propval\n"); - } -} - -void -dbconf_print_dbinfo(DBConfDBInfo_t *db_info) -{ - DBPropVal_t *next; - - if (db_info) { - fprintf(stderr, "dbname: \"%s\"\n", db_info->dbname); - fprintf(stderr, "url: \t\"%s\"\n", db_info->url ? db_info->url : ""); - next = db_info->firstprop; - while (next) { - dbconf_print_propval(next); - next = next->next; - } - } else { - fprintf(stderr, "Null db_info\n"); - } -} - -void -dbconf_print_confinfo(DBConfInfo_t *conf_info) -{ - DBConfDBInfo_t *next; - - if (conf_info) { - next = conf_info->firstdb; - while (next) { - dbconf_print_dbinfo(next); - next = next->next; - } - } else { - fprintf(stderr, "Null conf_info\n"); - } -} - - -NSAPI_PUBLIC int -dbconf_output_db_directive(FILE *fp, const char *dbname, const char *url) -{ - fprintf(fp, "%s %s %s\n", DB_DIRECTIVE, dbname, url); - return LDAPU_SUCCESS; -} - -NSAPI_PUBLIC int -dbconf_output_propval(FILE *fp, const char *dbname, const char *prop, const char *val, const int encoded) -{ - if (encoded && val && *val) { - char *new_val = dbconf_encodeval(val); - - if (!new_val) - return LDAPU_ERR_OUT_OF_MEMORY; - fprintf(fp, "%s:%s %s %s\n", dbname, ENCODED, - prop, new_val); - free(new_val); - } else { - fprintf(fp, "%s:%s %s\n", dbname, prop, val ? val : ""); - } - - return LDAPU_SUCCESS; -} - - -NSAPI_PUBLIC int -dbconf_get_dbnames(const char *dbmap, char ***dbnames_out, int *cnt_out) -{ - DBConfInfo_t *conf_info = 0; - DBConfDBInfo_t *db = 0; - int cnt = 0; - char **dbnames = 0; - char *heap = 0; - int rv; - - *dbnames_out = 0; - *cnt_out = 0; - - rv = dbconf_read_config_file(dbmap, &conf_info); - - if (rv != LDAPU_SUCCESS) - return rv; - - db = conf_info->firstdb; - - - dbnames = (char **)malloc(32 * 1024); - heap = (char *)dbnames + 2 * 1024; - - if (!dbnames) { - dbconf_free_confinfo(conf_info); - return LDAPU_ERR_OUT_OF_MEMORY; - } - - *dbnames_out = dbnames; - - while (db) { - *dbnames++ = heap; - strcpy(heap, db->dbname); - heap += strlen(db->dbname) + 1; - db = db->next; - cnt++; - } - - *dbnames = NULL; - *cnt_out = cnt; - dbconf_free_confinfo(conf_info); - - return LDAPU_SUCCESS; -} - -NSAPI_PUBLIC int -dbconf_free_dbnames(char **dbnames) -{ - if (dbnames) - free(dbnames); - - return LDAPU_SUCCESS; -} diff --git a/lib/ldaputil/encode.c b/lib/ldaputil/encode.c deleted file mode 100644 index 3219c8d..0000000 --- a/lib/ldaputil/encode.c +++ /dev/null @@ -1,151 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 - - -/* This was malloc.h - but it's moved to stdlib.h on most platforms, and FBSD is strict */ -/* Make it stdlib.h, and revert to malloc.h with ifdefs if we have issues here. WB 2016 */ -#include -#include -#include -#include - -/* The magic set of 64 chars in the uuencoded data */ -static unsigned char uuset[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/'}; - -static int -do_uuencode(unsigned char *src, unsigned char *dst, int srclen) -{ - int i, r; - unsigned char *p; - - /* To uuencode, we snip 8 bits from 3 bytes and store them as -6 bits in 4 bytes. 6*4 == 8*3 (get it?) and 6 bits per byte -yields nice clean bytes - -It goes like this: - AAAAAAAA BBBBBBBB CCCCCCCC -turns into the standard set of uuencode ascii chars indexed by numbers: - 00AAAAAA 00AABBBB 00BBBBCC 00CCCCCC - -Snip-n-shift, snip-n-shift, etc.... - -*/ - - for (p = dst, i = 0; i < srclen; i += 3) { - /* Do 3 bytes of src */ - register char b0, b1, b2; - - b0 = src[0]; - if (i == srclen - 1) - b1 = b2 = '\0'; - else if (i == srclen - 2) { - b1 = src[1]; - b2 = '\0'; - } else { - b1 = src[1]; - b2 = src[2]; - } - - *p++ = uuset[b0 >> 2]; - *p++ = uuset[(((b0 & 0x03) << 4) | ((b1 & 0xf0) >> 4))]; - *p++ = uuset[(((b1 & 0x0f) << 2) | ((b2 & 0xc0) >> 6))]; - *p++ = uuset[b2 & 0x3f]; - src += 3; - } - *p = 0; /* terminate the string */ - r = (unsigned char *)p - (unsigned char *)dst; /* remember how many we did */ - - /* Always do 4-for-3, but if not round threesome, have to go - clean up the last extra bytes */ - - for (; i != srclen; i--) - *--p = '='; - - return r; -} - -const unsigned char pr2six[256] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}; - -static char * -_uudecode(const char *bufcoded) -{ - register const char *bufin = bufcoded; - register unsigned char *bufout; - register int nprbytes; - unsigned char *bufplain; - int nbytesdecoded; - - /* Find the length */ - while (pr2six[(int)*(bufin++)] <= 63) - ; - nprbytes = bufin - bufcoded - 1; - nbytesdecoded = ((nprbytes + 3) / 4) * 3; - - bufout = (unsigned char *)malloc(nbytesdecoded + 1); - bufplain = bufout; - - bufin = bufcoded; - - while (nprbytes > 0) { - *(bufout++) = (unsigned char)(pr2six[(int)(*bufin)] << 2 | pr2six[(int)bufin[1]] >> 4); - *(bufout++) = (unsigned char)(pr2six[(int)bufin[1]] << 4 | pr2six[(int)bufin[2]] >> 2); - *(bufout++) = (unsigned char)(pr2six[(int)bufin[2]] << 6 | pr2six[(int)bufin[3]]); - bufin += 4; - nprbytes -= 4; - } - - if (nprbytes & 03) { - if (pr2six[(int)bufin[-2]] > 63) - nbytesdecoded -= 2; - else - nbytesdecoded -= 1; - } - bufplain[nbytesdecoded] = '\0'; - - return (char *)bufplain; -} - - -char * -dbconf_encodeval(const char *val) -{ - int len = strlen(val); - char *dst = (char *)malloc(2 * len); - - if (dst) { - do_uuencode((unsigned char *)val, (unsigned char *)dst, len); - } - - return dst; -} - -char * -dbconf_decodeval(const char *val) -{ - return _uudecode(val); -} diff --git a/lib/ldaputil/errors.c b/lib/ldaputil/errors.c deleted file mode 100644 index ada1d31..0000000 --- a/lib/ldaputil/errors.c +++ /dev/null @@ -1,210 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -#include - -NSAPI_PUBLIC char * -ldapu_err2string(int err) -{ - char *rv; - - switch (err) { - - /* Error codes defined in certmap.h */ - case LDAPU_SUCCESS: - rv = "success"; - break; - case LDAPU_FAILED: - rv = "ldap search didn't find an ldap entry"; - break; - case LDAPU_CERT_MAP_FUNCTION_FAILED: - rv = "Cert mapping function failed"; - break; - case LDAPU_CERT_SEARCH_FUNCTION_FAILED: - rv = "Cert search function failed"; - break; - case LDAPU_CERT_VERIFY_FUNCTION_FAILED: - rv = "Cert verify function failed"; - break; - case LDAPU_CERT_MAP_INITFN_FAILED: - rv = "Certmap InitFn function failed"; - break; - - - /* Error codes returned by ldapdb.c */ - case LDAPU_ERR_URL_INVALID_PREFIX: - rv = "invalid local ldap database url prefix -- must be ldapdb://"; - break; - case LDAPU_ERR_URL_NO_BASEDN: - rv = "base dn is missing in ldapdb url"; - break; - case LDAPU_ERR_OUT_OF_MEMORY: - rv = "out of memory"; - break; - case LDAPU_ERR_LDAP_INIT_FAILED: - rv = "Couldn't initialize connection to the ldap directory server"; - break; - case LDAPU_ERR_LCACHE_INIT_FAILED: - rv = "Couldn't initialize connection to the local ldap directory"; - break; - case LDAPU_ERR_LDAP_SET_OPTION_FAILED: - rv = "ldap_set_option failed for local ldap database"; - break; - case LDAPU_ERR_NO_DEFAULT_CERTDB: - rv = "default cert database not initialized when using LDAP over SSL"; - break; - - - /* Errors returned by ldapauth.c */ - case LDAPU_ERR_CIRCULAR_GROUPS: - rv = "Circular groups were detected during group membership check"; - break; - case LDAPU_ERR_INVALID_STRING: - rv = "Invalid string"; - break; - case LDAPU_ERR_INVALID_STRING_INDEX: - rv = "Invalid string index"; - break; - case LDAPU_ERR_MISSING_ATTR_VAL: - rv = "Missing attribute value from the search result"; - break; - - - /* Errors returned by dbconf.c */ - case LDAPU_ERR_CANNOT_OPEN_FILE: - rv = "cannot open the config file"; - break; - case LDAPU_ERR_DBNAME_IS_MISSING: - rv = "database name is missing"; - break; - case LDAPU_ERR_PROP_IS_MISSING: - rv = "database property is missing"; - break; - case LDAPU_ERR_DIRECTIVE_IS_MISSING: - rv = "illegal directive in the config file"; - break; - case LDAPU_ERR_NOT_PROPVAL: - rv = "internal error - LDAPU_ERR_NOT_PROPVAL"; - break; - - - /* Error codes returned by certmap.c */ - case LDAPU_ERR_NO_ISSUERDN_IN_CERT: - rv = "cannot extract issuer DN from the cert"; - break; - case LDAPU_ERR_NO_ISSUERDN_IN_CONFIG_FILE: - rv = "issuer DN missing for non-default certmap"; - break; - case LDAPU_ERR_CERTMAP_INFO_MISSING: - rv = "cert to ldap entry mapping information is missing"; - break; - case LDAPU_ERR_MALFORMED_SUBJECT_DN: - rv = "Found malformed subject DN in the certificate"; - break; - case LDAPU_ERR_MAPPED_ENTRY_NOT_FOUND: - rv = "Certificate couldn't be mapped to an ldap entry"; - break; - case LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN: - rv = "Unable to load certmap plugin library"; - break; - case LDAPU_ERR_MISSING_INIT_FN_IN_CONFIG: - rv = "InitFn must be provided when using certmap plugin library"; - break; - case LDAPU_ERR_MISSING_INIT_FN_IN_LIB: - rv = "Could not find InitFn in the certmap plugin library"; - break; - case LDAPU_ERR_CERT_VERIFY_FAILED: - rv = "Could not matching certificate in User's LDAP entry"; - break; - case LDAPU_ERR_CERT_VERIFY_NO_CERTS: - rv = "User's LDAP entry doesn't have any certificates to compare"; - break; - case LDAPU_ERR_MISSING_LIBNAME: - rv = "Library name is missing in the config file"; - break; - case LDAPU_ERR_MISSING_INIT_FN_NAME: - rv = "Init function name is missing in the config file"; - break; - case LDAPU_ERR_WRONG_ARGS: - rv = "ldaputil API function called with wrong arguments"; - break; - case LDAPU_ERR_RENAME_FILE_FAILED: - rv = "Renaming of file failed"; - break; - case LDAPU_ERR_MISSING_VERIFYCERT_VAL: - rv = "VerifyCert property value must be on or off"; - break; - case LDAPU_ERR_CANAME_IS_MISSING: - rv = "Cert issuer name is missing"; - break; - case LDAPU_ERR_CAPROP_IS_MISSING: - rv = "property name is missing"; - break; - case LDAPU_ERR_UNKNOWN_CERT_ATTR: - rv = "unknown cert attribute"; - break; - - - case LDAPU_ERR_EMPTY_LDAP_RESULT: - rv = "ldap search returned empty result"; - break; - case LDAPU_ERR_MULTIPLE_MATCHES: - rv = "ldap search returned multiple matches when one expected"; - break; - case LDAPU_ERR_MISSING_RES_ENTRY: - rv = "Could not extract entry from the ldap search result"; - break; - case LDAPU_ERR_MISSING_UID_ATTR: - rv = "ldap entry is missing the 'uid' attribute value"; - break; - case LDAPU_ERR_INVALID_ARGUMENT: - rv = "invalid argument passed to the certmap API function"; - break; - case LDAPU_ERR_INVALID_SUFFIX: - rv = "invalid LDAP directory suffix"; - break; - - - /* Error codes returned by cert.c */ - case LDAPU_ERR_EXTRACT_SUBJECTDN_FAILED: - rv = "Couldn't extract the subject DN from the certificate"; - break; - case LDAPU_ERR_EXTRACT_ISSUERDN_FAILED: - rv = "Couldn't extract the issuer DN from the certificate"; - break; - case LDAPU_ERR_EXTRACT_DERCERT_FAILED: - rv = "Couldn't extract the original DER encoding from the certificate"; - break; - - - case LDAPU_ERR_NOT_IMPLEMENTED: - rv = "function not implemented yet"; - break; - case LDAPU_ERR_INTERNAL: - rv = "ldaputil internal error"; - break; - - default: - if (err > 0) { - /* LDAP errors are +ve */ - rv = ldap_err2string(err); - } else { - rv = "internal error - unknown error code"; - } - break; - } - - return rv; -} diff --git a/lib/ldaputil/examples/Makefile b/lib/ldaputil/examples/Makefile deleted file mode 100644 index d7aa9d6..0000000 --- a/lib/ldaputil/examples/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# -# BEGIN COPYRIGHT BLOCK -# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. -# Copyright (C) 2005 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# END COPYRIGHT BLOCK -# -# -# Makefile for certmap example program. -# - -# -# Please set the ARCH variable to one of the following: -# SOLARIS, IRIX, HPUX -# -ARCH = - - -# -# Please set the SROOT to be same as your server root -# -SROOT = - -# -# Uncomment the following if you need the debug build -# -#COMMON_DEFS = -g - -ifndef ARCH -arch: - @echo "Please edit the Makefile and set the variable: ARCH" - @exit 1 -endif - -ifndef SROOT -sroot: - @echo "Please edit the Makefile and set the server root variable: SROOT" - @exit 1 -endif - -ifeq ($(ARCH), SOLARIS) -CC_CMD = cc -DSOLARIS -D_REENTRANT -LD_SHAREDCMD = ld -G -endif - -ifeq ($(ARCH), HPUX) - BIN = certmap.sl -else - BIN = certmap.so -endif - -OBJS = init.o plugin.o - -INCLUDE_FLAGS=-I. -I$(SROOT)/include - -INC_FILES = \ - $(SROOT)/include/certmap.h \ - $(SROOT)/include/ldap.h \ - $(SROOT)/include/lber.h - -all: $(BIN) - -$(INC_FILES): - @echo - @echo "To extend the Certificate to LDAP entry mapping by" - @echo "writing your own functions, you need to download the" - @echo "Certmap API (version 1.0) and LDAP SDK (version 1.0)." - @echo "Please download these from http://???" - @echo "Make sure the following files exist:" - @echo "\t$(SROOT)/include/certmap.h" - @echo "\t$(SROOT)/include/ldap.h" - @echo "\t$(SROOT)/include/lber.h" - @echo - @exit 1 - -$(BIN): $(INC_FILES) $(OBJS) - $(LD_SHAREDCMD) $(OBJS) -o $@ $(EXTRA_LDDEFINES) - -certmap.dll: $(OBJS) - $(LD_SHAREDCMD) $(OBJS) -o $@ $(EXTRA_LDDEFINES) - -.c.o: - $(CC_CMD) $(COMMON_DEFS) $(INCLUDE_FLAGS) -c $< - -clean: - rm -f $(OBJS) certmap.so $(EXTRA_CLEAN) diff --git a/lib/ldaputil/examples/README b/lib/ldaputil/examples/README deleted file mode 100644 index 3ea7a74..0000000 --- a/lib/ldaputil/examples/README +++ /dev/null @@ -1,100 +0,0 @@ -# BEGIN COPYRIGHT BLOCK -# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. -# Copyright (C) 2005 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# END COPYRIGHT BLOCK -# - -This directory contains an example program to demonstrate -writing plugins using the "Certificate to LDAP Mapping" API. -Please read the "Managing Servers" manual to find out -about how certificate to ldap mapping can be configured using -the /userdb/certmap.conf file. Also refer to the -"Certificate to LDAP Mapping API" documentation to find out -about the various API functions and how you can write your -plugin. - -This example demonstrate use of most of the API functions. It -defines a mapping function, a search function, and a verify -function. Read the API doc to learn about these functions. -The init.c file also contains an init function which sets the -mapping, search and verify functions. - -The Mapping Function --------------------- - -The mapping function extracts the attributes "CN", "E", "O" and -"C" from the certificate's subject DN using the function -ldapu_get_cert_ava_val. If the attributes "C" doesn't exists -then it defaults to "US". It then gets the value of a custom -certmap.conf property "defaultOU" using the function -ldapu_certmap_info_attrval. This demonstrates how you can have -your own custom properties defined in the certmap.conf file. -The mapping function then returns an ldapdn of the form: -"cn=, ou=, o=, c=". - -If the "E" attribute has a value, it returns a filter -"mail=". Finally, the mapping function frees the structures -returned by some of the API functions it called. - - -The Search Function -------------------- - -The search function calls a dummy function to get the -certificate's serial number. It then does a subtree search in -the entire directory for the filter -"certSerialNumber=". If this fails, it calls the -default search function. This demonstrates how you can use the -default functions in your custom functions. - -The Verify Function -------------------- - -The verify function returns LDAPU_SUCCESS if only one entry was -returned by the search function. Otherwise, it returns -LDAPU_CERT_VERIFY_FUNCTION_FAILED. - - -Error Reporting ---------------- - -To report errors/warning, there is a function defined called -plugin_ereport. This function demonstrates how to get the -subject DN and the issuer DN from the certificate. - -Build Procedure ---------------- -On UNIX: Edit the Makefile, and set the variables ARCH & SROOT -according to the comments in the Makefile. Download LDAP C SDK -from the mozilla.org site and make the ldap include -files available in /include. Copy the -../include/certmap.h file to the /include directory. -Use 'gmake' to build the plugin. A shared library plugin.so -(plugin.sl on HP) will be created in the current directory. - -On NT: Execute the following command: -NMAKE /f "Certmap.mak" CFG="Certmap - Win32 Debug" -Certmap.dll will be created in the Debug subdirectory. - -Certmap.conf Configuration --------------------------- -Save a copy of certmap.conf file. -Change the certmap.conf file as follows: - -certmap default default -default:defaultOU marketing -default:library -default:InitFn plugin_init_fn - - -After experimenting with this example, restore the old copy of -certmap.conf file. Or else, set the certmap.conf file as follows: - -certmap default default -default:DNComps -default:FilterComps e, mail, uid -default:VerifyCert on diff --git a/lib/ldaputil/examples/init.c b/lib/ldaputil/examples/init.c deleted file mode 100644 index 74db977..0000000 --- a/lib/ldaputil/examples/init.c +++ /dev/null @@ -1,43 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -#include -#include -#include "certmap.h" /* Public Certmap API */ -#include "plugin.h" /* must define extern "C" functions */ - - -NSAPI_PUBLIC int -plugin_init_fn(void *certmap_info, const char *issuerName, const char *issuerDN, const char *libname) -{ - static int initialized = 0; - int rv; - - /* Make sure CertmapDLLInit is initialized only once */ - if (!initialized) { - - initialized = 1; - } - - fprintf(stderr, "plugin_init_fn called.\n"); - ldapu_set_cert_mapfn(issuerDN, plugin_mapping_fn); - ldapu_set_cert_verifyfn(issuerDN, plugin_verify_fn); - - if (!default_searchfn) - default_searchfn = ldapu_get_cert_searchfn(issuerDN); - - ldapu_set_cert_searchfn(issuerDN, plugin_search_fn); - return LDAPU_SUCCESS; -} diff --git a/lib/ldaputil/examples/plugin.c b/lib/ldaputil/examples/plugin.c deleted file mode 100644 index baacb70..0000000 --- a/lib/ldaputil/examples/plugin.c +++ /dev/null @@ -1,240 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -#include -#include -#include "certmap.h" /* Public Certmap API */ -#include "plugin.h" /* must define extern "C" functions */ - - -CertSearchFn_t default_searchfn = 0; - - -/* plugin_ereport - - This function prints an error message to stderr. It prints the issuerDN - and subjectDN alongwith the given message. - */ -static void -plugin_ereport(const char *msg, void *cert) -{ - int rv; - char *subjectDN; - char *issuerDN; - char *default_subjectDN = "Failed to get the subject DN"; - char *default_issuerDN = "Failed to get the issuer DN"; - - rv = ldapu_get_cert_subject_dn(cert, &subjectDN); - - if (rv != LDAPU_SUCCESS || !subjectDN) { - subjectDN = default_subjectDN; - } - - rv = ldapu_get_cert_issuer_dn(cert, &issuerDN); - - if (rv != LDAPU_SUCCESS || !issuerDN) { - issuerDN = default_issuerDN; - } - - fprintf(stderr, "%s. Issuer: %s, Subject: %s\n", msg, issuerDN, - subjectDN); - - if (default_subjectDN != subjectDN) - ldapu_free(subjectDN); - if (default_issuerDN != issuerDN) - ldapu_free(issuerDN); -} - - -/* plugin_mapping_fn - - This mapping function extracts "CN", "O" and "C" attributes from the - subject DN to form ldapDN. It inserts "ou=" between the - "CN" and the "O" attr-value pair. The can be configured in - the certmap.conf config file. - If the "C" attr is absent, it defaults to "US". - It extracts the "E" attribute to form the filter. - */ -int -plugin_mapping_fn(void *cert, LDAP *ld, void *certmap_info, char **ldapDN, char **filter) -{ - char **cn_val; /* get this from the cert */ - char **o_val; /* get this from the cert */ - char **c_val; /* get this from the cert */ - char **e_val; /* get this from the cert */ - char *ou_val; /* get this from the config file */ - int len; - int rv; - - fprintf(stderr, "plugin_mapping_fn called.\n"); - - rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "CN", &cn_val); - - if (rv != LDAPU_SUCCESS || !cn_val) { - plugin_ereport("plugin_mapping_fn: Failed to extract \"CN\" from the cert", cert); - return LDAPU_CERT_MAP_FUNCTION_FAILED; - } - - rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "O", &o_val); - - if (rv != LDAPU_SUCCESS || !o_val) { - plugin_ereport("plugin_mapping_fn: Failed to extract \"O\" from the cert", cert); - return LDAPU_CERT_MAP_FUNCTION_FAILED; - } - - rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "C", &c_val); - - if (rv != LDAPU_SUCCESS || !c_val) { - plugin_ereport("plugin_mapping_fn: Failed to extract \"C\" from the cert", cert); - } - - rv = ldapu_get_cert_ava_val(cert, LDAPU_SUBJECT_DN, "E", &e_val); - - if (rv != LDAPU_SUCCESS || !e_val) { - /* Don't return error -- just print the warning */ - plugin_ereport("plugin_mapping_fn: Failed to extract \"E\" from the cert", cert); - } - - /* Get the "OU" from the "defaultOU" property from the config file */ - rv = ldapu_certmap_info_attrval(certmap_info, "defaultOU", &ou_val); - - if (rv != LDAPU_SUCCESS || !ou_val) { - plugin_ereport("plugin_mapping_fn: Failed to get \"defaultOU\" from the configuration", cert); - return LDAPU_CERT_MAP_FUNCTION_FAILED; - } - - len = strlen("cn=, ou=, o=, c=") + strlen(cn_val[0]) + strlen(ou_val) + - strlen(o_val[0]) + (c_val ? strlen(c_val[0]) : strlen("US")) + 1; - *ldapDN = (char *)ldapu_malloc(len); - - if (!*ldapDN) { - plugin_ereport("plugin_mapping_fn: Ran out of memory", cert); - return LDAPU_CERT_MAP_FUNCTION_FAILED; - } - - if (e_val) { - len = strlen("mail=") + strlen(e_val[0]) + 1; - *filter = (char *)ldapu_malloc(len); - - if (!*filter) { - free(*ldapDN); - plugin_ereport("plugin_mapping_fn: Ran out of memory", cert); - return LDAPU_CERT_MAP_FUNCTION_FAILED; - } - sprintf(*filter, "mail=%s", e_val[0]); - } else { - *filter = 0; - } - - sprintf(*ldapDN, "cn=%s, ou=%s, o=%s, c=%s", cn_val[0], ou_val, - o_val[0], c_val ? c_val[0] : "US"); - - ldapu_free_cert_ava_val(cn_val); - ldapu_free_cert_ava_val(o_val); - ldapu_free_cert_ava_val(c_val); - ldapu_free_cert_ava_val(e_val); - ldapu_free(ou_val); - - fprintf(stderr, "plugin_mapping_fn Returned:\n\tldapDN: \"%s\"\n\tfilter: \"%s\"\n", - *ldapDN, *filter ? *filter : ""); - - return LDAPU_SUCCESS; -} - - -int -plugin_cert_serial_number(void *cert) -{ - /* Just a stub function. You can get the DER encoded cert by using the - function ldapu_get_cert_der: - */ - unsigned char *derCert; - unsigned int len; - int rv; - int sno; - - rv = ldapu_get_cert_der(cert, &derCert, &len); - - /* extract the serial number from derCert */ - sno = 43534754; /* a fake value for now */ - - ldapu_free((char *)derCert); - - return sno; -} - -/* plugin_search_fn - - This function first does a search based on the cert's serial number. - If that fails, it calls the default search function. - */ -int -plugin_search_fn(void *cert, LDAP *ld, void *certmap_info, const char *suffix, const char *ldapdn, const char *filter, const char **attrs, LDAPMessage **res) -{ - int rv; - char snoFilter[256]; - - fprintf(stderr, "plugin_search_fn called.\n"); - sprintf(snoFilter, "certSerialNumber=%d", - plugin_cert_serial_number(cert)); - - /* Search the entire LDAP tree for "certSerialNumber=" */ - rv = ldap_search_s(ld, suffix, LDAP_SCOPE_SUBTREE, snoFilter, - (char **)attrs, 0, res); - - /* ldap_search_s returns LDAP_SUCCESS (rather than LDAPU_SUCCESS) - if there is no error but there may not be any matching entries. - */ - if (rv == LDAP_SUCCESS) { - /* There was no error but check if any entries matched */ - int numEntries = ldap_count_entries(ld, *res); - - if (numEntries > 0) { - /* at least one entry matched */ - /* change the return value to LDAPU_SUCCESS from LDAP_SUCCESS */ - rv = LDAPU_SUCCESS; - } else { - /* Try the default search function */ - rv = (*default_searchfn)(cert, ld, certmap_info, suffix, ldapdn, - filter, attrs, res); - } - } - - /* It's ok to return the error code from ldap_search_s */ - return rv; -} - -/* - plugin_verify_fn - - This function returns success if only one entry exists in 'res'. - */ -int -plugin_verify_fn(void *cert, LDAP *ld, void *certmap_info, LDAPMessage *res, LDAPMessage **entry) -{ - int rv; - int numEntries; - - fprintf(stderr, "plugin_verify_fn called.\n"); - numEntries = ldap_count_entries(ld, res); - - if (numEntries == 1) { - *entry = ldap_first_entry(ld, res); - rv = LDAPU_SUCCESS; - } else { - plugin_ereport("plugin_verify_fn: Failing because multiple entries matched.", - cert); - *entry = 0; - rv = LDAPU_CERT_VERIFY_FUNCTION_FAILED; - } - - return rv; -} diff --git a/lib/ldaputil/examples/plugin.h b/lib/ldaputil/examples/plugin.h deleted file mode 100644 index e7e5282..0000000 --- a/lib/ldaputil/examples/plugin.h +++ /dev/null @@ -1,35 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 - -#ifndef _CERTMAP_PLUGIN_H -#define _CERTMAP_PLUGIN_H - -extern CertSearchFn_t default_searchfn; - -#ifdef __cplusplus -extern "C" { -#endif - -extern int plugin_mapping_fn(void *cert, LDAP *ld, void *certmap_info, char **ldapDN, char **filter); - -extern int plugin_search_fn(void *cert, LDAP *ld, void *certmap_info, const char *basedn, const char *dn, const char *filter, const char **attrs, LDAPMessage **res); - -extern int plugin_verify_fn(void *cert, LDAP *ld, void *certmap_info, LDAPMessage *res, LDAPMessage **entry); - -NSAPI_PUBLIC int plugin_init_fn(void *certmap_info, const char *issuerName, const char *issuerDN, const char *dllname); - -#ifdef __cplusplus -} -#endif - -#endif /* _CERTMAP_PLUGIN_H */ diff --git a/lib/ldaputil/init.c b/lib/ldaputil/init.c deleted file mode 100644 index c7d59d1..0000000 --- a/lib/ldaputil/init.c +++ /dev/null @@ -1,138 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 -#include -#include -#include - -/*#include "base/file.h"*/ -#include "ldaputil/certmap.h" -/*#include "ldaputil/ldapdb.h"*/ -#include "ldaputil/ldaputil.h" -#include "ldaputil/cert.h" -#include "ldaputil/errors.h" -#include "ldaputil/init.h" - -#include "slapi-plugin.h" - -#ifndef FILE_PATHSEP -#define FILE_PATHSEP '/' -#endif -#ifdef HPUX -#ifdef __ia64 -#define DLL_SUFFIX ".so" -#else -#define DLL_SUFFIX ".sl" -#endif -#else -#define DLL_SUFFIX ".so" -#endif - -static int -load_server_libs(const char *dir) -{ - int rv = LDAPU_SUCCESS; - PRDir *ds; - int suffix_len = strlen(DLL_SUFFIX); - - if ((ds = PR_OpenDir(dir)) != NULL) { - PRDirEntry *d; - - /* Dir exists */ - while ((d = PR_ReadDir(ds, PR_SKIP_BOTH))) { - PRLibrary *lib = 0; - const char *libname = d->name; - int len = strlen(libname); - int is_lib; - - is_lib = (len > suffix_len && !strcmp(libname + len - suffix_len, DLL_SUFFIX)); - - if (is_lib) { - char path[1024]; - - PR_snprintf(path, sizeof(path), "%s%c%s", dir, FILE_PATHSEP, libname); - lib = PR_LoadLibrary(path); - if (!lib) - rv = LDAPU_ERR_UNABLE_TO_LOAD_PLUGIN; - } - } - } else { - /* It's ok if dir doesn't exists */ - } - - return rv; -} - -NSAPI_PUBLIC int -ldaputil_init(const char *config_file, - const char *dllname, - const char *serv_root, - const char *serv_type, - const char *serv_id) -{ - int rv = LDAPU_SUCCESS; - static int initialized = 0; - - /* If already initialized, cleanup the old structures */ - if (initialized) - ldaputil_exit(); - - if (config_file && *config_file) { - char dir[1024]; - - LDAPUCertMapListInfo_t *certmap_list; - LDAPUCertMapInfo_t *certmap_default; - - if (serv_root && *serv_root) { - /* Load common libraries */ - PR_snprintf(dir, sizeof(dir), "%s%clib%c%s", serv_root, FILE_PATHSEP, - FILE_PATHSEP, "common"); - rv = load_server_libs(dir); - - if (rv != LDAPU_SUCCESS) - return rv; - - if (serv_type && *serv_type) { - /* Load server type specific libraries */ - sprintf(dir, "%s%clib%c%s", serv_root, FILE_PATHSEP, - FILE_PATHSEP, serv_type); - rv = load_server_libs(dir); - - if (rv != LDAPU_SUCCESS) - return rv; - - if (serv_id && *serv_id) { - /* Load server instance specific libraries */ - sprintf(dir, "%s%clib%c%s", serv_root, FILE_PATHSEP, - FILE_PATHSEP, serv_id); - rv = load_server_libs(dir); - - if (rv != LDAPU_SUCCESS) - return rv; - } - } - } - - rv = ldapu_certmap_init(config_file, dllname, &certmap_list, - &certmap_default); - } - - initialized = 1; - - if (rv != LDAPU_SUCCESS) - return rv; - - return rv; -} diff --git a/lib/ldaputil/ldapauth.c b/lib/ldaputil/ldapauth.c deleted file mode 100644 index 3420519..0000000 --- a/lib/ldaputil/ldapauth.c +++ /dev/null @@ -1,214 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 - -/* - * ldapauth.cpp: Implements LDAP integration in the web server. - * - * Nitin More, John Kristian - */ - -/* #define DBG_PRINT */ - -#include /* for BUFSIZ */ -#include /* for strncpy, strcat */ -#include -#include - -#define DEFINE_LDAPU_STRINGS 1 -#include -#include -#include - -#include - -#include "slapi-plugin.h" - -/* - * ldapu_find - * Description: - * Caller should free res if it is not NULL. - * Arguments: - * ld Pointer to LDAP (assumes connection has been - * established and the client has called the - * appropriate bind routine) - * base basedn (where to start the search) - * scope scope for the search. One of - * LDAP_SCOPE_SUBTREE, LDAP_SCOPE_ONELEVEL, and - * LDAP_SCOPE_BASE - * filter LDAP filter - * attrs A NULL-terminated array of strings indicating which - * attributes to return for each matching entry. Passing - * NULL for this parameter causes all available - * attributes to be retrieved. - * attrsonly A boolean value that should be zero if both attribute - * types and values are to be returned, non-zero if only - * types are wanted. - * res A result parameter which will contain the results of - * the search upon completion of the call. - * Return Values: - * LDAPU_SUCCESS if entry is found - * LDAPU_FAILED if entry is not found - * if error, where can be passed to - * ldap_err2string to get an error string. - */ -int -ldapu_find(LDAP *ld, const char *base, int scope, const char *filter, const char **attrs, int attrsonly, LDAPMessage **res) -{ - int retval; -#ifdef USE_THIS_CODE /* ASYNCHRONOUS */ - int msgid; -#endif - int numEntries; - - *res = 0; - - /* If base is NULL set it to null string */ - if (!base) { - DBG_PRINT1("ldapu_find: basedn is missing -- assuming null string\n"); - base = ""; - } - - if (!filter || !*filter) { - DBG_PRINT1("ldapu_find: filter is missing -- assuming objectclass=*\n"); - filter = ldapu_strings[LDAPU_STR_FILTER_DEFAULT]; - } - - DBG_PRINT2("\tbase:\t\"%s\"\n", base); - DBG_PRINT2("\tfilter:\t\"%s\"\n", filter ? filter : ""); - DBG_PRINT2("\tscope:\t\"%s\"\n", - (scope == LDAP_SCOPE_SUBTREE ? "LDAP_SCOPE_SUBTREE" - : (scope == LDAP_SCOPE_ONELEVEL ? "LDAP_SCOPE_ONELEVEL" - : "LDAP_SCOPE_BASE"))); - - retval = ldapu_search_s(ld, base, scope, filter, (char **)attrs, - attrsonly, res); - - if (retval != LDAP_SUCCESS) { - /* retval = ldap_result2error(ld, *res, 0); */ - DBG_PRINT2("ldapu_search_s: %s\n", ldapu_err2string(retval)); - return (retval); - } - - numEntries = ldapu_count_entries(ld, *res); - - if (numEntries == 1) { - /* success */ - return LDAPU_SUCCESS; - } else if (numEntries == 0) { - /* not found -- but not an error */ - DBG_PRINT1("ldapu_search_s: Entry not found\n"); - return LDAPU_FAILED; - } else if (numEntries > 0) { - /* Found more than one entry! */ - DBG_PRINT1("ldapu_search_s: Found more than one entry\n"); - return LDAPU_ERR_MULTIPLE_MATCHES; - } else { - /* should never get here */ - DBG_PRINT1("ldapu_search_s: should never reach here\n"); - ldapu_msgfree(ld, *res); - return LDAP_OPERATIONS_ERROR; - } -} - - -/* Search function for the cases where base = "" = NULL suffix, that is, search to - * be performed on the entire DIT tree. - * We actually do various searches taking a naming context at a time as the base for - * the search. */ - -int -ldapu_find_entire_tree(LDAP *ld, int scope, const char *filter, const char **attrs, int attrsonly, LDAPMessage ***res) -{ - int retval = LDAPU_FAILED; - int rv, i, num_namingcontexts; - LDAPMessage *result_entry, *result = NULL; - const char *suffix_attr[2] = {"namingcontexts", NULL}; - /* these are private suffixes that may contain pseudo users - e.g. replication manager that may have certs */ - int num_private_suffix = 1; - const char *private_suffix_list[2] = {"cn=config", NULL}; - char **suffix_list, **suffix = NULL; - - rv = ldapu_find(ld, "", LDAP_SCOPE_BASE, "objectclass=*", suffix_attr, 0, &result); - if (rv != LDAP_SUCCESS) { - if (result) - ldapu_msgfree(ld, result); - return rv; - } - - result_entry = ldapu_first_entry(ld, result); - suffix = ldapu_get_values(ld, result_entry, suffix_attr[0]); - suffix_list = suffix; - num_namingcontexts = slapi_ldap_count_values(suffix); - /* add private suffixes to our list of suffixes to search */ - if (num_private_suffix) { - suffix_list = ldapu_realloc(suffix_list, - sizeof(char *) * (num_namingcontexts + num_private_suffix + 1)); - if (!suffix_list) { - if (result) { - ldapu_msgfree(ld, result); - } - retval = LDAPU_FAILED; - return retval; - } - for (i = num_namingcontexts; i < (num_namingcontexts + num_private_suffix); ++i) { - suffix_list[i] = strdup(private_suffix_list[i - num_namingcontexts]); - } - suffix_list[i] = NULL; - num_namingcontexts += num_private_suffix; - suffix = suffix_list; - } - if (result) - ldapu_msgfree(ld, result); - result = 0; - i = 0; - - /* ugaston - the caller function must remember to free the memory allocated here */ - *res = (LDAPMessage **)ldapu_malloc((num_namingcontexts + 1) * sizeof(LDAPMessage *)); - while (suffix && *suffix) { - rv = ldapu_find(ld, *suffix, scope, filter, attrs, attrsonly, &result); - if (scope == LDAP_SCOPE_BASE && rv == LDAP_SUCCESS) { - retval = rv; - (*res)[i++] = result; - break; - } - - switch (rv) { - case LDAP_SUCCESS: - if (retval == LDAP_SUCCESS) { - retval = LDAPU_ERR_MULTIPLE_MATCHES; - (*res)[i++] = result; - break; - } - /* FALLTHROUGH */ - case LDAPU_ERR_MULTIPLE_MATCHES: - retval = rv; - (*res)[i++] = result; - break; - default: - if (retval != LDAP_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES) { - retval = rv; - } - if (result) - ldapu_msgfree(ld, result); - result = 0; - break; - } - - suffix++; - } - - (*res)[i] = NULL; - ldapu_value_free(ld, suffix_list); - return retval; -} diff --git a/lib/ldaputil/ldapu-changes.html b/lib/ldaputil/ldapu-changes.html deleted file mode 100644 index 00ec08f..0000000 --- a/lib/ldaputil/ldapu-changes.html +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - - - -
-

-Change Log for the ldaputil library

- -
-Author: Nitin More
- -
-E-mail:  nitin@netscape.com
- -
-Phone: (415) 937-4240
- -
- -
-
- -
- -
Changes since Apr 17, 1997
- - -

Last Update: Aug 25, 1997 - -

All the new changes have been checked into the server3_tier_branch.  -The server3_branch is frozen & contains the version of 'ldaputil' for -the SuiteSpot 3.0 release. -

-Changed:

-Several bug fixes went in since I last modified this file.  The important -ones are: -
    -
  • -79373: Attributes -listed multiple times in certmap.conf were turining that attribute off -and enabling some other attribute.  (For example, if you have "E" -as well as "MAIL" in FilterComps, they cancelled each other).
  • - -
  • -58474: If nested -group checks goes on for 30 recursions, a circular groups error is returned.
  • - -
  • -80004: after -thoroughly testing the certmap utility, several (mostly minor) oddities -were found & fixed.
  • -
- -

-Added:

- -
    -
  • -79370: Group -membership check is optimized now through the new function ldapu_auth_userdn_groupids.  -Use this new function to get the optimization.  This function's algorithm -is as follows:
  • - -
    The first search uses the following filter (w/o the group names!): -
      (| (& (objectclass=groupofuniquenames) -
            (uniquemember=<userDN>)) -
         (& (objectclass=groupofnames)(member=<userDN>))) -
       
    -This gives us all the groups the user is member of.  We ask for only -the "CN" attributes of the returned groups.  We check if "CN" of any -of the returned groups is one of the groups we have.  If yes, we have -succeeded.  If there are no groups returned then we have failed.  -Otherwise, we continue with the nested group check.  To perform that -check, we need DNs of all the groups the user is member of, which we already -have from the previous search.  Now we repeat the search as follows: -
      (| (& (objectclass=groupofuniquenames) -
            (| (uniquemember=<grp1DN>)... -(uniquemember=<grpNDN>)) -
         (& (objectclass=groupofnames) -
            (| (member=<grp1DN>)... (member=<grpNDN>))
    -We check the list of groups returned by this search with the groups in -the ACL and recursively continue until we succeed or no more groups are -returned from the searches. - -

    Advantages of this new function is it checks multiple groups at the -same time.  Previously we were performing 2 ldap lookups per group.  -Now we achieve this in a single ldap lookup! - -

    Caution: this function allows multiple groups -with the same "CN". - -

    To use this function, you need to provide a list of group names in any -form (e.g comma separated string, a hash table, array of strings, etc.) -and a function to compare the name returned by the ldap lookup with your -group names.

- -
Changes since Mar 22, 1997
- - -

Last Update: Apr 17, 1997 - -

Now that all beta releases are out for servers using this library, I -could do some incompatible changes to make this library more flexible. -No more incompatible changes are planned (except for possibly one: see -http://scopus/bugsplat/show_bug.cgi?id=58482). All 3.0 SuiteSpot -servers supporting client auth need to upgrade to this version. -

-Changed:

- -
    -
  • -Exchanged certmap.h & ldaputil.h: ldaputil.h had public API -but when the file was installed on the server root, it was called certmap.h. -Since we already had a certmap.h, this was causing lot of confusion. If -you were including "certmap.h", now include "ldaputil.h" and vice versa.
  • - -
  • -Renamed 'SearchComps' to 'FilterComps': Shouldn't affect your code -but may affect tests and documentation.
  • - -
  • -'VerifyCert' must be either "on" or "off": VerifyCert didn't have -a value. Now it must have a value. If it has a value of "on" then the "verification" -step is on, otherwise it is off.
  • - -
  • -Important bug fixes: One bug was causing stack corruption & -weird unpredictable results. The other important bug was to map 'E' in -cert to 'MAIL' in LDAP.
  • -
- -

-Added:

- -
    -
  • -Settable 'search function': User defined search function can be -set for cert to ldap mapping.
  • - -
  • -ldapu_get_cert_ava_val & ldapu_free_cert_ava_val: API functions -to get & free an attribute's value(s) from either the subject DN or -the issuer DN from a cert.
  • -
- -

-Open Bugs:

- -
    Following bugs may not get fixed by RTM.
- -
    -
  • -51279: 'uniquemember' -bug
  • - -
  • -58474:  -'circular groups' results in infinite loop
  • - -
  • -58478: Don't -allow a space as a valid delimeter for DNComps & FilterComps.
  • - -
  • -58482: Make -the 'search function' configurable.
  • - -
    -
- -
-

-Changes since Mar 18, 1997

- -
-Last Update: Mar 22, 1997
- -
-A query on how to map a verisign certificate prompted these changes.  -I was hoping I don't have to do any major changes when I wrote this document -on Mar 18. These are incompatible changes -- please review them before -you upgrade.  I have checked in this file in CVS under "ns/netsite/lib/ldaputil/ldapu-changes.html". -I have added all the "XYZ_branch" and "XYZ_point" tags to this file so -that you can easily see this file in your tree.  When I make significant -changes to this file/library, I will retag this file for your branch to -make it same as the server3_branch. [Let me know if I shouldn't do it for -your branch].
- -

-Changed:

- -
    -
  • -ldapu_cert_to_ldap_entry: The major change was to allow for the -mapped DN to be NULL and in that case, start the search from the basedn -of the LDAP server. This required API change so that the basedn can be -passed to the ldapu_cert_to_ldap_entry function. This change was required -for correctly mapping certs from verisign w/o writing plugins. The Verisign -certs can be mapped correctly using the following setting in the <ServerRoot>/userdb/certmap.conf -file:
  • - -
      certmap verisign <verisign's DN> -
      verisign:dncomps -
      verisign:searchcomps cn, e
    -The mapped DN will be NULL so basedn will be used.  The filter will -be -
    (& (cn="<user's CN>") (mail="<user's mail>")).  The -new signature of ldapu_cert_to_ldap_entry is as follows: -
        int ldapu_cert_to_ldap_entry(void *cert, LDAP *ld, -const char *basedn, LDAPMessage **res); -
  • -verify cert functions: A major change in how verify cert functions -work.  This is function is now called even when multiple potential -matches are found for the cert.  The mapping is successful if the -verify function can reduce the number of matches to exactly one.  -For example, if there are multiple "Joe Smith" entries, at most one of -those will have the cert in it's "userCertificate" attr.  The verify -function will select that entry.  The verify function is called with -"LDAPMessage *res" containing all the potential matches.  It should -return a pointer to the matched entry in the new "LDAPMessage **entry" -parameter.  The new signature for CertVerifyFn_t is as follows:
  • - -
        typedef int (*CertVerifyFn_t)(void *cert, LDAP *ld, -LDAPMessage *res, -
      - -

                                                           -LDAPMessage **entry); -

  • -typedef int (*CertMapInitiFn_t): Renamed from CertmapInitFn_t.  -Now this has two extra parameters to make it easy to use it in a plugin. -Other API functions require "issuerDN" but this function was called with -"LDAPUCertMapInfo_t *certinfo".  There was no public API function -to get the issuerDN from "certinfo". The new signature for CertMapInitFn_t -is as follows:
  • - -
        typedef int (*CertMapInitFn_t)(void *certmap_info, -const char *issuerName, -
                                                              -const char *issuerDN); -
  • -(ldapauth.h) ldapu_auth_* functions: For multiple matches, these -functions now return LDAPU_ERR_MULTIPLE_MATCHES instead of LDAPU_FAILED. -This change was required to make nested group membership work efficiently -and enable the new functionality of verify cert function.
  • -
- -
- -
- -
-

-Changes since Feb 1, 1997

- -
-Last Update: Mar 18, 1997
- -
-There have been several changes to the netsite/lib/ldaputil recently. If -you use this library, please start using the latest version of ldaputil -on the server3_branch so that all the servers go out with the same API -and behavior. Review the changes before you upgrade. If you don't plan -to upgrade, please let me know.
- -

-Renamed:

-Following structures and functions are renamed. But this shouldn't affect -you unless you have written a certmap plugin. -
-struct CertMappingFunction_t ---> CertMapFn_t
- -
-struct CertVerifyFunction_t ---> CertVerifyFn_t
- -
-ldapu_set_cert_mapping_function ---> ldapu_set_cert_mapfn
- -
-ldapu_get_cert_mapping_function ---> ldapu_get_cert_mapfn
- -
-ldapu_set_cert_verify_function ---> ldapu_set_cert_verifyfn
- -
-ldapu_get_cert_verify_function ---> ldapu_get_cert_verifyfn
- -

-Removed: (from .h)

-Removed the following functions from .h files. They are still in the .c -file as static functions. I don't think these should be public. If you -think otherwise, let me know. -
-ldapu_cert_mapping_function_default
- -
-ldapu_cert_verify_function_default
- -
-ldapu_search_cert_dn
- -
-ldapu_subject_dn_to_ldap_dn
- -

-Changed:

-The following changes may affect you. Please review them carefully before -you upgrade to the latest version of ldaputil. -
    -
  • -ldapu_auth_userdn_groupdn -- added const char *base argument -for group within group membership check
  • - -
  • -ldap_init and bind calls using LDAPDatabase_t *ldb retry once -if the LDAP server is/went down.
  • - -
  • -typedef CertVerifyFn_t has different arguments.
  • - -
  • -DNs from cert with escaped/quoted characters are correctly handled now.
  • - -
  • -cert to ldap entry mapping is optimized by not using string comparisons -during thruntime. A bitmask is created when the config file is read, cert -data is mapped to bits and compared against the bitmask.
  • - -
  • -Only the required attrs are retrieved in most ldap_search_s calls -from ldaputil. Some new functions were added to keep older functions the -same.
  • - -
  • -Fixed a core dump in ldapu_free_LDAPDatabase_t when using the local db.
  • - -
  • -ldaputil functions for initializing connection to the LDAP server and binding -to the server are thread-safe now. This requires linking to netsite/lib/base. -If you don't use libbase, you can turn off the thread-safe code using a -compile time option.
  • -
- -

-Added:

- -
    -
  • -Documentation to functions in ldaputil.h (customer API) - ldaputil.h is -the external public API for customers to write there plugins.
  • - -
  • -ldapu_get_cert_der - returns raw DER encoded cert data
  • - -
  • -ldapu_cert_to_user - Similar to ldapu_cert_to_ldap_entry but only -retrieves the 'uid' and 'userCertificate' attributes from LDAP and also -extracts the user id.
  • - -
  • -ldapu_find_uid_attrs and ldapu_find_group_attrs - Similar -to ldapu_find_uid and ldapu_find_group resp., but only retrieves the specified -attributes. Internally used during password verification and group membership -checks.
  • - -
  • -ldapu_certinfo_delete, ldapu_certinfo_modify and ldapu_certinfo_save -- API for the certmap config file GUI tool which is not yet developed. -Any volunteers?
  • -
- -

-TODO/Bugs:

- -
    -
  • -uniquemember attribute is not handled correctly in the group membership -check. If the user's entry has 'x500UniqueIdentifier' attribute populated, -the group could refer to the user entry by the user's dn followed by '#' -and an unique identifier. For example, the group entry could have:
  • - -
    -uniquemember: cn=Joe Smith,o=Netscape,c=US#jsmith
    - -
    -where, 'jsmith' is one of the values of the 'x500UniqueIdentifier' attribute -for the Joe Smith entry.
    - -
    -
    -
- - - diff --git a/lib/ldaputil/ldaputili.h b/lib/ldaputil/ldaputili.h deleted file mode 100644 index d1d2708..0000000 --- a/lib/ldaputil/ldaputili.h +++ /dev/null @@ -1,68 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 - -#ifndef _LDAPU_LDAPUTILI_H -#define _LDAPU_LDAPUTILI_H - -#include - -#include - -#define BIG_LINE 1024 - -extern const int SEC_OID_AVA_UNKNOWN; /* unknown OID */ - -#ifdef __cplusplus -extern "C" { -#endif - -SECStatus CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, int srclen); - -extern void *ldapu_list_empty(LDAPUList_t *list, LDAPUListNodeFn_t free_fn, void *arg); -extern void ldapu_list_move(LDAPUList_t *from, LDAPUList_t *into); - -extern int ldapu_get_cert_ava_val(void *cert_in, int which_dn, const char *attr, char ***val_out); - -extern int ldapu_member_certificate_match(void *cert, const char *desc); - -/* Each of several LDAP API functions has a counterpart here. - * They behave the same, but their implementation may be replaced - * by calling ldapu_VTable_set(); as Directory Server does. - */ -#ifdef USE_LDAP_SSL -extern LDAP *ldapu_ssl_init(const char *host, int port, int encrypted); -#else -extern LDAP *ldapu_init(const char *host, int port); -#endif -extern int ldapu_set_option(LDAP *ld, int opt, void *val); -extern int ldapu_simple_bind_s(LDAP *ld, const char *username, const char *passwd); -extern int ldapu_unbind(LDAP *ld); -extern int ldapu_search_s(LDAP *ld, const char *base, int scope, const char *filter, char **attrs, int attrsonly, LDAPMessage **res); -extern int ldapu_count_entries(LDAP *ld, LDAPMessage *chain); -extern LDAPMessage *ldapu_first_entry(LDAP *ld, LDAPMessage *chain); -extern LDAPMessage *ldapu_next_entry(LDAP *ld, LDAPMessage *entry); -extern int ldapu_msgfree(LDAP *ld, LDAPMessage *chain); -extern char *ldapu_get_dn(LDAP *ld, LDAPMessage *entry); -extern void ldapu_memfree(LDAP *ld, void *dn); -extern char *ldapu_first_attribute(LDAP *ld, LDAPMessage *entry, BerElement **ber); -extern char *ldapu_next_attribute(LDAP *ld, LDAPMessage *entry, BerElement *ber); -extern void ldapu_ber_free(LDAP *ld, BerElement *ber, int freebuf); -extern char **ldapu_get_values(LDAP *ld, LDAPMessage *entry, const char *target); -extern struct berval **ldapu_get_values_len(LDAP *ld, LDAPMessage *entry, const char *target); -extern void ldapu_value_free(LDAP *ld, char **vals); -extern void ldapu_value_free_len(LDAP *ld, struct berval **vals); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/lib/ldaputil/vtable.c b/lib/ldaputil/vtable.c deleted file mode 100644 index 15b833a..0000000 --- a/lib/ldaputil/vtable.c +++ /dev/null @@ -1,210 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. - * Copyright (C) 2005 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 "ldaputili.h" -#include - -static LDAPUVTable_t ldapu_VTable = {0}; - -/* Replace ldapu_VTable. Subsequently, ldaputil will call the - functions in 'from' (not the LDAP API) to access the directory. - */ -void -ldapu_VTable_set(LDAPUVTable_t *from) -{ - if (from) { - memcpy(&ldapu_VTable, from, sizeof(LDAPUVTable_t)); - } -} - -int -ldapu_set_option(LDAP *ld, int option, void *optdata) -{ - if (ldapu_VTable.ldapuV_set_option) { - return ldapu_VTable.ldapuV_set_option(ld, option, optdata); - } - return LDAP_LOCAL_ERROR; -} - -int -ldapu_simple_bind_s(LDAP *ld, const char *who, const char *passwd) -{ - if (ldapu_VTable.ldapuV_simple_bind_s) { - return ldapu_VTable.ldapuV_simple_bind_s(ld, who, passwd); - } - return LDAP_LOCAL_ERROR; -} - -int -ldapu_unbind(LDAP *ld) -{ - if (ldapu_VTable.ldapuV_unbind) { - return ldapu_VTable.ldapuV_unbind(ld); - } - return LDAP_LOCAL_ERROR; -} - -int -ldapu_search_s(LDAP *ld, const char *base, int scope, const char *filter, char **attrs, int attrsonly, LDAPMessage **res) -{ - if (ldapu_VTable.ldapuV_search_s) { - return ldapu_VTable.ldapuV_search_s(ld, base, scope, filter, attrs, attrsonly, res); - } - return LDAP_LOCAL_ERROR; -} - -int -ldapu_count_entries(LDAP *ld, LDAPMessage *chain) -{ - if (ldapu_VTable.ldapuV_count_entries) { - return ldapu_VTable.ldapuV_count_entries(ld, chain); - } - return 0; -} - -LDAPMessage * -ldapu_first_entry(LDAP *ld, LDAPMessage *chain) -{ - if (ldapu_VTable.ldapuV_first_entry) { - return ldapu_VTable.ldapuV_first_entry(ld, chain); - } - return NULL; -} - -LDAPMessage * -ldapu_next_entry(LDAP *ld, LDAPMessage *entry) -{ - if (ldapu_VTable.ldapuV_next_entry) { - return ldapu_VTable.ldapuV_next_entry(ld, entry); - } - return NULL; -} - -int -ldapu_msgfree(LDAP *ld, LDAPMessage *chain) -{ - if (ldapu_VTable.ldapuV_msgfree) { - return ldapu_VTable.ldapuV_msgfree(ld, chain); - } - return LDAP_SUCCESS; -} - -char * -ldapu_get_dn(LDAP *ld, LDAPMessage *entry) -{ - if (ldapu_VTable.ldapuV_get_dn) { - return ldapu_VTable.ldapuV_get_dn(ld, entry); - } - return NULL; -} - -void -ldapu_memfree(LDAP *ld, void *p) -{ - if (ldapu_VTable.ldapuV_memfree) { - ldapu_VTable.ldapuV_memfree(ld, p); - } -} - -char * -ldapu_first_attribute(LDAP *ld, LDAPMessage *entry, BerElement **ber) -{ - if (ldapu_VTable.ldapuV_first_attribute) { - return ldapu_VTable.ldapuV_first_attribute(ld, entry, ber); - } - return NULL; -} - -char * -ldapu_next_attribute(LDAP *ld, LDAPMessage *entry, BerElement *ber) -{ - if (ldapu_VTable.ldapuV_next_attribute) { - return ldapu_VTable.ldapuV_next_attribute(ld, entry, ber); - } - return NULL; -} - -void -ldapu_ber_free(LDAP *ld, BerElement *ber, int freebuf) -{ - if (ldapu_VTable.ldapuV_ber_free) { - ldapu_VTable.ldapuV_ber_free(ld, ber, freebuf); - } -} - -char ** -ldapu_get_values(LDAP *ld, LDAPMessage *entry, const char *desc) -{ - if (ldapu_VTable.ldapuV_get_values) { - return ldapu_VTable.ldapuV_get_values(ld, entry, desc); - } else if (!ldapu_VTable.ldapuV_value_free && ldapu_VTable.ldapuV_get_values_len) { - auto struct berval **bvals = - ldapu_VTable.ldapuV_get_values_len(ld, entry, desc); - if (bvals) { - auto char **vals = (char **) - ldapu_malloc((ldap_count_values_len(bvals) + 1) * sizeof(char *)); - if (vals) { - auto char **val; - auto struct berval **bval; - for (val = vals, bval = bvals; *bval; ++val, ++bval) { - auto const size_t len = (*bval)->bv_len; - *val = (char *)ldapu_malloc(len + 1); - memcpy(*val, (*bval)->bv_val, len); - (*val)[len] = '\0'; - } - *val = NULL; - ldapu_value_free_len(ld, bvals); - return vals; - } - } - ldapu_value_free_len(ld, bvals); - } - return NULL; -} - -void -ldapu_value_free(LDAP *ld, char **vals) -{ - if (ldapu_VTable.ldapuV_value_free) { - ldapu_VTable.ldapuV_value_free(ld, vals); - } else if (!ldapu_VTable.ldapuV_get_values && vals) { - auto char **val; - for (val = vals; *val; ++val) { - free(*val); - } - free(vals); - } -} - -struct berval ** -ldapu_get_values_len(LDAP *ld, LDAPMessage *entry, const char *desc) -{ - if (ldapu_VTable.ldapuV_get_values_len) { - return ldapu_VTable.ldapuV_get_values_len(ld, entry, desc); - } - return NULL; -} - -void -ldapu_value_free_len(LDAP *ld, struct berval **vals) -{ - if (ldapu_VTable.ldapuV_value_free_len) { - ldapu_VTable.ldapuV_value_free_len(ld, vals); - } else if (!ldapu_VTable.ldapuV_get_values_len && vals) { - auto struct berval **val; - for (val = vals; *val; ++val) { - free(*val); - } - free(vals); - } -} diff --git a/src/libsds/include/sds.h b/src/libsds/include/sds.h index c649c03..c92b3b8 100644 --- a/src/libsds/include/sds.h +++ b/src/libsds/include/sds.h @@ -1079,7 +1079,7 @@ sds_result sds_bptree_verify(sds_bptree_instance *binst); * \param fn The function to be applied to each key-value pair. * \retval Result of the operation as sds_result. */ -sds_result sds_bptree_map(sds_bptree_instance *binst, void (*fn)(void *k, void *v)); +sds_result sds_bptree_map(sds_bptree_instance *binst, void *arg, void (*fn)(void *k, void *v, void *arg)); /** * From instance a, and instance b, create a new insance that contains the * keys and values where keys exist in a or b but not both. @@ -1227,7 +1227,16 @@ sds_result sds_bptree_cow_wrtxn_abort(sds_bptree_transaction **btxn); * \retval Result of the operation as sds_result. */ sds_result sds_bptree_cow_wrtxn_commit(sds_bptree_transaction **btxn); - +/** + * Retrieve the current transaction's id. There is *no guarantee* that this + * id increments sequentially, or avoids any kind of int rollover. It's only + * purpose is to allow generational timestamping of external data to assert + * if a newer transaction has passed since. + * + * \param btxn The transaction from which you wish to retrieve the ID + * \retval The transaction ID. + */ +uint64_t sds_bptree_txn_get_id(sds_bptree_transaction *btxn); /** * Search a tree with a valid transaction reference. This returns KEY_PRESENT * or KEY_NOT_PRESENT if the search suceeds or not. Search may operation on a valid @@ -1288,10 +1297,19 @@ sds_result sds_bptree_cow_insert(sds_bptree_transaction *btxn, void *key, void * * \param value The value to update. May be NULL. * \retval Result of the operation as sds_result. */ - sds_result sds_bptree_cow_update(sds_bptree_transaction *btxn, void *key, void *value); /** + * Map over all key/values in the tree within this txn. If the transaction is readonly + * you should only read these values. If the txn is write, you may modify value. + * + * \param btxn The transaction to map over. + * \param arg A generic argument that will be passed to each invocation of fn. + * \param fn the function to apply to each key/value. + */ +sds_result sds_bptree_cow_map(sds_bptree_transaction *btxn, void *arg, void (*fn)(void *k, void *v, void *arg)); + +/** * Search atomic functions as search, but implies a single short lived read transaction. * * If you have multiple searches to make, it is better to use a read transaction due to diff --git a/src/libsds/sds/bpt/bpt.c b/src/libsds/sds/bpt/bpt.c index 8703e2d..79951e0 100644 --- a/src/libsds/sds/bpt/bpt.c +++ b/src/libsds/sds/bpt/bpt.c @@ -384,6 +384,9 @@ sds_bptree_destroy(sds_bptree_instance *binst) { // Remove all the other elements sds_result result = SDS_SUCCESS; + if (binst == NULL) { + return SDS_NULL_POINTER; + } result = sds_bptree_map_nodes(binst, binst->root, sds_bptree_node_destroy); // Finally remove the binst sds_free(binst); diff --git a/src/libsds/sds/bpt/common.c b/src/libsds/sds/bpt/common.c index edae1e3..275ee10 100644 --- a/src/libsds/sds/bpt/common.c +++ b/src/libsds/sds/bpt/common.c @@ -522,7 +522,7 @@ sds_bptree_leaf_delete(sds_bptree_instance *binst, sds_bptree_node *node, void * /* extract the contents (if any) */ void *value = node->values[index]; - if (value != NULL) { + if (value != NULL && binst->value_free_fn != NULL) { binst->value_free_fn(value); } /* Delete the key + value */ diff --git a/src/libsds/sds/bpt/set.c b/src/libsds/sds/bpt/set.c index 9f00d35..523c56c 100644 --- a/src/libsds/sds/bpt/set.c +++ b/src/libsds/sds/bpt/set.c @@ -66,7 +66,7 @@ sds_bptree_list_advance(sds_bptree_node **item, size_t *index) /* Tree mapping functions */ /* Shouldn't this make a set of results? */ sds_result -sds_bptree_map(sds_bptree_instance *binst, void (*fn)(void *k, void *v)) +sds_bptree_map(sds_bptree_instance *binst, void *arg, void (*fn)(void *k, void *v, void *arg)) { /* If this is the non-cow tree, this is easy. */ /* Find the bottom left node, then iterate to the right! */ @@ -74,7 +74,7 @@ sds_bptree_map(sds_bptree_instance *binst, void (*fn)(void *k, void *v)) while (work_node != NULL) { for (size_t index = 0; index < work_node->item_count; index++) { - fn(work_node->keys[index], work_node->values[index]); + fn(work_node->keys[index], work_node->values[index], arg); } work_node = (sds_bptree_node *)work_node->values[SDS_BPTREE_DEFAULT_CAPACITY]; } diff --git a/src/libsds/sds/bpt_cow/bpt_cow.c b/src/libsds/sds/bpt_cow/bpt_cow.c index 0b6f229..81d03a1 100644 --- a/src/libsds/sds/bpt_cow/bpt_cow.c +++ b/src/libsds/sds/bpt_cow/bpt_cow.c @@ -469,6 +469,51 @@ sds_bptree_cow_update(sds_bptree_transaction *btxn, void *key, void *value) return SDS_SUCCESS; } +sds_result +sds_bptree_cow_map(sds_bptree_transaction *btxn, void *arg, void (*fn)(void *k, void *v, void *arg)) { + /* + * For each leaf node, map fn to each value. + * + * This is really similar to map_nodes, but only applies fn at level == 0 + */ + + if (btxn == NULL) { + return SDS_INVALID_TXN; + } + + sds_bptree_node_list *cur = sds_malloc(sizeof(sds_bptree_node_list)); + sds_bptree_node_list *prev = cur; + sds_bptree_node_list *tail = cur; + + cur->node = btxn->root; + cur->next = NULL; + sds_result final_result = SDS_SUCCESS; + + while (cur != NULL) { + if (cur->node->level > 0) { + /* Has to be <= here as this is access values, not keys! */ + for (size_t i = 0; i <= cur->node->item_count; i++) { + /* Alloc a new element, and shuffle along .... */ + if (cur->node->values[i] != NULL) { + tail->next = sds_malloc(sizeof(sds_bptree_node_list)); + tail = tail->next; + tail->node = (sds_bptree_node *)cur->node->values[i]; + tail->next = NULL; + } + } + } else { + /* We have a leaf! map the fn over valid values. */ + for (size_t index = 0; index < cur->node->item_count; index++) { + fn(cur->node->keys[index], cur->node->values[index], arg); + } + } + prev = cur; + cur = cur->next; + free(prev); + } + return final_result; +} + // Does this need to work on a transaction perhaps to verify the tree is "sane"? sds_result sds_bptree_cow_verify(sds_bptree_cow_instance *binst) diff --git a/src/libsds/sds/bpt_cow/txn.c b/src/libsds/sds/bpt_cow/txn.c index 1da0ae4..c6be44e 100644 --- a/src/libsds/sds/bpt_cow/txn.c +++ b/src/libsds/sds/bpt_cow/txn.c @@ -54,7 +54,7 @@ sds_bptree_txn_create(sds_bptree_cow_instance *binst) // The initial ref count is 0, and we only up to 1 when we commit. // Atomically set this to 0. - __atomic_and_fetch(&(btxn->reference_count), 0, __ATOMIC_SEQ_CST); + __atomic_and_fetch(&(btxn->reference_count), 0, __ATOMIC_RELAXED); #ifdef SDS_DEBUG // Update our needed checksums @@ -76,6 +76,23 @@ sds_bptree_txn_create(sds_bptree_cow_instance *binst) * ============================================================== */ +uint64_t +sds_bptree_txn_get_id(sds_bptree_transaction *btxn) { + if (btxn != NULL) { + return btxn->txn_id; + } + return 0; +} + +/* ========================= WARNING ============================ + * UNLESS YOU HAVE READ: + * https://www.kernel.org/doc/Documentation/memory-barriers.txt + * and SERIOUSLY understand it, and how it works you *MUST* not + * edit this file. This section of the code relies on a deep + * understanding of locking and memory barriers. + * ============================================================== + */ + // Should be caled by txn decrement. static void sds_bptree_txn_free(sds_bptree_transaction *btxn) @@ -111,7 +128,7 @@ sds_bptree_txn_free(sds_bptree_transaction *btxn) static void sds_bptree_txn_increment(sds_bptree_transaction *btxn) { - __atomic_add_fetch(&(btxn->reference_count), 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(&(btxn->reference_count), 1, __ATOMIC_RELAXED); // PR_AtomicIncrement(&(btxn->reference_count)); #ifdef SDS_DEBUG @@ -140,7 +157,7 @@ sds_bptree_txn_decrement(sds_bptree_transaction *btxn) // Atomic dec the counter. // PR_AtomicDecrement returns the set value. - uint32_t result = __atomic_sub_fetch(&(btxn->reference_count), 1, __ATOMIC_SEQ_CST); + uint32_t result = __atomic_sub_fetch(&(btxn->reference_count), 1, __ATOMIC_RELAXED); /* WARNING: After this point, another thread MAY free btxn under us. * You MUST *not* deref btxn after this point. */ @@ -169,7 +186,7 @@ sds_bptree_txn_decrement(sds_bptree_transaction *btxn) // * there are more parents left, so we are > 0 // * there are still active holders left, so we are > 0 if (btxn != NULL) { - result = __atomic_sub_fetch(&(btxn->reference_count), 1, __ATOMIC_SEQ_CST); + result = __atomic_sub_fetch(&(btxn->reference_count), 1, __ATOMIC_RELAXED); } } } @@ -400,7 +417,7 @@ sds_bptree_cow_wrtxn_commit(sds_bptree_transaction **btxn) // Say we are alive and commited - 2 means "our former transaction owns us" // and "we are the active root". uint32_t default_ref_count = 2; - __atomic_store(&((*btxn)->reference_count), &default_ref_count, __ATOMIC_SEQ_CST); + __atomic_store(&((*btxn)->reference_count), &default_ref_count, __ATOMIC_RELAXED); // Set it. (*btxn)->binst->txn = *btxn; // Update our parent to reference us. diff --git a/src/libsds/test/test_sds_cow.c b/src/libsds/test/test_sds_cow.c index 9fd5ab0..0ece309 100644 --- a/src/libsds/test/test_sds_cow.c +++ b/src/libsds/test/test_sds_cow.c @@ -562,6 +562,40 @@ test_cow_update(void **state) assert_int_equal(sds_bptree_cow_rotxn_close(&ro_btxn_b), SDS_SUCCESS); } +static void +test_cow_map_cb(void *key __attribute((unused)), void *value, void *arg __attribute((unused))) { + *(uint64_t *)value = 12345; +} + +static void +test_cow_map(void **state) +{ + sds_bptree_cow_instance *binst = *state; + sds_bptree_transaction *wr_btxn = NULL; + sds_result result = SDS_SUCCESS; + + assert_int_equal(sds_bptree_cow_wrtxn_begin(binst, &wr_btxn), SDS_SUCCESS); + + /* Insert a set of keys with values */ + for (uint64_t i = 10; i < (10 + SDS_BPTREE_DEFAULT_CAPACITY); i++) { + result = sds_bptree_cow_insert(wr_btxn, (void *)&i, sds_uint64_t_dup((void *)&i)); + assert_int_equal(result, SDS_SUCCESS); + } + /* Map and change the values */ + + assert_int_equal(sds_bptree_cow_map(wr_btxn, NULL, test_cow_map_cb), SDS_SUCCESS); + + for (uint64_t i = 10; i < (10 + SDS_BPTREE_DEFAULT_CAPACITY); i++) { + uint64_t *output = NULL; + assert_int_equal(sds_bptree_cow_retrieve(wr_btxn, (void *)&i, (void **)&output), SDS_KEY_PRESENT); + assert_int_equal(result, SDS_SUCCESS); + assert_int_equal(*output, 12345); + } + /* Assert the values have changed. */ + + assert_int_equal(sds_bptree_cow_wrtxn_commit(&wr_btxn), SDS_SUCCESS); +} + int run_cow_tests(void) { @@ -627,6 +661,9 @@ run_cow_tests(void) cmocka_unit_test_setup_teardown(test_cow_update, bptree_test_cow_setup, bptree_test_cow_teardown), + cmocka_unit_test_setup_teardown(test_cow_map, + bptree_test_cow_setup, + bptree_test_cow_teardown), }; return cmocka_run_group_tests_name("bpt_cow", tests, NULL, NULL); } diff --git a/src/libsds/test/test_sds_set.c b/src/libsds/test/test_sds_set.c index e1080dc..3831bba 100644 --- a/src/libsds/test/test_sds_set.c +++ b/src/libsds/test/test_sds_set.c @@ -16,7 +16,7 @@ static int32_t cb_count = 0; static void -test_31_map_cb(void *k __attribute__((unused)), void *v __attribute__((unused))) +test_31_map_cb(void *k __attribute__((unused)), void *v __attribute__((unused)), void *arg __attribute__((unused))) { cb_count++; } @@ -39,7 +39,7 @@ test_31_map(void **state) result = sds_bptree_verify(binst); assert_int_equal(result, SDS_SUCCESS); } - sds_bptree_map(binst, test_31_map_cb); + sds_bptree_map(binst, NULL, test_31_map_cb); assert_int_equal(cb_count, 199); } -- 1.8.3.1