diff options
Diffstat (limited to 'lib/ldaputil')
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, ©); + + 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: <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. +The server3_branch is frozen & 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. 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. (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 & 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>. +Use this new function to get the optimization. This function's algorithm +is as follows:</LI> + +<BR>The first search uses the following filter (w/o the group names!): +<OL><TT>(| (& (objectclass=groupofuniquenames)</TT> +<BR><TT> (uniquemember=<userDN>))</TT> +<BR><TT> (& (objectclass=groupofnames)(member=<userDN>)))</TT> +<BR> </OL> +This gives us all the groups the user is member of. We ask for only +the "CN" attributes of the returned groups. We check if "CN" of any +of the returned groups is one of the groups we have. If yes, we have +succeeded. If there are no groups returned then we have failed. +Otherwise, we continue with the nested group check. To perform that +check, we need DNs of all the groups the user is member of, which we already +have from the previous search. Now we repeat the search as follows: +<OL><TT>(| (& (objectclass=groupofuniquenames)</TT> +<BR><TT> (| (uniquemember=<grp1DN>)... +(uniquemember=<grpNDN>))</TT> +<BR><TT> (& (objectclass=groupofnames)</TT> +<BR><TT> (| (member=<grp1DN>)... (member=<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. Previously we were performing 2 ldap lookups per group. +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 & 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 & +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 & ldapu_free_cert_ava_val</B>: API functions +to get & 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>: +'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 & 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. +I was hoping I don't have to do any major changes when I wrote this document +on Mar 18. These are incompatible changes -- please review them before +you upgrade. I have checked in this file in CVS under "ns/netsite/lib/ldaputil/ldapu-changes.html". +I have added all the "XYZ_branch" and "XYZ_point" tags to this file so +that you can easily see this file in your tree. When I make significant +changes to this file/library, I will retag this file for your branch to +make it same as the server3_branch. [Let me know if I shouldn't do it for +your branch].</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 <ServerRoot>/userdb/certmap.conf +file:</LI> + +<UL><TT><FONT SIZE=+1>certmap verisign <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. The filter will +be +<BR>(& (cn="<user's CN>") (mail="<user's mail>")). The +new signature of ldapu_cert_to_ldap_entry is as follows: +<BR> 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. This is function is now called even when multiple potential +matches are found for the cert. The mapping is successful if the +verify function can reduce the number of matches to exactly one. +For example, if there are multiple "Joe Smith" entries, at most one of +those will have the cert in it's "userCertificate" attr. The verify +function will select that entry. The verify function is called with +"LDAPMessage *res" containing all the potential matches. It should +return a pointer to the matched entry in the new "LDAPMessage **entry" +parameter. The new signature for CertVerifyFn_t is as follows:</LI> + +<BR> typedef int (*CertVerifyFn_t)(void *cert, LDAP *ld, +LDAPMessage *res, +<BR> + +<P> +LDAPMessage **entry); +<LI> +typedef int (*<B>CertMapInitiFn_t)</B>: Renamed from CertmapInitFn_t. +Now this has two extra parameters to make it easy to use it in a plugin. +Other API functions require "issuerDN" but this function was called with +"LDAPUCertMapInfo_t *certinfo". There was no public API function +to get the issuerDN from "certinfo". The new signature for CertMapInitFn_t +is as follows:</LI> + +<BR> typedef int (*CertMapInitFn_t)(void *certmap_info, +const char *issuerName, +<BR> +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); + } +} |