summaryrefslogtreecommitdiffstats
path: root/lib/ldaputil/ldapdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ldaputil/ldapdb.c')
-rw-r--r--lib/ldaputil/ldapdb.c583
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;
+}
+