summaryrefslogtreecommitdiffstats
path: root/lib/ldaputil
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ldaputil')
-rw-r--r--lib/ldaputil/Makefile65
-rw-r--r--lib/ldaputil/cert.c452
-rw-r--r--lib/ldaputil/certmap.c1950
-rw-r--r--lib/ldaputil/certmap.conf48
-rw-r--r--lib/ldaputil/dbconf.c704
-rw-r--r--lib/ldaputil/encode.c142
-rw-r--r--lib/ldaputil/errors.c202
-rw-r--r--lib/ldaputil/examples/Certmap.mak254
-rw-r--r--lib/ldaputil/examples/Makefile91
-rw-r--r--lib/ldaputil/examples/README97
-rw-r--r--lib/ldaputil/examples/init.c40
-rw-r--r--lib/ldaputil/examples/plugin.c239
-rw-r--r--lib/ldaputil/examples/plugin.h33
-rw-r--r--lib/ldaputil/init.c185
-rw-r--r--lib/ldaputil/ldapauth.c1099
-rw-r--r--lib/ldaputil/ldapdb.c583
-rw-r--r--lib/ldaputil/ldapu-changes.html403
-rw-r--r--lib/ldaputil/ldaputili.h62
-rw-r--r--lib/ldaputil/utest/Makefile117
-rw-r--r--lib/ldaputil/utest/auth.cpp574
-rwxr-xr-xlib/ldaputil/utest/authtest106
-rw-r--r--lib/ldaputil/utest/certmap.conf36
-rw-r--r--lib/ldaputil/utest/dblist.conf15
-rw-r--r--lib/ldaputil/utest/example.c116
-rw-r--r--lib/ldaputil/utest/plugin.c115
-rw-r--r--lib/ldaputil/utest/plugin.h20
-rw-r--r--lib/ldaputil/utest/stubs.c107
-rw-r--r--lib/ldaputil/utest/stubs.cpp102
-rw-r--r--lib/ldaputil/utest/test.ref448
-rw-r--r--lib/ldaputil/vtable.c427
30 files changed, 8832 insertions, 0 deletions
diff --git a/lib/ldaputil/Makefile b/lib/ldaputil/Makefile
new file mode 100644
index 00000000..c6c95400
--- /dev/null
+++ b/lib/ldaputil/Makefile
@@ -0,0 +1,65 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# Makefile for libldapu.a (ldaputil library)
+#
+MCOM_ROOT=../../..
+MODULE=LibLdapUtil
+
+OBJDEST=$(OBJDIR)/lib/ldaputil
+
+include ../../nsconfig.mk
+
+ifeq ($(ARCH), WINNT)
+LIBS=$(OBJDIR)/lib/libldapu.lib
+MODULE_CFLAGS+= -DWINSOCK
+else
+LIBS=$(OBJDIR)/lib/libldapu.a
+endif
+
+LOCAL_DEPS = $(LDAPSDK_DEP)
+
+MCC_INCLUDE=-I$(NSROOT)/include \
+ -I$(MCOM_ROOT)/include \
+ $(DBM_INCLUDE) $(LDAPSDK_INCLUDE) \
+ $(SECURITY_INCLUDE) $(NSPR_INCLUDE)
+
+all: $(OBJDEST) $(LOCAL_DEPS) $(LIBS)
+
+$(OBJDEST):
+ mkdir -p $(OBJDEST)
+
+OSOBJS =
+
+OBJS=$(addprefix $(OBJDEST)/, \
+ ldapauth.o \
+ ldapdb.o \
+ dbconf.o \
+ certmap.o \
+ cert.o \
+ init.o \
+ encode.o \
+ errors.o \
+ vtable.o \
+ )
+
+MODULE_CFLAGS+= $(TESTFLAGS)
+MODULE_CFLAGS+= -DLDAPDB_THREAD_SAFE -I.
+
+ifeq ($(LDAP_NO_LIBLCACHE),1)
+MODULE_CFLAGS+=-DNO_LIBLCACHE
+endif
+
+
+$(LIBS): $(OBJS)
+ rm -f $@
+ $(AR) $(OBJS)
+ $(RANLIB) $@
+
+include $(INCLUDE_DEPENDS)
+
diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c
new file mode 100644
index 00000000..34c41845
--- /dev/null
+++ b/lib/ldaputil/cert.c
@@ -0,0 +1,452 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+#include <malloc.h>
+
+/* removed for ns security integration
+#include <sec.h>
+*/
+#include "prmem.h"
+#include "key.h"
+#include "cert.h"
+#include <ldaputil/certmap.h>
+#include <ldaputil/errors.h>
+#include <ldaputil/cert.h>
+#include "ldaputili.h"
+
+NSAPI_PUBLIC int ldapu_get_cert (void *SSLendpoint, void **cert)
+{
+ /* 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) {
+ ldap_value_free (*rdn);
+ }
+ free (rdns);
+}
+
+static char***
+_explode_dn (const char* dn)
+{
+ auto char*** exp = NULL;
+ if (dn && *dn) {
+ auto char** rdns = ldap_explode_dn (dn, 0);
+ if (rdns) {
+ auto size_t expLen = 0;
+ auto char** rdn;
+ for (rdn = rdns; *rdn; ++rdn) {
+ auto char** avas = ldap_explode_rdn (*rdn, 0);
+ if (avas && *avas) {
+ exp = (char***) ldapu_realloc (exp, sizeof(char**) * (expLen + 2));
+ if (exp) {
+ exp[expLen++] = avas;
+ } else {
+ ldap_value_free (avas);
+ break;
+ }
+ } else { /* parse error */
+ if (avas) {
+ ldap_value_free (avas);
+ }
+ if (exp) {
+ exp[expLen] = NULL;
+ _rdns_free (exp);
+ exp = NULL;
+ }
+ break;
+ }
+ }
+ if (exp) {
+ exp[expLen] = NULL;
+ }
+ ldap_value_free (rdns);
+ }
+ }
+ return exp;
+}
+
+static size_t
+_rdns_count (char*** rdns)
+{
+ auto size_t count = 0;
+ auto char*** rdn;
+ for (rdn = rdns; *rdns; ++rdns) {
+ auto char** ava;
+ for (ava = *rdns; *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 {
+ char* (*getter) (CERTName* dn);
+ 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 <draft-ietf-asid-ldapv3-dn>,
+ 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
+ * <rv> 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
new file mode 100644
index 00000000..c2c718fc
--- /dev/null
+++ b/lib/ldaputil/certmap.c
@@ -0,0 +1,1950 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <malloc.h>
+
+/* removed for ns security integration
+#include <sec.h>
+*/
+#include <plstr.h>
+#include <prlink.h>
+#include <key.h>
+#include <cert.h>
+#include <ldaputil/certmap.h>
+#include <ldaputil/ldapauth.h>
+#include <ldaputil/errors.h>
+#include <ldaputil/ldaputil.h>
+#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 );
+
+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;
+}
+
+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_list_alloc (LDAPUList_t **list)
+{
+ *list = (LDAPUList_t *)malloc(sizeof(LDAPUList_t));
+
+ if (!*list) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ memset((void *)*list, 0, sizeof(LDAPUList_t));
+ 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 *)malloc(sizeof(LDAPUListNode_t));
+
+ if (!node) {
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ memset((void *)node, 0, sizeof(LDAPUListNode_t));
+ node->info = info;
+
+ return ldapu_list_add_node(list, node);
+}
+
+static int ldapu_list_remove_node (LDAPUList_t *list, LDAPUListNode_t *node)
+{
+ if (list->head == node) {
+ list->head = node->next;
+ if (list->tail == node) list->tail = 0; /* removed the only node */
+ }
+ else if (list->tail == node) {
+ list->tail = node->prev;
+ }
+ else {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ }
+
+ node->next = 0;
+ node->prev = 0;
+ return LDAPU_SUCCESS;
+}
+
+static int ldapu_list_copy (const LDAPUList_t *from, LDAPUList_t **to,
+ LDAPUListNodeFn_t copy_fn)
+{
+ LDAPUListNode_t *node = from->head;
+ LDAPUListNode_t *newnode;
+ LDAPUList_t *list;
+ int rv;
+
+ *to = 0;
+ rv = ldapu_list_alloc(&list);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ while(node) {
+ newnode = (LDAPUListNode_t *)(*copy_fn)(node->info, 0);
+ if (!newnode) return LDAPU_ERR_OUT_OF_MEMORY;
+ rv = ldapu_list_add_info(list, newnode);
+ if (rv != LDAPU_SUCCESS) return rv;
+ node = node->next;
+ }
+
+ *to = list;
+ return LDAPU_SUCCESS;
+}
+
+static int ldapu_list_find_node (const LDAPUList_t *list,
+ LDAPUListNode_t **found,
+ LDAPUListNodeFn_t find_fn,
+ void *find_arg)
+{
+ LDAPUListNode_t *node = list->head;
+
+ while(node) {
+ if ((*find_fn)(node->info, find_arg) == LDAPU_SUCCESS) {
+ *found = node;
+ return LDAPU_SUCCESS;
+ }
+ node = node->next;
+ }
+
+ return LDAPU_ERR_CERTMAP_INFO_MISSING;
+}
+
+static int ldapu_list_print (LDAPUList_t *list, LDAPUListNodeFn_t print_fn,
+ LDAPUPrintInfo_t *pinfo)
+{
+ LDAPUListNode_t *node = list->head;
+ int rv;
+
+ while(node) {
+ rv = (int)(*print_fn)(node->info, pinfo);
+ if (rv != LDAPU_SUCCESS) return rv;
+ node = node->next;
+ }
+
+ return LDAPU_SUCCESS;
+}
+
+
+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 void *ldapu_propval_copy (void *info, void *arg)
+{
+ LDAPUPropVal_t *propval = (LDAPUPropVal_t *)info;
+ LDAPUPropVal_t *copy = 0;
+ int rv;
+
+ rv = ldapu_propval_alloc(propval->prop, propval->val, &copy);
+
+ if (rv != LDAPU_SUCCESS) return 0;
+ return copy;
+}
+
+#define PRINT_STR(x) (x ? x : "<NULL>")
+
+static void * ldapu_propval_print (void *info, void *arg)
+{
+ LDAPUPropVal_t *propval = (LDAPUPropVal_t *)info;
+ LDAPUPrintInfo_t *pinfo = (LDAPUPrintInfo_t *)arg;
+
+ if (!pinfo || !pinfo->fp) {
+ fprintf(stderr, "\tprop = \"%s\", \tval = \"%s\"\n",
+ PRINT_STR(propval->prop),
+ PRINT_STR(propval->val));
+ }
+ else {
+ char *issuerName = (char *)pinfo->arg;
+
+ fprintf(pinfo->fp, "%s:%s %s\n", issuerName,
+ propval->prop ? propval->prop : "",
+ propval->val ? propval->val : "");
+ }
+
+ return 0;
+}
+
+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 void print_oid_bitmask (long bitmask)
+{
+ fprintf(stderr, "%x: ", bitmask);
+
+ if (PresentInComps(bitmask, SEC_OID_AVA_COUNTRY_NAME))
+ fprintf(stderr, " C");
+ if (PresentInComps(bitmask, SEC_OID_AVA_ORGANIZATION_NAME))
+ fprintf(stderr, " O");
+ if (PresentInComps(bitmask, SEC_OID_AVA_COMMON_NAME))
+ fprintf(stderr, " CN");
+ if (PresentInComps(bitmask, SEC_OID_AVA_LOCALITY))
+ fprintf(stderr, " L");
+ if (PresentInComps(bitmask, SEC_OID_AVA_STATE_OR_PROVINCE))
+ fprintf(stderr, " ST");
+ if (PresentInComps(bitmask, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME))
+ fprintf(stderr, " OU");
+ if (PresentInComps(bitmask, SEC_OID_PKCS9_EMAIL_ADDRESS))
+ fprintf(stderr, " E");
+ if (PresentInComps(bitmask, SEC_OID_RFC1274_UID))
+ fprintf(stderr, " UID");
+ if (PresentInComps(bitmask, SEC_OID_RFC1274_MAIL))
+ fprintf(stderr, " MAIL");
+ if (PresentInComps(bitmask, SEC_OID_AVA_DC))
+ fprintf(stderr, " DC");
+ /* check for not yet known oid */
+ if (PresentInComps(bitmask, 34325))
+ fprintf(stderr, " UNKNOWN");
+
+ fprintf(stderr, "\n");
+}
+
+static void *ldapu_certinfo_print (void *info, void *arg)
+{
+ LDAPUCertMapInfo_t *certinfo = (LDAPUCertMapInfo_t*)info;
+ LDAPUPrintInfo_t *pinfo = (LDAPUPrintInfo_t *)arg;
+
+ if (!certinfo) return (void *)LDAPU_ERR_WRONG_ARGS;
+
+ if (!pinfo || !pinfo->fp) {
+ fprintf(stderr, "Printing cert mapinfo: \"%s\" ...\n",
+ PRINT_STR(certinfo->issuerName));
+ fprintf(stderr, "\tissuerDN = \"%s\"\n",
+ PRINT_STR(certinfo->issuerDN));
+ fprintf(stderr, "\tParsed dncomps: ");
+ print_oid_bitmask(certinfo->dncomps);
+ fprintf(stderr, "\tParsed filtercomps: ");
+ print_oid_bitmask(certinfo->filtercomps);
+
+ if (certinfo->propval) {
+ fprintf(stderr, "\tPrinting propval pairs: ...\n");
+ if (certinfo->propval)
+ ldapu_list_print(certinfo->propval, ldapu_propval_print, pinfo);
+ }
+ else {
+ fprintf(stderr, "\tNo propval pairs\n");
+ }
+ }
+ else {
+ LDAPUPrintInfo_t pinfo2;
+
+ pinfo2.fp = pinfo->fp;
+ pinfo2.arg = certinfo->issuerName;
+
+ /* Write certinfo to pinfo->fp */
+ fprintf(pinfo->fp, "%s %s %s\n", LIB_DIRECTIVE, certinfo->issuerName,
+ certinfo->issuerDN ? certinfo->issuerDN : "");
+ if (certinfo->propval)
+ ldapu_list_print(certinfo->propval, ldapu_propval_print, &pinfo2);
+ fprintf(pinfo->fp, "\n");
+ }
+
+ return (void *)LDAPU_SUCCESS;
+}
+
+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;
+ int rv;
+
+ *certinfo_out = 0;
+
+ certinfo = (LDAPUCertMapInfo_t *)malloc(sizeof(LDAPUCertMapInfo_t));
+
+ if (!certinfo) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ memset((void *)certinfo, 0, sizeof(LDAPUCertMapInfo_t));
+
+ /* 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) {
+ LDAPUPropValList_t *propval_list;
+ LDAPUPropVal_t *propval;
+ DBPropVal_t *dbpropval;
+
+ dbpropval = db_info->firstprop;
+
+ rv = ldapu_list_alloc(&propval_list);
+
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ while(dbpropval) {
+ propval = (LDAPUPropVal_t *)malloc(sizeof(LDAPUPropVal_t));
+
+ if (!propval) {
+ free(certinfo);
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ 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) {
+ free(certinfo);
+ return rv;
+ }
+
+ dbpropval = dbpropval->next;
+ }
+
+ certinfo->propval = propval_list;
+ }
+
+ *certinfo_out = certinfo;
+
+ return LDAPU_SUCCESS;
+}
+
+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, 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 = ""; /* present but empty */
+ }
+
+ rv = parse_into_bitmask (dncomps, &certinfo->dncomps, -1);
+
+ if (dncomps && *dncomps) free(dncomps);
+
+ 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 = ""; /* present but empty */
+ }
+
+ rv = parse_into_bitmask (filtercomps, &certinfo->filtercomps, 0);
+
+ if (filtercomps && *filtercomps) free(filtercomps);
+
+ 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 || !*searchAttr)
+ rv = LDAPU_SUCCESS;
+ else {
+ certinfo->searchAttr = searchAttr ? strdup(searchAttr) : 0;
+
+ 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 && *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 || !*subjectDN) return rv;
+ len = strlen(certmap_info->searchAttr) + strlen(subjectDN) +
+ strlen("=") + 1;
+ certFilter = (char *)malloc(len * sizeof(char));
+ if (!certFilter) 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;
+ int rv = LDAPU_FAILED;
+ while(cur) {
+ if (!ldapu_strcasecmp(n_issuerDN, ((LDAPUCertMapInfo_t *)cur->info)->issuerDN))
+ {
+ *certmap_info = cur->info;
+ rv = LDAPU_SUCCESS;
+ 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,
+ 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 */
+ }
+
+ /* 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;
+ int rv;
+
+ /* don't free the certmap_info -- its a pointer to an internal structure */
+ rv = 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;
+ int rv;
+
+ /* don't free the certmap_info -- its a pointer to an internal structure */
+ rv = 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;
+ int rv;
+
+ /* don't free the certmap_info -- its a pointer to an internal structure */
+ rv = ldapu_issuer_certinfo(issuerDN, (void **)&certmap_info);
+ /* certmap_info may be NULL -- use the default */
+
+ return ldapu_get_cert_verifyfn_sub(certmap_info);
+}
+
+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);
+}
+
+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;
+ void *entry_cert = 0;
+ 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;
+
+ *res_out = 0;
+ *user = 0;
+
+ rv = ldapu_cert_to_ldap_entry(cert, ld, basedn, &res);
+
+ if (rv != LDAPU_SUCCESS) {
+ return rv;
+ }
+
+ if (!res) {
+ return LDAPU_ERR_EMPTY_LDAP_RESULT;
+ }
+
+ /* Extract user login (the 'uid' attr) from 'res' */
+ numEntries = ldapu_count_entries(ld, res);
+
+ if (numEntries != 1) {
+ return LDAPU_ERR_MULTIPLE_MATCHES;
+ }
+
+ entry = ldapu_first_entry(ld, res);
+
+ if (!entry) {
+ return LDAPU_ERR_MISSING_RES_ENTRY;
+ }
+
+ attrVals = ldapu_get_values(ld, entry,
+ ldapu_strings[LDAPU_STR_ATTR_USER]);
+
+ if (!attrVals || !attrVals[0]) {
+ return LDAPU_ERR_MISSING_UID_ATTR;
+ }
+
+ *user = strdup(attrVals[0]);
+ ldapu_value_free(ld, attrVals);
+
+/* ldapu_msgfree(res); */
+
+ if (!*user) {
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ *res_out = res;
+ return LDAPU_SUCCESS;
+}
+
+
+NSAPI_PUBLIC int ldapu_certinfo_modify (const char *issuerName,
+ const char *issuerDN,
+ const LDAPUPropValList_t *propval)
+{
+ LDAPUCertMapInfo_t *certinfo = 0;
+ int rv;
+
+ /* Make sure issuerName & issuerDN are both NULL or are both non-NULL */
+ if (!issuerName || !*issuerName) {
+ /* issuerDN must be NULL */
+ if (issuerDN) return LDAPU_ERR_WRONG_ARGS;
+ }
+ else if (!issuerDN || !*issuerDN) {
+ /* error - issuerName must be NULL but it is not */
+ return LDAPU_ERR_WRONG_ARGS;
+ }
+
+ if (!issuerDN) {
+ /* Modify the default certinfo */
+ certinfo = default_certmap_info;
+ }
+ else {
+ rv = ldapu_issuer_certinfo(issuerDN, (void **)&certinfo);
+
+ if (rv != LDAPU_SUCCESS) {
+ /* allocate new certinfo & add to the list */
+ certinfo = (LDAPUCertMapInfo_t *)malloc(sizeof(LDAPUCertMapInfo_t));
+ if (!certinfo) return LDAPU_ERR_OUT_OF_MEMORY;
+ memset((void *)certinfo, 0, sizeof(LDAPUCertMapInfo_t));
+
+ certinfo->issuerName = strdup(issuerName);
+ certinfo->issuerDN = strdup(issuerDN);
+
+ if (!certinfo->issuerName || !certinfo->issuerDN)
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+ }
+
+ /* Now modify the certinfo */
+ /* Free the old propval list and add new propval */
+ ldapu_propval_list_free(certinfo->propval);
+
+ if (propval) {
+ rv = ldapu_list_copy (propval, &certinfo->propval, ldapu_propval_copy);
+ if (rv != LDAPU_SUCCESS) return rv;
+ }
+
+ /* process_certinfo processes the info and adds to the certmap_listinfo */
+ process_certinfo(certinfo);
+
+ return LDAPU_SUCCESS;
+}
+
+/* ldapu_propval_same - returns LDAPU_SUCCESS or LDAPU_FAILED */
+static void * ldapu_propval_same (void *info, void *find_arg)
+{
+ /* check if info has find_arg as the issuerDN */
+ const char *issuerDN = (const char *)find_arg;
+ const LDAPUCertMapInfo_t *certinfo = (const LDAPUCertMapInfo_t *) info;
+
+ if (!ldapu_strcasecmp(certinfo->issuerDN, issuerDN))
+ return (void *)LDAPU_SUCCESS;
+ else
+ return (void *)LDAPU_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_certinfo_delete (const char *issuerDN)
+{
+ int rv;
+ LDAPUListNode_t *node;
+
+ if (!issuerDN || !*issuerDN)
+ return LDAPU_ERR_WRONG_ARGS;
+
+ rv = ldapu_list_find_node (certmap_listinfo, &node, ldapu_propval_same,
+ (void *)issuerDN);
+
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_list_remove_node (certmap_listinfo, node);
+
+ return rv;
+}
+
+NSAPI_PUBLIC int ldapu_certinfo_save (const char *fname,
+ const char *old_fname,
+ const char *tmp_fname)
+{
+ /* Copy the header from the old_fname into a temporary file
+ * Save the default_certmap_info and certmap_listinfo into the temporary
+ * file. Rename the temporary file to the new file.
+ */
+ FILE *ofp;
+ FILE *tfp;
+ char buf[BIG_LINE];
+ char *ptr;
+ int eof;
+ int rv;
+ LDAPUPrintInfo_t pinfo;
+
+#ifdef XP_WIN32
+ if ((ofp = fopen(old_fname, "rt")) == NULL)
+#else
+ if ((ofp = fopen(old_fname, "r")) == NULL)
+#endif
+ {
+ return LDAPU_ERR_CANNOT_OPEN_FILE;
+ }
+
+ if ((tfp = fopen(tmp_fname, "w")) == NULL)
+ {
+ return LDAPU_ERR_CANNOT_OPEN_FILE;
+ }
+
+ eof = 0;
+ while(!eof) {
+ if (!fgets(buf, BIG_LINE, ofp)) break;
+
+ ptr = buf;
+
+ /* skip leading whitespace */
+ while(*ptr && isspace(*ptr)) ++ptr;
+
+ if (*ptr && *ptr != '#') {
+ /* It's not a comment, we are done */
+ break;
+ }
+
+ fprintf(tfp, "%s", buf);
+ }
+
+ fclose(ofp);
+
+ /* Output the default_certmap_info */
+ pinfo.fp = tfp;
+ pinfo.arg = default_certmap_info->issuerName;
+
+ rv = (int)ldapu_certinfo_print (default_certmap_info, &pinfo);
+
+ if (rv != LDAPU_SUCCESS) {
+ fclose(tfp);
+ return rv;
+ }
+
+ if (certmap_listinfo) {
+ rv = ldapu_list_print (certmap_listinfo, ldapu_certinfo_print,
+ &pinfo);
+
+ if (rv != LDAPU_SUCCESS) {
+ fclose(tfp);
+ return rv;
+ }
+ }
+
+ fclose(tfp);
+
+ /* replace old file with the tmp file */
+#ifdef _WIN32
+ if ( !MoveFileEx(tmp_fname, fname, MOVEFILE_REPLACE_EXISTING ))
+#else
+ if ( rename( tmp_fname, fname) != 0 )
+#endif
+ {
+ return LDAPU_ERR_RENAME_FILE_FAILED;
+ }
+
+ return LDAPU_SUCCESS;
+}
+
+static void * ldapu_propval_free (void *propval_in, void *arg)
+{
+ 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)
+{
+ 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);
+}
+
+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 *)malloc(sizeof(LDAPUCertMapListInfo_t));
+
+ *certmap_list = 0;
+ *certmap_default = 0;
+ sprintf(this_dllname, "%s", dllname);
+
+ if (!certmap_listinfo) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ memset((void *)certmap_listinfo, 0, sizeof(LDAPUCertMapListInfo_t));
+
+ 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
new file mode 100644
index 00000000..35c1e9c8
--- /dev/null
+++ b/lib/ldaputil/certmap.conf
@@ -0,0 +1,48 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# This file configures how a certificate is mapped to an LDAP entry. See the
+# documentation for more information on this file.
+#
+# The format of this file is as follows:
+# certmap <name> <issuerDN>
+# <name>:<prop1> [<val1>]
+# <name>:<prop2> [<val2>]
+#
+# Notes:
+#
+# 1. Mapping can be defined per issuer of a certificate. If mapping doesn't
+# exists for a particular 'issuerDN' then the server uses the default
+# mapping.
+#
+# 2. There must be an entry for <name>=default and issuerDN "default".
+# This mapping is the default mapping.
+#
+# 3. '#' can be used to comment out a line.
+#
+# 4. DNComps & FilterComps are used to form the base DN and filter resp. for
+# performing an LDAP search while mapping the cert to a user entry.
+#
+# 5. DNComps can be one of the following:
+# commented out - take the user's DN from the cert as is
+# empty - search the entire LDAP tree (DN == suffix)
+# attr names - a comma separated list of attributes to form DN
+#
+# 6. FilterComps can be one of the following:
+# commented out - set the filter to "objectclass=*"
+# empty - set the filter to "objectclass=*"
+# attr names - a comma separated list of attributes to form the filter
+#
+
+certmap default default
+#default:DNComps
+#default:FilterComps e, uid
+#default:verifycert on
+#default:CmapLdapAttr certSubjectDN
+#default:library <path_to_shared_lib_or_dll>
+#default:InitFn <Init function's name>
diff --git a/lib/ldaputil/dbconf.c b/lib/ldaputil/dbconf.c
new file mode 100644
index 00000000..684d62df
--- /dev/null
+++ b/lib/ldaputil/dbconf.c
@@ -0,0 +1,704 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#include <ldaputil/errors.h>
+#include <ldaputil/certmap.h>
+#include <ldaputil/encode.h>
+#include <ldaputil/dbconf.h>
+
+#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:
+ * "<dbname>:prop val" OR "<dbname>: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) return LDAPU_ERR_OUT_OF_MEMORY;
+ memset((void *)propval, 0, sizeof(DBPropVal_t));
+ propval->prop = strdup(prop);
+ propval->val = val ? strdup(val) : 0;
+
+ if (!propval->prop || (val && !propval->val)) {
+ dbconf_free_propval(propval);
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ if (encval) free(val); /* val was allocated by dbconf_decodeval */
+
+ 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;
+ int found_directive = 0;
+ 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 <name> <url>" */
+ 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;
+
+#ifdef XP_WIN32
+ if ((fp = fopen(file, "rt")) == NULL)
+#else
+ if ((fp = fopen(file, "r")) == NULL)
+#endif
+ {
+ 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;
+
+#ifdef XP_WIN32
+ if ((fp = fopen(file, "rt")) == NULL)
+#else
+ if ((fp = fopen(file, "r")) == NULL)
+#endif
+ {
+ 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);
+ }
+
+ 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_strncasecmp - is like strncasecmp on UNIX but also accepts null strings.
+ */
+/* Not tested */
+static int ldapu_strncasecmp (const char *s1, const char *s2, size_t len)
+{
+ int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */
+
+ if (0 == len) return 0;
+ else if (!s1) return !s2 ? 0 : 0-tolower(*s2);
+ else if (!s2) return tolower(*s1);
+
+#ifdef XP_WIN32
+ while(len > 0 && *s1 && *s2 &&
+ (ls1 = tolower(*s1)) == (ls2 = tolower(*s2)))
+ {
+ s1++; s2++; len--;
+ }
+
+ if (0 == len)
+ return 0;
+ else if (!*s1)
+ return *s2 ? 0-tolower(*s2) : 0;
+ else if (!*s2)
+ return tolower(*s1);
+ else
+ return ls1 - ls2;
+#else
+ return strncasecmp(s1, s2, len);
+#endif
+}
+
+
+/*
+ * ldapu_strcasecmp - is like strcasecmp on UNIX but also accepts null strings.
+ */
+int ldapu_strcasecmp (const char *s1, const char *s2)
+{
+ int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */
+
+ if (!s1) return !s2 ? 0 : 0-tolower(*s2);
+ else if (!s2) return tolower(*s1);
+
+#ifdef XP_WIN32
+ while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; }
+
+ if (!*s1)
+ return *s2 ? 0-tolower(*s2) : 0;
+ else if (!*s2)
+ return tolower(*s1);
+ else
+ return ls1 - ls2;
+#else
+ return strcasecmp(s1, s2);
+#endif
+}
+
+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
new file mode 100644
index 00000000..3157418c
--- /dev/null
+++ b/lib/ldaputil/encode.c
@@ -0,0 +1,142 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <malloc.h>
+#include <string.h>
+#include <ldaputil/certmap.h>
+#include <ldaputil/encode.h>
+
+/* 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
new file mode 100644
index 00000000..d2f50843
--- /dev/null
+++ b/lib/ldaputil/errors.c
@@ -0,0 +1,202 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <ldaputil/errors.h>
+#include <ldaputil/certmap.h>
+
+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/Certmap.mak b/lib/ldaputil/examples/Certmap.mak
new file mode 100644
index 00000000..618db42b
--- /dev/null
+++ b/lib/ldaputil/examples/Certmap.mak
@@ -0,0 +1,254 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=Certmap - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to Certmap - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "Certmap - Win32 Release" && "$(CFG)" !=\
+ "Certmap - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Certmap.mak" CFG="Certmap - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Certmap - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "Certmap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "Certmap - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+MTL=mktyplib.exe
+
+!IF "$(CFG)" == "Certmap - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\Certmap.dll"
+
+CLEAN :
+ -@erase "$(INTDIR)\init.obj"
+ -@erase "$(INTDIR)\plugin.obj"
+ -@erase "$(OUTDIR)\Certmap.dll"
+ -@erase "$(OUTDIR)\Certmap.exp"
+ -@erase "$(OUTDIR)\Certmap.lib"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "c:\netscape\suitespot\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "c:\netscape\suitespot\include" /D "WIN32"\
+ /D "NDEBUG" /D "_WINDOWS" /Fp"$(INTDIR)/Certmap.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/Certmap.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/Certmap.pdb" /machine:I386 /out:"$(OUTDIR)/Certmap.dll"\
+ /implib:"$(OUTDIR)/Certmap.lib"
+LINK32_OBJS= \
+ "$(INTDIR)\init.obj" \
+ "$(INTDIR)\plugin.obj" \
+ "C:\Netscape\SuiteSpot\lib\nsldap32v10.lib"
+
+"$(OUTDIR)\Certmap.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "Certmap - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\Certmap.dll"
+
+CLEAN :
+ -@erase "$(INTDIR)\init.obj"
+ -@erase "$(INTDIR)\plugin.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\Certmap.dll"
+ -@erase "$(OUTDIR)\Certmap.exp"
+ -@erase "$(OUTDIR)\Certmap.ilk"
+ -@erase "$(OUTDIR)\Certmap.lib"
+ -@erase "$(OUTDIR)\Certmap.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "c:\netscape\suitespot\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MTd /W3 /Gm /GX /Zi /Od /I "c:\netscape\suitespot\include" /D\
+ "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fp"$(INTDIR)/Certmap.pch" /YX\
+ /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/Certmap.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /dll /incremental:yes\
+ /pdb:"$(OUTDIR)/Certmap.pdb" /debug /machine:I386 /out:"$(OUTDIR)/Certmap.dll"\
+ /implib:"$(OUTDIR)/Certmap.lib"
+LINK32_OBJS= \
+ "$(INTDIR)\init.obj" \
+ "$(INTDIR)\plugin.obj" \
+ "C:\Netscape\SuiteSpot\lib\nsldap32v10.lib"
+
+"$(OUTDIR)\Certmap.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "Certmap - Win32 Release"
+# Name "Certmap - Win32 Debug"
+
+!IF "$(CFG)" == "Certmap - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Certmap - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\plugin.c
+DEP_CPP_PLUGI=\
+ ".\plugin.h"\
+ "c:\netscape\suitespot\include\certmap.h"\
+ "c:\netscape\suitespot\include\lber.h"\
+ "c:\netscape\suitespot\include\ldap.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\plugin.obj" : $(SOURCE) $(DEP_CPP_PLUGI) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\init.c
+DEP_CPP_INIT_=\
+ ".\plugin.h"\
+ "c:\netscape\suitespot\include\certmap.h"\
+ "c:\netscape\suitespot\include\lber.h"\
+ "c:\netscape\suitespot\include\ldap.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\init.obj" : $(SOURCE) $(DEP_CPP_INIT_) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=C:\Netscape\SuiteSpot\lib\nsldap32v10.lib
+
+!IF "$(CFG)" == "Certmap - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Certmap - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/lib/ldaputil/examples/Makefile b/lib/ldaputil/examples/Makefile
new file mode 100644
index 00000000..4e8b1b4e
--- /dev/null
+++ b/lib/ldaputil/examples/Makefile
@@ -0,0 +1,91 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# 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), IRIX)
+CC_CMD = cc
+LD_SHAREDCMD = ld -32 -shared
+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
new file mode 100644
index 00000000..626ef2c9
--- /dev/null
+++ b/lib/ldaputil/examples/README
@@ -0,0 +1,97 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+This directory contains an example program to demonstrate
+writing plugins using the "Certificate to LDAP Mapping" API.
+Please read the "Managing Netscape Servers" manual to find out
+about how certificate to ldap mapping can be configured using
+the <ServerRoot>/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=<name>, ou=<defaultOU>, o=<o>, c=<c>".
+
+If the "E" attribute has a value, it returns a filter
+"mail=<e>". 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=<serial No.>". 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 SDK
+from the Netscape's DevEdge site and make the ldap include
+files available in <SROOT>/include. Copy the
+../include/certmap.h file to the <SROOT>/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 <path to the shared 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
new file mode 100644
index 00000000..fc606dd9
--- /dev/null
+++ b/lib/ldaputil/examples/init.c
@@ -0,0 +1,40 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#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) {
+#ifdef WIN32
+ CertmapDLLInit(rv, libname);
+
+ if (rv != LDAPU_SUCCESS) return rv;
+#endif
+ 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
new file mode 100644
index 00000000..4e4adfaf
--- /dev/null
+++ b/lib/ldaputil/examples/plugin.c
@@ -0,0 +1,239 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "certmap.h" /* Public Certmap API */
+#include "plugin.h" /* must define extern "C" functions */
+
+#ifdef WIN32
+CertmapDLLInitFnTbl /* Initialize Certmap Function Table */
+#endif
+
+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=<defaultOU>" between the
+ "CN" and the "O" attr-value pair. The <defaultOU> 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 : "<NULL>");
+
+ 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=<serial No.>" */
+ 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
new file mode 100644
index 00000000..f73ab377
--- /dev/null
+++ b/lib/ldaputil/examples/plugin.h
@@ -0,0 +1,33 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#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
new file mode 100644
index 00000000..8c403e77
--- /dev/null
+++ b/lib/ldaputil/init.c
@@ -0,0 +1,185 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+#include <prlink.h>
+#include <prio.h>
+
+/*#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"
+
+#ifdef XP_WIN32
+#define DLL_SUFFIX ".dll"
+#ifndef FILE_PATHSEP
+#define FILE_PATHSEP '\\'
+#endif
+#else
+#ifndef FILE_PATHSEP
+#define FILE_PATHSEP '/'
+#endif
+#ifdef HPUX
+#define DLL_SUFFIX ".sl"
+#else
+#define DLL_SUFFIX ".so"
+#endif
+#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;
+ 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];
+
+ sprintf(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 */
+ sprintf(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;
+}
+
+static LDAPUDispatchVector_t __ldapu_vector = {
+ ldapu_cert_to_ldap_entry,
+ ldapu_set_cert_mapfn,
+ ldapu_get_cert_mapfn,
+ ldapu_set_cert_searchfn,
+ ldapu_get_cert_searchfn,
+ ldapu_set_cert_verifyfn,
+ ldapu_get_cert_verifyfn,
+ ldapu_get_cert_subject_dn,
+ ldapu_get_cert_issuer_dn,
+ ldapu_get_cert_ava_val,
+ ldapu_free_cert_ava_val,
+ ldapu_get_cert_der,
+ ldapu_issuer_certinfo,
+ ldapu_certmap_info_attrval,
+ ldapu_err2string,
+ ldapu_free_old,
+ ldapu_malloc,
+ ldapu_strdup,
+ ldapu_free
+};
+
+#ifdef XP_UNIX
+LDAPUDispatchVector_t *__ldapu_table = &__ldapu_vector;
+#endif
+
+#if 0
+NSAPI_PUBLIC int CertMapDLLInitFn(LDAPUDispatchVector_t **table)
+{
+ *table = &__ldapu_vector;
+}
+#endif
+
+NSAPI_PUBLIC int CertMapDLLInitFn(LDAPUDispatchVector_t **table)
+{
+ *table = (LDAPUDispatchVector_t *)malloc(sizeof(LDAPUDispatchVector_t));
+
+ if (!*table) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ (*table)->f_ldapu_cert_to_ldap_entry = ldapu_cert_to_ldap_entry;
+ (*table)->f_ldapu_set_cert_mapfn = ldapu_set_cert_mapfn;
+ (*table)->f_ldapu_get_cert_mapfn = ldapu_get_cert_mapfn;
+ (*table)->f_ldapu_set_cert_searchfn = ldapu_set_cert_searchfn;
+ (*table)->f_ldapu_get_cert_searchfn = ldapu_get_cert_searchfn;
+ (*table)->f_ldapu_set_cert_verifyfn = ldapu_set_cert_verifyfn;
+ (*table)->f_ldapu_get_cert_verifyfn = ldapu_get_cert_verifyfn;
+ (*table)->f_ldapu_get_cert_subject_dn = ldapu_get_cert_subject_dn;
+ (*table)->f_ldapu_get_cert_issuer_dn = ldapu_get_cert_issuer_dn;
+ (*table)->f_ldapu_get_cert_ava_val = ldapu_get_cert_ava_val;
+ (*table)->f_ldapu_free_cert_ava_val = ldapu_free_cert_ava_val;
+ (*table)->f_ldapu_get_cert_der = ldapu_get_cert_der;
+ (*table)->f_ldapu_issuer_certinfo = ldapu_issuer_certinfo;
+ (*table)->f_ldapu_certmap_info_attrval = ldapu_certmap_info_attrval;
+ (*table)->f_ldapu_err2string = ldapu_err2string;
+ (*table)->f_ldapu_free_old = ldapu_free_old;
+ (*table)->f_ldapu_malloc = ldapu_malloc;
+ (*table)->f_ldapu_strdup = ldapu_strdup;
+ (*table)->f_ldapu_free = ldapu_free;
+ return LDAPU_SUCCESS;
+}
diff --git a/lib/ldaputil/ldapauth.c b/lib/ldaputil/ldapauth.c
new file mode 100644
index 00000000..d8088269
--- /dev/null
+++ b/lib/ldaputil/ldapauth.c
@@ -0,0 +1,1099 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ldapauth.cpp: Implements LDAP integration in the web server.
+ *
+ * Nitin More, John Kristian
+ */
+
+/* #define DBG_PRINT */
+
+#include <stdio.h> /* for BUFSIZ */
+#include <string.h> /* for strncpy, strcat */
+#include <ldap.h>
+
+#include <ldaputil/certmap.h>
+#include <ldaputil/errors.h>
+#include <ldaputil/ldapauth.h>
+
+#include <ldaputili.h>
+
+/* If we are not interested in the returned attributes, just ask for one
+ * attribute in the call to ldap_search. Also don't ask for the attribute
+ * value -- just the attr.
+ */
+static const char *default_search_attrs[] = { "c" , 0 };
+static int default_search_attrsonly = 1;
+
+/*
+ * 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
+ * <rv> if error, where <rv> 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 : "<NULL>");
+ 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 = 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;
+ }
+ 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;
+}
+
+
+
+/*
+ * ldapu_find_uid_attrs
+ * Description:
+ * Maps the given uid to a user dn. Caller should free res if it is not
+ * NULL. Accepts the attrs & attrsonly args.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's name
+ * base basedn (where to start the search)
+ * attrs list of attributes to retrieve
+ * attrsonly flag indicating if attr values are to be retrieved
+ * 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
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_uid_attrs (LDAP *ld, const char *uid, const char *base,
+ const char **attrs, int attrsonly,
+ LDAPMessage **res)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ char filter[ BUFSIZ ];
+ int retval;
+
+ /* setup filter as (uid=<uid>) */
+ sprintf(filter, ldapu_strings[LDAPU_STR_FILTER_USER], uid);
+
+ retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_find_uid
+ * Description:
+ * Maps the given uid to a user dn. 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)
+ * uid User's name
+ * base basedn (where to start the search)
+ * 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
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_uid (LDAP *ld, const char *uid, const char *base,
+ LDAPMessage **res)
+{
+ const char **attrs = 0; /* get all attributes ... */
+ int attrsonly = 0; /* ... and their values */
+ int retval;
+
+ retval = ldapu_find_uid_attrs(ld, uid, base, attrs, attrsonly, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_find_userdn
+ * Description:
+ * Maps the given uid to a user dn. Caller should free dn if it is not
+ * NULL.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's name
+ * base basedn (where to start the search)
+ * dn user dn
+ * Return Values:
+ * LDAPU_SUCCESS if entry is found
+ * LDAPU_FAILED if entry is not found
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_userdn (LDAP *ld, const char *uid, const char *base,
+ char **dn)
+{
+ LDAPMessage *res = 0;
+ int retval;
+
+ retval = ldapu_find_uid_attrs(ld, uid, base, default_search_attrs,
+ default_search_attrsonly, &res);
+
+ if (retval == LDAPU_SUCCESS) {
+ LDAPMessage *entry;
+
+ entry = ldapu_first_entry(ld, res);
+ *dn = ldapu_get_dn(ld, entry);
+ }
+ else {
+ *dn = 0;
+ }
+
+ if (res) ldapu_msgfree(ld, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_find_group_attrs
+ * Description:
+ * Maps the given groupid to a group dn. Caller should free res if it is
+ * not NULL. Accepts the attrs & attrsonly args.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * groupid Groups's name
+ * base basedn (where to start the search)
+ * attrs list of attributes to retrieve
+ * attrsonly flag indicating if attr values are to be retrieved
+ * 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
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_group_attrs (LDAP *ld, const char *groupid,
+ const char *base, const char **attrs,
+ int attrsonly, LDAPMessage **res)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ char filter[ BUFSIZ ];
+ int retval;
+
+ /* setup the filter */
+ sprintf(filter,
+ ldapu_strings[LDAPU_STR_FILTER_GROUP],
+ groupid);
+
+ retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_find_group
+ * Description:
+ * Maps the given groupid to a group dn. 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)
+ * groupid Groups's name
+ * base basedn (where to start the search)
+ * 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
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_group (LDAP *ld, const char *groupid, const char *base,
+ LDAPMessage **res)
+{
+ const char **attrs = 0; /* get all attributes ... */
+ int attrsonly = 0; /* ... and their values */
+ int retval;
+
+ retval = ldapu_find_group_attrs (ld, groupid, base, attrs, attrsonly, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_find_groupdn
+ * Description:
+ * Maps the given groupid to a group dn. Caller should free dn if it is
+ * not NULL.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * groupid Groups's name
+ * base basedn (where to start the search)
+ * dn group dn
+ * Return Values:
+ * LDAPU_SUCCESS if entry is found
+ * LDAPU_FAILED if entry is not found
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_find_groupdn (LDAP *ld, const char *groupid, const char *base,
+ char **dn)
+{
+ LDAPMessage *res = 0;
+ int retval;
+
+ retval = ldapu_find_group_attrs(ld, groupid, base, default_search_attrs,
+ default_search_attrsonly, &res);
+
+ if (retval == LDAPU_SUCCESS) {
+ LDAPMessage *entry;
+
+ /* get ldap entry */
+ entry = ldapu_first_entry(ld, res);
+ *dn = ldapu_get_dn(ld, entry);
+ }
+ else {
+ *dn = 0;
+ }
+
+ if (res) ldapu_msgfree(ld, res);
+
+ return retval;
+}
+
+
+/*
+ * continuable_err
+ * Description:
+ * Returns true for benign errors (i.e. errors for which recursive
+ * search can continue.
+ * Return Values:
+ * 0 (zero) - if not a benign error
+ * 1 - if a benign error -- search can continue.
+ */
+static int continuable_err (int err)
+{
+ return (err == LDAPU_FAILED);
+}
+
+int ldapu_auth_udn_gdn_recurse (LDAP *ld, const char *userdn,
+ const char *groupdn, const char *base,
+ int recurse_cnt)
+{
+ char filter[ BUFSIZ ];
+ const char **attrs = default_search_attrs;
+ int attrsonly = default_search_attrsonly;
+ LDAPMessage *res = 0;
+ int retval;
+ char member_filter[ BUFSIZ ];
+
+ if (recurse_cnt >= 30)
+ return LDAPU_ERR_CIRCULAR_GROUPS;
+
+ /* setup the filter */
+ sprintf(member_filter, ldapu_strings[LDAPU_STR_FILTER_MEMBER], userdn, userdn);
+
+ retval = ldapu_find(ld, groupdn, LDAP_SCOPE_BASE, member_filter, attrs,
+ attrsonly, &res);
+
+ if (res) ldap_msgfree(res);
+
+ if (retval != LDAPU_SUCCESS && continuable_err(retval)) {
+ LDAPMessage *entry;
+
+ DBG_PRINT2("Find parent groups of \"%s\"\n", userdn);
+
+ /* Modify the filter to include the objectclass check */
+ sprintf(filter, ldapu_strings[LDAPU_STR_FILTER_MEMBER_RECURSE],
+ member_filter);
+ retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter,
+ attrs, attrsonly, &res);
+
+ if (retval == LDAPU_SUCCESS || retval == LDAPU_ERR_MULTIPLE_MATCHES) {
+ /* Found at least one group the userdn is member of */
+
+ if (!res) {
+ /* this should never happen */
+ retval = LDAPU_ERR_EMPTY_LDAP_RESULT;
+ }
+ else {
+ retval = LDAPU_ERR_MISSING_RES_ENTRY;
+
+ for (entry = ldap_first_entry(ld, res); entry != NULL;
+ entry = ldap_next_entry(ld, entry))
+ {
+ char *dn = ldap_get_dn(ld, entry);
+
+ retval = ldapu_auth_udn_gdn_recurse(ld, dn, groupdn,
+ base, recurse_cnt+1);
+ ldap_memfree(dn);
+
+ if (retval == LDAPU_SUCCESS || !continuable_err(retval)) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (res) ldap_msgfree(res);
+ }
+
+ return retval;
+}
+
+/*
+ * ldapu_auth_userdn_groupdn:
+ * Description:
+ * Checks if the user (userdn) belongs to the given group (groupdn).
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * userdn User's full DN -- actually it could be a group
+ * dn to check subgroup membership.
+ * groupdn Group's full DN
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_userdn_groupdn (LDAP *ld, const char *userdn,
+ const char *groupdn, const char *base)
+{
+ return ldapu_auth_udn_gdn_recurse(ld, userdn, groupdn, base, 0);
+}
+
+
+/*
+ * ldapu_auth_uid_groupdn:
+ * Description:
+ * Similar to ldapu_auth_userdn_groupdn but first maps the uid to a
+ * full user DN before calling ldapu_auth_userdn_groupdn.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's login name
+ * groupdn Group's full DN
+ * base basedn (where to start the search)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_uid_groupdn (LDAP *ld, const char *uid, const char *groupdn,
+ const char *base)
+{
+ int retval;
+ char *dn;
+
+ /* First find userdn for the given uid and
+ then call ldapu_auth_userdn_groupdn */
+ retval = ldapu_find_userdn(ld, uid, base, &dn);
+
+ if (retval == LDAPU_SUCCESS) {
+
+ retval = ldapu_auth_userdn_groupdn(ld, dn, groupdn, base);
+ ldap_memfree(dn);
+ }
+
+ return retval;
+}
+
+/*
+ * ldapu_auth_uid_groupid:
+ * Description:
+ * Similar to ldapu_auth_uid_groupdn but first maps the groupid to a
+ * full group DN before calling ldapu_auth_uid_groupdn.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's login name
+ * groupid Group's name
+ * base basedn (where to start the search)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_uid_groupid (LDAP *ld, const char *uid,
+ const char *groupid, const char *base)
+{
+ int retval;
+ char *dn;
+
+ /* First find groupdn for the given groupid and
+ then call ldapu_auth_uid_groupdn */
+ retval = ldapu_find_groupdn(ld, groupid, base, &dn);
+
+ if (retval == LDAPU_SUCCESS) {
+ retval = ldapu_auth_uid_groupdn(ld, uid, dn, base);
+ ldapu_memfree(ld, dn);
+ }
+
+ return retval;
+}
+
+/*
+ * ldapu_auth_userdn_groupid:
+ * Description:
+ * Similar to ldapu_auth_userdn_groupdn but first maps the groupid to a
+ * full group DN before calling ldapu_auth_userdn_groupdn.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * userdn User's full DN
+ * groupid Group's name
+ * base basedn (where to start the search)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_userdn_groupid (LDAP *ld, const char *userdn,
+ const char *groupid, const char *base)
+{
+ int retval;
+ char *groupdn;
+
+ /* First find groupdn for the given groupid and
+ then call ldapu_auth_userdn_groupdn */
+ retval = ldapu_find_groupdn(ld, groupid, base, &groupdn);
+
+ if (retval == LDAPU_SUCCESS) {
+ retval = ldapu_auth_userdn_groupdn(ld, userdn, groupdn, base);
+ ldap_memfree(groupdn);
+ }
+
+ return retval;
+}
+
+
+LDAPUStr_t *ldapu_str_alloc (const int size)
+{
+ LDAPUStr_t *lstr = (LDAPUStr_t *)ldapu_malloc(sizeof(LDAPUStr_t));
+
+ if (!lstr) return 0;
+ lstr->size = size < 0 ? 1024 : size;
+ lstr->str = (char *)ldapu_malloc(lstr->size*sizeof(char));
+ lstr->len = 0;
+ lstr->str[lstr->len] = 0;
+
+ return lstr;
+}
+
+
+void ldapu_str_free (LDAPUStr_t *lstr)
+{
+ if (lstr) {
+ if (lstr->str) ldapu_free(lstr->str);
+ ldapu_free((void *)lstr);
+ }
+}
+
+
+int ldapu_str_append(LDAPUStr_t *lstr, const char *arg)
+{
+ int arglen = strlen(arg);
+ int len = lstr->len + arglen;
+
+ if (len >= lstr->size) {
+ /* realloc some more */
+ lstr->size += arglen > 4095 ? arglen+1 : 4096;
+ lstr->str = (char *)ldapu_realloc(lstr->str, lstr->size);
+ if (!lstr->str) return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ memcpy((void *)&(lstr->str[lstr->len]), (void *)arg, arglen);
+ lstr->len += arglen;
+ lstr->str[lstr->len] = 0;
+ return LDAPU_SUCCESS;
+}
+
+
+/*
+ * ldapu_auth_userdn_groupids_recurse:
+ * Description:
+ * Checks if the user is member of the given comma separated list of
+ * group names.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * filter filter to use in the search
+ * groupids some representation of group names. Example,
+ * a comma separated names in a string, hash
+ * table, etc. This function doesn't need to
+ * know the name of the groups. It calls the
+ * following function to check if one of the
+ * groups returned by the search is in the list.
+ * grpcmpfn group name comparison function.
+ * base basedn (where to start the search)
+ * recurse_cnt recursion count to detect circular groups
+ * group_out if successful, pointer to the user's group
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of one of the groups
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+static int ldapu_auth_userdn_groupids_recurse (LDAP *ld, const char *filter,
+ void *groupids,
+ LDAPU_GroupCmpFn_t grpcmpfn,
+ const char *base,
+ int recurse_cnt,
+ char **group_out)
+{
+ LDAPMessage *res = 0;
+ const char *attrs[] = { "CN", 0 };
+ int attrsonly = 0;
+ LDAPMessage *entry;
+ int rv;
+ int retval;
+ int i;
+ int done;
+
+ if (recurse_cnt >= 30)
+ return LDAPU_ERR_CIRCULAR_GROUPS;
+
+ /* Perform the ldap lookup */
+ retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter, attrs,
+ attrsonly, &res);
+
+ if (retval != LDAPU_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES ) {
+ /* user is not a member of any group */
+ if (res) ldap_msgfree(res);
+ return retval;
+ }
+
+ retval = LDAPU_FAILED;
+ done = 0;
+
+ /* check if one of the matched groups is one of the given groups */
+ for (entry = ldap_first_entry(ld, res); entry != NULL && !done;
+ entry = ldap_next_entry(ld, entry))
+ {
+ struct berval **bvals;
+
+ if ((bvals = ldap_get_values_len(ld, entry, "CN")) == NULL) {
+ /* This shouldn't happen */
+ retval = LDAPU_ERR_MISSING_ATTR_VAL;
+ continue;
+ }
+
+ /* "CN" may have multiple values */
+ /* Check each value of "CN" against the 'groupids' */
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ rv = (*grpcmpfn)(groupids, bvals[i]->bv_val, bvals[i]->bv_len);
+ if (rv == LDAPU_SUCCESS) {
+ char *group = (char *)ldapu_malloc(bvals[i]->bv_len+1);
+
+ if (!group) {
+ retval = LDAPU_ERR_OUT_OF_MEMORY;
+ }
+ else {
+ strncpy(group, bvals[i]->bv_val, bvals[i]->bv_len);
+ group[bvals[i]->bv_len] = 0;
+ *group_out = group;
+ retval = LDAPU_SUCCESS;
+ }
+ done = 1; /* exit from the outer loop too */
+ break;
+ }
+ }
+
+ ldap_value_free_len(bvals);
+ }
+
+ if (retval == LDAPU_FAILED) {
+ /* None of the matched groups is in 'groupids' */
+ /* Perform the nested group membership check */
+ LDAPUStr_t *filter1;
+ LDAPUStr_t *filter2;
+ char *rfilter = 0;
+ int rlen;
+ /* Finally we need a filter which looks like:
+ (| (& (objectclass=groupofuniquenames)
+ (| (uniquemember=<grp1dn>)(uniquemember=<grp2dn>) ...))
+ (& (objectclass=groupofnames)
+ (| (member=<grp1dn>)(member=<grp2dn>) ...)))
+ Construct 2 sub-filters first as follows:
+ (uniquemember=<grp1dn>)(uniquemember=<grp2dn>)... AND
+ (member=<grp1dn>)(member=<grp2dn>)...
+ Then insert them in the main filter.
+ */
+ filter1 = ldapu_str_alloc(1024);
+ filter2 = ldapu_str_alloc(1024);
+ if (!filter1 || !filter2) return LDAPU_ERR_OUT_OF_MEMORY;
+ rv = LDAPU_SUCCESS;
+
+ for (entry = ldap_first_entry(ld, res); entry != NULL;
+ entry = ldap_next_entry(ld, entry))
+ {
+ char *dn = ldap_get_dn(ld, entry);
+ if (((rv = ldapu_str_append(filter1, "(uniquemember="))
+ != LDAPU_SUCCESS) ||
+ ((rv = ldapu_str_append(filter1, dn)) != LDAPU_SUCCESS) ||
+ ((rv = ldapu_str_append(filter1, ")")) != LDAPU_SUCCESS) ||
+ ((rv = ldapu_str_append(filter2, "(member="))
+ != LDAPU_SUCCESS) ||
+ ((rv = ldapu_str_append(filter2, dn)) != LDAPU_SUCCESS) ||
+ ((rv = ldapu_str_append(filter2, ")")) != LDAPU_SUCCESS))
+ {
+ ldap_memfree(dn);
+ break;
+ }
+ ldap_memfree(dn);
+ }
+
+ if (rv != LDAPU_SUCCESS) {
+ /* something went wrong in appending to filter1 or filter2 */
+ ldapu_str_free(filter1);
+ ldapu_str_free(filter2);
+ retval = rv;
+ }
+ else {
+ /* Insert the 2 filters in the main filter */
+ rlen = filter1->len + filter2->len +
+ strlen("(| (& (objectclass=groupofuniquenames)"
+ "(| ))"
+ "(& (objectclass=groupofnames)"
+ "(| )))") + 1;
+ rfilter = (char *)ldapu_malloc(rlen);
+ if (!rfilter) return LDAPU_ERR_OUT_OF_MEMORY;
+ sprintf(rfilter,
+ "(| (& (objectclass=groupofuniquenames)"
+ "(| %s))"
+ "(& (objectclass=groupofnames)"
+ "(| %s)))",
+ filter1->str, filter2->str);
+ ldapu_str_free(filter1);
+ ldapu_str_free(filter2);
+ retval = ldapu_auth_userdn_groupids_recurse(ld, rfilter, groupids,
+ grpcmpfn, base,
+ ++recurse_cnt,
+ group_out);
+ ldapu_free(rfilter);
+ }
+
+ }
+
+ if (res) ldap_msgfree(res);
+ return retval;
+}
+
+/*
+ * ldapu_auth_userdn_groupids:
+ * Description:
+ * Checks if the user is member of the given comma separated list of
+ * group names.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * userdn User's full DN
+ * groupids some representation of group names. Example,
+ * a comma separated names in a string, hash
+ * table, etc. This function doesn't need to
+ * know the name of the groups. It calls the
+ * following function to check if one of the
+ * groups returned by the search is in the list.
+ * grpcmpfn group name comparison function.
+ * base basedn (where to start the search)
+ * group_out if successful, pointer to the user's group
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of one of the groups
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_userdn_groupids (LDAP *ld, const char *userdn,
+ void *groupids,
+ LDAPU_GroupCmpFn_t grpcmpfn,
+ const char *base,
+ char **group_out)
+{
+ char *filter;
+ int len;
+ int rv;
+
+ *group_out = 0;
+ /* allocate a big enough filter */
+ /* The filter looks like:
+ (| (& (objectclass=groupofuniquenames)(uniquemember=<userdn>))
+ (& (objectclass=groupofnames)(member=<userdn>)))
+ */
+
+ len = 2 * strlen(userdn) + 1 +
+ strlen("(| (& (objectclass=groupofuniquenames)(uniquemember=))"
+ "(& (objectclass=groupofnames)(member=)))");
+ filter = (char *)ldapu_malloc(len);
+
+ if (!filter) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ sprintf(filter, "(| (& (objectclass=groupofuniquenames)(uniquemember=%s))"
+ "(& (objectclass=groupofnames)(member=%s)))",
+ userdn, userdn);
+
+ rv = ldapu_auth_userdn_groupids_recurse(ld, filter, groupids,
+ grpcmpfn, base,
+ 0, group_out);
+
+ ldapu_free(filter);
+ return rv;
+}
+
+
+/*
+ * ldapu_auth_userdn_attrfilter:
+ * Description:
+ * Checks if the user's entry has the given attributes
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * userdn User's full DN
+ * attrfilter attribute filter
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_userdn_attrfilter (LDAP *ld, const char *userdn,
+ const char *attrfilter)
+{
+ const char *base = userdn;
+ int scope = LDAP_SCOPE_BASE;
+ const char *filter = attrfilter;
+ const char **attrs = default_search_attrs;
+ int attrsonly = default_search_attrsonly;
+ LDAPMessage *res = 0;
+ int retval;
+
+ retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res);
+
+ if (res) ldapu_msgfree(ld, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_auth_uid_attrfilter:
+ * Description:
+ * Checks if the user's entry has the given attributes. First maps
+ the uid to userdn.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's name
+ * attrfilter attribute filter
+ * base basedn (where to start the search)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user is member of the group
+ * LDAPU_FAILED if user is not a member of the group
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_uid_attrfilter (LDAP *ld, const char *uid, const char *attrfilter,
+ const char *base)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ char filter[ BUFSIZ ];
+ const char **attrs = default_search_attrs;
+ int attrsonly = default_search_attrsonly;
+ LDAPMessage *res = 0;
+ int retval;
+
+ /* setup filter as (& (uid=<uid>) (attrfilter)) */
+ if (*attrfilter == '(')
+ sprintf(filter, "(& (uid=%s) %s)", uid, attrfilter);
+ else
+ sprintf(filter, "(& (uid=%s) (%s))", uid, attrfilter);
+
+ retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res);
+
+ if (res) ldapu_msgfree(ld, res);
+
+ return retval;
+}
+
+/*
+ * ldapu_auth_userdn_password:
+ * Description:
+ * Checks the user's password against LDAP by binding using the
+ * userdn and the password.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * userdn User's full DN
+ * password User's password (clear text)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user credentials are valid
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_userdn_password (LDAP *ld, const char *userdn, const char *password)
+{
+ LDAPMessage *res = 0;
+ int retval;
+
+ DBG_PRINT2("\tuserdn:\t\"%s\"\n", userdn);
+ DBG_PRINT2("\tpassword:\t\"%s\"\n", password);
+
+ retval = ldap_simple_bind_s(ld, userdn, password);
+
+ if (retval != LDAP_SUCCESS)
+ {
+ DBG_PRINT2("ldap_simple_bind_s: %s\n", ldap_err2string(retval));
+ return(retval);
+ }
+
+ return LDAPU_SUCCESS;
+}
+
+/*
+ * ldapu_auth_uid_password:
+ * Description:
+ * First converts the uid to userdn and calls
+ * ldapu_auth_userdn_password.
+ * Arguments:
+ * ld Pointer to LDAP (assumes connection has been
+ * established and the client has called the
+ * appropriate bind routine)
+ * uid User's name
+ * password User's password (clear text)
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS if user credentials are valid
+ * <rv> if error, where <rv> can be passed to
+ * ldap_err2string to get an error string.
+ */
+int ldapu_auth_uid_password (LDAP *ld, const char *uid,
+ const char *password, const char *base)
+{
+ int retval;
+ char *dn;
+
+ /* First find userdn for the given uid and
+ then call ldapu_auth_userdn_password */
+ retval = ldapu_find_userdn(ld, uid, base, &dn);
+
+ if (retval == LDAPU_SUCCESS) {
+ retval = ldapu_auth_userdn_password(ld, dn, password);
+ ldapu_memfree(ld, dn);
+ }
+
+ return retval;
+}
+
+
+/* ldapu_string_set --
+ * This function is not tested yet for its usefulness. This is to be used to
+ * customize the strings used in the LDAP searches performed through
+ * 'ldaputil'. This could also be extended to setting customized error
+ * messages (and even i18n equivalent of error messages).
+ */
+NSAPI_PUBLIC int ldapu_string_set (const int type, const char *filter)
+{
+ if (!filter || !*filter) return LDAPU_ERR_INVALID_STRING;
+
+ if (type < 0 || type >= LDAPU_STR_MAX_INDEX)
+ return LDAPU_ERR_INVALID_STRING_INDEX;
+
+ ldapu_strings[type] = strdup(filter);
+
+ if (!ldapu_strings[type]) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ return LDAPU_SUCCESS;
+}
+
+
+NSAPI_PUBLIC const char *ldapu_string_get (const int type)
+{
+ if (type < 0 || type >= LDAPU_STR_MAX_INDEX)
+ return 0;
+
+ return ldapu_strings[type];
+}
diff --git a/lib/ldaputil/ldapdb.c b/lib/ldaputil/ldapdb.c
new file mode 100644
index 00000000..2dfc30ca
--- /dev/null
+++ b/lib/ldaputil/ldapdb.c
@@ -0,0 +1,583 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef DONT_USE_LDAP_SSL
+#define USE_LDAP_SSL
+#endif
+
+
+#include <string.h>
+#include <malloc.h>
+
+#ifdef LDAPDB_THREAD_SAFE
+#include <nspr.h>
+#include <prthread.h>
+#include <prmon.h>
+/* removed for new ns security integration
+#include <xp_error.h>
+*/
+#endif /* LDAPDB_THREAD_SAFE */
+
+#include "ldaputil/errors.h"
+#include "ldaputil/certmap.h"
+#include "ldaputil/ldapdb.h"
+
+#ifdef USE_LDAP_SSL
+/* removed for new ns security integration
+#include <sec.h>
+#include <key.h>
+#include "cert.h"
+*/
+#include <ssl.h>
+#include "ldap_ssl.h"
+#endif
+
+#include "ldaputili.h"
+
+#define LDAPDB_PREFIX_WITH_SLASHES "ldapdb://"
+#define LDAPDB_PREFIX_WITH_SLASHES_LEN 9
+
+#ifndef LDAPDB_THREAD_SAFE
+#define ldb_crit_init(x)
+#define ldb_crit_enter(x)
+#define ldb_crit_exit(x)
+#else
+#ifdef NSPR20
+uintn tsdindex;
+#else
+int32 tsdindex;
+#endif
+
+static void ldb_crit_init (LDAPDatabase_t *ldb)
+{
+ ldb->crit = PR_NewMonitor();
+}
+
+static void ldb_crit_enter (LDAPDatabase_t *ldb)
+{
+ PR_EnterMonitor(ldb->crit);
+}
+
+static void ldb_crit_exit (LDAPDatabase_t *ldb)
+{
+ PR_ExitMonitor(ldb->crit);
+}
+
+struct ldap_error {
+ int le_errno;
+ char *le_matched;
+ char *le_errmsg;
+};
+
+
+static void set_ld_error( int err, char *matched, char *errmsg, void *dummy )
+{
+ struct ldap_error *le;
+
+#ifdef NSPR20
+ if (!(le = (struct ldap_error *) PR_GetThreadPrivate(tsdindex))) {
+ le = (struct ldap_error *) malloc(sizeof(struct ldap_error));
+ memset((void *)le, 0, sizeof(struct ldap_error));
+ PR_SetThreadPrivate(tsdindex, (void *)le);
+ }
+#else
+ le = (struct ldap_error *) PR_GetThreadPrivate( PR_CurrentThread(), tsdindex );
+#endif
+ le->le_errno = err;
+ if ( le->le_matched != NULL ) {
+ ldap_memfree( le->le_matched );
+ }
+ le->le_matched = matched;
+ if ( le->le_errmsg != NULL ) {
+ ldap_memfree( le->le_errmsg );
+ }
+ le->le_errmsg = errmsg;
+}
+
+static int get_ld_error( char **matched, char **errmsg, void *dummy )
+{
+ struct ldap_error *le;
+
+#ifdef NSPR20
+ le = (struct ldap_error *) PR_GetThreadPrivate( tsdindex);
+#else
+ le = (struct ldap_error *) PR_GetThreadPrivate( PR_CurrentThread(), tsdindex );
+#endif
+ if ( matched != NULL ) {
+ *matched = le->le_matched;
+ }
+ if ( errmsg != NULL ) {
+ *errmsg = le->le_errmsg;
+ }
+ return( le->le_errno );
+}
+
+static void set_errno( int err )
+{
+ PR_SetError( err, 0);
+}
+
+static int get_errno( void )
+{
+ return( PR_GetError() );
+}
+
+#ifdef LDAP_OPT_DNS_FN_PTRS /* not supported in older LDAP SDKs */
+static LDAPHostEnt *
+ldapu_copyPRHostEnt2LDAPHostEnt( LDAPHostEnt *ldhp, PRHostEnt *prhp )
+{
+ ldhp->ldaphe_name = prhp->h_name;
+ ldhp->ldaphe_aliases = prhp->h_aliases;
+ ldhp->ldaphe_addrtype = prhp->h_addrtype;
+ ldhp->ldaphe_length = prhp->h_length;
+ ldhp->ldaphe_addr_list = prhp->h_addr_list;
+ return( ldhp );
+}
+
+static LDAPHostEnt *
+ldapu_gethostbyname( const char *name, LDAPHostEnt *result,
+ char *buffer, int buflen, int *statusp, void *extradata )
+{
+ PRHostEnt prhent;
+
+ if( !statusp || ( *statusp = (int)PR_GetHostByName( name, buffer,
+ buflen, &prhent )) == PR_FAILURE ) {
+ return( NULL );
+ }
+
+ return( ldapu_copyPRHostEnt2LDAPHostEnt( result, &prhent ));
+}
+
+static LDAPHostEnt *
+ldapu_gethostbyaddr( const char *addr, int length, int type,
+ LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
+ void *extradata )
+{
+ return( (LDAPHostEnt *)PR_GetError() );
+}
+#endif /* LDAP_OPT_DNS_FN_PTRS */
+#endif /* LDAPDB_THREAD_SAFE */
+
+
+static void unescape_ldap_basedn (char *str)
+{
+ if (strchr(str, '%')) {
+ register int x = 0, y = 0;
+ int l = strlen(str);
+ char digit;
+
+ while(x < l) {
+ if((str[x] == '%') && (x < (l - 2))) {
+ ++x;
+ digit = (str[x] >= 'A' ?
+ ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0'));
+ digit *= 16;
+
+ ++x;
+ digit += (str[x] >= 'A' ?
+ ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0'));
+
+ str[y] = digit;
+ }
+ else {
+ str[y] = str[x];
+ }
+ x++;
+ y++;
+ }
+ str[y] = '\0';
+ }
+}
+
+/*
+ * extract_path_and_basedn:
+ * Description:
+ * Parses the ldapdb url and returns pathname to the lcache.conf file
+ * and basedn. The caller must free the memory allocated for the
+ * returned path and the basedn.
+ * Arguments:
+ * url URL (must begin with ldapdb://)
+ * path Pathname to the lcache.conf file
+ * basedn basedn for the ldapdb.
+ * Return Values: (same as ldap_find)
+ * LDAPU_SUCCESS if the URL is parsed successfully
+ * <rv> if error, one of the LDAPU_ errors.
+ */
+static int extract_path_and_basedn(const char *url_in, char **path_out,
+ char **basedn_out)
+{
+ char *url = strdup(url_in);
+ char *basedn;
+ char *path;
+
+ *path_out = 0;
+ *basedn_out = 0;
+
+ if (!url) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ if (strncmp(url, LDAPDB_URL_PREFIX, LDAPDB_URL_PREFIX_LEN)) {
+ free(url);
+ return LDAPU_ERR_URL_INVALID_PREFIX;
+ }
+
+ path = url + LDAPDB_URL_PREFIX_LEN;
+
+ if (strncmp(path, "//", 2)) {
+ free(url);
+ return LDAPU_ERR_URL_INVALID_PREFIX;
+ }
+
+ path += 2;
+
+ /* Find base DN -- empty string is OK */
+ if ((basedn = strrchr(path, '/')) == NULL) {
+ free(url);
+ return LDAPU_ERR_URL_NO_BASEDN;
+ }
+
+ *basedn++ = '\0'; /* terminate the path */
+ unescape_ldap_basedn(basedn);
+ *basedn_out = strdup(basedn);
+ *path_out = strdup(path);
+ free(url);
+ return (*basedn_out && *path_out) ? LDAPU_SUCCESS : LDAPU_ERR_OUT_OF_MEMORY;
+}
+
+NSAPI_PUBLIC int ldapu_ldapdb_url_parse (const char *url,
+ LDAPDatabase_t **ldb_out)
+{
+ char *path = 0;
+ char *basedn = 0;
+ LDAPDatabase_t *ldb = 0;
+ int rv;
+
+ rv = extract_path_and_basedn(url, &path, &basedn);
+
+ if (rv != LDAPU_SUCCESS) {
+ if (path) free(path);
+ if (basedn) free(basedn);
+ return rv;
+ }
+
+ ldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t));
+
+ if (!ldb) {
+ if (path) free(path);
+ if (basedn) free(basedn);
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ memset((void *)ldb, 0, sizeof(LDAPDatabase_t));
+ ldb->basedn = basedn; /* extract_path_and_basedn has allocated */
+ ldb->host = path; /* memory for us -- don't make a copy */
+ ldb_crit_init(ldb);
+ *ldb_out = ldb;
+
+ return LDAPU_SUCCESS;
+}
+
+
+/*
+ * ldapu_url_parse:
+ * Description:
+ * Parses the ldapdb or ldap url and returns a LDAPDatabase_t struct
+ * Arguments:
+ * url URL (must begin with ldapdb://)
+ * binddn DN to use to bind to ldap server.
+ * bindpw Password to use to bind to ldap server.
+ * ldb a LDAPDatabase_t struct filled from parsing
+ * the url.
+ * Return Values: (same as ldap_find)
+ * LDAPU_SUCCESS if the URL is parsed successfully
+ * <rv> if error, one of the LDAPU_ errors.
+ */
+NSAPI_PUBLIC int ldapu_url_parse (const char *url, const char *binddn,
+ const char *bindpw,
+ LDAPDatabase_t **ldb_out)
+{
+ LDAPDatabase_t *ldb;
+ LDAPURLDesc *ludp = 0;
+ int rv;
+
+ *ldb_out = 0;
+
+ if (!strncmp(url, LDAPDB_PREFIX_WITH_SLASHES,
+ LDAPDB_PREFIX_WITH_SLASHES_LEN))
+ {
+ return ldapu_ldapdb_url_parse(url, ldb_out);
+ }
+
+ /* call ldapsdk's parse function */
+ rv = ldap_url_parse((char *)url, &ludp);
+
+ if (rv != LDAP_SUCCESS) {
+ if (ludp) ldap_free_urldesc(ludp);
+ return LDAPU_ERR_URL_PARSE_FAILED;
+ }
+
+ ldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t));
+
+ if (!ldb) {
+ ldap_free_urldesc(ludp);
+ return LDAPU_ERR_OUT_OF_MEMORY;
+ }
+
+ memset((void *)ldb, 0, sizeof(LDAPDatabase_t));
+ ldb->host = ludp->lud_host ? strdup(ludp->lud_host) : 0;
+ ldb->use_ssl = ludp->lud_options & LDAP_URL_OPT_SECURE;
+ ldb->port = ludp->lud_port ? ludp->lud_port : ldb->use_ssl ? 636 : 389;
+ ldb->basedn = ludp->lud_dn ? strdup(ludp->lud_dn) : 0;
+ ldb_crit_init(ldb);
+ ldap_free_urldesc(ludp);
+
+ if (binddn) ldb->binddn = strdup(binddn);
+
+ if (bindpw) ldb->bindpw = strdup(bindpw);
+
+ /* success */
+ *ldb_out = ldb;
+
+ return LDAPU_SUCCESS;
+}
+
+NSAPI_PUBLIC void ldapu_free_LDAPDatabase_t (LDAPDatabase_t *ldb)
+{
+ if (ldb->host) free(ldb->host);
+ if (ldb->basedn) free(ldb->basedn);
+ if (ldb->filter) free(ldb->filter);
+ if (ldb->binddn) free(ldb->binddn);
+ if (ldb->bindpw) free(ldb->bindpw);
+ if (ldb->ld) ldapu_unbind(ldb->ld);
+ memset((void *)ldb, 0, sizeof(LDAPDatabase_t));
+ free(ldb);
+}
+
+NSAPI_PUBLIC LDAPDatabase_t *ldapu_copy_LDAPDatabase_t (const LDAPDatabase_t *ldb)
+{
+ LDAPDatabase_t *nldb = (LDAPDatabase_t *)malloc(sizeof(LDAPDatabase_t));
+
+ if (!nldb) return 0;
+
+ memset((void *)nldb, 0, sizeof(LDAPDatabase_t));
+ nldb->use_ssl = ldb->use_ssl;
+ if (ldb->host) nldb->host = strdup(ldb->host);
+ nldb->port = ldb->port;
+ if (ldb->basedn) nldb->basedn = strdup(ldb->basedn);
+ nldb->scope = ldb->scope;
+ if (ldb->filter) nldb->filter = strdup(ldb->filter);
+ nldb->ld = 0;
+ if (ldb->binddn) nldb->binddn = strdup(ldb->binddn);
+ if (ldb->bindpw) nldb->bindpw = strdup(ldb->bindpw);
+ nldb->bound = 0;
+ ldb_crit_init(nldb);
+
+ return nldb;
+}
+
+NSAPI_PUBLIC int ldapu_is_local_db (const LDAPDatabase_t *ldb)
+{
+ return ldb->port ? 0 : 1;
+}
+
+static int LDAP_CALL LDAP_CALLBACK
+ldapu_rebind_proc (LDAP *ld, char **whop, char **passwdp,
+ int *authmethodp, int freeit, void *arg)
+{
+ if (freeit == 0) {
+ LDAPDatabase_t *ldb = (LDAPDatabase_t *)arg;
+ *whop = ldb->binddn;
+ *passwdp = ldb->bindpw;
+ *authmethodp = LDAP_AUTH_SIMPLE;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+NSAPI_PUBLIC int ldapu_ldap_init(LDAPDatabase_t *ldb)
+{
+ LDAP *ld = 0;
+
+ ldb_crit_enter(ldb);
+
+#ifdef USE_LDAP_SSL
+ /* Note. This assume the security related initialization is done */
+ /* The step needed is :
+ PR_Init
+ RNG_SystemInfoForRNG
+ RNG_RNGInit
+ CERT_OpenCertDBFilename
+ CERT_SetDefaultCertDB
+ SECMOD_init
+
+ And because ldapssl_init depends on security initialization, it is
+ no good for non-ssl init
+ */
+ if (ldb->use_ssl)
+ ld = ldapssl_init(ldb->host, ldb->port, ldb->use_ssl);
+ else ldap_init(ldb->host, ldb->port);
+#else
+ ld = ldapu_init(ldb->host, ldb->port);
+#endif
+
+ if (ld == NULL) {
+ DBG_PRINT1("ldapu_ldap_init: Failed to initialize connection");
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LDAP_INIT_FAILED;
+ }
+
+#ifdef LDAPDB_THREAD_SAFE
+ {
+ struct ldap_thread_fns tfns;
+
+#ifdef NSPR20
+ PR_NewThreadPrivateIndex(&tsdindex, NULL);
+#else
+ tsdindex = PR_NewThreadPrivateID();
+#endif
+
+ /* set mutex pointers */
+ memset( &tfns, '\0', sizeof(struct ldap_thread_fns) );
+ tfns.ltf_mutex_alloc = (void *(*)(void))PR_NewMonitor;
+ tfns.ltf_mutex_free = (void (*)(void *))PR_DestroyMonitor;
+ tfns.ltf_mutex_lock = (int (*)(void *)) PR_EnterMonitor;
+ tfns.ltf_mutex_unlock = (int (*)(void *)) PR_ExitMonitor;
+ tfns.ltf_get_errno = get_errno;
+ tfns.ltf_set_errno = set_errno;
+ tfns.ltf_get_lderrno = get_ld_error;
+ tfns.ltf_set_lderrno = set_ld_error;
+ /* set ld_errno pointers */
+ if ( ldapu_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *) &tfns )
+ != 0 ) {
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LDAP_SET_OPTION_FAILED;
+ }
+ }
+#ifdef LDAP_OPT_DNS_FN_PTRS /* not supported in older LDAP SDKs */
+ {
+ /* install DNS functions */
+ struct ldap_dns_fns dnsfns;
+ memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) );
+ dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE;
+ dnsfns.lddnsfn_gethostbyname = ldapu_gethostbyname;
+ dnsfns.lddnsfn_gethostbyaddr = ldapu_gethostbyaddr;
+ if ( ldapu_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns )
+ != 0 ) {
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LDAP_SET_OPTION_FAILED;
+ }
+ }
+#endif /* LDAP_OPT_DNS_FN_PTRS */
+#endif /* LDAPDB_THREAD_SAFE */
+
+ if (ldapu_is_local_db(ldb)) {
+ /* No more Local db support, force error! */
+ return LDAPU_ERR_LCACHE_INIT_FAILED;
+#if 0
+ int optval = 1;
+
+ if (lcache_init(ld, ldb->host) != 0) {
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LCACHE_INIT_FAILED;
+ }
+
+ if (ldap_set_option(ld, LDAP_OPT_CACHE_ENABLE, &optval) != 0) {
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LDAP_SET_OPTION_FAILED;
+ }
+
+ optval = LDAP_CACHE_LOCALDB;
+
+ if (ldap_set_option(ld, LDAP_OPT_CACHE_STRATEGY, &optval) != 0) {
+ ldb_crit_exit(ldb);
+ return LDAPU_ERR_LDAP_SET_OPTION_FAILED;
+ }
+#endif
+ }
+ else if (ldb->binddn && *ldb->binddn) {
+ /* Set the rebind proc */
+ /* Rebind proc is used when chasing a referral */
+ ldap_set_rebind_proc(ld, ldapu_rebind_proc, (void *)ldb);
+ }
+
+ ldb->ld = ld;
+ ldb_crit_exit(ldb);
+
+ return LDAPU_SUCCESS;
+}
+
+NSAPI_PUBLIC int ldapu_ldap_rebind (LDAPDatabase_t *ldb)
+{
+ int retry;
+ int rv = LDAPU_FAILED;
+
+ ldb_crit_enter(ldb);
+
+ if (ldb->ld) {
+ retry = (ldb->bound != -1 ? 1 : 0); /* avoid recursion */
+
+#ifdef USE_LDAP_SSL
+ if (ldb->use_ssl && !CERT_GetDefaultCertDB()) {
+ /* default cert database has not been initialized */
+ rv = LDAPU_ERR_NO_DEFAULT_CERTDB;
+ }
+ else
+#endif
+ {
+ rv = ldap_simple_bind_s(ldb->ld, ldb->binddn, ldb->bindpw);
+ }
+
+ /* retry once if the LDAP server is down */
+ if (rv == LDAP_SERVER_DOWN && retry) {
+ ldb->bound = -1; /* to avoid recursion */
+ rv = ldapu_ldap_reinit_and_rebind(ldb);
+ }
+
+ if (rv == LDAPU_SUCCESS) ldb->bound = 1;
+ }
+
+ ldb_crit_exit(ldb);
+
+ return rv;
+}
+
+NSAPI_PUBLIC int ldapu_ldap_init_and_bind (LDAPDatabase_t *ldb)
+{
+ int rv = LDAPU_SUCCESS;
+
+ ldb_crit_enter(ldb);
+
+ if (!ldb->ld) {
+ rv = ldapu_ldap_init(ldb);
+ /* ldb->bound may be set to -1 to avoid recursion */
+ if (ldb->bound == 1) ldb->bound = 0;
+ }
+
+ /* bind as binddn & bindpw if not bound already */
+ if (rv == LDAPU_SUCCESS && ldb->bound != 1) {
+ rv = ldapu_ldap_rebind (ldb);
+ }
+
+ ldb_crit_exit(ldb);
+
+ return rv;
+}
+
+NSAPI_PUBLIC int ldapu_ldap_reinit_and_rebind (LDAPDatabase_t *ldb)
+{
+ int rv;
+
+ ldb_crit_enter(ldb);
+
+ if (ldb->ld) {
+ ldapu_unbind(ldb->ld);
+ ldb->ld = 0;
+ }
+
+ rv = ldapu_ldap_init_and_bind(ldb);
+ ldb_crit_exit(ldb);
+ return rv;
+}
+
diff --git a/lib/ldaputil/ldapu-changes.html b/lib/ldaputil/ldapu-changes.html
new file mode 100644
index 00000000..124181f5
--- /dev/null
+++ b/lib/ldaputil/ldapu-changes.html
@@ -0,0 +1,403 @@
+<!-- BEGIN COPYRIGHT BLOCK
+ Copyright 2001 Sun Microsystems, Inc.
+ Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ All rights reserved.
+ END COPYRIGHT BLOCK -->
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="Nitin More">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.01 [en] (WinNT; U) [Netscape]">
+</HEAD>
+<BODY>
+
+<CENTER>
+<H1>
+Change Log for the ldaputil library</H1></CENTER>
+
+<DT>
+Author: Nitin More</DT>
+
+<DT>
+E-mail:&nbsp; <A HREF="mailto:nitin@netscape.com">nitin@netscape.com</A></DT>
+
+<DT>
+Phone: (415) 937-4240</DT>
+
+<CENTER></CENTER>
+
+<CENTER>
+<HR WIDTH="100%"></CENTER>
+
+<CENTER></CENTER>
+
+<CENTER><B><FONT SIZE=+1>Changes since Apr 17, 1997</FONT></B></CENTER>
+
+
+<P>Last Update: Aug 25, 1997
+
+<P>All the new changes have been checked into the server3_tier_branch.&nbsp;
+The server3_branch is frozen &amp; contains the version of 'ldaputil' for
+the SuiteSpot 3.0 release.
+<H2>
+Changed:</H2>
+Several bug fixes went in since I last modified this file.&nbsp; The important
+ones are:
+<UL>
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=79373">79373</A>: Attributes
+listed multiple times in certmap.conf were turining that attribute off
+and enabling some other attribute.&nbsp; (For example, if you have "E"
+as well as "MAIL" in FilterComps, they cancelled each other).</LI>
+
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58474">58474</A>: If nested
+group checks goes on for 30 recursions, a circular groups error is returned.</LI>
+
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=80004">80004</A>: after
+thoroughly testing the certmap utility, several (mostly minor) oddities
+were found &amp; fixed.</LI>
+</UL>
+
+<H2>
+Added:</H2>
+
+<UL>
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=79370">79370</A>: Group
+membership check is optimized now through the new function <B>ldapu_auth_userdn_groupids</B>.&nbsp;
+Use this new function to get the optimization.&nbsp; This function's algorithm
+is as follows:</LI>
+
+<BR>The first search uses the following filter (w/o the group names!):
+<OL><TT>(| (&amp; (objectclass=groupofuniquenames)</TT>
+<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (uniquemember=&lt;userDN>))</TT>
+<BR><TT>&nbsp;&nbsp; (&amp; (objectclass=groupofnames)(member=&lt;userDN>)))</TT>
+<BR>&nbsp;</OL>
+This gives us all the groups the user is member of.&nbsp; We ask for only
+the "CN" attributes of the returned groups.&nbsp; We check if "CN" of any
+of the returned groups is one of the groups we have.&nbsp; If yes, we have
+succeeded.&nbsp; If there are no groups returned then we have failed.&nbsp;
+Otherwise, we continue with the nested group check.&nbsp; To perform that
+check, we need DNs of all the groups the user is member of, which we already
+have from the previous search.&nbsp; Now we repeat the search as follows:
+<OL><TT>(| (&amp; (objectclass=groupofuniquenames)</TT>
+<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (| (uniquemember=&lt;grp1DN>)...
+(uniquemember=&lt;grpNDN>))</TT>
+<BR><TT>&nbsp;&nbsp; (&amp; (objectclass=groupofnames)</TT>
+<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (| (member=&lt;grp1DN>)... (member=&lt;grpNDN>))</TT></OL>
+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.
+
+<P>Advantages of this new function is it checks multiple groups at the
+same time.&nbsp; Previously we were performing 2 ldap lookups per group.&nbsp;
+Now we achieve this in a single ldap lookup!
+
+<P><B><BLINK>Caution</BLINK></B>: this function allows multiple groups
+with the same "CN".
+
+<P>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.</UL>
+
+<CENTER><B><FONT SIZE=+1>Changes since Mar 22, 1997</FONT></B></CENTER>
+
+
+<P>Last Update: Apr 17, 1997
+
+<P>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). <B><BLINK>All 3.0 SuiteSpot
+servers supporting client auth need to upgrade to this version.</BLINK></B>
+<H2>
+Changed:</H2>
+
+<UL>
+<LI>
+<B>Exchanged certmap.h &amp; ldaputil.h: </B>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.</LI>
+
+<LI>
+<B>Renamed 'SearchComps' to 'FilterComps'</B>: Shouldn't affect your code
+but may affect tests and documentation.</LI>
+
+<LI>
+<B>'VerifyCert' must be either "on" or "off"</B>: 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.</LI>
+
+<LI>
+<B>Important bug fixes</B>: One bug was causing stack corruption &amp;
+weird unpredictable results. The other important bug was to map 'E' in
+cert to 'MAIL' in LDAP.</LI>
+</UL>
+
+<H2>
+Added:</H2>
+
+<UL>
+<LI>
+<B>Settable 'search function'</B>: User defined search function can be
+set for cert to ldap mapping.</LI>
+
+<LI>
+<B>ldapu_get_cert_ava_val &amp; ldapu_free_cert_ava_val</B>: API functions
+to get &amp; free an attribute's value(s) from either the subject DN or
+the issuer DN from a cert.</LI>
+</UL>
+
+<H2>
+Open Bugs:</H2>
+
+<UL>Following bugs may not get fixed by RTM.</UL>
+
+<UL>
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=51279">51279</A>: 'uniquemember'
+bug</LI>
+
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58474">58474</A>:&nbsp;
+'circular groups' results in infinite loop</LI>
+
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58478">58478</A>: Don't
+allow a space as a valid delimeter for DNComps &amp; FilterComps.</LI>
+
+<LI>
+<A HREF="http://scopus/bugsplat/show_bug.cgi?id=58482">58482</A>: Make
+the 'search function' configurable.</LI>
+
+<BR>
+<HR WIDTH="100%"></UL>
+
+<CENTER>
+<H3>
+Changes since Mar 18, 1997</H3></CENTER>
+
+<DT>
+Last Update: Mar 22, 1997</DT>
+
+<DT>
+A query on how to map a verisign certificate prompted these changes.&nbsp;
+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.&nbsp; 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.&nbsp; 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].</DT>
+
+<H2>
+Changed:</H2>
+
+<UL>
+<LI>
+<B>ldapu_cert_to_ldap_entry</B>: 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 &lt;ServerRoot>/userdb/certmap.conf
+file:</LI>
+
+<UL><TT><FONT SIZE=+1>certmap verisign &lt;verisign's DN></FONT></TT>
+<BR><TT><FONT SIZE=+1>verisign:dncomps</FONT></TT>
+<BR><TT><FONT SIZE=+1>verisign:searchcomps cn, e</FONT></TT></UL>
+The mapped DN will be NULL so basedn will be used.&nbsp; The filter will
+be
+<BR>(&amp; (cn="&lt;user's CN>") (mail="&lt;user's mail>")).&nbsp; The
+new signature of ldapu_cert_to_ldap_entry is as follows:
+<BR>&nbsp;&nbsp;&nbsp; int ldapu_cert_to_ldap_entry(void *cert, LDAP *ld,
+const char *basedn, LDAPMessage **res);
+<LI>
+<B>verify cert functions</B>: A major change in how verify cert functions
+work.&nbsp; This is function is now called even when multiple potential
+matches are found for the cert.&nbsp; The mapping is successful if the
+verify function can reduce the number of matches to exactly one.&nbsp;
+For example, if there are multiple "Joe Smith" entries, at most one of
+those will have the cert in it's "userCertificate" attr.&nbsp; The verify
+function will select that entry.&nbsp; The verify function is called with
+"LDAPMessage *res" containing all the potential matches.&nbsp; It should
+return a pointer to the matched entry in the new "LDAPMessage **entry"
+parameter.&nbsp; The new signature for CertVerifyFn_t is as follows:</LI>
+
+<BR>&nbsp;&nbsp;&nbsp; typedef int (*CertVerifyFn_t)(void *cert, LDAP *ld,
+LDAPMessage *res,
+<BR>&nbsp;
+
+<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+LDAPMessage **entry);
+<LI>
+typedef int (*<B>CertMapInitiFn_t)</B>: Renamed from CertmapInitFn_t.&nbsp;
+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".&nbsp; There was no public API function
+to get the issuerDN from "certinfo". The new signature for CertMapInitFn_t
+is as follows:</LI>
+
+<BR>&nbsp;&nbsp;&nbsp; typedef int (*CertMapInitFn_t)(void *certmap_info,
+const char *issuerName,
+<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+const char *issuerDN);
+<LI>
+(ldapauth.h) <B>ldapu_auth_* functions:</B> 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.</LI>
+</UL>
+
+<DT>
+
+<HR WIDTH="100%"></DT>
+
+<CENTER>
+<H3>
+Changes since Feb 1, 1997</H3></CENTER>
+
+<DT>
+Last Update: Mar 18, 1997</DT>
+
+<DT>
+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.</DT>
+
+<H2>
+Renamed:</H2>
+Following structures and functions are renamed. But this shouldn't affect
+you unless you have written a certmap plugin.
+<DD>
+<TT><FONT SIZE=+1>struct CertMappingFunction_t ---> CertMapFn_t</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>struct CertVerifyFunction_t ---> CertVerifyFn_t</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_set_cert_mapping_function ---> ldapu_set_cert_mapfn</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_get_cert_mapping_function ---> ldapu_get_cert_mapfn</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_set_cert_verify_function ---> ldapu_set_cert_verifyfn</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_get_cert_verify_function ---> ldapu_get_cert_verifyfn</FONT></TT></DD>
+
+<H2>
+Removed: (from .h)</H2>
+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.
+<DD>
+<TT><FONT SIZE=+1>ldapu_cert_mapping_function_default</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_cert_verify_function_default</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_search_cert_dn</FONT></TT></DD>
+
+<DD>
+<TT><FONT SIZE=+1>ldapu_subject_dn_to_ldap_dn</FONT></TT></DD>
+
+<H2>
+Changed:</H2>
+The following changes may affect you. Please review them carefully before
+you upgrade to the latest version of ldaputil.
+<UL>
+<LI>
+<TT>ldapu_auth_userdn_groupdn</TT> -- added <TT>const char *base</TT> argument
+for group within group membership check</LI>
+
+<LI>
+ldap_init and bind calls using <TT>LDAPDatabase_t *ldb</TT> retry once
+if the LDAP server is/went down.</LI>
+
+<LI>
+<TT>typedef CertVerifyFn_t</TT> has different arguments.</LI>
+
+<LI>
+DNs from cert with escaped/quoted characters are correctly handled now.</LI>
+
+<LI>
+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.</LI>
+
+<LI>
+Only the required attrs are retrieved in most <TT>ldap_search_s </TT>calls
+from ldaputil. Some new functions were added to keep older functions the
+same.</LI>
+
+<LI>
+Fixed a core dump in ldapu_free_LDAPDatabase_t when using the local db.</LI>
+
+<LI>
+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.</LI>
+</UL>
+
+<H2>
+Added:</H2>
+
+<UL>
+<LI>
+Documentation to functions in ldaputil.h (customer API) - ldaputil.h is
+the external public API for customers to write there plugins.</LI>
+
+<LI>
+<TT>ldapu_get_cert_der</TT> - returns raw DER encoded cert data</LI>
+
+<LI>
+<TT>ldapu_cert_to_user</TT> - Similar to ldapu_cert_to_ldap_entry but only
+retrieves the 'uid' and 'userCertificate' attributes from LDAP and also
+extracts the user id.</LI>
+
+<LI>
+<TT>ldapu_find_uid_attrs</TT> and <TT>ldapu_find_group_attrs</TT> - 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.</LI>
+
+<LI>
+<TT>ldapu_certinfo_delete, ldapu_certinfo_modify and ldapu_certinfo_save</TT>
+- API for the certmap config file GUI tool which is not yet developed.
+Any volunteers?</LI>
+</UL>
+
+<H2>
+<FONT SIZE=+1>TODO/Bugs:</FONT></H2>
+
+<UL>
+<LI>
+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:</LI>
+
+<DD>
+uniquemember: cn=Joe Smith,o=Netscape,c=US#jsmith</DD>
+
+<DT>
+where, 'jsmith' is one of the values of the 'x500UniqueIdentifier' attribute
+for the Joe Smith entry.</DT>
+
+<DT>
+</DT>
+</UL>
+
+</BODY>
+</HTML>
diff --git a/lib/ldaputil/ldaputili.h b/lib/ldaputil/ldaputili.h
new file mode 100644
index 00000000..fe8a65f3
--- /dev/null
+++ b/lib/ldaputil/ldaputili.h
@@ -0,0 +1,62 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _LDAPU_LDAPUTILI_H
+#define _LDAPU_LDAPUTILI_H
+
+#include <ldaputil/ldaputil.h>
+
+#include <ssl.h>
+
+#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/utest/Makefile b/lib/ldaputil/utest/Makefile
new file mode 100644
index 00000000..e6f2e5c6
--- /dev/null
+++ b/lib/ldaputil/utest/Makefile
@@ -0,0 +1,117 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# Makefile for ldaputil unit test.
+#
+MCOM_ROOT=../../../..
+MODULE=LibLdapUtil
+
+OBJDEST=.
+UTESTDEST=utest
+
+include ../../../nsconfig.mk
+
+MODULE_CFLAGS=-I$(NSROOT)/include
+
+include $(INCLUDE_DEPENDS)
+
+TESTFLAGS = -DUTEST -DDBG_PRINT -DDONT_USE_LDAP_SSL
+
+CC=CC
+PURIFY=
+
+CSRC = stubs.c
+CPPSRC = auth.cpp
+TSRC = authtest
+SRC = $(CSRC) $(CPPSRC) $(TSRC)
+XSRC = ../ldapauth.c ../ldapdb.c ../errors.c ../dbconf.c ../certmap.c ../ldapauth.c ../init.c ../encode.c
+
+COBJ = $(CPPSRC:%.cpp=%.o) $(CSRC:%.c=%.o)
+XOBJ = $(XSRC:../%.c=../utest/%.o)
+
+ifeq ($(ARCH), WINNT)
+ BINS=./auth.exe
+ LDAP_LIBLINK = $(addprefix $(LDAP_LIBPATH)/, $(addsuffix .lib, $(LDAP_LIBNAMES)))
+ XLIBS = ${LDAP_LIBLINK}
+ LOCAL_LINK_EXE = link -OUT:"$@" /MAP $(ARCH_LINK_DEBUG) $(LCFLAGS) /NOLOGO \
+ /PDB:NONE /INCREMENTAL:NO /SUBSYSTEM:windows $(XLIBS)
+else
+ BINS = auth
+ LDAP_LIBLINK = -L$(LDAP_LIBPATH) $(addprefix -l, ${LDAP_SOLIB_NAMES})
+endif
+
+ifeq ($(ARCH), SOLARIS)
+ XLIBS = -R$(LDAP_LIBPATH) ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC) -lthread -lposix4 -lsocket -lnsl -ldl
+else
+ ifeq ($(ARCH), IRIX)
+ XLIBS = ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC)
+ else
+ ifeq ($(ARCH), WINNT)
+ echo "XLIBS = ${XLIBS}"
+ else
+ #Other UNIX platforms
+ XLIBS = -R$(LDAP_LIBPATH) ${LDAP_LIBLINK} $(LIBNSPR) $(LIBSEC) -lthread -lposix4 -lsocket -lnsl -ldl
+ endif
+ endif
+endif
+
+PLUGIN = plugin.so
+
+all: $(LIBLDAP) $(COBJ) $(TSRC) ${BINS} $(PLUGIN)
+ ./authtest 2> test.out
+ diff test.out test.ref
+ @echo
+ @echo "The unit test is passed if there is no diff output, and the"
+ @echo "Purify window shows no errors and 0 bytes leaked."
+ @echo
+ @echo "Run - gmake coverage - manually to get code coverage analysis."
+ @echo
+
+auth: $(XOBJ) $(COBJ)
+ $(PURIFY) $(CC) $(XLIBS) $^ -o $@
+
+auth.exe: $(XOBJ) $(COBJ)
+ $(PURIFY) $(LOCAL_LINK_EXE) $(XOBJ) $(COBJ) ${XLIBS}
+
+testcert: testcert.o $(XOBJ) ../utest/cert.o
+ $(PURIFY) $(CC) $(XLIBS) $^ -o $@
+
+%.o:%.c
+ $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) $< -o $@
+
+../utest/%.o:../%.c
+ $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o
+
+../utest/%.o:../%.cpp
+ $(PURIFY) $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o
+
+PLUGIN_INC = ./include
+
+$(PLUGIN_INC):
+ mkdir -p include
+
+certmap.h: ../../../include/ldaputil/extcmap.h
+ \rm -rf $(PLUGIN_INC)/$@
+ cp $^ $(PLUGIN_INC)/$@
+
+ldap.h: $(LDAP_INCLUDE)/ldap.h
+ \rm -rf $(PLUGIN_INC)/$@
+ cp $^ $(PLUGIN_INC)/$@
+
+lber.h: $(LDAP_INCLUDE)/lber.h
+ \rm -rf $(PLUGIN_INC)/$@
+ cp $^ $(PLUGIN_INC)/$@
+
+example.o: example.c $(PLUGIN_INC) certmap.h ldap.h lber.h
+ $(CC) -c -I$(PLUGIN_INC) $*.c -o $(OBJDEST)/$*.o
+
+plugin.o: plugin.c $(PLUGIN_INC) certmap.h ldap.h lber.h
+ $(PURIFY) $(CC) -c -I. -I$(PLUGIN_INC) $*.c -o $(OBJDEST)/$*.o
+
+$(PLUGIN): plugin.o
+ $(LINK_DLL) $^
diff --git a/lib/ldaputil/utest/auth.cpp b/lib/ldaputil/utest/auth.cpp
new file mode 100644
index 00000000..e952f742
--- /dev/null
+++ b/lib/ldaputil/utest/auth.cpp
@@ -0,0 +1,574 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <prinit.h> // for PR_Init
+#include <prpriv.h> // for PR_Exit
+#include <ldaputil/certmap.h>
+#include <ldaputil/init.h>
+#include <ldaputil/ldapdb.h>
+#include <ldaputil/ldapauth.h>
+#include <ldaputil/dbconf.h>
+#include <ldaputil/ldaputil.h>
+#include <ldap.h>
+
+static const char* dllname = "plugin.so";
+
+char *global_issuer_dn = "o=Netscape Communications, c=US";
+
+#define NSPR_INIT(Program) (PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 8))
+
+static int ldapu_certinfo_save_test (const char *fname, const char *old_fname)
+{
+ int rv;
+
+ /* Read the original certmap config file first */
+ rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ }
+
+ return rv;
+}
+
+static int ldapu_certinfo_delete_test (const char *fname, const char *old_fname)
+{
+ int rv;
+
+ /* Read the original certmap config file first */
+ rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ /* rv = ldapu_certinfo_delete("o=Ace Industry, c=US"); */
+ rv = ldapu_certinfo_delete("o=Netscape Communications, c=US");
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ }
+
+ return rv;
+}
+
+static int ldapu_certinfo_new_test (const char *fname, const char *old_fname)
+{
+ int rv;
+ LDAPUPropValList_t *propval_list;
+ LDAPUPropVal_t *propval;
+
+ /* Read the original certmap config file first */
+ rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ /* Setup propval_list */
+ rv = ldapu_list_alloc(&propval_list);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_propval_alloc("prop1", "val1", &propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_list_add_info(propval_list, propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_propval_alloc("prop2", "val2", &propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_list_add_info(propval_list, propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_propval_alloc("prop3", 0, &propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_list_add_info(propval_list, propval);
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ rv = ldapu_certinfo_modify("newmap", "o=Mcom Communications, c=US",
+ propval_list);
+
+ ldapu_propval_list_free(propval_list);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ }
+
+ return rv;
+}
+
+static int get_dbnames_test (const char *mapfile)
+{
+ char **names;
+ int cnt;
+ int rv;
+ int i;
+
+ rv = dbconf_get_dbnames(mapfile, &names, &cnt);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "get_dbnames_test failed. Reason: %s\n",
+ ldapu_err2string(rv));
+ }
+ else {
+ for(i = 0; i < cnt; i++) {
+ fprintf(stderr, "\tdbname[%d] = \"%s\"\n",
+ i, names[i]);
+ }
+ }
+
+ dbconf_free_dbnames(names);
+
+ return rv;
+}
+
+static int case_ignore_strcmp (const char *s1, const char *s2)
+{
+ int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */
+
+ if (!s1) return !s2 ? 0 : 0-tolower(*s2);
+ else if (!s2) return tolower(*s1);
+
+ while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; }
+
+ if (!*s1)
+ return *s2 ? 0-tolower(*s2) : 0;
+ else if (!*s2)
+ return tolower(*s1);
+ else
+ return ls1 - ls2;
+}
+
+#define STRCASECMP3(s1, s2, rv) \
+{ \
+ int i = case_ignore_strcmp(s1, s2); \
+ fprintf(stderr, "strcasecmp(\"%s\", \"%s\")\t=\t%d\t%s\tExpected: %d\n", \
+ s1 ? s1 : "<NULL>", s2 ? s2 : "<NULL>", \
+ i, i == rv ? "SUCCESS" : "FAILED", rv); \
+}
+
+#ifndef XP_WIN32
+#define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, strcasecmp(s1, s2))
+#else
+#define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, case_ignore_strcmp(s1, s2))
+#endif
+
+static void strcasecmp_test ()
+{
+ STRCASECMP3(0, "aBcD", 0-tolower('a'));
+ STRCASECMP3(0, 0, 0);
+ STRCASECMP3("aBcD", 0, tolower('a'));
+
+ STRCASECMP("AbCd", "aBcD");
+ STRCASECMP("AbCd", "abcd");
+ STRCASECMP("ABCD", "ABCD");
+ STRCASECMP("abcd", "abcd");
+
+ STRCASECMP("AbCd", "aBcD3");
+ STRCASECMP("AbCd", "abcd3");
+ STRCASECMP("ABCD", "ABCD3");
+ STRCASECMP("abcd", "abcd3");
+
+ STRCASECMP("AbCd1", "aBcD");
+ STRCASECMP("AbCd2", "abcd");
+ STRCASECMP("ABCDX", "ABCD");
+ STRCASECMP("abcdY", "abcd");
+
+ STRCASECMP("AbCd5", "aBcD1");
+ STRCASECMP("AbCd5", "abcd1");
+ STRCASECMP("ABCD5", "ABCD1");
+ STRCASECMP("abcd5", "abcd1");
+
+ STRCASECMP("AbCd2", "aBcDp");
+ STRCASECMP("AbCd2", "abcdQ");
+ STRCASECMP("ABCD2", "ABCDr");
+ STRCASECMP("abcd2", "abcdS");
+}
+
+static int certmap_tests (const char *config_file) { return 0; }
+
+static int read_config_test (const char *config_file, const char *dbname,
+ const char *url,
+ const char *binddn, const char *bindpw)
+{
+ int rv;
+ DBConfDBInfo_t *db_info;
+ char *dn;
+ char *pw;
+
+ rv = dbconf_read_default_dbinfo(config_file, &db_info);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "config_test failed: %s\n",
+ ldapu_err2string(rv));
+ return LDAPU_FAILED;
+ }
+
+ if (strcmp(db_info->dbname, dbname) ||
+ strcmp(db_info->url, url)) {
+ fprintf(stderr, "config_test failed: %s\n",
+ "first line in config file is wrong");
+ return LDAPU_FAILED;
+ }
+
+ if ((ldapu_dbinfo_attrval(db_info, "binddn", &dn) != LDAPU_SUCCESS) ||
+ (ldapu_dbinfo_attrval(db_info, "bindpw", &pw) != LDAPU_SUCCESS))
+ {
+ fprintf(stderr, "config_test failed: %s\n",
+ "properties are missing");
+ return LDAPU_FAILED;
+ }
+
+ if (strcmp(dn, binddn) ||
+ strcmp(pw, bindpw)) {
+ fprintf(stderr, "config_test failed: %s\n",
+ "property values are wrong");
+ return LDAPU_FAILED;
+ }
+
+ fprintf(stderr, "binddn from config file: \"%s\"\n", dn);
+ fprintf(stderr, "bindpw from config file: \"%s\"\n", pw);
+
+ /* cleanup */
+ dbconf_free_dbinfo(db_info);
+ free(dn);
+ free(pw);
+
+ return LDAPU_SUCCESS;
+}
+
+static int config_test (const char *binddn, const char *bindpw)
+{
+ char *config_file = "config_out.conf";
+ FILE *fp = fopen(config_file, "w");
+ const char *dbname = "default";
+ const char *url = "file:/foobar/path";
+ int rv;
+
+ if (!fp) return LDAPU_FAILED;
+
+ dbconf_output_db_directive(fp, dbname, url);
+ dbconf_output_propval(fp, dbname, "binddn", binddn, 0);
+ dbconf_output_propval(fp, dbname, "bindpw", bindpw, 1);
+
+ fclose(fp);
+
+ fprintf(stderr, "Config file written: %s\n", config_file);
+
+ rv = read_config_test(config_file, dbname, url, binddn, bindpw);
+
+ return rv;
+}
+
+static int
+compare_groupid(const void *arg, const char *group, const int len)
+{
+ auto const char* groupid = (const char*)arg;
+ auto int err = LDAPU_FAILED;
+ if (len == strlen (groupid) && !strncasecmp (groupid, group, len)) {
+ err = LDAPU_SUCCESS;
+ }
+ return err;
+}
+
+static int
+compare_group(LDAP* directory, LDAPMessage* entry, void* set)
+{
+ auto int err = LDAPU_FAILED;
+ auto char** vals = ldap_get_values (directory, entry, "CN");
+ if (vals) {
+ auto char** val;
+ for (val = vals; *val; ++val) {
+ if (!strcasecmp (*val, (char*)set)) {
+ err = LDAPU_SUCCESS;
+ break;
+ }
+ }
+ ldap_value_free (vals);
+ }
+ return err;
+}
+
+int perform_test (int argc, char *argv[])
+{
+ int test_type;
+ int retval = LDAPU_SUCCESS;
+ DBConfDBInfo_t *db_info;
+ LDAPDatabase_t *ldb;
+ LDAP *ld;
+ char *dbmap_file = "dblist.conf";
+ char *binddn = 0;
+ char *bindpw = 0;
+ char *basedn;
+ int retry = 1;
+ int rv;
+
+ fprintf(stderr, "\nStart of test: ./auth %s \"%s\" \"%s\"\n",
+ argv[1], argv[2], argv[3]);
+
+ rv = dbconf_read_default_dbinfo(dbmap_file, &db_info);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "Error reading dbmap file \"%s\". Reason: %s\n",
+ dbmap_file, ldapu_err2string(rv));
+ return rv;
+ }
+
+ ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDDN, &binddn);
+ ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDPW, &bindpw);
+
+ rv = ldapu_url_parse (db_info->url, binddn, bindpw, &ldb);
+ free(binddn);
+ free(bindpw);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "Error parsing ldap url \"%s\". Reason: %s\n",
+ db_info->url, ldapu_err2string(rv));
+ return rv;
+ }
+
+ basedn = ldb->basedn;
+
+ test_type = atoi(argv[1]);
+
+ retry = 1;
+
+ while(retry) {
+ retry = 0;
+
+ rv = ldapu_ldap_init_and_bind (ldb);
+
+ if (rv != LDAPU_SUCCESS) {
+ fprintf(stderr, "Error initializing connection to LDAP. Reason: %s\n",
+ ldapu_err2string(rv));
+ return rv;
+ }
+
+ ld = ldb->ld;
+
+ switch(test_type) {
+ case 1:
+ fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupdn:\t\"%s\"\n",
+ argv[2], argv[3]);
+ retval = ldapu_auth_userdn_groupdn(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 2:
+ fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupdn:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_uid_groupdn(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 3:
+ fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_uid_groupid(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 4:
+ fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_userdn_groupid(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 5:
+ fprintf(stderr, "\nuserdn:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_userdn_attrfilter(ld, argv[2], argv[3]);
+ break;
+
+ case 6:
+ fprintf(stderr, "\nuid:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_uid_attrfilter(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 7:
+ fprintf(stderr, "\nuserdn:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_userdn_password(ld, argv[2], argv[3]);
+ break;
+
+ case 8:
+ fprintf(stderr, "\nuid:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_uid_password(ld, argv[2], argv[3], basedn);
+ break;
+
+ case 9: {
+ /* plugin test */
+ LDAPMessage *entry = 0;
+ LDAPMessage *res = 0;
+
+ fprintf(stderr, "Cert Map issuer DN: \"%s\"\n", argv[2]);
+ fprintf(stderr, "Cert Map subject DN: \"%s\"\n", argv[3]);
+ retval = ldaputil_init("certmap.conf", dllname, NULL, NULL, NULL);
+
+ if (retval != LDAPU_SUCCESS) {
+ fprintf(stderr, "Cert Map info test failed. Reason: %s\n",
+ ldapu_err2string(retval));
+ break;
+ }
+
+ if (*(argv[2]))
+ global_issuer_dn = argv[2];
+ else
+ global_issuer_dn = 0;
+
+ retval = ldapu_cert_to_ldap_entry(argv[3], ld, ldb->basedn, &res);
+
+ if (retval == LDAPU_SUCCESS) {
+ char *dn;
+
+ entry = ldap_first_entry(ld, res);
+ dn = ldap_get_dn(ld, entry);
+ fprintf(stderr, "Matched entry to cert: \"%s\"\n", dn);
+ ldap_memfree(dn);
+ }
+ else if (retval == LDAPU_FAILED) {
+ /* Not an error but couldn't map the cert */
+ }
+ else {
+ fprintf(stderr, "Cert Map info test failed. Reason: %s\n",
+ ldapu_err2string(retval));
+ break;
+ }
+
+ /* TEMPORARY -- when & how to free the entry */
+ if (res) ldap_msgfree(res);
+
+ break;
+ } /* case 9 */
+
+ case 10:
+ if ((retval = config_test(argv[2], argv[3])) == LDAPU_SUCCESS) {
+ fprintf(stderr, "Config file test succeeded\n");
+ }
+ else {
+ fprintf(stderr, "Config file test failed\n");
+ }
+ break;
+
+ case 11:
+ retval = get_dbnames_test(argv[2]);
+ break;
+
+ case 12:
+ retval = ldapu_certinfo_save_test(argv[2], argv[3]);
+ break;
+
+ case 13:
+ retval = ldapu_certinfo_delete_test(argv[2], argv[3]);
+ break;
+
+ case 14:
+ retval = ldapu_certinfo_new_test(argv[2], argv[3]);
+ break;
+
+ case 15:
+ fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
+ {
+ auto LDAPU_DNList_t* userDNs = ldapu_DNList_alloc();
+ ldapu_DNList_add(userDNs, argv[2]);
+ retval = ldapu_auth_usercert_groups(ld, basedn, userDNs, NULL,
+ argv[3], compare_group, 30, NULL);
+ ldapu_DNList_free(userDNs);
+ }
+ break;
+
+ case 16:
+ fprintf(stderr, "\nuserCert:\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
+ retval = ldapu_auth_usercert_groupids(ld, NULL/*userDN*/, argv[2], argv[3],
+ compare_groupid, basedn, NULL/*group_out*/);
+ break;
+
+ } /* switch */
+
+ if (retval == LDAP_SERVER_DOWN) {
+ /* retry */
+ retry = 1;
+ ldb->ld = 0;
+ }
+ else if (retval == LDAPU_SUCCESS) {
+ fprintf(stderr, "Authentication succeeded.\n");
+ }
+ else {
+ fprintf(stderr, "Authentication failed.\n");
+ }
+ }
+
+ /* cleanup */
+// ldapu_free_LDAPDatabase_t(ldb);
+// dbconf_free_dbinfo(db_info);
+// ldaputil_exit();
+ return retval;
+}
+
+int main (int argc, char *argv[])
+{
+ int rv;
+
+ NSPR_INIT("auth");
+
+ if (argc != 4) {
+ fprintf(stderr, "argc = %d\n", argc);
+ fprintf(stderr, "usage: %s test_type user_dn group_dn\n", argv[0]);
+ fprintf(stderr, "\t%s 1 <userdn> <groupdn>\n", argv[0]);
+ fprintf(stderr, "\t%s 2 <uid> <groupdn>\n", argv[0]);
+ fprintf(stderr, "\t%s 3 <uid> <groupid>\n", argv[0]);
+ fprintf(stderr, "\t%s 4 <userdn> <groupid>\n", argv[0]);
+ fprintf(stderr, "\t%s 5 <userdn> <attrFilter>\n", argv[0]);
+ fprintf(stderr, "\t%s 6 <uid> <attrFilter>\n", argv[0]);
+ fprintf(stderr, "\t%s 7 <userdn> <password>\n", argv[0]);
+ fprintf(stderr, "\t%s 8 <uid> <password>\n", argv[0]);
+ fprintf(stderr, "\t%s 9 <certmap.conf> <subjectDN>\n", argv[0]);
+ fprintf(stderr, "\t%s 10 <binddn> <bindpw>\n", argv[0]);
+ fprintf(stderr, "\t%s 11 <dbmap> <ignore>\n", argv[0]);
+ fprintf(stderr, "\t%s 12 <newconfig> <oldconfig> ... to test save\n", argv[0]);
+ fprintf(stderr, "\t%s 13 <newconfig> <oldconfig> ... to test delete\n", argv[0]);
+ fprintf(stderr, "\t%s 14 <newconfig> <oldconfig> ... to test add\n", argv[0]);
+ fprintf(stderr, "\t%s 15 <userdn> <groupid>\n", argv[0]);
+ fprintf(stderr, "\t%s 16 <userCertDescription> <groupid>\n", argv[0]);
+ exit(LDAP_PARAM_ERROR);
+ }
+
+ rv = perform_test(argc, argv);
+ /* PR_Exit(); */
+
+ return rv;
+}
+
diff --git a/lib/ldaputil/utest/authtest b/lib/ldaputil/utest/authtest
new file mode 100755
index 00000000..c713349b
--- /dev/null
+++ b/lib/ldaputil/utest/authtest
@@ -0,0 +1,106 @@
+#!/bin/ksh
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# setup for test
+USERDN="cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+USER2DN="cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+
+UID="hmiller"
+U2ID="scarter"
+
+GROUPDN="cn=Directory Administrators, o=Ace Industry, c=US"
+GROUPID="Directory Administrators"
+
+ATTRFILTER="mail=hmiller@aceindustry.com"
+ATTR2FILTER="mail=scarter@aceindustry.com"
+
+function check_result {
+ echo "\nStart of test: $1 $2 \"$3\" \"$4\""
+ if ( `$1 $2 "$3" "$4"` ) then
+ if [ $5 == "fail" ]; then
+ echo "**** Test Failed ****";
+ else
+ echo "Test Succeeded";
+ fi
+ else
+ if [ $5 == "fail" ]; then
+ echo "Test Succeeded";
+ else
+ echo "**** Test Failed ****";
+ fi
+ fi
+}
+
+function must_fail {
+ check_result $1 $2 "$3" "$4" "fail";
+}
+
+function must_succeed {
+ check_result $1 $2 "$3" "$4" "success";
+}
+
+# test for <userdn> <groupdn>
+must_succeed ./auth 1 "${USERDN}" "${GROUPDN}"
+must_fail ./auth 1 "${USER2DN}" "${GROUPDN}"
+
+# test for <uid> <groupdn>
+must_succeed ./auth 2 "${UID}" "${GROUPDN}"
+must_fail ./auth 2 "${U2ID}" "${GROUPDN}"
+
+# test for <uid> <groupid>
+must_succeed ./auth 3 "${UID}" "${GROUPID}"
+must_fail ./auth 3 "${U2ID}" "${GROUPID}"
+
+# test for <userdn> <groupid>
+must_succeed ./auth 4 "${USERDN}" "${GROUPID}"
+must_fail ./auth 4 "${USER2DN}" "${GROUPID}"
+must_succeed ./auth 15 "${USERDN}" "${GROUPID}"
+must_fail ./auth 15 "${USER2DN}" "${GROUPID}"
+must_succeed ./auth 16 "{${USERDN}" "${GROUPID}"
+must_fail ./auth 16 "{${USER2DN}" "${GROUPID}"
+
+# test for <userdn> <attrFilter>
+must_succeed ./auth 5 "${USERDN}" "${ATTRFILTER}"
+must_fail ./auth 5 "${USERDN}" "${ATTR2FILTER}"
+must_fail ./auth 5 "${USER2DN}" "${ATTRFILTER}"
+
+# test for <uid> <attrFilter>
+must_succeed ./auth 6 "${UID}" "${ATTRFILTER}"
+must_fail ./auth 6 "${UID}" "${ATTR2FILTER}"
+must_fail ./auth 6 "${U2ID}" "${ATTRFILTER}"
+
+# test for <userdn> <password>
+must_succeed ./auth 7 "${USERDN}" "hillock"
+must_fail ./auth 7 "${USERDN}" "garbage"
+
+# test for <uid> <password>
+must_succeed ./auth 8 "${UID}" "hillock"
+must_fail ./auth 8 "${UID}" "garbage"
+
+#test for cert to ldap entry mapping
+must_succeed ./auth 9 "o=Ace Industry, c=US" "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+#must_fail ./auth 9 "default" "cn=Kirsten Vaughan, o=Ace Industry, c=US"
+
+# test for encode/decode bindpw
+must_succeed ./auth 10 "cn=Foo Bar, o=Netscape Communication, c=US" "foobar"
+
+# test for reading dbnames from dbswitch.conf file
+must_succeed ./auth 11 dblist.conf ignore
+
+# test for saving certmap info
+must_succeed ./auth 12 certmap.new certmap.conf
+cat certmap.conf certmap.new 1>&2
+
+# test for delete certmap info
+must_succeed ./auth 13 certmap.new certmap.conf
+cat certmap.conf certmap.new 1>&2
+
+# test for add certmap info
+must_succeed ./auth 14 certmap.new certmap.conf
+cat certmap.conf certmap.new 1>&2
+
diff --git a/lib/ldaputil/utest/certmap.conf b/lib/ldaputil/utest/certmap.conf
new file mode 100644
index 00000000..53ab6fca
--- /dev/null
+++ b/lib/ldaputil/utest/certmap.conf
@@ -0,0 +1,36 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+#default:DNComps o, ou ,c
+#default:FilterComps cn
+
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+#default1:verifycert
+
+# Following line has trailing spaces
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
diff --git a/lib/ldaputil/utest/dblist.conf b/lib/ldaputil/utest/dblist.conf
new file mode 100644
index 00000000..c38580ac
--- /dev/null
+++ b/lib/ldaputil/utest/dblist.conf
@@ -0,0 +1,15 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+directory default ldap://:3334/o=Airius.com
+directory default1 ldap:///o=Ace Industry, c=US
+directory default2 ldap:///o=Ace Industry, c=US
+directory default3 ldap:///o=Ace Industry, c=US
+directory default4 ldap:///o=Ace Industry, c=US
+directory default5 ldap:///o=Ace Industry, c=US
+directory default6 ldap:///o=Ace Industry, c=US
diff --git a/lib/ldaputil/utest/example.c b/lib/ldaputil/utest/example.c
new file mode 100644
index 00000000..185fbe51
--- /dev/null
+++ b/lib/ldaputil/utest/example.c
@@ -0,0 +1,116 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+
+#include <certmap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The init function must be defined extern "C" if using a C++ compiler */
+int plugin_init_fn (void *certmap_info, const char *issuerName,
+ const char *issuerDN);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+static int extract_ldapdn_and_filter (const char *subjdn, void *certmap_info,
+ char **ldapDN, char **filter)
+{
+ /* extract the ldapDN and filter from subjdn */
+ /* You can also use the ldapu_certmap_info_attrval function to get value
+ of a config file parameter for the certmap_info. */
+ return LDAPU_SUCCESS;
+}
+
+static int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info,
+ char **ldapDN, char **filter)
+{
+ char *subjdn;
+ int rv;
+
+ fprintf(stderr, "plugin_mapping_fn called.\n");
+ rv = ldapu_get_cert_subject_dn(cert, &subjdn);
+
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ *ldapDN = 0;
+ *filter = 0;
+
+ rv = extract_ldapdn_and_filter(subjdn, certmap_info, ldapDN, filter);
+
+ if (rv != LDAPU_SUCCESS) {
+ /* This function must return LDAPU_FAILED or
+ LDAPU_CERT_MAP_FUNCTION_FAILED on error */
+ return LDAPU_CERT_MAP_FUNCTION_FAILED;
+ }
+
+ return LDAPU_SUCCESS;
+}
+
+static int plugin_cmp_certs (void *subject_cert,
+ void *entry_cert_binary,
+ unsigned long entry_cert_len)
+{
+ /* compare the certs */
+ return LDAPU_SUCCESS;
+}
+
+static int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info,
+ LDAPMessage *res, LDAPMessage **entry_out)
+{
+ LDAPMessage *entry;
+ struct berval **bvals;
+ char *cert_attr = "userCertificate;binary";
+ int i;
+ int rv;
+
+ fprintf(stderr, "plugin_verify_fn called.\n");
+ *entry_out = 0;
+
+ for (entry = ldap_first_entry(ld, res); entry != NULL;
+ entry = ldap_next_entry(ld, entry))
+ {
+ if ((bvals = ldap_get_values_len(ld, entry, cert_attr)) == NULL) {
+ rv = LDAPU_CERT_VERIFY_FUNCTION_FAILED;
+ /* Maybe one of the remaining entries will match */
+ continue;
+ }
+
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ rv = plugin_cmp_certs (cert,
+ bvals[i]->bv_val,
+ bvals[i]->bv_len);
+
+ if (rv == LDAPU_SUCCESS) {
+ break;
+ }
+ }
+
+ ldap_value_free_len(bvals);
+
+ if (rv == LDAPU_SUCCESS) {
+ *entry_out = entry;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+int plugin_init_fn (void *certmap_info, const char *issuerName,
+ const char *issuerDN)
+{
+ fprintf(stderr, "plugin_init_fn called.\n");
+ ldapu_set_cert_mapfn(issuerDN, plugin_mapping_fn);
+ ldapu_set_cert_verifyfn(issuerDN, plugin_verify_fn);
+ return LDAPU_SUCCESS;
+}
+
diff --git a/lib/ldaputil/utest/plugin.c b/lib/ldaputil/utest/plugin.c
new file mode 100644
index 00000000..29f7765b
--- /dev/null
+++ b/lib/ldaputil/utest/plugin.c
@@ -0,0 +1,115 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <plugin.h> /* must define extern "C" functions */
+#include <certmap.h> /* Public Certmap API */
+
+static CertSearchFn_t default_searchfn = 0;
+
+static int plugin_attr_val (void *cert, int which_dn, const char *attr)
+{
+ char **val;
+ int rv = ldapu_get_cert_ava_val(cert, which_dn, attr, &val);
+ char **attr_val = val; /* preserve the pointer for free */
+
+ if (rv != LDAPU_SUCCESS || !val) {
+ fprintf(stderr, "\t%s: *** Failed ***\n", attr);
+ }
+ else if (!*val) {
+ fprintf(stderr, "\t%s: *** Empty ***\n", attr);
+ }
+ else {
+ fprintf(stderr, "\t%s: \"%s\"", attr, *val++);
+ while(*val) {
+ fprintf(stderr, ", \"%s\"", *val++);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ ldapu_free_cert_ava_val(attr_val);
+
+ return LDAPU_SUCCESS;
+}
+
+static int plugin_mapping_fn (void *cert, LDAP *ld, void *certmap_info,
+ char **ldapDN, char **filter)
+{
+ char *subjdn;
+ char *issuerDN;
+ char *ptr;
+ char *comma;
+
+ fprintf(stderr, "plugin_mapping_fn called.\n");
+ ldapu_get_cert_subject_dn(cert, &subjdn);
+ ldapu_get_cert_issuer_dn(cert, &issuerDN);
+
+ fprintf(stderr, "Value of attrs from subject DN & issuer DN:\n");
+ fprintf(stderr, "\tCert: \"%s\"\n", (char *)cert);
+ fprintf(stderr, "\tsubjdn: \"%s\"\n", subjdn);
+ plugin_attr_val(cert, LDAPU_SUBJECT_DN, "cn");
+ plugin_attr_val(cert, LDAPU_SUBJECT_DN, "ou");
+ plugin_attr_val(cert, LDAPU_SUBJECT_DN, "o");
+ plugin_attr_val(cert, LDAPU_SUBJECT_DN, "c");
+ fprintf(stderr, "\tissuerDN: \"%s\"\n", issuerDN);
+ plugin_attr_val(cert, LDAPU_ISSUER_DN, "cn");
+ plugin_attr_val(cert, LDAPU_ISSUER_DN, "ou");
+ plugin_attr_val(cert, LDAPU_ISSUER_DN, "o");
+ plugin_attr_val(cert, LDAPU_ISSUER_DN, "c");
+
+ if (subjdn && *subjdn) {
+ comma = ptr = strchr(subjdn, ',');
+
+ while(*ptr == ',' || isspace(*ptr)) ptr++;
+ *ldapDN = strdup(ptr);
+
+ /* Set filter to the first AVA in the subjdn */
+ *filter = subjdn;
+ *comma = 0;
+ }
+ else {
+ *ldapDN = 0;
+ *filter = 0;
+ }
+
+ return LDAPU_SUCCESS;
+}
+
+static 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)
+{
+ fprintf(stderr, "plugin_search_fn called.\n");
+ return (*default_searchfn)(cert, ld, certmap_info, basedn, dn, filter,
+ attrs, res);
+}
+
+static int plugin_verify_fn (void *cert, LDAP *ld, void *certmap_info,
+ LDAPMessage *res, LDAPMessage **entry)
+{
+ fprintf(stderr, "plugin_verify_fn called.\n");
+ *entry = ldap_first_entry(ld, res);
+ return LDAPU_SUCCESS;
+}
+
+int plugin_init_fn (void *certmap_info, const char *issuerName,
+ const char *issuerDN)
+{
+ 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/utest/plugin.h b/lib/ldaputil/utest/plugin.h
new file mode 100644
index 00000000..124a121a
--- /dev/null
+++ b/lib/ldaputil/utest/plugin.h
@@ -0,0 +1,20 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _CERTMAP_PLUGIN_H
+#define _CERTMAP_PLUGIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int plugin_init_fn (void *certmap_info, const char *issuerName,
+ const char *issuerDN);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CERTMAP_PLUGIN_H */
diff --git a/lib/ldaputil/utest/stubs.c b/lib/ldaputil/utest/stubs.c
new file mode 100644
index 00000000..03717331
--- /dev/null
+++ b/lib/ldaputil/utest/stubs.c
@@ -0,0 +1,107 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <ctype.h> /* isspace */
+#include <string.h>
+#include <stdio.h> /* sprintf */
+#include <stdlib.h> /* malloc */
+
+#include <ldap.h>
+#include <ldaputil/certmap.h>
+#include <ldaputil/cert.h>
+#include <ldaputil/errors.h>
+
+#define BIG_LINE 1024
+
+NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN)
+{
+ char *cert = (char *)cert_in;
+
+ *subjectDN = strdup((char *)cert);
+ return *subjectDN ? LDAPU_SUCCESS : LDAPU_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert, char **issuerDN)
+{
+ extern char *global_issuer_dn;
+ /* TEMPORARY -- not implemented yet*/
+ *issuerDN = global_issuer_dn ? strdup(global_issuer_dn) : 0;
+ return LDAPU_SUCCESS;
+}
+
+/* A stub to remove link errors -- ignore SSL */
+LDAP *ldapssl_init (const char *host, int port, int secure)
+{
+ LDAP *ld = 0;
+
+ if ((ld = ldap_init(host, port)) == NULL) {
+ fprintf(stderr, "ldap_init: Failed to initialize connection");
+ return(0);
+ }
+
+ return ld;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn,
+ const char *attr, char ***val_out)
+{
+ int rv;
+ char *cert_dn;
+ char **ptr;
+ char **val;
+ char *dnptr;
+ char attr_eq1[BIG_LINE];
+ char attr_eq2[BIG_LINE];
+ char *comma;
+
+ *val_out = 0;
+
+ if (which_dn == LDAPU_SUBJECT_DN)
+ rv = ldapu_get_cert_subject_dn(cert_in, &cert_dn);
+ else if (which_dn == LDAPU_ISSUER_DN)
+ rv = ldapu_get_cert_issuer_dn(cert_in, &cert_dn);
+ else
+ return LDAPU_ERR_INVALID_ARGUMENT;
+
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ val = (char **)malloc(32*sizeof(char *));
+
+ if (!val) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ ptr = val;
+ sprintf(attr_eq1, "%s =", attr);
+ sprintf(attr_eq2, "%s=", attr);
+
+ while(cert_dn &&
+ ((dnptr = strstr(cert_dn, attr_eq1)) ||
+ (dnptr = strstr(cert_dn, attr_eq2))))
+ {
+ dnptr = strchr(dnptr, '=');
+ dnptr++;
+ while(isspace(*dnptr)) dnptr++;
+ comma = strchr(dnptr, ',');
+
+ if (comma) {
+ *ptr = (char *)malloc((comma-dnptr+1)*sizeof(char));
+ strncpy(*ptr, dnptr, (comma-dnptr));
+ (*ptr++)[comma-dnptr] = 0;
+ }
+ else {
+ *ptr++ = strdup(dnptr);
+ }
+ cert_dn = comma;
+ }
+
+ *ptr = 0;
+ *val_out = val;
+ return LDAPU_SUCCESS;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der,
+ unsigned int *len)
+{
+ return LDAPU_FAILED;
+}
diff --git a/lib/ldaputil/utest/stubs.cpp b/lib/ldaputil/utest/stubs.cpp
new file mode 100644
index 00000000..92e6f978
--- /dev/null
+++ b/lib/ldaputil/utest/stubs.cpp
@@ -0,0 +1,102 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <ctype.h> /* isspace */
+#include <string.h>
+#include <stdio.h> /* sprintf */
+#include <stdlib.h> /* malloc */
+
+#include <ldaputil/ldaputil.h>
+#include <ldaputil/cert.h>
+#include <ldaputil/errors.h>
+#include "../ldaputili.h"
+
+#define BIG_LINE 1024
+
+NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN)
+{
+ char *cert = (char *)cert_in;
+
+ *subjectDN = strdup((char *)cert);
+ return *subjectDN ? LDAPU_SUCCESS : LDAPU_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert, char **issuerDN)
+{
+ /* TEMPORARY -- not implemented yet*/
+ *issuerDN = strdup("o=Netscape Communications, c=US");
+ return *issuerDN ? LDAPU_SUCCESS : LDAPU_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn,
+ const char *attr, char ***val_out)
+{
+ int rv;
+ char *cert_dn;
+ char **ptr;
+ char **val;
+ char *dnptr;
+ char attr_eq1[BIG_LINE];
+ char attr_eq2[BIG_LINE];
+ char *comma;
+
+ *val_out = 0;
+
+ if (which_dn == LDAPU_SUBJECT_DN)
+ rv = ldapu_get_cert_subject_dn(cert_in, &cert_dn);
+ else if (which_dn == LDAPU_ISSUER_DN)
+ rv = ldapu_get_cert_issuer_dn(cert_in, &cert_dn);
+ else
+ return LDAPU_ERR_INVALID_ARGUMENT;
+
+ if (rv != LDAPU_SUCCESS) return rv;
+
+ val = (char **)malloc(32*sizeof(char *));
+
+ if (!val) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ ptr = val;
+ sprintf(attr_eq1, "%s =", attr);
+ sprintf(attr_eq2, "%s=", attr);
+
+ while(cert_dn &&
+ ((dnptr = strstr(cert_dn, attr_eq1)) ||
+ (dnptr = strstr(cert_dn, attr_eq2))))
+ {
+ dnptr = strchr(dnptr, '=');
+ dnptr++;
+ while(isspace(*dnptr)) dnptr++;
+ comma = strchr(dnptr, ',');
+
+ if (comma) {
+ *ptr = (char *)malloc((comma-dnptr+1)*sizeof(char));
+ strncpy(*ptr, dnptr, (comma-dnptr));
+ (*ptr++)[comma-dnptr] = 0;
+ }
+ else {
+ *ptr++ = strdup(dnptr);
+ }
+ cert_dn = comma;
+ }
+
+ *ptr = 0;
+ *val_out = val;
+ return LDAPU_SUCCESS;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der,
+ unsigned int *len)
+{
+ return LDAPU_FAILED;
+}
+
+int
+ldapu_member_certificate_match (void* cert, const char* desc)
+{
+ if (!strcasecmp ((char*)cert, desc)) {
+ return LDAPU_SUCCESS;
+ }
+ return LDAPU_FAILED;
+}
diff --git a/lib/ldaputil/utest/test.ref b/lib/ldaputil/utest/test.ref
new file mode 100644
index 00000000..fae39056
--- /dev/null
+++ b/lib/ldaputil/utest/test.ref
@@ -0,0 +1,448 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+Start of test: ./auth 1 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "cn=Directory Administrators, o=Ace Industry, c=US"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+groupdn: "cn=Directory Administrators, o=Ace Industry, c=US"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+Authentication succeeded.
+
+Start of test: ./auth 1 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "cn=Directory Administrators, o=Ace Industry, c=US"
+
+userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+groupdn: "cn=Directory Administrators, o=Ace Industry, c=US"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 2 "hmiller" "cn=Directory Administrators, o=Ace Industry, c=US"
+
+uid: "hmiller"
+groupdn: "cn=Directory Administrators, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=hmiller"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+Authentication succeeded.
+
+Start of test: ./auth 2 "scarter" "cn=Directory Administrators, o=Ace Industry, c=US"
+
+uid: "scarter"
+groupdn: "cn=Directory Administrators, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=scarter"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 3 "hmiller" "Directory Administrators"
+
+uid: "hmiller"
+groupid: "Directory Administrators"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=hmiller"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+Authentication succeeded.
+
+Start of test: ./auth 3 "scarter" "Directory Administrators"
+
+uid: "scarter"
+groupid: "Directory Administrators"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=scarter"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 4 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "Directory Administrators"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+groupid: "Directory Administrators"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US) (member=cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+Authentication succeeded.
+
+Start of test: ./auth 4 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "Directory Administrators"
+
+userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+groupid: "Directory Administrators"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (cn=Directory Administrators) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ base: "cn=Directory Administrators, o=Ace Industry, c=US"
+ filter: "(| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US))"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Find parent groups of "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (| (uniquemember=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US) (member=cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US)) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 5 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "mail=hmiller@aceindustry.com"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+attrFilter: "mail=hmiller@aceindustry.com"
+ base: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ filter: "mail=hmiller@aceindustry.com"
+ scope: "LDAP_SCOPE_BASE"
+Authentication succeeded.
+
+Start of test: ./auth 5 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "mail=scarter@aceindustry.com"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+attrFilter: "mail=scarter@aceindustry.com"
+ base: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ filter: "mail=scarter@aceindustry.com"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 5 "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US" "mail=hmiller@aceindustry.com"
+
+userdn: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+attrFilter: "mail=hmiller@aceindustry.com"
+ base: "cn=Sam Carter, ou=Accounting, o=Ace Industry, c=US"
+ filter: "mail=hmiller@aceindustry.com"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 6 "hmiller" "mail=hmiller@aceindustry.com"
+
+uid: "hmiller"
+attrFilter: "mail=hmiller@aceindustry.com"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (uid=hmiller) (mail=hmiller@aceindustry.com))"
+ scope: "LDAP_SCOPE_SUBTREE"
+Authentication succeeded.
+
+Start of test: ./auth 6 "hmiller" "mail=scarter@aceindustry.com"
+
+uid: "hmiller"
+attrFilter: "mail=scarter@aceindustry.com"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (uid=hmiller) (mail=scarter@aceindustry.com))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 6 "scarter" "mail=hmiller@aceindustry.com"
+
+uid: "scarter"
+attrFilter: "mail=hmiller@aceindustry.com"
+ base: "o=Ace Industry, c=US"
+ filter: "(& (uid=scarter) (mail=hmiller@aceindustry.com))"
+ scope: "LDAP_SCOPE_SUBTREE"
+ldap_search_s: Entry not found
+Authentication failed.
+
+Start of test: ./auth 7 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "hillock"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+password: "hillock"
+ userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ password: "hillock"
+Authentication succeeded.
+
+Start of test: ./auth 7 "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US" "garbage"
+
+userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+password: "garbage"
+ userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ password: "garbage"
+ldap_simple_bind_s: Invalid credentials
+Authentication failed.
+
+Start of test: ./auth 8 "hmiller" "hillock"
+
+uid: "hmiller"
+password: "hillock"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=hmiller"
+ scope: "LDAP_SCOPE_SUBTREE"
+ userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ password: "hillock"
+Authentication succeeded.
+
+Start of test: ./auth 8 "hmiller" "garbage"
+
+uid: "hmiller"
+password: "garbage"
+ base: "o=Ace Industry, c=US"
+ filter: "uid=hmiller"
+ scope: "LDAP_SCOPE_SUBTREE"
+ userdn: "cn=Harry Miller, ou=Human Resources, o=Ace Industry, c=US"
+ password: "garbage"
+ldap_simple_bind_s: Invalid credentials
+Authentication failed.
+
+Start of test: ./auth 9 "o=Ace Industry, c=US" "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+Cert Map issuer DN: "o=Ace Industry, c=US"
+Cert Map subject DN: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+plugin_init_fn called.
+plugin_init_fn called.
+plugin_mapping_fn called.
+Value of attrs from subject DN & issuer DN:
+ Cert: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+ subjdn: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+ cn: "Kirsten Vaughan"
+ ou: "Human Resources"
+ o: "Ace Industry"
+ c: "US"
+ issuerDN: "o=Ace Industry, c=US"
+ cn: *** Empty ***
+ ou: *** Empty ***
+ o: "Ace Industry"
+ c: "US"
+plugin_search_fn called.
+ base: "ou=Human Resources, o=Ace Industry, c=US"
+ filter: "cn=Kirsten Vaughan"
+ scope: "LDAP_SCOPE_BASE"
+ldap_search_s: Entry not found
+ base: "ou=Human Resources, o=Ace Industry, c=US"
+ filter: "cn=Kirsten Vaughan"
+ scope: "LDAP_SCOPE_SUBTREE"
+plugin_verify_fn called.
+Matched entry to cert: "cn=Kirsten Vaughan, ou=Human Resources, o=Ace Industry, c=US"
+Authentication succeeded.
+
+Start of test: ./auth 10 "cn=Foo Bar, o=Netscape Communication, c=US" "foobar"
+Config file written: config_out.conf
+binddn from config file: "cn=Foo Bar, o=Netscape Communication, c=US"
+bindpw from config file: "foobar"
+Config file test succeeded
+Authentication succeeded.
+
+Start of test: ./auth 11 "dblist.conf" "ignore"
+ dbname[0] = "default"
+ dbname[1] = "default1"
+ dbname[2] = "default2"
+ dbname[3] = "default3"
+ dbname[4] = "default4"
+ dbname[5] = "default5"
+ dbname[6] = "default6"
+Authentication succeeded.
+
+Start of test: ./auth 12 "certmap.new" "certmap.conf"
+plugin_init_fn called.
+plugin_init_fn called.
+Authentication succeeded.
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+#default:DNComps o, ou ,c
+#default:FilterComps cn
+
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+#default1:verifycert
+
+# Following line has trailing spaces
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+
+Start of test: ./auth 13 "certmap.new" "certmap.conf"
+plugin_init_fn called.
+plugin_init_fn called.
+Authentication succeeded.
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+#default:DNComps o, ou ,c
+#default:FilterComps cn
+
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+#default1:verifycert
+
+# Following line has trailing spaces
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+
+Start of test: ./auth 14 "certmap.new" "certmap.conf"
+plugin_init_fn called.
+plugin_init_fn called.
+Authentication succeeded.
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+#default:DNComps o, ou ,c
+#default:FilterComps cn
+
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+#default1:verifycert
+
+# Following line has trailing spaces
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+# Comments before any certmap directive - line 1
+# Comments before any certmap directive - line 2
+
+# Comments before any certmap directive - line 3
+# Comments before any certmap directive - line 4
+# Comments before any certmap directive - line 5
+
+
+# Comments before any certmap directive - line 6
+
+certmap default default
+
+certmap default1 o=Netscape Communications, c=US
+default1:library ./plugin.so
+default1:InitFn plugin_init_fn
+default1:DNComps ou o c
+default1:FilterComps l
+
+certmap default2 o=Ace Industry, c=US
+default2:InitFn plugin_init_fn
+default2:DNComps cn o ou c
+default2:FilterComps l
+default2:verifycert on
+
+certmap newmap o=Mcom Communications, c=US
+newmap:prop1 val1
+newmap:prop2 val2
+newmap:prop3
+
diff --git a/lib/ldaputil/vtable.c b/lib/ldaputil/vtable.c
new file mode 100644
index 00000000..b50678c4
--- /dev/null
+++ b/lib/ldaputil/vtable.c
@@ -0,0 +1,427 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "ldaputili.h"
+#include <ldap.h>
+#ifdef USE_LDAP_SSL
+#include <ldap_ssl.h>
+#endif
+
+#if defined( _WINDOWS ) && ! defined( _WIN32 )
+/* On 16-bit WINDOWS platforms, it's erroneous to call LDAP API functions
+ * via a function pointer, since they are not declared LDAP_CALLBACK.
+ * So, we define the following functions, which are LDAP_CALLBACK, and
+ * simply delegate to their counterparts in the LDAP API.
+ */
+
+#ifdef USE_LDAP_SSL
+static LDAP_CALL LDAP_CALLBACK LDAP*
+ldapuVd_ssl_init( const char *host, int port, int encrypted )
+{
+ return ldapssl_init (host, port, encrypted);
+}
+#else
+static LDAP_CALL LDAP_CALLBACK LDAP*
+ldapuVd_init ( const char *host, int port )
+{
+ return ldap_init (host, port);
+}
+#endif
+
+static LDAP_CALL LDAP_CALLBACK int
+ldapuVd_set_option( LDAP *ld, int opt, void *val )
+{
+ return ldap_set_option (ld, opt, val);
+}
+
+static LDAP_CALL LDAP_CALLBACK int
+ldapuVd_simple_bind_s( LDAP* ld, const char *username, const char *passwd )
+{
+ return ldap_simple_bind_s (ld, username, passwd);
+}
+
+static LDAP_CALL LDAP_CALLBACK int
+ldapuVd_unbind( LDAP *ld )
+{
+ return ldap_unbind (ld);
+}
+
+static LDAP_CALL LDAP_CALLBACK int
+ldapuVd_search_s( LDAP* ld, const char* baseDN, int scope, const char* filter,
+ char** attrs, int attrsonly, LDAPMessage** result )
+{
+ return ldap_search_s (ld, baseDN, scope, filter, attrs, attrsonly, result);
+}
+
+static LDAP_CALL LDAP_CALLBACK int
+ldapuVd_count_entries( LDAP* ld, LDAPMessage* msg )
+{
+ return ldap_count_entries (ld, msg);
+}
+
+static LDAP_CALL LDAP_CALLBACK LDAPMessage*
+ldapuVd_first_entry( LDAP* ld, LDAPMessage* msg )
+{
+ return ldap_first_entry (ld, msg);
+}
+
+static LDAP_CALL LDAP_CALLBACK LDAPMessage*
+ldapuVd_next_entry( LDAP* ld, LDAPMessage* entry )
+{
+ return ldap_next_entry(ld, entry);
+}
+
+static LDAP_CALL LDAP_CALLBACK char*
+ldapuVd_get_dn( LDAP* ld, LDAPMessage* entry )
+{
+ return ldap_get_dn (ld, entry);
+}
+
+static LDAP_CALL LDAP_CALLBACK char*
+ldapuVd_first_attribute( LDAP* ld, LDAPMessage* entry, BerElement** iter )
+{
+ return ldap_first_attribute (ld, entry, iter);
+}
+
+static LDAP_CALL LDAP_CALLBACK char*
+ldapuVd_next_attribute( LDAP* ld, LDAPMessage* entry, BerElement* iter)
+{
+ return ldap_next_attribute (ld, entry, iter);
+}
+
+static LDAP_CALL LDAP_CALLBACK char**
+ldapuVd_get_values( LDAP *ld, LDAPMessage *entry, const char *desc )
+{
+ return ldap_get_values (ld, entry, desc);
+}
+
+static LDAP_CALL LDAP_CALLBACK struct berval**
+ldapuVd_get_values_len( LDAP *ld, LDAPMessage *entry, const char *desc )
+{
+ return ldap_get_values_len (ld, entry, desc);
+}
+
+#else
+/* On other platforms, an LDAP API function can be called via a pointer. */
+#ifdef USE_LDAP_SSL
+#define ldapuVd_ssl_init ldapssl_init
+#else
+#define ldapuVd_init ldap_init
+#endif
+#define ldapuVd_set_option ldap_set_option
+#define ldapuVd_simple_bind_s ldap_simple_bind_s
+#define ldapuVd_unbind ldap_unbind
+#define ldapuVd_set_option ldap_set_option
+#define ldapuVd_simple_bind_s ldap_simple_bind_s
+#define ldapuVd_unbind ldap_unbind
+#define ldapuVd_search_s ldap_search_s
+#define ldapuVd_count_entries ldap_count_entries
+#define ldapuVd_first_entry ldap_first_entry
+#define ldapuVd_next_entry ldap_next_entry
+#define ldapuVd_get_dn ldap_get_dn
+#define ldapuVd_first_attribute ldap_first_attribute
+#define ldapuVd_next_attribute ldap_next_attribute
+#define ldapuVd_get_values ldap_get_values
+#define ldapuVd_get_values_len ldap_get_values_len
+
+#endif
+
+/* Several functions in the standard LDAP API have no LDAP* parameter,
+ but all the VTable functions do. Here are some little functions that
+ make up the difference, by ignoring their LDAP* parameter:
+*/
+static int LDAP_CALL LDAP_CALLBACK
+ldapuVd_msgfree( LDAP *ld, LDAPMessage *chain )
+{
+ return ldap_msgfree (chain);
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+ldapuVd_memfree( LDAP *ld, void *dn )
+{
+ ldap_memfree (dn);
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+ldapuVd_ber_free( LDAP *ld, BerElement *ber, int freebuf )
+{
+ ldap_ber_free (ber, freebuf);
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+ldapuVd_value_free( LDAP *ld, char **vals )
+{
+ ldap_value_free (vals);
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+ldapuVd_value_free_len( LDAP *ld, struct berval **vals )
+{
+ ldap_value_free_len (vals);
+}
+
+static LDAPUVTable_t ldapu_VTable = {
+/* By default, the VTable points to the standard LDAP API. */
+#ifdef USE_LDAP_SSL
+ ldapuVd_ssl_init,
+#else
+ ldapuVd_init,
+#endif
+ ldapuVd_set_option,
+ ldapuVd_simple_bind_s,
+ ldapuVd_unbind,
+ ldapuVd_search_s,
+ ldapuVd_count_entries,
+ ldapuVd_first_entry,
+ ldapuVd_next_entry,
+ ldapuVd_msgfree,
+ ldapuVd_get_dn,
+ ldapuVd_memfree,
+ ldapuVd_first_attribute,
+ ldapuVd_next_attribute,
+ ldapuVd_ber_free,
+ ldapuVd_get_values,
+ ldapuVd_value_free,
+ ldapuVd_get_values_len,
+ ldapuVd_value_free_len
+};
+
+/* 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));
+ }
+}
+
+#ifdef USE_LDAP_SSL
+LDAP*
+ldapu_ssl_init( const char *defhost, int defport, int defsecure )
+{
+ if (ldapu_VTable.ldapuV_ssl_init) {
+ return ldapu_VTable.ldapuV_ssl_init (defhost, defport, defsecure);
+ }
+ return NULL;
+}
+#else
+LDAP*
+ldapu_init( const char *defhost, int defport )
+{
+ if (ldapu_VTable.ldapuV_init) {
+ return ldapu_VTable.ldapuV_init (defhost, defport);
+ }
+ return NULL;
+}
+#endif
+
+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);
+ } else if (!ldapu_VTable.ldapuV_value_free_len
+ && ldapu_VTable.ldapuV_get_values) {
+ auto char** vals =
+ ldapu_VTable.ldapuV_get_values (ld, entry, desc);
+ if (vals) {
+ auto struct berval** bvals = (struct berval**)
+ ldapu_malloc ((ldap_count_values (vals) + 1)
+ * sizeof(struct berval*));
+ if (bvals) {
+ auto char** val;
+ auto struct berval** bval;
+ for (val = vals, bval = bvals; *val; ++val, ++bval) {
+ auto const size_t len = strlen(*val);
+ *bval = (struct berval*) ldapu_malloc (sizeof(struct berval) + len);
+ (*bval)->bv_len = len;
+ (*bval)->bv_val = ((char*)(*bval)) + sizeof(struct berval);
+ memcpy ((*bval)->bv_val, *val, len);
+ }
+ *bval = NULL;
+ return bvals;
+ }
+ }
+ }
+ 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);
+ }
+}