diff options
author | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
---|---|---|
committer | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
commit | b2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch) | |
tree | cf58939393a9032182c4fbc4441164a9456e82f8 /lib/ldaputil/ldapdb.c | |
download | ds-ldapserver7x.tar.gz ds-ldapserver7x.tar.xz ds-ldapserver7x.zip |
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'lib/ldaputil/ldapdb.c')
-rw-r--r-- | lib/ldaputil/ldapdb.c | 583 |
1 files changed, 583 insertions, 0 deletions
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; +} + |