summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsldap/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsldap/common')
-rw-r--r--usr/src/lib/libsldap/common/mapfile-vers8
-rw-r--r--usr/src/lib/libsldap/common/ns_cache_door.c56
-rw-r--r--usr/src/lib/libsldap/common/ns_cache_door.h68
-rw-r--r--usr/src/lib/libsldap/common/ns_common.c114
-rw-r--r--usr/src/lib/libsldap/common/ns_config.c349
-rw-r--r--usr/src/lib/libsldap/common/ns_confmgr.c173
-rw-r--r--usr/src/lib/libsldap/common/ns_connect.c2755
-rwxr-xr-xusr/src/lib/libsldap/common/ns_connmgmt.c2631
-rwxr-xr-xusr/src/lib/libsldap/common/ns_connmgmt.h305
-rw-r--r--usr/src/lib/libsldap/common/ns_init.c16
-rw-r--r--usr/src/lib/libsldap/common/ns_internal.h59
-rw-r--r--usr/src/lib/libsldap/common/ns_reads.c518
-rw-r--r--usr/src/lib/libsldap/common/ns_sldap.h163
-rw-r--r--usr/src/lib/libsldap/common/ns_standalone.c2451
-rw-r--r--usr/src/lib/libsldap/common/ns_writes.c402
15 files changed, 8053 insertions, 2015 deletions
diff --git a/usr/src/lib/libsldap/common/mapfile-vers b/usr/src/lib/libsldap/common/mapfile-vers
index f55257708b..3ae31a41dc 100644
--- a/usr/src/lib/libsldap/common/mapfile-vers
+++ b/usr/src/lib/libsldap/common/mapfile-vers
@@ -30,6 +30,12 @@
SUNWprivate_1.1 {
global:
+ __ns_ldap_initStandalone;
+ __ns_ldap_getConnectionInfoFromDUA;
+ __ns_ldap_getRootDSE;
+ __ns_ldap_pingOfflineServers;
+ __ns_ldap_cancelStandalone;
+ __ns_ldap_initAuth;
__ns_ldap_getAcctMgmt;
__s_api_get_canonical_name;
__ns_ldap_getAttrStruct;
@@ -38,6 +44,8 @@ SUNWprivate_1.1 {
__ns_ldap_check_dns_preq;
__ns_ldap_check_gssapi_preq;
__ns_ldap_check_all_preq;
+ __s_api_ip2hostname;
+ __s_api_hostname2ip;
} SUNWprivate_1.0;
SUNWprivate_1.0 {
diff --git a/usr/src/lib/libsldap/common/ns_cache_door.c b/usr/src/lib/libsldap/common/ns_cache_door.c
index 2932d7c5a6..6c4d9c2765 100644
--- a/usr/src/lib/libsldap/common/ns_cache_door.c
+++ b/usr/src/lib/libsldap/common/ns_cache_door.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -40,7 +40,7 @@
/*
*
- * Routine that actually performs the door call.
+ * Routines that actually performs the door call.
* Note that we cache a file descriptor. We do
* the following to prevent disasters:
*
@@ -80,12 +80,15 @@ extern int errno;
static mutex_t _door_lock = DEFAULTMUTEX;
static int doorfd = -1;
+/*
+ * This function does the first part: ensures a file descriptor is
+ * cached and usable.
+ */
int
-__ns_ldap_trydoorcall(ldap_data_t **dptr, int *ndata, int *adata)
+__ns_ldap_trydoorcall_getfd()
{
static door_info_t real_door;
door_info_t my_door;
- door_arg_t param;
/*
* the first time in we try and open and validate the door.
@@ -107,7 +110,7 @@ try_again:
if ((doorfd = open(LDAP_CACHE_DOOR, O_RDONLY, 0))
== -1) {
(void) mutex_unlock(&_door_lock);
- return (NOSERVER);
+ return (NS_CACHE_NOSERVER);
}
/*
@@ -120,15 +123,15 @@ try_again:
tbc[i++] = doorfd;
if ((doorfd = dup(doorfd)) < 0) {
while (i--)
- (void) close(tbc[i]);
+ (void) close(tbc[i]);
doorfd = -1;
(void) mutex_unlock(&_door_lock);
- return (NOSERVER);
+ return (NS_CACHE_NOSERVER);
}
}
while (i--)
- (void) close(tbc[i]);
+ (void) close(tbc[i]);
/*
* mark this door descriptor as close on exec
@@ -143,7 +146,7 @@ try_again:
(void) close(doorfd);
doorfd = -1;
(void) mutex_unlock(&_door_lock);
- return (NOSERVER);
+ return (NS_CACHE_NOSERVER);
}
} else {
if (door_info(doorfd, &my_door) == -1 ||
@@ -165,6 +168,17 @@ try_again:
}
(void) mutex_unlock(&_door_lock);
+ return (NS_CACHE_SUCCESS);
+}
+
+/*
+ * This function does the second part: sends a door request to
+ * the ldap_cachemgr daemon.
+ */
+int
+__ns_ldap_trydoorcall_send(ldap_data_t **dptr, int *ndata, int *adata)
+{
+ door_arg_t param;
param.rbuf = (char *)*dptr;
param.rsize = *ndata;
@@ -173,21 +187,35 @@ try_again:
param.desc_ptr = NULL;
param.desc_num = 0;
if (door_call(doorfd, &param) == -1) {
- return (NOSERVER);
+ return (NS_CACHE_NOSERVER);
}
*adata = (int)param.data_size;
*ndata = (int)param.rsize;
*dptr = (ldap_data_t *)param.data_ptr;
if (*adata == 0 || *dptr == NULL) {
- return (NOSERVER);
+ return (NS_CACHE_NOSERVER);
}
return ((*dptr)->ldap_ret.ldap_return_code);
}
-#pragma fini(_doorfd_close)
-static void
-_doorfd_close()
+/*
+ * This function does part 1 and 2: makes sure a file descriptor is
+ * available and sends a door request to the ldap_cachemgr daemon.
+ */
+int
+__ns_ldap_trydoorcall(ldap_data_t **dptr, int *ndata, int *adata)
+{
+ int rc;
+
+ if ((rc = __ns_ldap_trydoorcall_getfd()) == NS_CACHE_SUCCESS)
+ return (__ns_ldap_trydoorcall_send(dptr, ndata, adata));
+ else
+ return (rc);
+}
+
+void
+__ns_ldap_doorfd_close()
{
(void) mutex_lock(&_door_lock);
if (doorfd != -1) {
diff --git a/usr/src/lib/libsldap/common/ns_cache_door.h b/usr/src/lib/libsldap/common/ns_cache_door.h
index d6c9e3a56e..391394cbe7 100644
--- a/usr/src/lib/libsldap/common/ns_cache_door.h
+++ b/usr/src/lib/libsldap/common/ns_cache_door.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -69,6 +69,34 @@ typedef struct ldap_strlist {
} ldap_strlist_t;
/*
+ * Structure used to request/inform config and server status changes.
+ */
+
+typedef struct ldap_get_chg_cookie {
+ pid_t mgr_pid; /* current process id of ldap_cachemgr */
+ uint32_t seq_num; /* current config sequence number */
+} ldap_get_chg_cookie_t;
+
+typedef struct ldap_get_change {
+ uint32_t op; /* start or stop */
+ ldap_get_chg_cookie_t cookie; /* get status change cookie */
+} ldap_get_change_t;
+
+typedef struct ldap_get_change_out {
+ uint32_t type; /* config change or server change */
+ ldap_get_chg_cookie_t cookie; /* get status change cookie */
+ uint32_t server_count; /* if server change: num of servers */
+ uint32_t data_size; /* if server change: size of data */
+ char data[sizeof (int)]; /* real size is data_size */
+} ldap_get_change_out_t;
+
+typedef struct ldap_config_out {
+ ldap_get_chg_cookie_t cookie; /* get status change cookie */
+ uint32_t data_size; /* length of the config string */
+ char config_str[sizeof (int)]; /* real size is data_size */
+} ldap_config_out_t;
+
+/*
* structure returned by server for all calls
*/
@@ -86,6 +114,8 @@ typedef struct {
char buff[4];
char ber[4]; /* BER/DER encoded packet */
ldap_strlist_t strlist;
+ ldap_config_out_t config_str;
+ ldap_get_change_out_t changes;
} ldap_u;
} ldap_return_t;
@@ -107,6 +137,7 @@ typedef struct {
} addr;
char servername[sizeof (int)]; /* Format: server:port */
ldap_strlist_t strlist;
+ ldap_get_change_t get_change;
} ldap_u;
} ldap_call_t;
/*
@@ -154,6 +185,8 @@ typedef union {
#define SETCACHE 23
/* NativeLDAP II get cache data statistics */
#define GETCACHESTAT 24
+ /* Configuration change or server status change notification */
+#define GETSTATUSCHANGE 25
/*
* GETLDAPSERVER request flags
@@ -167,6 +200,23 @@ typedef union {
#define NS_CACHE_ADDR_IP "I"
/*
+ * GETSTATUSCHANGE operation: start or stop
+ */
+#define NS_STATUS_CHANGE_OP_START 1
+#define NS_STATUS_CHANGE_OP_STOP 2
+
+/*
+ * GETSTATUSCHANGE change type: config or server
+ */
+#define NS_STATUS_CHANGE_TYPE_CONFIG 1
+#define NS_STATUS_CHANGE_TYPE_SERVER 2
+
+/*
+ * Server status change
+ */
+#define NS_SERVER_CHANGE_UP "0" /* mapped to NS_SERVER_UP */
+#define NS_SERVER_CHANGE_DOWN "1" /* mapped to NS_SERVER_DOWN */
+/*
* GETCACHE/SETCACHE data flags
*/
#define NS_CACHE_DN2DOMAIN "DM"
@@ -186,14 +236,20 @@ typedef union {
#define LDAP_CACHE_DOOR_COOKIE ((void*)(0xdeadbeef^LDAP_CACHE_DOOR_VERSION))
#define UPDATE_DOOR_COOKIE ((void*)(0xdeadcafe)
-#define SUCCESS 0
-#define NOTFOUND -1
-#define CREDERROR -2
-#define SERVERERROR -3
-#define NOSERVER -4
+#define NS_CACHE_SUCCESS 0
+#define NS_CACHE_NOTFOUND -1
+#define NS_CACHE_CREDERROR -2
+#define NS_CACHE_SERVERERROR -3
+#define NS_CACHE_NOSERVER -4
int
__ns_ldap_trydoorcall(ldap_data_t **dptr, int *ndata, int *adata);
+int
+__ns_ldap_trydoorcall_getfd();
+int
+__ns_ldap_trydoorcall_send(ldap_data_t **dptr, int *ndata, int *adata);
+void
+__ns_ldap_doorfd_close();
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libsldap/common/ns_common.c b/usr/src/lib/libsldap/common/ns_common.c
index f3b3066e1a..268393e50d 100644
--- a/usr/src/lib/libsldap/common/ns_common.c
+++ b/usr/src/lib/libsldap/common/ns_common.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -302,6 +302,39 @@ __ns_ldap_freeCred(ns_cred_t ** credp)
}
/*
+ * FUNCTION: __s_api_is_auth_matched
+ *
+ * Compare an authentication structure.
+ *
+ * RETURN VALUES: B_TRUE if matched, B_FALSE otherwise.
+ * INPUT: auth1, auth2
+ */
+boolean_t
+__s_api_is_auth_matched(const ns_cred_t *auth1,
+ const ns_cred_t *auth2)
+{
+ if ((auth1->auth.type != auth2->auth.type) ||
+ (auth1->auth.tlstype != auth2->auth.tlstype) ||
+ (auth1->auth.saslmech != auth2->auth.saslmech) ||
+ (auth1->auth.saslopt != auth2->auth.saslopt))
+ return (B_FALSE);
+
+ if ((((auth1->auth.type == NS_LDAP_AUTH_SASL) &&
+ ((auth1->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
+ (auth1->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
+ (auth1->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
+ ((auth1->cred.unix_cred.userID == NULL) ||
+ (auth1->cred.unix_cred.passwd == NULL) ||
+ ((strcasecmp(auth1->cred.unix_cred.userID,
+ auth2->cred.unix_cred.userID) != 0)) ||
+ ((strcmp(auth1->cred.unix_cred.passwd,
+ auth2->cred.unix_cred.passwd) != 0))))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
* FUNCTION: __s_api_getDNs
*
* Retrieves the default base dn for the given
@@ -1925,7 +1958,7 @@ __s_api_prepend_automountmapname_to_dn(
"mapped to an empty string.\n"));
MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
- strdup(errstr), NULL);
+ strdup(errstr), NS_LDAP_MEMORY);
return (NS_LDAP_CONFIG);
}
@@ -2216,19 +2249,49 @@ __s_api_removeServer(const char *server)
char s_b[DOORBUFFERSIZE];
} space;
- ns_server_info_t r, *ret = &r;
+ ns_server_info_t r, *ret = &r;
const char *ireq;
ldap_data_t *sptr;
int ndata;
int adata;
int len;
int rc;
+ ns_ldap_error_t *error = NULL;
if (server == NULL)
return (-1);
ireq = NS_CACHE_NORESP;
+ if (__s_api_isStandalone()) {
+ /*
+ * Remove 'server' from the standalone server list.
+ * __s_api_findRootDSE() is the standalone version
+ * of getldap_get_serverInfo() used in ldap_cachemgr.
+ * Request NS_CACHE_NORESP indicates 'server' should
+ * be removed.
+ */
+ if (__s_api_findRootDSE(ireq,
+ server,
+ NS_CACHE_ADDR_IP,
+ NULL,
+ &error) != NS_LDAP_SUCCESS) {
+ syslog(LOG_WARNING,
+ "libsldap (\"standalone\" mode): "
+ " Unable to remove %s - %s",
+ server,
+ error != NULL && error->message != NULL ?
+ error->message : " no error info");
+ if (error != NULL) {
+ (void) __ns_ldap_freeError(&error);
+ }
+
+ return (-1);
+ }
+
+ return (0);
+ }
+
(void) memset(ret, 0, sizeof (ns_server_info_t));
(void) memset(space.s_b, 0, DOORBUFFERSIZE);
@@ -2278,3 +2341,48 @@ __s_api_free_server_info(ns_server_info_t *sinfo) {
__s_api_free2dArray(sinfo->controls);
sinfo->controls = NULL;
}
+
+/*
+ * Create an ns_ldap_error structure, set status to 'rc',
+ * and copy in the error message 'msg'.
+ */
+ns_ldap_error_t *
+__s_api_make_error(int rc, char *msg) {
+ ns_ldap_error_t *ep;
+
+ ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
+ if (ep == NULL)
+ return (NULL);
+
+ ep->status = rc;
+ if (msg != NULL)
+ ep->message = strdup(msg); /* OK if ep->message is NULL */
+
+ return (ep);
+}
+
+/*
+ * Make a copy of the input ns_ldap_error.
+ */
+ns_ldap_error_t *
+__s_api_copy_error(ns_ldap_error_t *errorp) {
+ ns_ldap_error_t *ep;
+ char *msg;
+
+ if (errorp == NULL)
+ return (NULL);
+
+ ep = (ns_ldap_error_t *)malloc(sizeof (*ep));
+ if (ep != NULL) {
+ *ep = *errorp;
+ if (ep->message != NULL) {
+ msg = strdup(ep->message);
+ if (msg == NULL) {
+ free(ep);
+ ep = NULL;
+ } else
+ ep->message = msg;
+ }
+ }
+ return (ep);
+}
diff --git a/usr/src/lib/libsldap/common/ns_config.c b/usr/src/lib/libsldap/common/ns_config.c
index e87ce9a72f..e985bcd38b 100644
--- a/usr/src/lib/libsldap/common/ns_config.c
+++ b/usr/src/lib/libsldap/common/ns_config.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,6 +32,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <libintl.h>
@@ -55,14 +56,17 @@
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
+#include "ns_connmgmt.h"
-#pragma fini(_free_config)
+#pragma fini(__s_api_free_sessionPool, __s_api_shutdown_conn_mgmt, \
+ _free_config, __ns_ldap_doorfd_close)
static mutex_t ns_parse_lock = DEFAULTMUTEX;
static mutex_t ns_loadrefresh_lock = DEFAULTMUTEX;
static ns_config_t *current_config = NULL;
static int cache_server = FALSE;
+extern thread_key_t ns_cmgkey;
/*
* Parameter Index Type validation routines
@@ -110,7 +114,7 @@ static boolean_t
timetorefresh(ns_config_t *cfg);
static ns_config_t *
-LoadCacheConfiguration(ns_ldap_error_t **error);
+LoadCacheConfiguration(ns_config_t *, ns_ldap_error_t **error);
static void **
dupParam(ns_param_t *ptr);
@@ -787,6 +791,8 @@ destroy_config(ns_config_t *ptr)
ParamIndexType i;
if (ptr != NULL) {
+ if (ptr == current_config)
+ current_config = NULL;
if (ptr->domainName != NULL)
free(ptr->domainName);
ptr->domainName = NULL;
@@ -827,16 +833,14 @@ __s_api_destroy_config(ns_config_t *cfg)
/*
* Increment the configuration use count by one - assumes ns_parse_lock has
- * been obtained
+ * been obtained.
*/
static ns_config_t *
-get_curr_config_unlocked()
+get_curr_config_unlocked(ns_config_t *cfg)
{
- ns_config_t *cfg;
ns_config_t *ret;
- cfg = current_config;
ret = cfg;
if (cfg != NULL) {
(void) mutex_lock(&cfg->config_mutex);
@@ -850,19 +854,20 @@ get_curr_config_unlocked()
}
/*
- * set_curr_config sets the current config to
- * the specified ns_config_t. Note that this function
- * is similar to the project private function __s_api_init_config
- * except that it does not release the new ns_config_t
+ * set_curr_config_global sets the current global config to the
+ * specified ns_config_t. Note that this function is similar
+ * to the project private function __s_api_init_config_global
+ * except that it does not release the new ns_config_t.
*/
-
static void
-set_curr_config(ns_config_t *ptr)
+set_curr_config_global(ns_config_t *ptr)
{
- ns_config_t *cfg;
+ ns_config_t *cfg;
+ ns_config_t *cur_cfg;
(void) mutex_lock(&ns_parse_lock);
- cfg = get_curr_config_unlocked();
+ cur_cfg = current_config;
+ cfg = get_curr_config_unlocked(cur_cfg);
if (cfg != ptr) {
__s_api_destroy_config(cfg);
current_config = ptr;
@@ -870,6 +875,43 @@ set_curr_config(ns_config_t *ptr)
(void) mutex_unlock(&ns_parse_lock);
}
+
+/*
+ * set_curr_config sets the current config or the per connection
+ * management one to the specified ns_config_t. Note that this function
+ * is similar to the project private function __s_api_init_config
+ * except that it does not release the new ns_config_t. Also note
+ * that if there's no per connection management one to set, the
+ * global current config will be set.
+ */
+
+static void
+set_curr_config(ns_config_t *ptr)
+{
+ ns_config_t *cfg;
+ ns_config_t *cur_cfg;
+ ns_conn_mgmt_t *cmg;
+ int rc;
+
+ rc = thr_getspecific(ns_cmgkey, (void **)&cmg);
+
+ /* set the per connection management config if possible */
+ if (rc == 0 && cmg != NULL && cmg->config != NULL) {
+ (void) mutex_lock(&cmg->cfg_lock);
+ cur_cfg = cmg->config;
+ cfg = get_curr_config_unlocked(cur_cfg);
+ if (cfg != ptr) {
+ __s_api_destroy_config(cfg);
+ cmg->config = ptr;
+ }
+ (void) mutex_unlock(&cmg->cfg_lock);
+ return;
+ }
+
+ /* else set the global current config */
+ set_curr_config_global(ptr);
+}
+
/*
* Decrements the ns_config_t usage count by one. Delete if delete flag
* is set and no other callers are using.
@@ -889,8 +931,20 @@ __s_api_release_config(ns_config_t *cfg)
}
/*
+ * __s_api_init_config function destroys the previous global configuration
+ * sets the new global configuration and then releases it
+ */
+void
+__s_api_init_config_global(ns_config_t *ptr)
+{
+ set_curr_config_global(ptr);
+ __s_api_release_config(ptr);
+}
+
+/*
* __s_api_init_config function destroys the previous configuration
- * sets the new configuration and then releases it
+ * sets the new configuration and then releases it. The configuration
+ * may be the global one or the per connection management one.
*/
void
__s_api_init_config(ns_config_t *ptr)
@@ -924,18 +978,50 @@ __s_api_create_config(void)
return (ret);
}
+/*
+ * __s_api_get_default_config_global returns the current global config
+ */
ns_config_t *
-__s_api_get_default_config(void)
+__s_api_get_default_config_global(void)
{
- ns_config_t *cfg;
+ ns_config_t *cfg;
+ ns_config_t *cur_cfg;
(void) mutex_lock(&ns_parse_lock);
- cfg = get_curr_config_unlocked();
+ cur_cfg = current_config;
+ cfg = get_curr_config_unlocked(cur_cfg);
(void) mutex_unlock(&ns_parse_lock);
return (cfg);
}
+/*
+ * __s_api_get_default_config returns the current global config or the
+ * per connection management one.
+ */
+ns_config_t *
+__s_api_get_default_config(void)
+{
+ ns_config_t *cfg;
+ ns_config_t *cur_cfg;
+ ns_conn_mgmt_t *cmg;
+ int rc;
+
+ rc = thr_getspecific(ns_cmgkey, (void **)&cmg);
+
+ /* get the per connection management config if available */
+ if (rc == 0 && cmg != NULL && cmg->config != NULL) {
+ (void) mutex_lock(&cmg->cfg_lock);
+ cur_cfg = cmg->config;
+ cfg = get_curr_config_unlocked(cur_cfg);
+ (void) mutex_unlock(&cmg->cfg_lock);
+ return (cfg);
+ }
+
+ /* else get the global current config */
+ return (__s_api_get_default_config_global());
+}
+
static char *
stripdup(const char *instr)
{
@@ -962,33 +1048,6 @@ stripdup(const char *instr)
return (ret);
}
-static boolean_t
-has_port(char **ppc, int cnt)
-{
- int j;
- const char *s;
- const char *begin;
-
- /*
- * Don't check that address is legal - only determine
- * if there is a port specified
- */
- if (ppc != NULL) {
- for (j = 0; j < cnt; j++) {
- begin = ppc[j];
- s = begin + strlen(begin);
- while (s >= begin) {
- if (*s == ']')
- break;
- else if (*s == COLONTOK)
- return (B_TRUE);
- s--;
- }
- }
- }
- return (B_FALSE);
-}
-
/*
* Note that __s_api_crosscheck is assumed to be called with an ns_config_t
* that is properly protected - so that it will not change during the
@@ -1002,13 +1061,8 @@ __s_api_crosscheck(ns_config_t *ptr, char *errstr, int check_dn)
int value, j;
time_t tm;
const char *str, *str1;
- boolean_t has_tls = B_FALSE;
- boolean_t is_ok = B_TRUE;
- int i, len, cnt;
- const char *begin;
- char **ppc;
- int *pi, self, gssapi;
-
+ int i, cnt;
+ int self, gssapi;
if (ptr == NULL)
return (NS_SUCCESS);
@@ -1106,60 +1160,6 @@ __s_api_crosscheck(ns_config_t *ptr, char *errstr, int check_dn)
}
/*
- * Check to see if port and tls are both configured. This is not
- * supported until starttls is supported.
- */
-
- pi = ptr->paramList[NS_LDAP_AUTH_P].ns_pi;
- if (pi != NULL) {
- cnt = ptr->paramList[NS_LDAP_AUTH_P].ns_acnt;
- for (j = 0; j < cnt && !has_tls; j++) {
- has_tls = (pi[j] == NS_LDAP_EA_TLS_NONE) ||
- (pi[j] == NS_LDAP_EA_TLS_SIMPLE) ||
- (pi[j] == NS_LDAP_EA_TLS_SASL_CRAM_MD5) ||
- (pi[j] == NS_LDAP_EA_TLS_SASL_DIGEST_MD5) ||
- (pi[j] == NS_LDAP_EA_TLS_SASL_DIGEST_MD5_INT) ||
- (pi[j] == NS_LDAP_EA_TLS_SASL_DIGEST_MD5_CONF) ||
- (pi[j] == NS_LDAP_EA_TLS_SASL_EXTERNAL);
- }
- }
-
- ppc = ptr->paramList[NS_LDAP_SERVICE_AUTH_METHOD_P].ns_ppc;
- if (!has_tls && ppc != NULL) {
- cnt = ptr->paramList[NS_LDAP_SERVICE_AUTH_METHOD_P].ns_acnt;
- for (j = 0; j < cnt && !has_tls; j++) {
- begin = ppc[j];
- /* skip over service tag */
- if (begin != NULL)
- begin = strchr(begin, ':');
- if (!has_tls && begin != NULL) {
- len = strlen(begin) - 3;
- for (i = 0; i < len; i++)
- if (strncasecmp(begin + i,
- "tls:", 4) == 0)
- break;
- has_tls = i < len;
- }
- }
- }
-
- if (has_tls) {
- is_ok = !has_port(ptr->paramList[NS_LDAP_SERVERS_P].ns_ppc,
- ptr->paramList[NS_LDAP_SERVERS_P].ns_acnt);
- ppc = ptr->paramList[NS_LDAP_SERVER_PREF_P].ns_ppc;
- if (is_ok)
- is_ok = !has_port(
- ptr->paramList[NS_LDAP_SERVER_PREF_P].ns_ppc,
- ptr->paramList[NS_LDAP_SERVER_PREF_P].ns_acnt);
- }
- if (!is_ok) {
- (void) snprintf(errstr, MAXERROR,
- gettext("Configuration Error: "
- "Cannot specify LDAP port with tls"));
- return (NS_PARSE_ERR);
- }
-
- /*
* If NS_LDAP_CACHETTL is not specified,
* init NS_LDAP_EXP_P here. Otherwise,
* ldap_cachemgr will never refresh the profile.
@@ -1377,11 +1377,12 @@ __ns_ldap_default_config()
/*
* Get the current configuration pointer and return it.
* If necessary initialize or refresh the current
- * configuration as applicable.
+ * configuration as applicable. If global is set, returns
+ * the global one.
*/
-ns_config_t *
-__s_api_loadrefresh_config()
+static ns_config_t *
+loadrefresh_config(boolean_t global)
{
ns_config_t *cfg;
ns_config_t *new_cfg;
@@ -1389,14 +1390,20 @@ __s_api_loadrefresh_config()
/* We want to refresh only one configuration at a time */
(void) mutex_lock(&ns_loadrefresh_lock);
- cfg = __s_api_get_default_config();
+ if (global == B_TRUE)
+ cfg = __s_api_get_default_config_global();
+ else
+ cfg = __s_api_get_default_config();
/* (re)initialize configuration if necessary */
- if (timetorefresh(cfg)) {
- new_cfg = LoadCacheConfiguration(&errorp);
- if (new_cfg != NULL) {
+ if (!__s_api_isStandalone() && timetorefresh(cfg)) {
+ new_cfg = LoadCacheConfiguration(cfg, &errorp);
+ if (new_cfg != NULL && new_cfg != cfg) {
__s_api_release_config(cfg);
- set_curr_config(new_cfg);
+ if (global == B_TRUE)
+ set_curr_config_global(new_cfg);
+ else
+ set_curr_config(new_cfg);
cfg = new_cfg;
}
if (errorp != NULL)
@@ -1407,6 +1414,31 @@ __s_api_loadrefresh_config()
}
/*
+ * Get the current global configuration pointer and return it.
+ * If necessary initialize or refresh the current
+ * configuration as applicable.
+ */
+
+ns_config_t *
+__s_api_loadrefresh_config_global()
+{
+ return (loadrefresh_config(B_TRUE));
+}
+
+/*
+ * Get the current configuration pointer and return it.
+ * If necessary initialize or refresh the current
+ * configuration as applicable. The configuration may
+ * be the global one or the per connection management one.
+ */
+
+ns_config_t *
+__s_api_loadrefresh_config()
+{
+ return (loadrefresh_config(B_FALSE));
+}
+
+/*
* In general this routine is not very usefull. Individual routines can be
* created to do this job. Once that is done, this function can be removed.
* Size of errstr buffer needs to be MAXERROR.
@@ -2621,7 +2653,9 @@ __ns_ldap_setParam(const ParamIndexType type,
int ret;
char errstr[2 * MAXERROR];
ns_config_t *cfg;
+ ns_config_t *cfg_g = (ns_config_t *)-1;
ns_config_t *new_cfg;
+ boolean_t reinit_connmgmt = B_FALSE;
/* We want to refresh only one configuration at a time */
(void) mutex_lock(&ns_loadrefresh_lock);
@@ -2654,9 +2688,16 @@ __ns_ldap_setParam(const ParamIndexType type,
}
/* (re)initialize configuration if necessary */
- if (cache_server == FALSE && timetorefresh(cfg)) {
- new_cfg = LoadCacheConfiguration(&errorp);
- __s_api_release_config(cfg);
+ if (!__s_api_isStandalone() &&
+ cache_server == FALSE && timetorefresh(cfg))
+ cfg_g = __s_api_get_default_config_global();
+ /* only (re)initialize the global configuration */
+ if (cfg == cfg_g) {
+ if (cfg_g != NULL)
+ __s_api_release_config(cfg_g);
+ new_cfg = LoadCacheConfiguration(cfg, &errorp);
+ if (new_cfg != cfg)
+ __s_api_release_config(cfg);
if (new_cfg == NULL) {
(void) snprintf(errstr, sizeof (errstr),
gettext("Unable to load configuration '%s' "
@@ -2670,11 +2711,17 @@ __ns_ldap_setParam(const ParamIndexType type,
(void) mutex_unlock(&ns_loadrefresh_lock);
return (NS_LDAP_CONFIG);
}
- set_curr_config(new_cfg);
- cfg = new_cfg;
+ if (new_cfg != cfg) {
+ set_curr_config_global(new_cfg);
+ cfg = new_cfg;
+ reinit_connmgmt = B_TRUE;
+ }
}
(void) mutex_unlock(&ns_loadrefresh_lock);
+ if (reinit_connmgmt == B_TRUE)
+ __s_api_reinit_conn_mgmt_new_config(cfg);
+
/* translate input and save in the parameter list */
ret = __ns_ldap_setParamValue(cfg, type, data, error);
@@ -2826,7 +2873,9 @@ __ns_ldap_getParam(const ParamIndexType Param,
ns_ldap_error_t *errorp;
ns_default_config *def;
ns_config_t *cfg;
+ ns_config_t *cfg_g = (ns_config_t *)-1;
ns_config_t *new_cfg;
+ boolean_t reinit_connmgmt = B_FALSE;
if (data == NULL)
return (NS_LDAP_INVALID_PARAM);
@@ -2838,9 +2887,16 @@ __ns_ldap_getParam(const ParamIndexType Param,
cfg = __s_api_get_default_config();
/* (re)initialize configuration if necessary */
- if (cache_server == FALSE && timetorefresh(cfg)) {
- new_cfg = LoadCacheConfiguration(&errorp);
- __s_api_release_config(cfg);
+ if (!__s_api_isStandalone() &&
+ cache_server == FALSE && timetorefresh(cfg))
+ cfg_g = __s_api_get_default_config_global();
+ /* only (re)initialize the global configuration */
+ if (cfg == cfg_g) {
+ if (cfg_g != NULL)
+ __s_api_release_config(cfg_g);
+ new_cfg = LoadCacheConfiguration(cfg, &errorp);
+ if (new_cfg != cfg)
+ __s_api_release_config(cfg);
if (new_cfg == NULL) {
(void) snprintf(errstr, sizeof (errstr),
gettext("Unable to load configuration "
@@ -2855,11 +2911,17 @@ __ns_ldap_getParam(const ParamIndexType Param,
(void) mutex_unlock(&ns_loadrefresh_lock);
return (NS_LDAP_CONFIG);
}
- set_curr_config(new_cfg);
- cfg = new_cfg;
+ if (new_cfg != cfg) {
+ set_curr_config_global(new_cfg);
+ cfg = new_cfg;
+ reinit_connmgmt = B_TRUE;
+ }
}
(void) mutex_unlock(&ns_loadrefresh_lock);
+ if (reinit_connmgmt == B_TRUE)
+ __s_api_reinit_conn_mgmt_new_config(cfg);
+
if (cfg == NULL) {
(void) snprintf(errstr, sizeof (errstr),
gettext("No configuration information available."));
@@ -3232,6 +3294,7 @@ __door_getldapconfig(char **buffer, int *buflen, ns_ldap_error_t **error)
char errstr[MAXERROR];
char *domainname;
ns_ldap_return_code retCode;
+ ldap_config_out_t *cfghdr;
*error = NULL;
@@ -3255,9 +3318,9 @@ __door_getldapconfig(char **buffer, int *buflen, ns_ldap_error_t **error)
sptr = &space->s_d;
switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
- case SUCCESS:
+ case NS_CACHE_SUCCESS:
break;
- case NOTFOUND:
+ case NS_CACHE_NOTFOUND:
(void) snprintf(errstr, sizeof (errstr),
gettext("Door call to "
"ldap_cachemgr failed - error: %d."),
@@ -3274,13 +3337,14 @@ __door_getldapconfig(char **buffer, int *buflen, ns_ldap_error_t **error)
retCode = NS_LDAP_SUCCESS;
/* copy info from door call to buffer here */
- *buflen = strlen(sptr->ldap_ret.ldap_u.config) + 1;
+ cfghdr = &sptr->ldap_ret.ldap_u.config_str;
+ *buflen = offsetof(ldap_config_out_t, config_str) +
+ cfghdr->data_size + 1;
*buffer = calloc(*buflen, sizeof (char));
if (*buffer == NULL) {
retCode = NS_LDAP_MEMORY;
- } else {
- (void) strcpy(*buffer, sptr->ldap_ret.ldap_u.config);
- }
+ } else
+ (void) memcpy(*buffer, cfghdr, *buflen - 1);
if (sptr != &space->s_d) {
(void) munmap((char *)sptr, ndata);
@@ -3309,6 +3373,7 @@ SetDoorInfo(char *buffer, ns_ldap_error_t **errorp)
int ret;
int first = 1;
int errfnd = 0;
+ ldap_config_out_t *cfghdr;
if (errorp == NULL)
return (NULL);
@@ -3319,6 +3384,11 @@ SetDoorInfo(char *buffer, ns_ldap_error_t **errorp)
return (NULL);
}
+ /* get config cookie from the header */
+ cfghdr = (ldap_config_out_t *)bufptr;
+ ptr->config_cookie = cfghdr->cookie;
+ bufptr = (char *)cfghdr->config_str;
+
strptr = (char *)strtok_r(bufptr, DOORLINESEP, &rest);
for (; ; ) {
if (strptr == NULL)
@@ -3371,12 +3441,15 @@ SetDoorInfo(char *buffer, ns_ldap_error_t **errorp)
}
static ns_config_t *
-LoadCacheConfiguration(ns_ldap_error_t **error)
+LoadCacheConfiguration(ns_config_t *oldcfg, ns_ldap_error_t **error)
{
char *buffer = NULL;
int buflen = 0;
int ret;
ns_config_t *cfg;
+ ldap_config_out_t *cfghdr;
+ ldap_get_chg_cookie_t old_cookie;
+ ldap_get_chg_cookie_t new_cookie;
*error = NULL;
ret = __door_getldapconfig(&buffer, &buflen, error);
@@ -3387,6 +3460,18 @@ LoadCacheConfiguration(ns_ldap_error_t **error)
return (NULL);
}
+ /* No need to reload configuration if config cookie is the same */
+ cfghdr = (ldap_config_out_t *)buffer;
+ new_cookie = cfghdr->cookie;
+ if (oldcfg != NULL)
+ old_cookie = oldcfg->config_cookie;
+
+ if (oldcfg != NULL && old_cookie.mgr_pid == new_cookie.mgr_pid &&
+ old_cookie.seq_num == new_cookie.seq_num) {
+ free(buffer);
+ return (oldcfg);
+ }
+
/* now convert from door format */
cfg = SetDoorInfo(buffer, error);
free(buffer);
diff --git a/usr/src/lib/libsldap/common/ns_confmgr.c b/usr/src/lib/libsldap/common/ns_confmgr.c
index 24b9f143c9..1fd58aac6f 100644
--- a/usr/src/lib/libsldap/common/ns_confmgr.c
+++ b/usr/src/lib/libsldap/common/ns_confmgr.c
@@ -236,6 +236,123 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error)
}
+static
+ns_ldap_return_code
+set_attr(ns_config_t *config_struct,
+ char *attr_name,
+ char *attr_val,
+ ns_ldap_error_t **errorp)
+{
+ ParamIndexType idx;
+ char errmsg[MAXERROR];
+
+ if (errorp == NULL) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ *errorp = NULL;
+
+ /*
+ * This double call is made due to the presence of
+ * two sets of LDAP config. attribute names.
+ * An LDAP configuration can be obtained either from a server
+ * or from SMF. The former sends a DUA with attributes' names
+ * styled like "preferredServerList". But local configurations
+ * will have names inherited from the /var/ldap/ldap* files such as
+ * "NS_LDAP_SERVER_PREF".
+ * So, the standalone bits are able to process both sets of
+ * attributes' names.
+ */
+ if (__s_api_get_profiletype(attr_name, &idx) < 0 &&
+ __s_api_get_versiontype(config_struct, attr_name, &idx) < 0) {
+ (void) snprintf(errmsg, sizeof (errmsg),
+ gettext("Illegal DUAProfile property: <%s>."), attr_name);
+ MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
+ return (NS_LDAP_CONFIG);
+ }
+
+ return (__ns_ldap_setParamValue(config_struct, idx, attr_val, errorp));
+}
+
+
+/*
+ * This function creates a configuration which will be used
+ * for all LDAP requests in the Standalone mode.
+ *
+ * INPUT:
+ * config - a buffer returned by __ns_ldap_getConnectionInfo()'s
+ * dua_profile parameter.
+ *
+ */
+ns_config_t *
+__s_api_create_config_door_str(char *config, ns_ldap_error_t **errorp)
+{
+ char *attr, *attrName, *attrVal, *rest;
+ ns_config_t *configStruct = NULL;
+ char errmsg[MAXERROR];
+
+ if (config == NULL || errorp == NULL)
+ return (NULL);
+
+ if ((configStruct = __s_api_create_config()) == NULL) {
+ return (NULL);
+ }
+
+ *errorp = NULL;
+
+ attr = strtok_r(config, DOORLINESEP, &rest);
+ if (!attr) {
+ __s_api_destroy_config(configStruct);
+ (void) snprintf(errmsg, sizeof (errmsg),
+ gettext("DUAProfile received from the server"
+ " has bad format"));
+ MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
+ return (NULL);
+ }
+
+ do {
+ __s_api_split_key_value(attr, &attrName, &attrVal);
+
+ if (attrName == NULL || attrVal == NULL) {
+ __s_api_destroy_config(configStruct);
+ (void) snprintf(errmsg, sizeof (errmsg),
+ gettext("Attribute %s is not valid"), attr);
+ MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG,
+ strdup(errmsg), NULL);
+ return (NULL);
+ }
+
+ /* Get the version of the profile. */
+ if (strcasecmp(attrName, "objectclass") == 0) {
+ if (strcasecmp(attrVal, _PROFILE2_OBJECTCLASS) == 0) {
+ if (__ns_ldap_setParamValue(configStruct,
+ NS_LDAP_FILE_VERSION_P,
+ NS_LDAP_VERSION_2,
+ errorp) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(configStruct);
+ return (NULL);
+ }
+ }
+ continue;
+ }
+
+ if (set_attr(configStruct, attrName, attrVal, errorp) !=
+ NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(configStruct);
+ return (NULL);
+ }
+ } while (attr = strtok_r(NULL, DOORLINESEP, &rest));
+
+ if (__s_api_crosscheck(configStruct, errmsg, B_FALSE) != NS_SUCCESS) {
+ MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
+ __s_api_destroy_config(configStruct);
+ return (NULL);
+ }
+
+ return (configStruct);
+}
+
+
/*
* Cache Manager side of configuration file loading
*/
@@ -342,7 +459,7 @@ _print2buf(LineBuf *line, char *toprint, int addsep)
*/
ns_ldap_error_t *
-__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname)
+__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname, ns_config_t *new)
{
ns_config_t *ptr;
char errstr[MAXERROR];
@@ -350,8 +467,19 @@ __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname)
char string[BUFSIZE];
char *str;
ParamIndexType i = 0;
+ int len;
+ ldap_config_out_t *cout;
- ptr = __s_api_get_default_config();
+ /*
+ * If new is NULL, it outputs the flatten data of current default
+ * config, if it's non-NULL, it outputs the flatten data of a temporary
+ * config. It's used to compare the new config data with the current
+ * default config data.
+ */
+ if (new == NULL)
+ ptr = __s_api_get_default_config();
+ else
+ ptr = new;
if (ptr == NULL) {
(void) snprintf(errstr, sizeof (errstr),
gettext("No configuration information available for %s."),
@@ -383,7 +511,46 @@ __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname)
str = NULL;
}
}
- __s_api_release_config(ptr);
+ if (new == NULL)
+ __s_api_release_config(ptr);
+
+ /*
+ * The new interface of the configuration between ldap_cachemgr
+ * & libsldap contains a header structure ldap_config_out_t.
+ * The flatten configuration data configinfo->str is cloned
+ * to cout->config_str, configinfo->len is saved in
+ * cout->data_size and cout->cookie is set later after this function
+ * is returned in ldap_cachemgr.
+ * configinfo->str & configinfo->len are reused to save info of
+ * header + data.
+ * The format:
+ * [cookie|data_size|config_str .............]
+ */
+
+ if (configinfo->str) {
+ len = sizeof (ldap_config_out_t) - sizeof (int) +
+ configinfo->len;
+ if ((cout = calloc(1, len)) == NULL) {
+ free(configinfo->str);
+ configinfo->str = NULL;
+ configinfo->len = 0;
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("calloc: Out of memory."));
+ MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
+ strdup(errstr), NULL);
+ return (errorp);
+ }
+ /*
+ * cout->cookie is set by the caller,
+ * which is in ldap_cachemgr.
+ */
+ cout->data_size = configinfo->len;
+ (void) memcpy(cout->config_str, configinfo->str,
+ configinfo->len);
+ free(configinfo->str);
+ configinfo->str = (char *)cout;
+ configinfo->len = len;
+ }
return (NULL);
}
diff --git a/usr/src/lib/libsldap/common/ns_connect.c b/usr/src/lib/libsldap/common/ns_connect.c
index 5d77fec52d..8965682bd6 100644
--- a/usr/src/lib/libsldap/common/ns_connect.c
+++ b/usr/src/lib/libsldap/common/ns_connect.c
@@ -18,7 +18,6 @@
*
* CDDL HEADER END
*/
-
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -43,41 +42,41 @@
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
+#include "ns_connmgmt.h"
#include "ldappr.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <procfs.h>
#include <unistd.h>
+#define USE_DEFAULT_PORT 0
+
+static ns_ldap_return_code performBind(const ns_cred_t *,
+ LDAP *,
+ int,
+ ns_ldap_error_t **,
+ int,
+ int);
+static ns_ldap_return_code createSession(const ns_cred_t *,
+ const char *,
+ uint16_t,
+ int,
+ LDAP **,
+ ns_ldap_error_t **);
+
extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
LDAPControl **, LDAPControl **);
extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
static int openConnection(LDAP **, const char *, const ns_cred_t *,
- int, ns_ldap_error_t **, int, int);
+ int, ns_ldap_error_t **, int, int, ns_conn_user_t *);
static void
_DropConnection(ConnectionID cID, int flag, int fini);
-/*
- * sessionLock, wait4session, sessionTid
- * are variables to synchronize the creation/retrieval of a connection.
- * MTperCon is a flag to enable/disable multiple threads sharing the same
- * connection.
- * sessionPoolLock is a mutex lock for the connection pool.
- * sharedConnNumber is the number of sharable connections in the pool.
- * sharedConnNumberLock is a mutex for sharedConnNumber.
- */
-static mutex_t sessionLock = DEFAULTMUTEX;
-static int wait4session = 0;
-static thread_t sessionTid = 0;
-int MTperConn = 1;
-static rwlock_t sessionPoolLock = DEFAULTRWLOCK;
+
+static mutex_t sessionPoolLock = DEFAULTMUTEX;
static Connection **sessionPool = NULL;
static int sessionPoolSize = 0;
-static int sharedConnNumber = 0;
-static mutex_t sharedConnNumberLock = DEFAULTMUTEX;
-
-static int check_nscd_proc(pid_t pid, boolean_t check_uid);
/*
* SSF values are for SASL integrity & privacy.
@@ -89,377 +88,51 @@ static int check_nscd_proc(pid_t pid, boolean_t check_uid);
/* Number of hostnames to allocate memory for */
#define NUMTOMALLOC 32
-/*
- * ns_mtckey is for sharing a ldap connection among multiple
- * threads; created by ns_ldap_init() in ns_init.c
- */
-extern thread_key_t ns_mtckey;
-
-/* Per thread LDAP error resides in thread-specific data. */
-struct ldap_error {
- int le_errno;
- char *le_matched;
- char *le_errmsg;
-};
-
-static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
-
-/* destructor */
-void
-ns_tsd_cleanup(void *key) {
- struct ldap_error *le = (struct ldap_error *)key;
-
- if (le == NULL)
- return;
- if (le->le_matched != NULL) {
- ldap_memfree(le->le_matched);
- }
- if (le->le_errmsg != NULL) {
- ldap_memfree(le->le_errmsg);
- }
- free(le);
-}
-
-/* Callback function for allocating a mutex */
-static void *
-ns_mutex_alloc(void)
-{
- mutex_t *mutexp = NULL;
-
- if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
- if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
- free(mutexp);
- mutexp = NULL;
- }
- }
- return (mutexp);
-}
-
-/* Callback function for freeing a mutex */
-static void
-ns_mutex_free(void *mutexp)
-{
- (void) mutex_destroy((mutex_t *)mutexp);
- free(mutexp);
-}
-
-/*
- * Function for setting up thread-specific data
- * where per thread LDAP error is stored
- */
-static int
-tsd_setup()
-{
- void *tsd;
- int rc;
-
- /* return success if TSD already set */
- rc = thr_getspecific(ns_mtckey, &tsd);
- if (rc == 0 && tsd != NULL)
- return (0);
-
- /* allocate and set TSD */
- tsd = (void *) calloc(1, sizeof (struct ldap_error));
- if (tsd == NULL)
- return (-1);
- rc = thr_setspecific(ns_mtckey, tsd);
- if (rc != 0) { /* must be ENOMEM */
- free(tsd);
- return (-1);
- }
- return (0);
-
-
-}
-
-/* Callback function for setting the per thread LDAP error */
-/*ARGSUSED*/
-static void
-set_ld_error(int err, char *matched, char *errmsg, void *dummy)
-{
- struct ldap_error *le;
-
- if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
- syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno"
- " %d", errno);
- return;
- }
-
- /* play safe, do nothing if TSD pointer is NULL */
- if (le == NULL)
- return;
-
- 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;
-}
-
-int
-/* check and allocate the thread-specific data for using a shared connection */
-__s_api_check_MTC_tsd()
-{
- if (tsd_setup() != 0)
- return (NS_LDAP_MEMORY);
-
- return (NS_LDAP_SUCCESS);
-}
-
-/* Callback function for getting the per thread LDAP error */
-/*ARGSUSED*/
-static int
-get_ld_error(char **matched, char **errmsg, void *dummy)
-{
- struct ldap_error *le;
-
- if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
- syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno"
- " %d", errno);
- return (errno);
- }
-
- /* play safe, return NULL error data, if TSD pointer is NULL */
- if (le == NULL)
- le = &ldap_error_NULL;
-
- if (matched != NULL) {
- *matched = le->le_matched;
- }
- if (errmsg != NULL) {
- *errmsg = le->le_errmsg;
- }
- return (le->le_errno);
-}
-
-/* Callback function for setting per thread errno */
-static void
-set_errno(int err)
-{
- errno = err;
-}
-
-/* Callback function for getting per thread errno */
-static int
-get_errno(void)
-{
- return (errno);
-}
/*
- * set up to allow multiple threads to use the same ldap connection
+ * This function get the servers from the lists and returns
+ * the first server with the empty lists of server controls and
+ * SASL mechanisms. It is invoked if it is not possible to obtain a server
+ * from ldap_cachemgr or the local list.
*/
-static int
-setup_mt_conn(LDAP *ld)
+static
+ns_ldap_return_code
+getFirstFromConfig(ns_server_info_t *ret, ns_ldap_error_t **error)
{
-
- struct ldap_thread_fns tfns;
- struct ldap_extra_thread_fns extrafns;
- int rc;
-
- /*
- * Set the function pointers for dealing with mutexes
- * and error information
- */
- (void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
- tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
- tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
- tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
- tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
- 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;
- tfns.ltf_lderrno_arg = NULL;
-
- /*
- * Set up this session to use those function pointers
- */
- rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
- (void *) &tfns);
- if (rc < 0) {
- syslog(LOG_WARNING, "libsldap: ldap_set_option "
- "(LDAP_OPT_THREAD_FN_PTRS)");
- return (-1);
- }
-
- /*
- * Set the function pointers for working with semaphores
- */
- (void) memset(&extrafns, '\0',
- sizeof (struct ldap_extra_thread_fns));
- extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
- extrafns.ltf_mutex_trylock = NULL;
- extrafns.ltf_sema_alloc = NULL;
- extrafns.ltf_sema_free = NULL;
- extrafns.ltf_sema_wait = NULL;
- extrafns.ltf_sema_post = NULL;
-
- /* Set up this session to use those function pointers */
- rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
- (void *) &extrafns);
- if (rc < 0) {
- syslog(LOG_WARNING, "libsldap: ldap_set_option "
- "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
- return (-1);
- }
-
- return (0);
-}
-
-static void
-ns_setup_mt_conn_and_tsd(LDAP *ld) {
- thread_t t = thr_self();
- void *tsd;
- /* set up to share this connection among threads */
- if (MTperConn == 1) {
- if (tsd_setup() == -1) {
- syslog(LOG_ERR, "tid= %d: unable "
- "to set up TSD\n", t);
- } else {
- if (setup_mt_conn(ld) == -1) {
- /* multiple threads per connection not supported */
- syslog(LOG_ERR, "tid= %d: multiple "
- "threads per connection not "
- "supported\n", t);
- (void) thr_getspecific(ns_mtckey, &tsd);
- ns_tsd_cleanup(tsd);
- (void) thr_setspecific(ns_mtckey, NULL);
- MTperConn = 0;
- }
- }
- }
-}
-
-/*
- * Check name and UID of process, if it is nscd.
- *
- * Input:
- * pid : PID of checked process
- * check_uid : check if UID == 0
- * Output:
- * 1 : nscd detected
- * 0 : nscd not confirmed
- */
-static int
-check_nscd_proc(pid_t pid, boolean_t check_uid)
-{
- psinfo_t pinfo;
- char fname[MAXPATHLEN];
- ssize_t ret;
- int fd;
-
- if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
- if ((fd = open(fname, O_RDONLY)) >= 0) {
- ret = read(fd, &pinfo, sizeof (psinfo_t));
- (void) close(fd);
- if ((ret == sizeof (psinfo_t)) &&
- (strcmp(pinfo.pr_fname, "nscd") == 0)) {
- if (check_uid && (pinfo.pr_uid != 0))
- return (0);
- return (1);
- }
+ char **servers = NULL;
+ ns_ldap_return_code ret_code;
+ char errstr[MAXERROR];
+
+ /* get first server from config list unavailable otherwise */
+ ret_code = __s_api_getServers(&servers, error);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ if (servers != NULL) {
+ __s_api_free2dArray(servers);
}
+ return (ret_code);
}
- return (0);
-}
-
-/*
- * Check if this process is peruser nscd.
- */
-int
-__s_api_peruser_proc(void)
-{
- pid_t my_ppid;
- static mutex_t nscdLock = DEFAULTMUTEX;
- static pid_t checkedPpid = (pid_t)-1;
- static int isPeruserNscd = 0;
-
- my_ppid = getppid();
-
- /*
- * Already checked before for this process? If yes, return cached
- * response.
- */
- if (my_ppid == checkedPpid) {
- return (isPeruserNscd);
- }
-
- (void) mutex_lock(&nscdLock);
-
- /* Check once more incase another thread has just complete this. */
- if (my_ppid == checkedPpid) {
- (void) mutex_unlock(&nscdLock);
- return (isPeruserNscd);
- }
-
- /* Reinitialize to be sure there is no residue after fork. */
- isPeruserNscd = 0;
-
- /* Am I the nscd process? */
- if (check_nscd_proc(getpid(), B_FALSE)) {
- /* Is my parent the nscd process with UID == 0. */
- isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
- }
-
- /* Remeber for whom isPeruserNscd is. */
- checkedPpid = my_ppid;
-
- (void) mutex_unlock(&nscdLock);
- return (isPeruserNscd);
-}
-
-/*
- * Check if this process is main nscd.
- */
-int
-__s_api_nscd_proc(void)
-{
- pid_t my_pid;
- static mutex_t nscdLock = DEFAULTMUTEX;
- static pid_t checkedPid = (pid_t)-1;
- static int isMainNscd = 0;
-
- /*
- * Don't bother checking if this process isn't root, this cannot
- * be main nscd.
- */
- if (getuid() != 0)
- return (0);
- my_pid = getpid();
-
- /*
- * Already checked before for this process? If yes, return cached
- * response.
- */
- if (my_pid == checkedPid) {
- return (isMainNscd);
+ if (servers == NULL || servers[0] == NULL) {
+ __s_api_free2dArray(servers);
+ (void) sprintf(errstr,
+ gettext("No server found in configuration"));
+ MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_CONFIG);
}
- (void) mutex_lock(&nscdLock);
-
- /* Check once more incase another thread has just done this. */
- if (my_pid == checkedPid) {
- (void) mutex_unlock(&nscdLock);
- return (isMainNscd);
+ ret->server = strdup(servers[0]);
+ if (ret->server == NULL) {
+ __s_api_free2dArray(servers);
+ return (NS_LDAP_MEMORY);
}
- /*
- * Am I the nscd process? UID is already checked, not needed from
- * psinfo.
- */
- isMainNscd = check_nscd_proc(my_pid, B_FALSE);
+ ret->saslMechanisms = NULL;
+ ret->controls = NULL;
- /* Remeber for whom isMainNscd is. */
- checkedPid = my_pid;
+ __s_api_free2dArray(servers);
- (void) mutex_unlock(&nscdLock);
- return (isMainNscd);
+ return (NS_LDAP_SUCCESS);
}
/*
@@ -467,7 +140,7 @@ __s_api_nscd_proc(void)
* the door functionality
*/
-static int
+int
__s_api_requestServer(const char *request, const char *server,
ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType)
{
@@ -475,17 +148,17 @@ __s_api_requestServer(const char *request, const char *server,
ldap_data_t s_d;
char s_b[DOORBUFFERSIZE];
} space;
- ldap_data_t *sptr;
- int ndata;
- int adata;
- char errstr[MAXERROR];
- const char *ireq;
- char *rbuf, *ptr, *rest;
- char *dptr;
- char **mptr, **mptr1, **cptr, **cptr1;
- int mcnt, ccnt;
- char **servers;
- int rc, len;
+ ldap_data_t *sptr;
+ int ndata;
+ int adata;
+ char errstr[MAXERROR];
+ const char *ireq;
+ char *rbuf, *ptr, *rest;
+ char *dptr;
+ char **mptr, **mptr1, **cptr, **cptr1;
+ int mcnt, ccnt;
+ int len;
+ ns_ldap_return_code ret_code;
if (ret == NULL || error == NULL) {
return (NS_LDAP_OP_FAILED);
@@ -493,13 +166,62 @@ __s_api_requestServer(const char *request, const char *server,
(void) memset(ret, 0, sizeof (ns_server_info_t));
*error = NULL;
- (void) memset(space.s_b, 0, DOORBUFFERSIZE);
-
if (request == NULL)
ireq = NS_CACHE_NEW;
else
ireq = request;
+ /*
+ * In the 'Standalone' mode a server will be obtained
+ * from the local libsldap's list
+ */
+ if (__s_api_isStandalone()) {
+ if (__s_api_findRootDSE(ireq,
+ server,
+ addrType,
+ ret,
+ error) != NS_LDAP_SUCCESS) {
+ syslog(LOG_WARNING,
+ "libsldap (\"standalone\" mode): "
+ "can not find any available server. "
+ "Return the first one from the lists");
+ if (*error != NULL) {
+ (void) __ns_ldap_freeError(error);
+ }
+
+ ret_code = getFirstFromConfig(ret, error);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ return (ret_code);
+ }
+
+ if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
+ ret_code = __s_api_ip2hostname(ret->server,
+ &ret->serverFQDN);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ (void) snprintf(errstr,
+ sizeof (errstr),
+ gettext("The %s address "
+ "can not be resolved into "
+ "a host name. Returning "
+ "the address as it is."),
+ ret->server);
+ MKERROR(LOG_ERR,
+ *error,
+ NS_CONFIG_NOTLOADED,
+ strdup(errstr),
+ NS_LDAP_MEMORY);
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+ }
+
+ return (NS_LDAP_SUCCESS);
+ }
+
+ (void) memset(space.s_b, 0, DOORBUFFERSIZE);
+
adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
if (server != NULL) {
adata += strlen(DOORLINESEP) + 1;
@@ -524,41 +246,39 @@ __s_api_requestServer(const char *request, const char *server,
sptr = &space.s_d;
switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
- case SUCCESS:
+ case NS_CACHE_SUCCESS:
break;
/* this case is for when the $mgr is not running, but ldapclient */
/* is trying to initialize things */
- case NOSERVER:
- /* get first server from config list unavailable otherwise */
- servers = NULL;
- rc = __s_api_getServers(&servers, error);
- if (rc != NS_LDAP_SUCCESS) {
- if (servers != NULL) {
- __s_api_free2dArray(servers);
- servers = NULL;
+ case NS_CACHE_NOSERVER:
+ ret_code = getFirstFromConfig(ret, error);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ return (ret_code);
+ }
+
+ if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
+ ret_code = __s_api_ip2hostname(ret->server,
+ &ret->serverFQDN);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ (void) snprintf(errstr,
+ sizeof (errstr),
+ gettext("The %s address "
+ "can not be resolved into "
+ "a host name. Returning "
+ "the address as it is."),
+ ret->server);
+ MKERROR(LOG_ERR,
+ *error,
+ NS_CONFIG_NOTLOADED,
+ strdup(errstr),
+ NS_LDAP_MEMORY);
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_INTERNAL);
}
- return (rc);
}
- if (servers == NULL || servers[0] == NULL) {
- __s_api_free2dArray(servers);
- servers = NULL;
- (void) sprintf(errstr,
- gettext("No server found in configuration"));
- MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
- strdup(errstr), NULL);
- return (NS_LDAP_CONFIG);
- }
- ret->server = strdup(servers[0]);
- if (ret->server == NULL) {
- __s_api_free2dArray(servers);
- return (NS_LDAP_MEMORY);
- }
- ret->saslMechanisms = NULL;
- ret->controls = NULL;
- __s_api_free2dArray(servers);
- servers = NULL;
return (NS_LDAP_SUCCESS);
- case NOTFOUND:
+ case NS_CACHE_NOTFOUND:
default:
return (NS_LDAP_OP_FAILED);
}
@@ -572,7 +292,7 @@ __s_api_requestServer(const char *request, const char *server,
(void) sprintf(errstr, gettext("No server returned from "
"ldap_cachemgr"));
MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
- strdup(errstr), NULL);
+ strdup(errstr), NS_LDAP_MEMORY);
return (NS_LDAP_OP_FAILED);
}
ret->server = strdup(ptr);
@@ -604,7 +324,7 @@ __s_api_requestServer(const char *request, const char *server,
mcnt = 0;
cptr = NULL;
ccnt = 0;
- for (; ; ) {
+ for (;;) {
ptr = strtok_r(NULL, DOORLINESEP, &rest);
if (ptr == NULL)
break;
@@ -694,121 +414,85 @@ __s_api_requestServer(const char *request, const char *server,
}
+#ifdef DEBUG
/*
* printCred(): prints the credential structure
*/
static void
-printCred(int pri, const ns_cred_t *cred)
+printCred(FILE *fp, const ns_cred_t *cred)
{
thread_t t = thr_self();
if (cred == NULL) {
- syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t);
+ (void) fprintf(fp, "tid= %d: printCred: cred is NULL\n", t);
return;
}
- syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type);
- syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
- syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
- syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
+ (void) fprintf(fp, "tid= %d: AuthType=%d", t, cred->auth.type);
+ (void) fprintf(fp, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
+ (void) fprintf(fp, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
+ (void) fprintf(fp, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
if (cred->hostcertpath)
- syslog(pri, "tid= %d: hostCertPath=%s\n",
+ (void) fprintf(fp, "tid= %d: hostCertPath=%s\n",
t, cred->hostcertpath);
if (cred->cred.unix_cred.userID)
- syslog(pri, "tid= %d: userID=%s\n",
+ (void) fprintf(fp, "tid= %d: userID=%s\n",
t, cred->cred.unix_cred.userID);
-#ifdef DEBUG
if (cred->cred.unix_cred.passwd)
- syslog(pri, "tid= %d: passwd=%s\n",
+ (void) fprintf(fp, "tid= %d: passwd=%s\n",
t, cred->cred.unix_cred.passwd);
-#endif
}
/*
* printConnection(): prints the connection structure
*/
static void
-printConnection(int pri, Connection *con)
+printConnection(FILE *fp, Connection *con)
{
thread_t t = thr_self();
if (con == NULL)
return;
- syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId);
- syslog(pri, "tid= %d: shared=%d\n", t, con->shared);
- syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit);
- syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID);
+ (void) fprintf(fp, "tid= %d: connectionID=%d\n", t, con->connectionId);
+ (void) fprintf(fp, "tid= %d: usedBit=%d\n", t, con->usedBit);
+ (void) fprintf(fp, "tid= %d: threadID=%d\n", t, con->threadID);
if (con->serverAddr) {
- syslog(pri, "tid= %d: serverAddr=%s\n",
+ (void) fprintf(fp, "tid= %d: serverAddr=%s\n",
t, con->serverAddr);
}
- printCred(pri, con->auth);
+ printCred(fp, con->auth);
}
-
-
+#endif
/*
- * addConnection(): set up a connection so that it can be shared
- * among multiple threads and then insert the connection in the
- * connection list.
+ * addConnection(): inserts a connection in the connection list.
+ * It will also sets use bit and the thread Id for the thread
+ * using the connection for the first time.
* Returns: -1 = failure, new Connection ID = success
- *
- * This function could exit with sessionLock locked. It will be
- * be unlocked in __s_api_getConnection() when it exits without getting a
- * connection.
*/
static int
addConnection(Connection *con)
{
- int i, noMTperC = 0;
- thread_t t = thr_self();
- struct ldap_thread_fns tfns;
- void *tsd;
+ int i;
if (!con)
return (-1);
-
- syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)",
- t, con->serverAddr);
-
- if (MTperConn == 1) {
- /*
- * Make sure ld has proper thread functions and tsd
- * is set up.
- */
- (void) memset(&tfns, 0, sizeof (struct ldap_thread_fns));
- /*
- * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs.
- * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd.
- */
- if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS,
- (void *)&tfns) != 0 ||
- tfns.ltf_get_lderrno != get_ld_error ||
- tfns.ltf_set_lderrno != set_ld_error) {
- MTperConn = 0;
- noMTperC = 1;
- } else {
- if (thr_getspecific(ns_mtckey, &tsd) != 0 ||
- tsd == NULL)
- noMTperC = 1;
- }
-
- } else {
- noMTperC = 1;
- }
-
- (void) rw_wrlock(&sessionPoolLock);
+#ifdef DEBUG
+ (void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID);
+#endif /* DEBUG */
+ (void) mutex_lock(&sessionPoolLock);
if (sessionPool == NULL) {
sessionPoolSize = SESSION_CACHE_INC;
sessionPool = calloc(sessionPoolSize,
- sizeof (struct connection **));
+ sizeof (Connection *));
if (!sessionPool) {
- (void) rw_unlock(&sessionPoolLock);
+ (void) mutex_unlock(&sessionPoolLock);
return (-1);
}
-
- syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t);
+#ifdef DEBUG
+ (void) fprintf(stderr, "Initialized sessionPool\n");
+#endif /* DEBUG */
}
for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
;
@@ -819,152 +503,44 @@ addConnection(Connection *con)
(sessionPoolSize + SESSION_CACHE_INC) *
sizeof (Connection *));
if (!cl) {
- (void) rw_unlock(&sessionPoolLock);
+ (void) mutex_unlock(&sessionPoolLock);
return (-1);
}
(void) memset(cl + sessionPoolSize, 0,
- SESSION_CACHE_INC * sizeof (struct connection *));
+ SESSION_CACHE_INC * sizeof (Connection *));
sessionPool = cl;
sessionPoolSize += SESSION_CACHE_INC;
- syslog(LOG_DEBUG, "tid: %d: Increased "
- "sessionPoolSize to: %d\n",
- t, sessionPoolSize);
+#ifdef DEBUG
+ (void) fprintf(stderr, "Increased sessionPoolSize to: %d\n",
+ sessionPoolSize);
+#endif /* DEBUG */
}
sessionPool[i] = con;
- if (noMTperC == 0) {
- con->shared++;
- con->pid = getpid();
- (void) mutex_lock(&sharedConnNumberLock);
- sharedConnNumber++;
- (void) mutex_unlock(&sharedConnNumberLock);
- } else
- con->usedBit = B_TRUE;
-
- (void) rw_unlock(&sessionPoolLock);
-
+ con->usedBit = B_TRUE;
+ (void) mutex_unlock(&sessionPoolLock);
con->connectionId = i + CONID_OFFSET;
-
- syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n",
- t, i);
- printConnection(LOG_DEBUG, con);
-
- /*
- * A connection can be shared now, unlock
- * the session mutex and let other
- * threads try to use this connection or
- * get their own.
- */
- if (wait4session != 0 && sessionTid == thr_self()) {
- wait4session = 0;
- sessionTid = 0;
- syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t);
- (void) mutex_unlock(&sessionLock);
- }
-
+#ifdef DEBUG
+ (void) fprintf(stderr, "Connection added [%d]\n", i);
+ printConnection(stderr, con);
+#endif /* DEBUG */
return (i + CONID_OFFSET);
}
/*
- * See if the specified session matches a currently available
- */
-
-static int
-findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
- Connection **conp)
-{
- Connection *cp;
- int id;
-
- if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
- return (-1);
-
- /*
- * If a new connection is requested, no need to continue.
- * If the process is not nscd and is not requesting keep connections
- * alive, no need to continue.
- */
- if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
- !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
- return (-1);
-
- *conp = NULL;
- if (sessionPool == NULL)
- return (-1);
- id = cID - CONID_OFFSET;
- if (id < 0 || id >= sessionPoolSize)
- return (-1);
-
- (void) rw_rdlock(&sessionPoolLock);
- if (sessionPool[id] == NULL) {
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
- cp = sessionPool[id];
-
- /*
- * Make sure the connection has the same type of authentication method
- */
- if ((cp->usedBit) ||
- (cp->notAvail) ||
- (cp->auth->auth.type != auth->auth.type) ||
- (cp->auth->auth.tlstype != auth->auth.tlstype) ||
- (cp->auth->auth.saslmech != auth->auth.saslmech) ||
- (cp->auth->auth.saslopt != auth->auth.saslopt)) {
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
- if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
- ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
- (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
- (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
- ((cp->auth->cred.unix_cred.userID == NULL) ||
- (strcasecmp(cp->auth->cred.unix_cred.userID,
- auth->cred.unix_cred.userID) != 0))) {
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
-
- /* An existing connection is found but it needs to be reset */
- if (flags & NS_LDAP_NEW_CONN) {
- (void) rw_unlock(&sessionPoolLock);
- DropConnection(cID, 0);
- return (-1);
- }
- /* found an available connection */
- cp->usedBit = B_TRUE;
- (void) rw_unlock(&sessionPoolLock);
- cp->threadID = thr_self();
- *conp = cp;
- return (cID);
-}
-
-/*
* findConnection(): find an available connection from the list
* that matches the criteria specified in Connection structure.
* If serverAddr is NULL, then find a connection to any server
* as long as it matches the rest of the parameters.
* Returns: -1 = failure, the Connection ID found = success.
- *
- * This function could exit with sessionLock locked. It will be
- * be unlocked in addConnection() when this thread adds the connection
- * to the pool or in __s_api_getConnection() when it exits without getting a
- * connection.
*/
-#define TRY_TIMES 10
static int
findConnection(int flags, const char *serverAddr,
const ns_cred_t *auth, Connection **conp)
{
Connection *cp;
- int i, j, conn_server_index, up_server_index, drop_conn;
- int rc;
- int try;
- ns_server_info_t sinfo;
- ns_ldap_error_t *errorp = NULL;
- char **servers;
- void **paramVal = NULL;
+ int i;
#ifdef DEBUG
- thread_t t = thr_self();
+ thread_t t;
#endif /* DEBUG */
if (auth == NULL || conp == NULL)
@@ -973,14 +549,15 @@ findConnection(int flags, const char *serverAddr,
/*
* If a new connection is requested, no need to continue.
- * If the process is not nscd and is not requesting keep connections
- * alive, no need to continue.
+ * If the process is not nscd and is not requesting keep
+ * connections alive, no need to continue.
*/
if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
!__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
return (-1);
#ifdef DEBUG
+ t = thr_self();
(void) fprintf(stderr, "tid= %d: Find connection\n", t);
(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
if (serverAddr && *serverAddr)
@@ -988,387 +565,49 @@ findConnection(int flags, const char *serverAddr,
t, serverAddr);
else
(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
- printCred(LOG_DEBUG, auth);
+ printCred(stderr, auth);
fflush(stderr);
#endif /* DEBUG */
-
- /*
- * If multiple threads per connection not supported,
- * no sessionPool means no connection
- */
- (void) rw_rdlock(&sessionPoolLock);
- if (MTperConn == 0 && sessionPool == NULL) {
- (void) rw_unlock(&sessionPoolLock);
+ if (sessionPool == NULL)
return (-1);
- }
-
- /*
- * If no sharable connections in cache, then serialize the opening
- * of connections. Make sure only one is being opened
- * at a time. Otherwise, we may end up with more
- * connections than we want (if multiple threads get
- * here at the same time)
- */
- (void) mutex_lock(&sharedConnNumberLock);
- if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
- (void) mutex_unlock(&sharedConnNumberLock);
- (void) rw_unlock(&sessionPoolLock);
- (void) mutex_lock(&sessionLock);
- (void) mutex_lock(&sharedConnNumberLock);
- if (sessionPool == NULL || (sharedConnNumber == 0 &&
- MTperConn == 1)) {
- (void) mutex_unlock(&sharedConnNumberLock);
- wait4session = 1;
- sessionTid = thr_self();
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: get "
- "connection ... \n", t);
- fflush(stderr);
-#endif /* DEBUG */
- /*
- * Exit with sessionLock locked. It will be
- * be unlocked in addConnection() when this
- * thread adds the connection to the pool or
- * in __s_api_getConnection() when it exits
- * without getting a connection.
- */
- return (-1);
- }
-
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: shareable connections "
- "exist\n", t);
- fflush(stderr);
-#endif /* DEBUG */
- (void) mutex_unlock(&sharedConnNumberLock);
- /*
- * There are sharable connections, check to see if
- * one can be shared.
- */
- (void) mutex_unlock(&sessionLock);
- (void) rw_rdlock(&sessionPoolLock);
- } else
- (void) mutex_unlock(&sharedConnNumberLock);
-
- try = 0;
- check_again:
-
+ (void) mutex_lock(&sessionPoolLock);
for (i = 0; i < sessionPoolSize; ++i) {
if (sessionPool[i] == NULL)
continue;
cp = sessionPool[i];
#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: checking connection "
- "[%d] ....\n", t, i);
- printConnection(LOG_DEBUG, cp);
+ (void) fprintf(stderr,
+ "tid: %d: checking connection [%d] ....\n", t, i);
+ printConnection(stderr, cp);
#endif /* DEBUG */
- if ((cp->usedBit) || (cp->notAvail) ||
- (cp->auth->auth.type != auth->auth.type) ||
- (cp->auth->auth.tlstype != auth->auth.tlstype) ||
- (cp->auth->auth.saslmech != auth->auth.saslmech) ||
- (cp->auth->auth.saslopt != auth->auth.saslopt) ||
- (serverAddr && *serverAddr &&
+ if ((cp->usedBit) || (serverAddr && *serverAddr &&
(strcasecmp(serverAddr, cp->serverAddr) != 0)))
continue;
- if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
- ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
- (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
- (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
- ((cp->auth->cred.unix_cred.userID == NULL) ||
- (cp->auth->cred.unix_cred.passwd == NULL) ||
- ((strcasecmp(cp->auth->cred.unix_cred.userID,
- auth->cred.unix_cred.userID) != 0)) ||
- ((strcmp(cp->auth->cred.unix_cred.passwd,
- auth->cred.unix_cred.passwd) != 0))))
- continue;
- if (!(serverAddr && *serverAddr)) {
- /*
- * Get preferred server list.
- * When preferred servers are merged with default
- * servers (S_LDAP_SERVER_P) by __s_api_getServer,
- * preferred servers are copied sequencially.
- * The order should be the same as the order retrieved
- * by __ns_ldap_getParam.
- */
- if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
- &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
- (void) __ns_ldap_freeError(&errorp);
- (void) __ns_ldap_freeParam(&paramVal);
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
- servers = (char **)paramVal;
- /*
- * Do fallback only if preferred servers are defined.
- */
- if (servers != NULL) {
- /*
- * Find the 1st available server
- */
- rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
- &sinfo, &errorp, NS_CACHE_ADDR_IP);
- if (rc != NS_LDAP_SUCCESS) {
- /*
- * Drop the connection.
- * Pass 1 to fini so it won't be locked
- * inside _DropConnection
- */
- _DropConnection(
- cp->connectionId,
- NS_LDAP_NEW_CONN, 1);
- (void) rw_unlock(
- &sessionPoolLock);
- (void) __ns_ldap_freeError(&errorp);
- (void) __ns_ldap_freeParam(
- (void ***)&servers);
- return (-1);
- }
- if (sinfo.server) {
- /*
- * Test if cp->serverAddr is a
- * preferred server.
- */
- conn_server_index = -1;
- for (j = 0; servers[j] != NULL; j++) {
- if (strcasecmp(servers[j],
- cp->serverAddr) == 0) {
- conn_server_index = j;
- break;
- }
- }
- /*
- * Test if sinfo.server is a preferred
- * server.
- */
- up_server_index = -1;
- for (j = 0; servers[j] != NULL; j++) {
- if (strcasecmp(sinfo.server,
- servers[j]) == 0) {
- up_server_index = j;
- break;
- }
- }
-
- /*
- * The following code is to fall back
- * to preferred servers if servers
- * are previously down but are up now.
- * If cp->serverAddr is a preferred
- * server, it falls back to the servers
- * ahead of it. If cp->serverAddr is
- * not a preferred server, it falls
- * back to any of preferred servers
- * returned by ldap_cachemgr.
- */
- if (conn_server_index >= 0 &&
- up_server_index >= 0) {
- /*
- * cp->serverAddr and
- * sinfo.server are preferred
- * servers.
- */
- if (up_server_index ==
- conn_server_index)
- /*
- * sinfo.server is the
- * same as
- * cp->serverAddr.
- * Keep the connection.
- */
- drop_conn = 0;
- else
- /*
- * 1.
- * up_server_index <
- * conn_server_index
- *
- * sinfo is ahead of
- * cp->serverAddr in
- * Need to fall back.
- * 2.
- * up_server_index >
- * conn_server_index
- * cp->serverAddr is
- * down. Drop it.
- */
- drop_conn = 1;
- } else if (conn_server_index >= 0 &&
- up_server_index == -1) {
- /*
- * cp->serverAddr is a preferred
- * server but sinfo.server is
- * not. Preferred servers are
- * ahead of default servers.
- * This means cp->serverAddr is
- * down. Drop it.
- */
- drop_conn = 1;
- } else if (conn_server_index == -1 &&
- up_server_index >= 0) {
- /*
- * cp->serverAddr is not a
- * preferred server but
- * sinfo.server is.
- * Fall back.
- */
- drop_conn = 1;
- } else {
- /*
- * Both index are -1
- * cp->serverAddr and
- * sinfo.server are not
- * preferred servers.
- * No fallback.
- */
- drop_conn = 0;
- }
- if (drop_conn) {
- /*
- * Drop the connection so the
- * new conection can fall back
- * to a new server later.
- * Pass 1 to fini so it won't
- * be locked inside
- * _DropConnection
- */
- _DropConnection(
- cp->connectionId,
- NS_LDAP_NEW_CONN, 1);
- (void) rw_unlock(
- &sessionPoolLock);
- (void) __ns_ldap_freeParam(
- (void ***)&servers);
- __s_api_free_server_info(
- &sinfo);
- return (-1);
- } else {
- /*
- * Keep the connection
- */
- (void) __ns_ldap_freeParam(
- (void ***)&servers);
- __s_api_free_server_info(
- &sinfo);
- }
- } else {
- (void) rw_unlock(&sessionPoolLock);
- syslog(LOG_WARNING, "libsldap: Null "
- "sinfo.server from "
- "__s_api_requestServer");
- (void) __ns_ldap_freeParam(
- (void ***)&servers);
- return (-1);
- }
- }
- }
+ if (__s_api_is_auth_matched(cp->auth, auth) == B_FALSE)
+ continue;
/* found an available connection */
- if (MTperConn == 0)
- cp->usedBit = B_TRUE;
- else {
- /*
- * if connection was established in a different
- * process, drop it and get a new one
- */
- if (cp->pid != getpid()) {
- (void) rw_unlock(&sessionPoolLock);
- DropConnection(cp->connectionId,
- NS_LDAP_NEW_CONN);
-
- goto get_conn;
- }
- /* allocate TSD for per thread ldap error */
- rc = tsd_setup();
-
- /* if we got TSD, this connection is shared */
- if (rc != -1)
- cp->shared++;
- else if (cp->shared == 0) {
- cp->usedBit = B_TRUE;
- cp->threadID = thr_self();
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
- }
- (void) rw_unlock(&sessionPoolLock);
-
+ cp->usedBit = B_TRUE;
+ (void) mutex_unlock(&sessionPoolLock);
+ cp->threadID = thr_self();
*conp = cp;
#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: Connection found "
- "cID=%d, shared =%d\n", t, i, cp->shared);
+ (void) fprintf(stderr,
+ "tid %d: Connection found cID=%d\n", t, i);
fflush(stderr);
#endif /* DEBUG */
return (i + CONID_OFFSET);
}
-
- get_conn:
-
- (void) rw_unlock(&sessionPoolLock);
-
- /*
- * If multiple threads per connection not supported,
- * we are done, just return -1 to tell the caller to
- * proceed with opening a connection
- */
- if (MTperConn == 0)
- return (-1);
-
- /*
- * No connection can be shared, test to see if
- * one is being opened. If trylock returns
- * EBUSY then it is, so wait until the opening
- * is done and try to see if the new connection
- * can be shared.
- */
- rc = mutex_trylock(&sessionLock);
- if (rc == EBUSY) {
- (void) mutex_lock(&sessionLock);
- (void) mutex_unlock(&sessionLock);
- (void) rw_rdlock(&sessionPoolLock);
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: check session "
- "pool again\n", t);
- fflush(stderr);
-#endif /* DEBUG */
- if (try < TRY_TIMES) {
- try++;
- goto check_again;
- } else {
- syslog(LOG_WARNING, "libsldap: mutex_trylock "
- "%d times. Stop.", TRY_TIMES);
- (void) rw_unlock(&sessionPoolLock);
- return (-1);
- }
- } else if (rc == 0) {
- /*
- * No connection can be shared, none being opened,
- * exit with sessionLock locked to open one. The
- * mutex will be unlocked in addConnection() when
- * this thread adds the new connection to the pool
- * or in __s_api_getConnection() when it exits
- * without getting a connection.
- */
- wait4session = 1;
- sessionTid = thr_self();
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: no connection found, "
- "none being opened, get connection ...\n", t);
- fflush(stderr);
-#endif /* DEBUG */
- return (-1);
- } else {
- syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
- "error %d", rc);
- return (-1);
- }
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
}
/*
* Free a Connection structure
*/
-static void
-freeConnection(Connection *con)
+void
+__s_api_freeConnection(Connection *con)
{
if (con == NULL)
return;
@@ -1397,7 +636,8 @@ static int
makeConnection(Connection **conp, const char *serverAddr,
const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
- int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
+ int nopasswd_acct_mgmt, int flags, char ***badsrvrs,
+ ns_conn_user_t *conn_user)
{
Connection *con = NULL;
ConnectionID id;
@@ -1418,8 +658,7 @@ makeConnection(Connection **conp, const char *serverAddr,
*conp = NULL;
(void) memset(&sinfo, 0, sizeof (sinfo));
- if ((wait4session == 0 || sessionTid != thr_self()) &&
- (id = findConnection(flags, serverAddr, auth, &con)) != -1) {
+ if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) {
/* connection found in cache */
#ifdef DEBUG
(void) fprintf(stderr, "tid= %d: connection found in "
@@ -1440,29 +679,60 @@ makeConnection(Connection **conp, const char *serverAddr,
}
if (serverAddr) {
- /*
- * We're given the server address, just use it.
- * In case of sasl/GSSAPI, serverAddr would need to be a FQDN.
- * We assume this is the case for now.
- *
- * Only the server address fields of sinfo structure are filled
- * in since these are the only relevant data that we have. Other
- * fields of this structure (controls, saslMechanisms) are
- * kept to NULL.
- */
- sinfo.server = strdup(serverAddr);
- if (sinfo.server == NULL) {
- return (NS_LDAP_MEMORY);
- }
- if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
- sinfo.serverFQDN = strdup(serverAddr);
- if (sinfo.serverFQDN == NULL) {
- free(sinfo.server);
+ if (__s_api_isInitializing()) {
+ /*
+ * When obtaining the root DSE, connect to the server
+ * passed here through the serverAddr parameter
+ */
+ sinfo.server = strdup(serverAddr);
+ if (sinfo.server == NULL)
return (NS_LDAP_MEMORY);
+ if (strcmp(serverAddrType,
+ NS_CACHE_ADDR_HOSTNAME) == 0) {
+ rc = __s_api_ip2hostname(sinfo.server,
+ &sinfo.serverFQDN);
+ if (rc != NS_LDAP_SUCCESS) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("The %s address "
+ "can not be resolved into "
+ "a host name. Returning "
+ "the address as it is."),
+ serverAddr);
+ MKERROR(LOG_ERR,
+ *errorp,
+ NS_CONFIG_NOTLOADED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ __s_api_free_server_info(&sinfo);
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+ } else {
+ /*
+ * We're given the server address, just use it.
+ * In case of sasl/GSSAPI, serverAddr would need
+ * to be a FQDN. We assume this is the case for now.
+ *
+ * Only the server address fields of sinfo structure
+ * are filled in since these are the only relevant
+ * data that we have. Other fields of this structure
+ * (controls, saslMechanisms) are kept to NULL.
+ */
+ sinfo.server = strdup(serverAddr);
+ if (sinfo.server == NULL) {
+ return (NS_LDAP_MEMORY);
+ }
+ if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
+ sinfo.serverFQDN = strdup(serverAddr);
+ if (sinfo.serverFQDN == NULL) {
+ free(sinfo.server);
+ return (NS_LDAP_MEMORY);
+ }
}
}
rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
- fail_if_new_pwd_reqd, passwd_mgmt);
+ fail_if_new_pwd_reqd, passwd_mgmt, conn_user);
if (rc == NS_LDAP_SUCCESS || rc ==
NS_LDAP_SUCCESS_WITH_INFO) {
exit_rc = rc;
@@ -1534,7 +804,7 @@ makeConnection(Connection **conp, const char *serverAddr,
}
/* make the connection */
rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
- fail_if_new_pwd_reqd, passwd_mgmt);
+ fail_if_new_pwd_reqd, passwd_mgmt, conn_user);
/* if success, go to create connection structure */
if (rc == NS_LDAP_SUCCESS ||
rc == NS_LDAP_SUCCESS_WITH_INFO) {
@@ -1654,7 +924,7 @@ create_con:
con->auth = __ns_ldap_dupAuth(auth);
if (con->auth == NULL) {
(void) ldap_unbind(ld);
- freeConnection(con);
+ __s_api_freeConnection(con);
/*
* If password control attached in **errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
@@ -1671,9 +941,23 @@ create_con:
con->pid = getpid();
con->ld = ld;
+ /* add MT connection to the MT connection pool */
+ if (conn_user != NULL && conn_user->conn_mt != NULL) {
+ if (__s_api_conn_mt_add(con, conn_user, errorp) ==
+ NS_LDAP_SUCCESS) {
+ *conp = con;
+ return (exit_rc);
+ } else {
+ (void) ldap_unbind(ld);
+ __s_api_freeConnection(con);
+ return ((*errorp)->status);
+ }
+ }
+
+ /* MT connection not supported or not required case */
if ((id = addConnection(con)) == -1) {
(void) ldap_unbind(ld);
- freeConnection(con);
+ __s_api_freeConnection(con);
/*
* If password control attached in **errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
@@ -1705,111 +989,56 @@ _DropConnection(ConnectionID cID, int flag, int fini)
{
Connection *cp;
int id;
- int use_lock = !fini;
+ int use_mutex = !fini;
struct timeval zerotime;
LDAPMessage *res;
zerotime.tv_sec = zerotime.tv_usec = 0L;
-#ifdef DEBUG
- thread_t t = thr_self();
-#endif /* DEBUG */
-
id = cID - CONID_OFFSET;
if (id < 0 || id >= sessionPoolSize)
return;
#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: "
- "Dropping connection cID=%d flag=0x%x, fini = %d\n",
- t, cID, flag, fini);
+ (void) fprintf(stderr,
+ "tid %d: Dropping connection cID=%d flag=0x%x\n",
+ thr_self(), cID, flag);
fflush(stderr);
#endif /* DEBUG */
- if (use_lock)
- (void) rw_wrlock(&sessionPoolLock);
+ if (use_mutex)
+ (void) mutex_lock(&sessionPoolLock);
cp = sessionPool[id];
/* sanity check before removing */
- if (!cp || (!fini && !cp->shared && !cp->usedBit)) {
-#ifdef DEBUG
- if (cp == NULL)
- (void) fprintf(stderr, "tid= %d: no "
- "need to remove (fini = %d, cp = %p)\n", t,
- fini, cp);
- else
- (void) fprintf(stderr, "tid= %d: no "
- "need to remove (fini = %d, cp = %p, shared = %d)"
- "\n", t, fini, cp, cp->shared);
- fflush(stderr);
-#endif /* DEBUG */
- if (use_lock)
- (void) rw_unlock(&sessionPoolLock);
+ if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) {
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
return;
}
if (!fini &&
- ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
+ ((flag & NS_LDAP_NEW_CONN) == 0) &&
((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() ||
__s_api_peruser_proc())) {
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
- "shared = %d)\n", t, fini, cp->shared);
-#endif /* DEBUG */
/* release Connection (keep alive) */
- if (cp->shared)
- cp->shared--;
cp->usedBit = B_FALSE;
cp->threadID = 0; /* unmark the threadID */
/*
- * Do sanity cleanup of remaining results if connection is not
- * shared by any requests.
+ * Do sanity cleanup of remaining results.
*/
- if (cp->shared <= 0) {
- while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
- &zerotime, &res) > 0) {
- if (res != NULL)
- (void) ldap_msgfree(res);
- }
+ while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
+ &zerotime, &res) > 0) {
+ if (res != NULL)
+ (void) ldap_msgfree(res);
}
- if (use_lock)
- (void) rw_unlock(&sessionPoolLock);
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
} else {
/* delete Connection (disconnect) */
- if (cp->shared > 0) {
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: Connection no "
- "longer available (fini = %d, shared = %d)\n",
- t, fini, cp->shared);
- fflush(stderr);
-#endif /* DEBUG */
- cp->shared--;
- /*
- * Mark this connection not available and decrement
- * sharedConnNumber. There could be multiple threads
- * sharing this connection so decrement
- * sharedConnNumber only once per connection.
- */
- if (cp->notAvail == 0) {
- cp->notAvail = 1;
- (void) mutex_lock(&sharedConnNumberLock);
- sharedConnNumber--;
- (void) mutex_unlock(&sharedConnNumberLock);
- }
- }
-
- if (cp->shared <= 0) {
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: unbind "
- "(fini = %d, shared = %d)\n",
- t, fini, cp->shared);
- fflush(stderr);
-#endif /* DEBUG */
- sessionPool[id] = NULL;
- (void) ldap_unbind(cp->ld);
- freeConnection(cp);
- }
-
- if (use_lock)
- (void) rw_unlock(&sessionPoolLock);
+ sessionPool[id] = NULL;
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
+ (void) ldap_unbind(cp->ld);
+ __s_api_freeConnection(cp);
}
}
@@ -1895,7 +1124,7 @@ process_pwd_mgmt(char *bind_type, int ldaprc,
pwd_status, 0, NULL);
} else {
MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
- NULL);
+ NS_LDAP_MEMORY);
}
if (controls)
ldap_controls_free(controls);
@@ -2037,7 +1266,7 @@ process_pwd_mgmt(char *bind_type, int ldaprc,
}
static int
-ldap_in_hosts_switch()
+ldap_in_nss_switch(char *db)
{
enum __nsw_parse_err pserr;
struct __nsw_switchconfig *conf;
@@ -2045,7 +1274,7 @@ ldap_in_hosts_switch()
const char *name;
int found = 0;
- conf = __nsw_getconfig("hosts", &pserr);
+ conf = __nsw_getconfig(db, &pserr);
if (conf == NULL) {
return (-1);
}
@@ -2065,214 +1294,68 @@ ldap_in_hosts_switch()
static int
openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
int timeoutSec, ns_ldap_error_t **errorp,
- int fail_if_new_pwd_reqd, int passwd_mgmt)
+ int fail_if_new_pwd_reqd, int passwd_mgmt,
+ ns_conn_user_t *conn_user)
{
- LDAP *ld = NULL;
- char *binddn, *passwd;
- char *digest_md5_name;
- const char *s;
- int ldapVersion = LDAP_VERSION3;
- int derefOption = LDAP_DEREF_ALWAYS;
- int zero = 0;
- int rc;
- char errstr[MAXERROR];
- int errnum = 0;
- LDAPMessage *resultMsg;
- int msgId;
- int useSSL = 0, port = 0;
- struct timeval tv;
- AuthType_t bindType;
- int timeoutMilliSec = timeoutSec * 1000;
- struct berval cred;
- char *sslServerAddr;
- char *s1;
- char *errmsg, *end = NULL;
- LDAPControl **controls;
- int pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
- ns_sasl_cb_param_t sasl_param;
+ LDAP *ld = NULL;
+ int ldapVersion = LDAP_VERSION3;
+ int derefOption = LDAP_DEREF_ALWAYS;
+ int zero = 0;
+ int timeoutMilliSec = timeoutSec * 1000;
+ uint16_t port = USE_DEFAULT_PORT;
+ char *s;
+ char errstr[MAXERROR];
+
+ ns_ldap_return_code ret_code = NS_LDAP_SUCCESS;
*errorp = NULL;
*ldp = NULL;
- switch (auth->auth.type) {
- case NS_LDAP_AUTH_NONE:
- case NS_LDAP_AUTH_SIMPLE:
- case NS_LDAP_AUTH_SASL:
- bindType = auth->auth.type;
- break;
- case NS_LDAP_AUTH_TLS:
- useSSL = 1;
- switch (auth->auth.tlstype) {
- case NS_LDAP_TLS_NONE:
- bindType = NS_LDAP_AUTH_NONE;
- break;
- case NS_LDAP_TLS_SIMPLE:
- bindType = NS_LDAP_AUTH_SIMPLE;
- break;
- case NS_LDAP_TLS_SASL:
- bindType = NS_LDAP_AUTH_SASL;
- break;
- default:
- (void) sprintf(errstr,
- gettext("openConnection: unsupported "
- "TLS authentication method "
- "(%d)"), auth->auth.tlstype);
- MKERROR(LOG_WARNING, *errorp,
- LDAP_AUTH_METHOD_NOT_SUPPORTED,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
- break;
- default:
- (void) sprintf(errstr,
- gettext("openConnection: unsupported "
- "authentication method (%d)"), auth->auth.type);
- MKERROR(LOG_WARNING, *errorp,
- LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
- NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- if (useSSL) {
- const char *hostcertpath;
- char *alloc_hcp = NULL;
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: +++TLS transport\n",
- thr_self());
-#endif /* DEBUG */
-
- if (prldap_set_session_option(NULL, NULL,
- PRLDAP_OPT_IO_MAX_TIMEOUT,
- timeoutMilliSec) != LDAP_SUCCESS) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: failed to initialize "
- "TLS security"));
- MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- hostcertpath = auth->hostcertpath;
- if (hostcertpath == NULL) {
- alloc_hcp = __s_get_hostcertpath();
- hostcertpath = alloc_hcp;
- }
-
- if (hostcertpath == NULL)
- return (NS_LDAP_MEMORY);
-
- if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
- if (alloc_hcp)
- free(alloc_hcp);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: failed to initialize "
- "TLS security (%s)"),
- ldapssl_err2string(rc));
- MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
- if (alloc_hcp)
- free(alloc_hcp);
-
- /* determine if the host name contains a port number */
- s = strchr(serverAddr, ']'); /* skip over ipv6 addr */
- if (s == NULL)
- s = serverAddr;
- s = strchr(s, ':');
- if (s != NULL) {
- /*
- * If we do get a port number, we will try stripping
- * it. At present, referrals will always have a
- * port number.
- */
- sslServerAddr = strdup(serverAddr);
- if (sslServerAddr == NULL)
- return (NS_LDAP_MEMORY);
- s1 = strrchr(sslServerAddr, ':');
- if (s1 != NULL)
- *s1 = '\0';
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: cannot use tls with %s. "
- "Trying %s"),
- serverAddr, sslServerAddr);
+ /* determine if the host name contains a port number */
+ s = strchr(serverAddr, ']'); /* skip over ipv6 addr */
+ s = strchr(s != NULL ? s : serverAddr, ':');
+ if (s != NULL) {
+ if (sscanf(s + 1, "%hu", &port) != 1) {
+ (void) snprintf(errstr,
+ sizeof (errstr),
+ gettext("openConnection: cannot "
+ "convert %s into a valid "
+ "port number for the "
+ "%s server. A default value "
+ "will be used."),
+ s,
+ serverAddr);
syslog(LOG_ERR, "libsldap: %s", errstr);
- } else
- sslServerAddr = (char *)serverAddr;
-
- ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
+ } else {
+ *s = '\0';
+ }
+ }
- if (sslServerAddr != serverAddr)
- free(sslServerAddr);
+ ret_code = createSession(auth,
+ serverAddr,
+ port,
+ timeoutMilliSec,
+ &ld,
+ errorp);
+ if (s != NULL) {
+ *s = ':';
+ }
+ if (ret_code != NS_LDAP_SUCCESS) {
+ return (ret_code);
+ }
- if (ld == NULL ||
- ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: failed to connect "
- "using TLS (%s)"), strerror(errno));
- MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
- } else {
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
- thr_self());
-#endif /* DEBUG */
- port = LDAP_PORT;
- if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
- (end = strchr(serverAddr, ':')) != NULL) {
- /*
- * The IP is converted to hostname so it's a
- * hostname:port up to this point.
- *
- * libldap passes hostname:port to the sasl layer.
- * The ldap service principal is constructed as
- * ldap/hostname:port@REALM. Kerberos authentication
- * will fail. So it needs to be parsed to construct
- * a valid principal ldap/hostname@REALM.
- *
- * For useSSL case above, it already parses port so
- * no need to parse serverAddr
- */
- *end = '\0';
- port = atoi(end + 1);
- }
+ /* check to see if the underlying libsldap supports MT connection */
+ if (conn_user != NULL) {
+ int rc;
- /* Warning message IF cannot connect to host(s) */
- if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
- char *p = strerror(errno);
- MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
- strdup(p), NULL);
- if (end)
- *end = ':';
- return (NS_LDAP_INTERNAL);
- } else {
- if (end)
- *end = ':';
- /* check and avoid gethostname recursion */
- if (ldap_in_hosts_switch() > 0 &&
- ! __s_api_isipv4((char *)serverAddr) &&
- ! __s_api_isipv6((char *)serverAddr)) {
- /* host: ldap - found, attempt to recover */
- if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
- "ldap") != 0) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "unrecoverable gethostname "
- "recursion detected "
- "in /etc/nsswitch.conf"));
- MKERROR(LOG_WARNING, *errorp,
- LDAP_CONNECT_ERROR,
- strdup(errstr), NULL);
- (void) ldap_unbind(ld);
- return (NS_LDAP_INTERNAL);
- }
- }
+ rc = __s_api_check_libldap_MT_conn_support(conn_user, ld,
+ errorp);
+ if (rc != NS_LDAP_SUCCESS) {
+ (void) ldap_unbind(ld);
+ return (rc);
}
}
- ns_setup_mt_conn_and_tsd(ld);
(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
/*
@@ -2292,277 +1375,19 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
/* retry if LDAP I/O was interrupted */
(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
- switch (bindType) {
- case NS_LDAP_AUTH_NONE:
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
- thr_self());
-#endif /* DEBUG */
- break;
- case NS_LDAP_AUTH_SIMPLE:
- binddn = auth->cred.unix_cred.userID;
- passwd = auth->cred.unix_cred.passwd;
- if (passwd == NULL || *passwd == '\0' ||
- binddn == NULL || *binddn == '\0') {
- (void) sprintf(errstr, gettext("openConnection: "
- "missing credentials for Simple bind"));
- MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
- strdup(errstr), NULL);
- (void) ldap_unbind(ld);
- return (NS_LDAP_INTERNAL);
- }
-
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: +++Simple bind\n",
- thr_self());
-#endif /* DEBUG */
- msgId = ldap_simple_bind(ld, binddn, passwd);
+ ret_code = performBind(auth,
+ ld,
+ timeoutSec,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
- if (msgId == -1) {
- (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
- (void *)&errnum);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: simple bind failed "
- "- %s"), ldap_err2string(errnum));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
- NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- tv.tv_sec = timeoutSec;
- tv.tv_usec = 0;
- rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
-
- if ((rc == -1) || (rc == 0)) {
- (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
- (void *)&errnum);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: simple bind failed "
- "- %s"), ldap_err2string(errnum));
- (void) ldap_msgfree(resultMsg);
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
- NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- /*
- * get ldaprc, controls, and error msg
- */
- rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
- &errmsg, NULL, &controls, 1);
-
- if (rc != LDAP_SUCCESS) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: simple bind failed "
- "- unable to parse result"));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- /* process the password management info, if any */
- pwd_rc = process_pwd_mgmt("simple",
- errnum, controls, errmsg,
- errorp,
- fail_if_new_pwd_reqd,
- passwd_mgmt);
-
- if (pwd_rc == NS_LDAP_INTERNAL) {
- (void) ldap_unbind(ld);
- return (pwd_rc);
- }
-
- if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
- *ldp = ld;
- return (pwd_rc);
- }
-
- break;
- case NS_LDAP_AUTH_SASL:
- if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
- auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
- (void) sprintf(errstr,
- gettext("openConnection: SASL options are "
- "not supported (%d) for non-GSSAPI sasl bind"),
- auth->auth.saslopt);
- MKERROR(LOG_WARNING, *errorp,
- LDAP_AUTH_METHOD_NOT_SUPPORTED,
- strdup(errstr), NULL);
- (void) ldap_unbind(ld);
- return (NS_LDAP_INTERNAL);
- }
- if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
- binddn = auth->cred.unix_cred.userID;
- passwd = auth->cred.unix_cred.passwd;
- if (passwd == NULL || *passwd == '\0' ||
- binddn == NULL || *binddn == '\0') {
- (void) sprintf(errstr,
- gettext("openConnection: missing "
- "credentials for SASL bind"));
- MKERROR(LOG_WARNING, *errorp,
- LDAP_INVALID_CREDENTIALS,
- strdup(errstr), NULL);
- (void) ldap_unbind(ld);
- return (NS_LDAP_INTERNAL);
- }
- cred.bv_val = passwd;
- cred.bv_len = strlen(passwd);
- }
-
- switch (auth->auth.saslmech) {
- case NS_LDAP_SASL_CRAM_MD5:
- /*
- * NOTE: if iDS changes to support cram_md5,
- * please add password management code here.
- * Since ldap_sasl_cram_md5_bind_s does not
- * return anything that could be used to
- * extract the ldap rc/errmsg/control to
- * determine if bind failed due to password
- * policy, a new cram_md5_bind API will need
- * to be introduced. See
- * ldap_x_sasl_digest_md5_bind() and case
- * NS_LDAP_SASL_DIGEST_MD5 below for details.
- */
- if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
- &cred, NULL, NULL)) != LDAP_SUCCESS) {
- (void) ldap_get_option(ld,
- LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "sasl/CRAM-MD5 bind failed - %s"),
- ldap_err2string(errnum));
- MKERROR(LOG_WARNING, *errorp, errnum,
- strdup(errstr), NULL);
- (void) ldap_unbind(ld);
- return (NS_LDAP_INTERNAL);
- }
- break;
- case NS_LDAP_SASL_DIGEST_MD5:
- digest_md5_name = malloc(strlen(binddn) + 5);
- /* 5 = strlen("dn: ") + 1 */
- if (digest_md5_name == NULL) {
- (void) ldap_unbind(ld);
- return (NS_LDAP_MEMORY);
- }
- (void) strcpy(digest_md5_name, "dn: ");
- (void) strcat(digest_md5_name, binddn);
-
- tv.tv_sec = timeoutSec;
- tv.tv_usec = 0;
- rc = ldap_x_sasl_digest_md5_bind(ld,
- digest_md5_name, &cred, NULL, NULL,
- &tv, &resultMsg);
-
- if (resultMsg == NULL) {
- free(digest_md5_name);
- (void) ldap_get_option(ld,
- LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "DIGEST-MD5 bind failed - %s"),
- ldap_err2string(errnum));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, errnum,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- /*
- * get ldaprc, controls, and error msg
- */
- rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
- &errmsg, NULL, &controls, 1);
-
- if (rc != LDAP_SUCCESS) {
- free(digest_md5_name);
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "DIGEST-MD5 bind failed "
- "- unable to parse result"));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- /* process the password management info, if any */
- pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
- errnum, controls, errmsg,
- errorp,
- fail_if_new_pwd_reqd,
- passwd_mgmt);
-
- if (pwd_rc == NS_LDAP_INTERNAL) {
- free(digest_md5_name);
- (void) ldap_unbind(ld);
- return (pwd_rc);
- }
-
- if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
- *ldp = ld;
- return (pwd_rc);
- }
-
- free(digest_md5_name);
- break;
- case NS_LDAP_SASL_GSSAPI:
- if (sasl_gssapi_inited == 0) {
- rc = __s_api_sasl_gssapi_init();
- if (rc != NS_LDAP_SUCCESS) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "GSSAPI initialization "
- "failed"));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, rc,
- strdup(errstr), NULL);
- return (rc);
- }
- }
- (void) memset(&sasl_param, 0,
- sizeof (ns_sasl_cb_param_t));
- sasl_param.authid = NULL;
- sasl_param.authzid = "";
- (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
- (void *)&min_ssf);
- (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
- (void *)&max_ssf);
-
- rc = ldap_sasl_interactive_bind_s(
- ld, NULL, "GSSAPI",
- NULL, NULL, LDAP_SASL_INTERACTIVE,
- __s_api_sasl_bind_callback,
- &sasl_param);
-
- if (rc != LDAP_SUCCESS) {
- (void) snprintf(errstr, sizeof (errstr),
- gettext("openConnection: "
- "GSSAPI bind failed "
- "- %d %s"), rc, ldap_err2string(rc));
- (void) ldap_unbind(ld);
- MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
- strdup(errstr), NULL);
- return (NS_LDAP_INTERNAL);
- }
-
- break;
- default:
- (void) ldap_unbind(ld);
- (void) sprintf(errstr,
- gettext("openConnection: unsupported SASL "
- "mechanism (%d)"), auth->auth.saslmech);
- MKERROR(LOG_WARNING, *errorp,
- LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
- NULL);
- return (NS_LDAP_INTERNAL);
- }
+ if (ret_code == NS_LDAP_SUCCESS ||
+ ret_code == NS_LDAP_SUCCESS_WITH_INFO) {
+ *ldp = ld;
}
- *ldp = ld;
- return (NS_LDAP_SUCCESS);
+ return (ret_code);
}
/*
@@ -2742,53 +1567,12 @@ __s_api_getDefaultAuth(
}
/*
- * FUNCTION: __s_api_getConnection
- *
- * Bind to the specified server or one from the server
- * list and return the pointer.
- *
- * This function can rebind or not (NS_LDAP_HARD), it can require a
- * credential or bind anonymously
- *
- * This function follows the DUA configuration schema algorithm
- *
- * RETURN VALUES:
- *
- * NS_LDAP_SUCCESS A connection was made successfully.
- * NS_LDAP_SUCCESS_WITH_INFO
- * A connection was made successfully, but with
- * password management info in *errorp
- * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
- * NS_LDAP_CONFIG If there are any config errors.
- * NS_LDAP_MEMORY Memory errors.
- * NS_LDAP_INTERNAL If there was a ldap error.
- *
- * INPUT:
- *
- * server Bind to this LDAP server only
- * flags If NS_LDAP_HARD is set function will not return until it has
- * a connection unless there is a authentication problem.
- * If NS_LDAP_NEW_CONN is set the function must force a new
- * connection to be created
- * If NS_LDAP_KEEP_CONN is set the connection is to be kept open
- * auth Credentials for bind. This could be NULL in which case
- * a default cred built from the config module is used.
- * sessionId cookie that points to a previous session
- * fail_if_new_pwd_reqd
- * a flag indicating this function should fail if the passwd
- * in auth needs to change immediately
- * nopasswd_acct_mgmt
- * a flag indicating that makeConnection should check before
- * binding if server supports LDAP V3 password less
- * account management
- *
- * OUTPUT:
+ * FUNCTION: getConnection
*
- * session pointer to a session with connection information
- * errorp Set if there are any INTERNAL, or CONFIG error.
+ * internal version of __s_api_getConnection()
*/
-int
-__s_api_getConnection(
+static int
+getConnection(
const char *server,
const int flags,
const ns_cred_t *cred, /* credentials for bind */
@@ -2796,7 +1580,8 @@ __s_api_getConnection(
Connection **session,
ns_ldap_error_t **errorp,
int fail_if_new_pwd_reqd,
- int nopasswd_acct_mgmt)
+ int nopasswd_acct_mgmt,
+ ns_conn_user_t *conn_user)
{
char errmsg[MAXERROR];
ns_auth_t **aMethod = NULL;
@@ -2818,14 +1603,12 @@ __s_api_getConnection(
}
*session = NULL;
- /* if we already have a session id try to reuse connection */
- if (*sessionId > 0) {
- rc = findConnectionById(flags, cred, *sessionId, &con);
- if (rc == *sessionId && con) {
- *session = con;
- return (NS_LDAP_SUCCESS);
- }
- *sessionId = 0;
+ /* reuse MT connection if needed and if available */
+ if (conn_user != NULL) {
+ rc = __s_api_conn_mt_get(server, flags, cred, session, errorp,
+ conn_user);
+ if (rc != NS_LDAP_NOTFOUND)
+ return (rc);
}
/* get profile version number */
@@ -2906,7 +1689,7 @@ __s_api_getConnection(
rc = makeConnection(&con, server, cred,
sessionId, timeoutSec, errorp,
fail_if_new_pwd_reqd,
- nopasswd_acct_mgmt, flags, &badSrvrs);
+ nopasswd_acct_mgmt, flags, &badSrvrs, conn_user);
/* not using bad server if credentials were supplied */
if (badSrvrs && *badSrvrs) {
__s_api_free2dArray(badSrvrs);
@@ -2938,7 +1721,7 @@ __s_api_getConnection(
sessionId, timeoutSec, errorp,
fail_if_new_pwd_reqd,
nopasswd_acct_mgmt, flags,
- &badSrvrs);
+ &badSrvrs, conn_user);
if (rc == NS_LDAP_SUCCESS ||
rc ==
NS_LDAP_SUCCESS_WITH_INFO) {
@@ -2985,7 +1768,7 @@ __s_api_getConnection(
sessionId, timeoutSec, errorp,
fail_if_new_pwd_reqd,
nopasswd_acct_mgmt, flags,
- &badSrvrs);
+ &badSrvrs, conn_user);
(void) __ns_ldap_freeCred(&authp);
if (rc == NS_LDAP_SUCCESS ||
rc ==
@@ -3006,22 +1789,6 @@ __s_api_getConnection(
}
done:
- /*
- * If unable to get a connection, and this is
- * the thread opening the shared connection,
- * unlock the session mutex and let other
- * threads try to get their own connection.
- */
- if (wait4session != 0 && sessionTid == thr_self()) {
- wait4session = 0;
- sessionTid = 0;
-#ifdef DEBUG
- (void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
- "unlocking sessionLock \n", thr_self());
- fflush(stderr);
-#endif /* DEBUG */
- (void) mutex_unlock(&sessionLock);
- }
if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
/*
* self_gssapi_only is true but no self/sasl/gssapi is
@@ -3047,13 +1814,85 @@ done:
return (rc);
}
-#pragma fini(_free_sessionPool)
-static void
-_free_sessionPool()
+/*
+ * FUNCTION: __s_api_getConnection
+ *
+ * Bind to the specified server or one from the server
+ * list and return the pointer.
+ *
+ * This function can rebind or not (NS_LDAP_HARD), it can require a
+ * credential or bind anonymously
+ *
+ * This function follows the DUA configuration schema algorithm
+ *
+ * RETURN VALUES:
+ *
+ * NS_LDAP_SUCCESS A connection was made successfully.
+ * NS_LDAP_SUCCESS_WITH_INFO
+ * A connection was made successfully, but with
+ * password management info in *errorp
+ * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
+ * NS_LDAP_CONFIG If there are any config errors.
+ * NS_LDAP_MEMORY Memory errors.
+ * NS_LDAP_INTERNAL If there was a ldap error.
+ *
+ * INPUT:
+ *
+ * server Bind to this LDAP server only
+ * flags If NS_LDAP_HARD is set function will not return until it has
+ * a connection unless there is a authentication problem.
+ * If NS_LDAP_NEW_CONN is set the function must force a new
+ * connection to be created
+ * If NS_LDAP_KEEP_CONN is set the connection is to be kept open
+ * auth Credentials for bind. This could be NULL in which case
+ * a default cred built from the config module is used.
+ * sessionId cookie that points to a previous session
+ * fail_if_new_pwd_reqd
+ * a flag indicating this function should fail if the passwd
+ * in auth needs to change immediately
+ * nopasswd_acct_mgmt
+ * a flag indicating that makeConnection should check before
+ * binding if server supports LDAP V3 password less
+ * account management
+ *
+ * OUTPUT:
+ *
+ * session pointer to a session with connection information
+ * errorp Set if there are any INTERNAL, or CONFIG error.
+ */
+int
+__s_api_getConnection(
+ const char *server,
+ const int flags,
+ const ns_cred_t *cred, /* credentials for bind */
+ ConnectionID *sessionId,
+ Connection **session,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd,
+ int nopasswd_acct_mgmt,
+ ns_conn_user_t *conn_user)
+{
+ int rc;
+
+ rc = getConnection(server, flags, cred, sessionId, session,
+ errorp, fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
+ conn_user);
+
+ if (rc != NS_LDAP_SUCCESS && rc != NS_LDAP_SUCCESS_WITH_INFO) {
+ if (conn_user != NULL && conn_user->conn_mt != NULL)
+ __s_api_conn_mt_remove(conn_user, rc, errorp);
+ }
+
+ return (rc);
+}
+
+void
+__s_api_free_sessionPool()
{
int id;
- (void) rw_wrlock(&sessionPoolLock);
+ (void) mutex_lock(&sessionPoolLock);
+
if (sessionPool != NULL) {
for (id = 0; id < sessionPoolSize; id++)
_DropConnection(id + CONID_OFFSET, 0, 1);
@@ -3061,5 +1900,689 @@ _free_sessionPool()
sessionPool = NULL;
sessionPoolSize = 0;
}
- (void) rw_unlock(&sessionPoolLock);
+ (void) mutex_unlock(&sessionPoolLock);
+}
+
+/*
+ * This function initializes a TLS LDAP session. On success LDAP* is returned
+ * (pointed by *ldp). Otherwise, the function returns an NS error code and
+ * provide an additional info pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+createTLSSession(const ns_cred_t *auth, const char *serverAddr,
+ uint16_t port, int timeoutMilliSec,
+ LDAP **ldp, ns_ldap_error_t **errorp)
+{
+ const char *hostcertpath;
+ char *alloc_hcp = NULL, errstr[MAXERROR];
+ int ldap_rc;
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "tid= %d: +++TLS transport\n",
+ thr_self());
+#endif /* DEBUG */
+
+ if (prldap_set_session_option(NULL, NULL,
+ PRLDAP_OPT_IO_MAX_TIMEOUT,
+ timeoutMilliSec) != LDAP_SUCCESS) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("createTLSSession: failed to initialize "
+ "TLS security"));
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ hostcertpath = auth->hostcertpath;
+ if (hostcertpath == NULL) {
+ alloc_hcp = __s_get_hostcertpath();
+ hostcertpath = alloc_hcp;
+ }
+
+ if (hostcertpath == NULL)
+ return (NS_LDAP_MEMORY);
+
+ if ((ldap_rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
+ if (alloc_hcp != NULL) {
+ free(alloc_hcp);
+ }
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("createTLSSession: failed to initialize "
+ "TLS security (%s)"),
+ ldapssl_err2string(ldap_rc));
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+ if (alloc_hcp)
+ free(alloc_hcp);
+
+ *ldp = ldapssl_init(serverAddr, port, 1);
+
+ if (*ldp == NULL ||
+ ldapssl_install_gethostbyaddr(*ldp, "ldap") != 0) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("createTLSSession: failed to connect "
+ "using TLS (%s)"), strerror(errno));
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * Convert (resolve) hostname to IP address.
+ *
+ * INPUT:
+ *
+ * server - \[IPv6_address\][:port]
+ * - IPv4_address[:port]
+ * - hostname[:port]
+ *
+ * newaddr - Buffer to which this function writes resulting address,
+ * including the port number, if specified in server argument.
+ *
+ * newaddr_size - Size of the newaddr buffer.
+ *
+ * errstr - Buffer to which error string is written if error occurs.
+ *
+ * errstr_size - Size of the errstr buffer.
+ *
+ * OUTPUT:
+ *
+ * Returns 1 for success, 0 in case of error.
+ *
+ * newaddr - See above (INPUT section).
+ *
+ * errstr - See above (INPUT section).
+ */
+static int
+cvt_hostname2ip(char *server, char *newaddr, int newaddr_size,
+ char *errstr, int errstr_size)
+{
+ char *s;
+ unsigned short port = 0;
+ int err;
+ char buffer[NSS_BUFLEN_HOSTS];
+ struct hostent result;
+
+ /* Determine if the host name contains a port number. */
+
+ /* Skip over IPv6 address. */
+ s = strchr(server, ']');
+ s = strchr(s != NULL ? s : server, ':');
+ if (s != NULL) {
+ if (sscanf(s + 1, "%hu", &port) != 1) {
+ /* Address misformatted. No port number after : */
+ (void) snprintf(errstr, errstr_size, "%s",
+ gettext("Invalid host:port format"));
+ return (0);
+ } else
+ /* Cut off the :<port> part. */
+ *s = '\0';
+ }
+
+ buffer[0] = '\0';
+ /*
+ * Resolve hostname and fill in hostent structure.
+ */
+ if (!__s_api_hostname2ip(server, &result, buffer, NSS_BUFLEN_HOSTS,
+ &err)) {
+ /*
+ * The only possible error here could be TRY_AGAIN if buffer was
+ * not big enough. NSS_BUFLEN_HOSTS should have been enough
+ * though.
+ */
+ (void) snprintf(errstr, errstr_size, "%s",
+ gettext("Unable to resolve address."));
+ return (0);
+ }
+
+
+ buffer[0] = '\0';
+ /*
+ * Convert the address to string.
+ */
+ if (!inet_ntop(result.h_addrtype, result.h_addr_list[0], buffer,
+ NSS_BUFLEN_HOSTS)) {
+ /* There's not much we can do. */
+ (void) snprintf(errstr, errstr_size, "%s",
+ gettext("Unable to convert address to string."));
+ return (0);
+ }
+
+ /* Put together the address and the port */
+ if (port > 0) {
+ switch (result.h_addrtype) {
+ case AF_INET6:
+ (void) snprintf(newaddr,
+ /* [IP]:<port>\0 */
+ 1 + strlen(buffer) + 1 + 1 + 5 + 1,
+ "[%s]:%hu",
+ buffer,
+ port);
+ break;
+ /* AF_INET */
+ default :
+ (void) snprintf(newaddr,
+ /* IP:<port>\0 */
+ strlen(buffer) + 1 + 5 + 1,
+ "%s:%hu",
+ buffer,
+ port);
+ break;
+ }
+ } else {
+ (void) strncpy(newaddr, buffer, newaddr_size);
+ }
+
+ return (1);
+}
+
+
+/*
+ * This finction initializes a none-TLS LDAP session. On success LDAP*
+ * is returned (pointed by *ldp). Otherwise, the function returns
+ * an NS error code and provides an additional info pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+createNonTLSSession(const char *serverAddr,
+ uint16_t port, int gssapi,
+ LDAP **ldp, ns_ldap_error_t **errorp)
+{
+ char errstr[MAXERROR];
+ char *addr;
+ int is_ip = 0;
+ /* [INET6_ADDRSTRLEN]:<port>\0 */
+ char svraddr[1+INET6_ADDRSTRLEN+1+1+5+1];
+#ifdef DEBUG
+ (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
+ thr_self());
+#endif /* DEBUG */
+
+ if (gssapi == 0) {
+ is_ip = (__s_api_isipv4((char *)serverAddr) ||
+ __s_api_isipv6((char *)serverAddr));
+ }
+
+ /*
+ * Let's try to resolve IP address of server.
+ */
+ if (is_ip == 0 && !gssapi && (ldap_in_nss_switch((char *)"hosts") > 0 ||
+ ldap_in_nss_switch((char *)"ipnodes") > 0)) {
+ addr = strdup(serverAddr);
+ if (addr == NULL)
+ return (NS_LDAP_MEMORY);
+ svraddr[0] = '\0';
+ if (cvt_hostname2ip(addr, svraddr, sizeof (svraddr),
+ errstr, MAXERROR) == 1) {
+ serverAddr = svraddr;
+ free(addr);
+ } else {
+ free(addr);
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+
+ /* Warning message IF cannot connect to host(s) */
+ if ((*ldp = ldap_init((char *)serverAddr, port)) == NULL) {
+ char *p = strerror(errno);
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(p), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This finction initializes an LDAP session.
+ *
+ * INPUT:
+ * auth - a structure specified an authenticastion method and credentials,
+ * serverAddr - the address of a server to which a connection
+ * will be established,
+ * port - a port being listened by the server,
+ * timeoutMilliSec - a timeout in milliseconds for the Bind operation.
+ *
+ * OUTPUT:
+ * ldp - a pointer to an LDAP structure which will be used
+ * for all the subsequent operations against the server.
+ * If an error accures, the function returns an NS error code
+ * and provides an additional info pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+createSession(const ns_cred_t *auth, const char *serverAddr,
+ uint16_t port, int timeoutMilliSec,
+ LDAP **ldp, ns_ldap_error_t **errorp)
+{
+ int useSSL = 0, gssapi = 0;
+ char errstr[MAXERROR];
+
+ switch (auth->auth.type) {
+ case NS_LDAP_AUTH_NONE:
+ case NS_LDAP_AUTH_SIMPLE:
+ case NS_LDAP_AUTH_SASL:
+ break;
+ case NS_LDAP_AUTH_TLS:
+ useSSL = 1;
+ break;
+ default:
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported "
+ "authentication method (%d)"), auth->auth.type);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ if (port == USE_DEFAULT_PORT) {
+ port = useSSL ? LDAPS_PORT : LDAP_PORT;
+ }
+
+ if (auth->auth.type == NS_LDAP_AUTH_SASL &&
+ auth->auth.saslmech == NS_LDAP_SASL_GSSAPI)
+ gssapi = 1;
+
+ if (useSSL)
+ return (createTLSSession(auth, serverAddr, port,
+ timeoutMilliSec, ldp, errorp));
+ else
+ return (createNonTLSSession(serverAddr, port, gssapi,
+ ldp, errorp));
+}
+
+/*
+ * This finction performs a non-SASL bind operation. If an error accures,
+ * the function returns an NS error code and provides an additional info
+ * pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+doSimpleBind(const ns_cred_t *auth,
+ LDAP *ld,
+ int timeoutSec,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd,
+ int passwd_mgmt)
+{
+ char *binddn, *passwd, errstr[MAXERROR], *errmsg;
+ int msgId, errnum = 0, ldap_rc;
+ ns_ldap_return_code ret_code;
+ LDAPMessage *resultMsg = NULL;
+ LDAPControl **controls;
+ struct timeval tv;
+
+ binddn = auth->cred.unix_cred.userID;
+ passwd = auth->cred.unix_cred.passwd;
+ if (passwd == NULL || *passwd == '\0' ||
+ binddn == NULL || *binddn == '\0') {
+ (void) sprintf(errstr, gettext("openConnection: "
+ "missing credentials for Simple bind"));
+ MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
+ strdup(errstr), NS_LDAP_MEMORY);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "tid= %d: +++Simple bind\n",
+ thr_self());
+#endif /* DEBUG */
+ msgId = ldap_simple_bind(ld, binddn, passwd);
+
+ if (msgId == -1) {
+ (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
+ (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- %s"), ldap_err2string(errnum));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ ldap_rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
+
+ if ((ldap_rc == -1) || (ldap_rc == 0)) {
+ (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
+ (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- %s"), ldap_err2string(errnum));
+ (void) ldap_msgfree(resultMsg);
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /*
+ * get ldaprc, controls, and error msg
+ */
+ ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
+ &errmsg, NULL, &controls, 1);
+
+ if (ldap_rc != LDAP_SUCCESS) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- unable to parse result"));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /* process the password management info, if any */
+ ret_code = process_pwd_mgmt("simple",
+ errnum, controls, errmsg,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
+
+ if (ret_code == NS_LDAP_INTERNAL) {
+ (void) ldap_unbind(ld);
+ }
+
+ return (ret_code);
+}
+
+/*
+ * This finction performs a SASL bind operation. If an error accures,
+ * the function returns an NS error code and provides an additional info
+ * pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+doSASLBind(const ns_cred_t *auth,
+ LDAP *ld,
+ int timeoutSec,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd,
+ int passwd_mgmt)
+{
+ char *binddn, *passwd, *digest_md5_name,
+ errstr[MAXERROR], *errmsg;
+ struct berval cred;
+ int ldap_rc, errnum = 0;
+ ns_ldap_return_code ret_code;
+ struct timeval tv;
+ LDAPMessage *resultMsg;
+ LDAPControl **controls;
+ int min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
+ ns_sasl_cb_param_t sasl_param;
+
+ if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
+ auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
+ (void) sprintf(errstr,
+ gettext("openConnection: SASL options are "
+ "not supported (%d) for non-GSSAPI sasl bind"),
+ auth->auth.saslopt);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ strdup(errstr), NS_LDAP_MEMORY);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
+ binddn = auth->cred.unix_cred.userID;
+ passwd = auth->cred.unix_cred.passwd;
+ if (passwd == NULL || *passwd == '\0' ||
+ binddn == NULL || *binddn == '\0') {
+ (void) sprintf(errstr,
+ gettext("openConnection: missing credentials "
+ "for SASL bind"));
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_INVALID_CREDENTIALS,
+ strdup(errstr), NS_LDAP_MEMORY);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ cred.bv_val = passwd;
+ cred.bv_len = strlen(passwd);
+ }
+
+ ret_code = NS_LDAP_SUCCESS;
+
+ switch (auth->auth.saslmech) {
+ case NS_LDAP_SASL_CRAM_MD5:
+ /*
+ * NOTE: if iDS changes to support cram_md5,
+ * please add password management code here.
+ * Since ldap_sasl_cram_md5_bind_s does not
+ * return anything that could be used to
+ * extract the ldap rc/errmsg/control to
+ * determine if bind failed due to password
+ * policy, a new cram_md5_bind API will need
+ * to be introduced. See
+ * ldap_x_sasl_digest_md5_bind() and case
+ * NS_LDAP_SASL_DIGEST_MD5 below for details.
+ */
+ if ((ldap_rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
+ &cred, NULL, NULL)) != LDAP_SUCCESS) {
+ (void) ldap_get_option(ld,
+ LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "sasl/CRAM-MD5 bind failed - %s"),
+ ldap_err2string(errnum));
+ MKERROR(LOG_WARNING, *errorp, errnum,
+ strdup(errstr), NS_LDAP_MEMORY);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ break;
+ case NS_LDAP_SASL_DIGEST_MD5:
+ digest_md5_name = malloc(strlen(binddn) + 5);
+ /* 5 = strlen("dn: ") + 1 */
+ if (digest_md5_name == NULL) {
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_MEMORY);
+ }
+ (void) strcpy(digest_md5_name, "dn: ");
+ (void) strcat(digest_md5_name, binddn);
+
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ ldap_rc = ldap_x_sasl_digest_md5_bind(ld,
+ digest_md5_name, &cred, NULL, NULL,
+ &tv, &resultMsg);
+
+ if (resultMsg == NULL) {
+ free(digest_md5_name);
+ (void) ldap_get_option(ld,
+ LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "DIGEST-MD5 bind failed - %s"),
+ ldap_err2string(errnum));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /*
+ * get ldaprc, controls, and error msg
+ */
+ ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
+ &errmsg, NULL, &controls, 1);
+
+ if (ldap_rc != LDAP_SUCCESS) {
+ free(digest_md5_name);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "DIGEST-MD5 bind failed "
+ "- unable to parse result"));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /* process the password management info, if any */
+ ret_code = process_pwd_mgmt("sasl/DIGEST-MD5",
+ errnum, controls, errmsg,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
+
+ if (ret_code == NS_LDAP_INTERNAL) {
+ (void) ldap_unbind(ld);
+ }
+
+ free(digest_md5_name);
+ break;
+ case NS_LDAP_SASL_GSSAPI:
+ if (sasl_gssapi_inited == 0) {
+ ret_code = __s_api_sasl_gssapi_init();
+ if (ret_code != NS_LDAP_SUCCESS) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "GSSAPI initialization "
+ "failed"));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, ret_code,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (ret_code);
+ }
+ }
+ (void) memset(&sasl_param, 0,
+ sizeof (ns_sasl_cb_param_t));
+ sasl_param.authid = NULL;
+ sasl_param.authzid = "";
+ (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
+ (void *)&min_ssf);
+ (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
+ (void *)&max_ssf);
+
+ ldap_rc = ldap_sasl_interactive_bind_s(
+ ld, NULL, "GSSAPI",
+ NULL, NULL, LDAP_SASL_INTERACTIVE,
+ __s_api_sasl_bind_callback,
+ &sasl_param);
+
+ if (ldap_rc != LDAP_SUCCESS) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "GSSAPI bind failed "
+ "- %d %s"),
+ ldap_rc,
+ ldap_err2string(ldap_rc));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ break;
+ default:
+ (void) ldap_unbind(ld);
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported SASL "
+ "mechanism (%d)"), auth->auth.saslmech);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ return (ret_code);
+}
+
+/*
+ * This function performs an LDAP Bind operation proceeding
+ * from a type of the connection specified by auth->auth.type.
+ *
+ * INPUT:
+ * auth - a structure specified an authenticastion method and credentials,
+ * ld - a pointer returned by the createSession() function,
+ * timeoutSec - a timeout in seconds for the Bind operation,
+ * fail_if_new_pwd_reqd - a flag indicating that the call should fail
+ * if a new password is required,
+ * passwd_mgmt - a flag indicating that the server supports
+ * password management.
+ *
+ * OUTPUT:
+ * If an error accures, the function returns an NS error code
+ * and provides an additional info pointed by *errorp.
+ */
+static
+ns_ldap_return_code
+performBind(const ns_cred_t *auth,
+ LDAP *ld,
+ int timeoutSec,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd,
+ int passwd_mgmt)
+{
+ int bindType;
+ char errstr[MAXERROR];
+
+ ns_ldap_return_code (*binder)(const ns_cred_t *auth,
+ LDAP *ld,
+ int timeoutSec,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd,
+ int passwd_mgmt) = NULL;
+
+ if (!ld) {
+ (void) sprintf(errstr,
+ "performBind: LDAP session "
+ "is not initialized.");
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ strdup(errstr), NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ bindType = auth->auth.type == NS_LDAP_AUTH_TLS ?
+ auth->auth.tlstype : auth->auth.type;
+
+ switch (bindType) {
+ case NS_LDAP_AUTH_NONE:
+#ifdef DEBUG
+ (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
+ thr_self());
+#endif /* DEBUG */
+ break;
+ case NS_LDAP_AUTH_SIMPLE:
+ binder = doSimpleBind;
+ break;
+ case NS_LDAP_AUTH_SASL:
+ binder = doSASLBind;
+ break;
+ default:
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported "
+ "authentication method "
+ "(%d)"), bindType);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ strdup(errstr), NS_LDAP_MEMORY);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ if (binder != NULL) {
+ return (*binder)(auth,
+ ld,
+ timeoutSec,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
+ }
+
+ return (NS_LDAP_SUCCESS);
}
diff --git a/usr/src/lib/libsldap/common/ns_connmgmt.c b/usr/src/lib/libsldap/common/ns_connmgmt.c
new file mode 100755
index 0000000000..c4f4f67e8b
--- /dev/null
+++ b/usr/src/lib/libsldap/common/ns_connmgmt.c
@@ -0,0 +1,2631 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <procfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <atomic.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <time.h>
+#include "solaris-int.h"
+#include "ns_connmgmt.h"
+#include "ns_cache_door.h"
+#include "ns_internal.h"
+
+/*
+ * Access (reference, shutdown, or reload) the current connection
+ * management control structure conn_mgmt_t.
+ */
+#define NS_CONN_MGMT_OP_REF 1
+#define NS_CONN_MGMT_OP_SHUTDOWN 2
+#define NS_CONN_MGMT_OP_RELOAD_CONFIG 3
+#define NS_CONN_MGMT_OP_NEW_CONFIG 4
+#define NS_CONN_MGMT_OP_LIB_INIT 5
+
+static ns_conn_mgmt_t *access_conn_mgmt(int);
+static ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
+static int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
+ ns_conn_user_t *);
+static int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
+void shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
+static int conn_signal(ns_conn_mt_t *);
+static int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
+static void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
+static ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
+ ns_conn_mgmt_t *cmg);
+static void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
+static void start_thread();
+
+static ns_conn_mgmt_t *ns_connmgmt = NULL;
+static ns_conn_mgmt_t *ns_connmgmt_parent = NULL;
+static mutex_t ns_connmgmt_lock = DEFAULTMUTEX;
+static boolean_t ns_connmgmt_shutting_down = B_FALSE;
+
+#define NS_CONN_MSG_NO_CONN_MGMT gettext( \
+ "libsldap: unable to allocate the connection management control")
+#define NS_CONN_MSG_NO_MTC_KEY gettext( \
+ "libsldap: unable to allocate the TSD key for per-thread ldap error")
+#define NS_CONN_MSG_NO_CMG_KEY gettext( \
+ "libsldap: unable to allocate the TSD key for connection management")
+#define NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
+#define NS_CONN_MSG_RELOADED gettext( \
+ "libsldap: configuration has been reloaded")
+#define NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
+ "libsldap: library unloaded or configuration has been reloaded")
+#define NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
+ "libsldap: received incorrect data from ldap_cachemgr")
+#define NS_CONN_MSG_MEMORY_ERROR gettext( \
+ "libsldap: unable to allocate memory")
+#define NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
+ "libsldap: unable to start the server monitor thread (%s)")
+#define NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
+ "libsldap: server down reported by ldap_cachemgr")
+
+static int ns_conn_free = 1;
+#define NS_CONN_UNLOCK_AND_FREE(free, cm, cmg) \
+{ \
+ (void) mutex_unlock(&(cm)->lock); \
+ if (free == 1) \
+ cmg = free_conn_mt((cm), 1); \
+ if (cmg != NULL) \
+ (void) mutex_unlock(&(cmg)->lock); \
+}
+
+#define NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
+{ \
+ char *msg = NULL; \
+ (void) mutex_lock(&(cmg)->lock); \
+ if ((cmg)->shutting_down == B_TRUE) \
+ msg = NS_CONN_MSG_SHUTDOWN; \
+ else if ((cmg)->cfg_reloaded == B_TRUE) \
+ msg = NS_CONN_MSG_RELOADED; \
+ if (msg != NULL) { \
+ (*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
+ (void) mutex_unlock(&(cmg)->lock); \
+ return (NS_LDAP_OP_FAILED); \
+ } \
+}
+
+/*
+ * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
+ * and their associated connection management structure among
+ * multiple threads. The pointers to the per-thread ldap error
+ * information and the connection management structure are
+ * saved in ns_mtckey and ns_cmgkey.
+ */
+thread_key_t ns_mtckey = THR_ONCE_KEY;
+thread_key_t ns_cmgkey = THR_ONCE_KEY;
+
+/* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
+struct ldap_error {
+ int le_errno;
+ char *le_matched;
+ char *le_errmsg;
+};
+
+/* NULL struct ldap_error */
+static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
+
+/* destructor: free the ldap error data in the thread specific area */
+static void
+ns_mtckey_cleanup(void *key) {
+ struct ldap_error *le = (struct ldap_error *)key;
+
+ if (le == NULL)
+ return;
+ if (le->le_matched != NULL) {
+ ldap_memfree(le->le_matched);
+ }
+ if (le->le_errmsg != NULL) {
+ ldap_memfree(le->le_errmsg);
+ }
+ free(le);
+}
+
+/* Free/detach the thread specific data structures */
+static void
+conn_tsd_free() {
+ void *tsd = NULL;
+ int rc;
+
+ /* free the per-thread ldap error info */
+ rc = thr_getspecific(ns_mtckey, &tsd);
+ if (rc == 0 && tsd != NULL)
+ ns_mtckey_cleanup(tsd);
+ (void) thr_setspecific(ns_mtckey, NULL);
+
+ /* detach the connection management control */
+ (void) thr_setspecific(ns_cmgkey, NULL);
+}
+
+/* per-thread callback function for allocating a mutex */
+static void *
+ns_mutex_alloc(void)
+{
+ mutex_t *mutexp = NULL;
+
+ if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
+ if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
+ free(mutexp);
+ mutexp = NULL;
+ }
+ }
+ return (mutexp);
+}
+
+/* per-thread callback function for freeing a mutex */
+static void
+ns_mutex_free(void *mutexp)
+{
+ (void) mutex_destroy((mutex_t *)mutexp);
+ free(mutexp);
+}
+
+/*
+ * Function for setting up thread-specific data
+ * where per thread LDAP error and the pointer
+ * to the active connection management control
+ * are stored.
+ */
+static int
+conn_tsd_setup(ns_conn_mgmt_t *cmg)
+{
+ void *tsd;
+ int rc;
+
+ rc = thr_setspecific(ns_cmgkey, cmg);
+ if (rc != 0) /* must be ENOMEM */
+ return (-1);
+
+ /* return success if the ns_mtckey TSD is already set */
+ rc = thr_getspecific(ns_mtckey, &tsd);
+ if (rc == 0 && tsd != NULL)
+ return (0);
+
+ /* allocate and set the ns_mtckey TSD */
+ tsd = (void *) calloc(1, sizeof (struct ldap_error));
+ if (tsd == NULL)
+ return (-1);
+ rc = thr_setspecific(ns_mtckey, tsd);
+ if (rc != 0) { /* must be ENOMEM */
+ free(tsd);
+ return (-1);
+ }
+ return (0);
+}
+
+/* Callback function for setting the per thread LDAP error */
+/*ARGSUSED*/
+static void
+set_ld_error(int err, char *matched, char *errmsg, void *dummy)
+{
+ struct ldap_error *le;
+ int eno;
+
+ if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
+ syslog(LOG_ERR, gettext(
+ "libsldap: set_ld_error: thr_getspecific failed (%s)."),
+ strerror(eno));
+ return;
+ }
+
+ /* play safe, do nothing if TSD pointer is NULL */
+ if (le == NULL) {
+ syslog(LOG_INFO, gettext(
+ "libsldap: set_ld_error: TSD pointer is NULL."));
+ return;
+ }
+
+ le->le_errno = err;
+
+ if (le->le_matched != NULL) {
+ ldap_memfree(le->le_matched);
+ le->le_matched = NULL;
+ }
+ le->le_matched = matched;
+
+ if (le->le_errmsg != NULL) {
+ ldap_memfree(le->le_errmsg);
+ le->le_errmsg = NULL;
+ }
+ le->le_errmsg = errmsg;
+}
+
+/* check and allocate the thread-specific data for using a MT connection */
+static int
+conn_tsd_check(ns_conn_mgmt_t *cmg)
+{
+ if (conn_tsd_setup(cmg) != 0)
+ return (NS_LDAP_MEMORY);
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/* Callback function for getting the per thread LDAP error */
+/*ARGSUSED*/
+static int
+get_ld_error(char **matched, char **errmsg, void *dummy)
+{
+ struct ldap_error *le;
+ int eno;
+
+ if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
+ syslog(LOG_ERR, gettext(
+ "libsldap: get_ld_error: thr_getspecific failed (%s)"),
+ strerror(eno));
+ return (eno);
+ }
+
+ /* play safe, return NULL error data, if TSD pointer is NULL */
+ if (le == NULL)
+ le = &ldap_error_NULL;
+
+ if (matched != NULL) {
+ *matched = le->le_matched;
+ }
+ if (errmsg != NULL) {
+ *errmsg = le->le_errmsg;
+ }
+ return (le->le_errno);
+}
+
+/* Callback function for setting per thread errno */
+static void
+set_errno(int err)
+{
+ errno = err;
+}
+
+/* Callback function for getting per thread errno */
+static int
+get_errno(void)
+{
+ return (errno);
+}
+
+/* set up an ldap session 'ld' for sharing among multiple threads */
+static int
+setup_mt_conn(LDAP *ld)
+{
+
+ struct ldap_thread_fns tfns;
+ struct ldap_extra_thread_fns extrafns;
+ int rc;
+
+ /*
+ * Set the function pointers for dealing with mutexes
+ * and error information
+ */
+ (void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
+ tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
+ tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
+ tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
+ tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
+ 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;
+ tfns.ltf_lderrno_arg = NULL;
+
+ /*
+ * Set up the ld to use those function pointers
+ */
+ rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
+ (void *) &tfns);
+ if (rc < 0) {
+ syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
+ "(LDAP_OPT_THREAD_FN_PTRS)"));
+ return (0);
+ }
+
+ /*
+ * Set the function pointers for working with semaphores
+ */
+ (void) memset(&extrafns, '\0',
+ sizeof (struct ldap_extra_thread_fns));
+ extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
+ extrafns.ltf_mutex_trylock = NULL;
+ extrafns.ltf_sema_alloc = NULL;
+ extrafns.ltf_sema_free = NULL;
+ extrafns.ltf_sema_wait = NULL;
+ extrafns.ltf_sema_post = NULL;
+
+ /* Set up the ld to use those function pointers */
+ rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
+ (void *) &extrafns);
+ if (rc < 0) {
+ syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
+ "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
+ return (0);
+ }
+
+ return (1);
+}
+
+/* set up an MT connection for sharing among multiple threads */
+static int
+setup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
+{
+ thread_t t = thr_self();
+
+ /* set up the per-thread data for using the MT connection */
+ if (conn_tsd_setup(cmg) == -1) {
+ syslog(LOG_WARNING,
+ gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
+ return (-1);
+ }
+
+ if (setup_mt_conn(ld) == 0) {
+ /* multiple threads per connection not supported */
+ syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
+ "threads per connection not supported\n"), t);
+ conn_tsd_free();
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Check name and UID of process, if it is nscd.
+ *
+ * Input:
+ * pid : PID of checked process
+ * check_uid : check if UID == 0
+ * Output:
+ * B_TRUE : nscd detected
+ * B_FALSE : nscd not confirmed
+ */
+static boolean_t
+check_nscd_proc(pid_t pid, boolean_t check_uid)
+{
+ psinfo_t pinfo;
+ char fname[MAXPATHLEN];
+ ssize_t ret;
+ int fd;
+
+ if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
+ if ((fd = open(fname, O_RDONLY)) >= 0) {
+ ret = read(fd, &pinfo, sizeof (psinfo_t));
+ (void) close(fd);
+ if ((ret == sizeof (psinfo_t)) &&
+ (strcmp(pinfo.pr_fname, "nscd") == 0)) {
+ if (check_uid && (pinfo.pr_uid != 0))
+ return (B_FALSE);
+ return (B_TRUE);
+ }
+ }
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Check if this process is peruser nscd.
+ */
+boolean_t
+__s_api_peruser_proc(void)
+{
+ pid_t my_ppid;
+ static mutex_t nscdLock = DEFAULTMUTEX;
+ static pid_t checkedPpid = (pid_t)-1;
+ static boolean_t isPeruserNscd = B_FALSE;
+
+ my_ppid = getppid();
+
+ /*
+ * Already checked before for this process? If yes, return cached
+ * response.
+ */
+ if (my_ppid == checkedPpid) {
+ return (isPeruserNscd);
+ }
+
+ (void) mutex_lock(&nscdLock);
+
+ /* Check once more incase another thread has just complete this. */
+ if (my_ppid == checkedPpid) {
+ (void) mutex_unlock(&nscdLock);
+ return (isPeruserNscd);
+ }
+
+ /* Reinitialize to be sure there is no residue after fork. */
+ isPeruserNscd = B_FALSE;
+
+ /* Am I the nscd process? */
+ if (check_nscd_proc(getpid(), B_FALSE)) {
+ /* Is my parent the nscd process with UID == 0. */
+ isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
+ }
+
+ /* Remeber for whom isPeruserNscd is. */
+ checkedPpid = my_ppid;
+
+ (void) mutex_unlock(&nscdLock);
+ return (isPeruserNscd);
+}
+
+/*
+ * Check if this process is main nscd.
+ */
+boolean_t
+__s_api_nscd_proc(void)
+{
+ pid_t my_pid;
+ static mutex_t nscdLock = DEFAULTMUTEX;
+ static pid_t checkedPid = (pid_t)-1;
+ static boolean_t isMainNscd = B_FALSE;
+
+ /*
+ * Don't bother checking if this process isn't root, this cannot
+ * be main nscd.
+ */
+ if (getuid() != 0)
+ return (B_FALSE);
+
+ my_pid = getpid();
+
+ /*
+ * Already checked before for this process? If yes, return cached
+ * response.
+ */
+ if (my_pid == checkedPid) {
+ return (isMainNscd);
+ }
+
+ (void) mutex_lock(&nscdLock);
+
+ /* Check once more incase another thread has just done this. */
+ if (my_pid == checkedPid) {
+ (void) mutex_unlock(&nscdLock);
+ return (isMainNscd);
+ }
+
+ /*
+ * Am I the nscd process? UID is already checked, not needed from
+ * psinfo.
+ */
+ isMainNscd = check_nscd_proc(my_pid, B_FALSE);
+
+ /* Remeber for whom isMainNscd is. */
+ checkedPid = my_pid;
+
+ (void) mutex_unlock(&nscdLock);
+ return (isMainNscd);
+}
+
+/*
+ * initialize a connection management control structure conn_mgmt_t
+ */
+ns_conn_mgmt_t *
+init_conn_mgmt()
+{
+ ns_conn_mgmt_t *cmg;
+
+ cmg = (ns_conn_mgmt_t *)calloc(1, sizeof (*cmg));
+ if (cmg == NULL) {
+ syslog(LOG_ERR, NS_CONN_MSG_NO_CONN_MGMT);
+ return (NULL);
+ }
+
+ /* is this process nscd or peruser nscd ? */
+ cmg->is_nscd = __s_api_nscd_proc();
+ cmg->is_peruser_nscd = __s_api_peruser_proc();
+
+ /*
+ * assume the underlying libldap allows multiple threads sharing
+ * the same ldap connection (MT connection)
+ */
+ cmg->ldap_mt = B_TRUE;
+ /* state is inactive until MT connection is required/requested */
+ cmg->state = NS_CONN_MGMT_INACTIVE;
+
+ (void) mutex_init(&cmg->lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&cmg->cfg_lock, USYNC_THREAD, NULL);
+ cmg->pid = getpid();
+
+ /* for nscd or peruser nscd, MT connection is required */
+ if (cmg->is_nscd == B_TRUE || cmg->is_peruser_nscd == B_TRUE)
+ cmg->state = NS_CONN_MGMT_ACTIVE;
+
+ /*
+ * reference (or initialize) the current Native LDAP configuration and
+ * if in nscd process, make it never refreshed
+ */
+ cmg->config = __s_api_get_default_config_global();
+ if (cmg->config == NULL)
+ cmg->config = __s_api_loadrefresh_config_global();
+ if (cmg->config != NULL) {
+ /*
+ * main nscd get config change notice from ldap_cachemgr
+ * so won't times out and refresh the config
+ */
+ if (cmg->is_nscd == B_TRUE)
+ (cmg->config)->paramList[NS_LDAP_EXP_P].ns_tm = 0;
+ cmg->cfg_cookie = cmg->config->config_cookie;
+ }
+
+ return (cmg);
+}
+
+static void
+mark_shutdown_or_reoladed(int op)
+{
+ ns_conn_mgmt_t *cmg = ns_connmgmt;
+
+ (void) mutex_lock(&cmg->lock);
+ if (op == NS_CONN_MGMT_OP_SHUTDOWN)
+ cmg->shutting_down = B_TRUE;
+ else
+ cmg->cfg_reloaded = B_TRUE;
+ atomic_inc_uint(&cmg->ref_cnt);
+ cmg->state = NS_CONN_MGMT_DETACHED;
+
+ if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG)
+ __s_api_init_config_global(NULL);
+
+ (void) mutex_unlock(&cmg->lock);
+}
+
+/*
+ * Return a pointer to the current connection management. If
+ * it has not been created, or is requested to recreate, then
+ * create and return the pointer. It is possible, the current
+ * one is created by the parent before fork, create a new
+ * one too in such a case.
+ */
+static ns_conn_mgmt_t *
+get_current_conn_mgmt(int op)
+{
+ ns_conn_mgmt_t *cmg = ns_connmgmt;
+ static pid_t checked_pid = (pid_t)-1;
+ pid_t mypid;
+
+ mypid = getpid();
+ if (cmg == NULL || checked_pid != mypid) {
+ checked_pid = mypid;
+
+ /*
+ * if current conn_mgmt not created yet or is from parent
+ * or is requested to recreate, create it
+ */
+ if (cmg == NULL || cmg->pid != mypid) {
+ if (cmg != NULL) {
+ /*
+ * We don't want to free the conn_mgmt
+ * allocated by the parent, since
+ * there may be ldap connections
+ * still being used. So leave it
+ * alone but keep it referenced,
+ * so that it will not be flagged
+ * as a piece of leaked memory.
+ */
+ ns_connmgmt_parent = cmg;
+ /*
+ * avoid lint warning; does not
+ * change the conn_mgmt in parent
+ */
+ ns_connmgmt_parent->state =
+ NS_CONN_MGMT_DETACHED;
+ }
+ ns_connmgmt = init_conn_mgmt();
+ cmg = ns_connmgmt;
+ /*
+ * ensure it will not be destroyed until explicitly
+ * shut down or reloaded
+ */
+ if (op == NS_CONN_MGMT_OP_REF)
+ atomic_inc_uint(&cmg->ref_cnt);
+ }
+ }
+
+ return (cmg);
+}
+
+static ns_conn_mgmt_t *
+access_conn_mgmt(int op)
+{
+ ns_conn_mgmt_t *cmg = NULL;
+ ns_conn_mgmt_t *cmg_prev;
+
+ (void) mutex_lock(&ns_connmgmt_lock);
+
+ /*
+ * connection management is not available when the libsldap is being
+ * unloaded or shut down
+ */
+ if (ns_connmgmt_shutting_down == B_TRUE) {
+ (void) mutex_unlock(&ns_connmgmt_lock);
+ return (NULL);
+ }
+
+ if (op == NS_CONN_MGMT_OP_SHUTDOWN) {
+ ns_connmgmt_shutting_down = B_TRUE;
+ if (ns_connmgmt != NULL) {
+ cmg = ns_connmgmt;
+ mark_shutdown_or_reoladed(op);
+ ns_connmgmt = NULL;
+ }
+ (void) mutex_unlock(&ns_connmgmt_lock);
+ return (cmg);
+ }
+
+ if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
+ op == NS_CONN_MGMT_OP_NEW_CONFIG) {
+ cmg_prev = ns_connmgmt;
+ mark_shutdown_or_reoladed(op);
+ /*
+ * the previous cmg (cmg_prev) will be freed later
+ * when its ref count reaches zero
+ */
+ ns_connmgmt = NULL;
+ }
+
+ cmg = get_current_conn_mgmt(op);
+ if (cmg == NULL) {
+ (void) mutex_unlock(&ns_connmgmt_lock);
+ return (NULL);
+ }
+
+ atomic_inc_uint(&cmg->ref_cnt);
+ if (op == NS_CONN_MGMT_OP_RELOAD_CONFIG ||
+ op == NS_CONN_MGMT_OP_NEW_CONFIG)
+ cmg = cmg_prev;
+ else { /* op is NS_CONN_MGMT_OP_REF or NS_CONN_MGMT_OP_LIB_INIT */
+ if (cmg->config == NULL)
+ cmg->config = __s_api_get_default_config();
+ }
+
+ (void) mutex_unlock(&ns_connmgmt_lock);
+ return (cmg);
+}
+
+/*
+ * free a connection management control
+ */
+static void
+free_conn_mgmt(ns_conn_mgmt_t *cmg)
+{
+ union {
+ ldap_data_t s_d;
+ char s_b[1024];
+ } space;
+ ldap_data_t *sptr;
+ int ndata;
+ int adata;
+ int rc;
+ ldap_get_chg_cookie_t cookie;
+
+ if (cmg == NULL)
+ return;
+ cookie = cmg->cfg_cookie;
+
+ __s_api_free2dArray(cmg->pservers);
+ /* destroy the previous config or release the current one */
+ if (cmg->config != NULL) {
+ if (cmg->state == NS_CONN_MGMT_DETACHED)
+ __s_api_destroy_config(cmg->config);
+ else
+ __s_api_release_config(cmg->config);
+ }
+
+ /* stop the server status/config-change monitor thread */
+ if (cmg->procchg_started == B_TRUE) {
+ if (cmg->procchg_tid != thr_self()) {
+ if (cmg->procchg_door_call == B_TRUE) {
+ adata = sizeof (ldap_call_t) + 1;
+ ndata = sizeof (space);
+ space.s_d.ldap_call.ldap_callnumber =
+ GETSTATUSCHANGE;
+ space.s_d.ldap_call.ldap_u.get_change.op =
+ NS_STATUS_CHANGE_OP_STOP;
+ space.s_d.ldap_call.ldap_u.get_change.cookie =
+ cookie;
+ sptr = &space.s_d;
+ rc = __ns_ldap_trydoorcall(&sptr, &ndata,
+ &adata);
+ if (rc != NS_CACHE_SUCCESS)
+ syslog(LOG_INFO,
+ gettext("libsldap: "
+ "free_conn_mgmt():"
+ " stopping door call "
+ " GETSTATUSCHANGE failed "
+ " (rc = %d)"), rc);
+ }
+ (void) pthread_cancel(cmg->procchg_tid);
+ cmg->procchg_started = B_FALSE;
+ }
+ }
+
+ free(cmg);
+}
+
+static ns_conn_mgmt_t *
+release_conn_mgmt(ns_conn_mgmt_t *cmg, boolean_t unlock_cmg)
+{
+ if (cmg == NULL)
+ return (NULL);
+ if (atomic_dec_uint_nv(&cmg->ref_cnt) == 0) {
+ if (cmg->state == NS_CONN_MGMT_DETACHED) {
+ if (unlock_cmg == B_TRUE)
+ (void) mutex_unlock(&cmg->lock);
+ free_conn_mgmt(cmg);
+ return (NULL);
+ } else {
+ syslog(LOG_WARNING,
+ gettext("libsldap: connection management "
+ " has a refcount of zero but the state "
+ " is not DETACHED (%d)"), cmg->state);
+ cmg = NULL;
+ }
+ }
+ return (cmg);
+}
+
+/*
+ * exposed function for initializing a connection management control structure
+ */
+ns_conn_mgmt_t *
+__s_api_conn_mgmt_init()
+{
+ if (thr_keycreate_once(&ns_mtckey, ns_mtckey_cleanup) != 0) {
+ syslog(LOG_WARNING, NS_CONN_MSG_NO_MTC_KEY);
+ return (NULL);
+ }
+
+ if (thr_keycreate_once(&ns_cmgkey, NULL) != 0) {
+ syslog(LOG_WARNING, NS_CONN_MSG_NO_CMG_KEY);
+ return (NULL);
+ }
+
+ return (access_conn_mgmt(NS_CONN_MGMT_OP_LIB_INIT));
+}
+
+/* initialize a connection user */
+ns_conn_user_t *
+__s_api_conn_user_init(int type, void *userinfo, boolean_t referral)
+{
+ ns_conn_user_t *cu;
+ ns_conn_mgmt_t *cmg;
+
+ /* delete the reference to the previously used conn_mgmt */
+ (void) thr_setspecific(ns_cmgkey, NULL);
+
+ cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
+ if (cmg == NULL)
+ return (NULL);
+
+ if (cmg->state != NS_CONN_MGMT_ACTIVE &&
+ cmg->state != NS_CONN_MGMT_INACTIVE) {
+ atomic_dec_uint(&cmg->ref_cnt);
+ return (NULL);
+ }
+
+ cu = (ns_conn_user_t *)calloc(1, sizeof (*cu));
+ if (cu == NULL) {
+ atomic_dec_uint(&cmg->ref_cnt);
+ return (NULL);
+ }
+
+ cu->type = type;
+ cu->state = NS_CONN_USER_ALLOCATED;
+ cu->tid = thr_self();
+ cu->userinfo = userinfo;
+ cu->referral = referral;
+ cu->ns_rc = NS_LDAP_SUCCESS;
+ cu->conn_mgmt = cmg;
+
+ (void) conn_tsd_setup(cmg);
+
+ return (cu);
+}
+
+/*
+ * Free the resources used by a connection user.
+ * The caller should ensure this conn_user is
+ * not associated with any conn_mt, i.e.,
+ * not in any conn_mt's linked list of conn_users.
+ * The caller needs to free the userinfo member
+ * as well.
+ */
+void
+__s_api_conn_user_free(ns_conn_user_t *cu)
+{
+ ns_conn_mgmt_t *cmg;
+
+ if (cu == NULL)
+ return;
+
+ cu->state = NS_CONN_USER_FREED;
+ if (cu->ns_error != NULL)
+ (void) __ns_ldap_freeError(&cu->ns_error);
+
+ cmg = cu->conn_mgmt;
+ conn_tsd_free();
+ (void) release_conn_mgmt(cmg, B_FALSE);
+ (void) free(cu);
+}
+
+/*
+ * Initialize an MT connection control structure
+ * that will be used to represent an ldap connection
+ * to be shared among multiple threads and to hold
+ * and manage all the conn_users using the ldap
+ * connection.
+ */
+static ns_conn_mt_t *
+init_conn_mt(ns_conn_mgmt_t *cmg, ns_ldap_error_t **ep)
+{
+ ns_conn_mt_t *cm;
+ ns_conn_mgmt_t *cmg_a;
+
+ cm = (ns_conn_mt_t *)calloc(1, sizeof (*cm));
+ if (cm == NULL) {
+ if (ep != NULL)
+ *ep = __s_api_make_error(NS_LDAP_MEMORY, NULL);
+ return (NULL);
+ }
+
+ cmg_a = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
+ if (cmg_a != cmg) {
+ if (cmg_a != NULL) {
+ (void) release_conn_mgmt(cmg_a, B_FALSE);
+ if (ep != NULL)
+ *ep = __s_api_make_error(NS_LDAP_OP_FAILED,
+ NS_CONN_MSG_SHUTDOWN_RELOADED);
+ }
+ return (NULL);
+ }
+
+ (void) mutex_init(&cm->lock, USYNC_THREAD, NULL);
+ cm->state = NS_CONN_MT_CONNECTING;
+ cm->tid = thr_self();
+ cm->pid = getpid();
+ cm->next = NULL;
+ cm->cu_head = NULL;
+ cm->cu_tail = NULL;
+ cm->conn = NULL;
+ cm->conn_mgmt = cmg;
+
+ return (cm);
+}
+
+/*
+ * Free an MT connection control structure, assume conn_mgmt is locked.
+ * 'unlock_cmg' is passed to release_conn_mgmt() to indicate the
+ * cmg needs to be unlocked or not.
+ */
+static ns_conn_mgmt_t *
+free_conn_mt(ns_conn_mt_t *cm, int unlock_cmg)
+{
+ ns_conn_mgmt_t *cmg;
+
+ if (cm == NULL)
+ return (NULL);
+ if (cm->ns_error != NULL)
+ (void) __ns_ldap_freeError(&cm->ns_error);
+ if (cm->conn != NULL) {
+ if (cm->conn->ld != NULL)
+ (void) ldap_unbind(cm->conn->ld);
+ __s_api_freeConnection(cm->conn);
+ }
+ cmg = cm->conn_mgmt;
+ free(cm);
+ return (release_conn_mgmt(cmg, unlock_cmg));
+}
+
+/* add a connection user to an MT connection */
+static void
+add_cu2cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
+{
+
+ if (cm->cu_head == NULL) {
+ cm->cu_head = cu;
+ cm->cu_tail = cu;
+ } else {
+ cm->cu_tail->next = cu;
+ cm->cu_tail = cu;
+ }
+ cm->cu_cnt++;
+}
+
+/* add an MT connection to the connection management */
+static void
+add_cm2cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
+{
+ /*
+ * add connection opened for WRITE to top of list
+ * for garbage collection purpose. This is to
+ * ensure the connection will be closed after a
+ * certain amount of time (60 seconds).
+ */
+ if (cmg->cm_head == NULL) {
+ cmg->cm_head = cm;
+ cmg->cm_tail = cm;
+ } else {
+ if (cm->opened_for == NS_CONN_USER_WRITE) {
+ cm->next = cmg->cm_head;
+ cmg->cm_head = cm;
+ } else {
+ cmg->cm_tail->next = cm;
+ cmg->cm_tail = cm;
+ }
+ }
+ cmg->cm_cnt++;
+}
+
+/* delete a connection user from an MT connection */
+static void
+del_cu4cm(ns_conn_user_t *cu, ns_conn_mt_t *cm)
+{
+ ns_conn_user_t *pu, *u;
+
+ if (cu == NULL || cm->cu_head == NULL || cm->cu_cnt == 0)
+ return;
+
+ /* only one conn_user on list */
+ if (cm->cu_head == cm->cu_tail) {
+ if (cu == cm->cu_head) {
+ cm->cu_head = cm->cu_tail = NULL;
+ cm->cu_cnt = 0;
+ cu->next = NULL;
+ }
+ return;
+ }
+
+ /* more than one and cu is the first one */
+ if (cu == cm->cu_head) {
+ cm->cu_head = cu->next;
+ cm->cu_cnt--;
+ cu->next = NULL;
+ return;
+ }
+
+ pu = cm->cu_head;
+ for (u = cm->cu_head->next; u; u = u->next) {
+ if (cu == u)
+ break;
+ pu = u;
+ }
+ if (pu != cm->cu_tail) {
+ pu->next = cu->next;
+ if (pu->next == NULL)
+ cm->cu_tail = pu;
+ cm->cu_cnt--;
+ cu->next = NULL;
+ } else {
+ syslog(LOG_INFO, gettext(
+ "libsldap: del_cu4cm(): connection user not found"));
+ }
+}
+
+/* delete an MT connection from the connection management control structure */
+static void
+del_cm4cmg(ns_conn_mt_t *cm, ns_conn_mgmt_t *cmg)
+{
+ ns_conn_mt_t *pm, *m;
+
+ if (cm == NULL || cmg->cm_head == NULL || cmg->cm_cnt == 0)
+ return;
+
+ /* only one conn_mt on list */
+ if (cmg->cm_head == cmg->cm_tail) {
+ if (cm == cmg->cm_head) {
+ cmg->cm_head = cmg->cm_tail = NULL;
+ cmg->cm_cnt = 0;
+ cm->next = NULL;
+ }
+ return;
+ }
+
+ /* more than one and cm is the first one */
+ if (cm == cmg->cm_head) {
+ cmg->cm_head = cm->next;
+ cmg->cm_cnt--;
+ cm->next = NULL;
+ return;
+ }
+
+ pm = cmg->cm_head;
+ for (m = cmg->cm_head->next; m; m = m->next) {
+ if (cm == m)
+ break;
+ pm = m;
+ }
+ if (pm != cmg->cm_tail) {
+ pm->next = cm->next;
+ if (pm->next == NULL)
+ cmg->cm_tail = pm;
+ cmg->cm_cnt--;
+ cm->next = NULL;
+ } else {
+ syslog(LOG_INFO, gettext(
+ "libsldap: del_cm4cmg(): MT connection not found"));
+ }
+}
+
+/*
+ * compare to see if the server and credential for authentication match
+ * those used by an MT connection
+ */
+static boolean_t
+is_server_cred_matched(const char *server, const ns_cred_t *cred,
+ ns_conn_mt_t *cm)
+{
+ Connection *cp = cm->conn;
+
+ /* check server first */
+ if (server != NULL && *server != 0) {
+ if (strcasecmp(server, cp->serverAddr) != 0)
+ return (B_FALSE);
+ }
+
+ if (cred == NULL)
+ return (B_TRUE);
+
+ /* then check cred */
+ return (__s_api_is_auth_matched(cp->auth, cred));
+}
+
+/*
+ * Wait until a pending MT connection becomes available.
+ * Return 1 if so, 0 if error.
+ *
+ * Assume the current conn_mgmt and the input conn_mt
+ * are locked.
+ */
+static int
+wait_for_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t *cm)
+{
+
+ cu->state = NS_CONN_USER_WAITING;
+ add_cu2cm(cu, cm);
+ cu->conn_mt = cm;
+
+ (void) mutex_unlock(&cm->lock);
+ /*
+ * It could take some time so we don't want to hold
+ * cm->conn_mgmt across the wait
+ */
+ (void) mutex_unlock(&(cm->conn_mgmt)->lock);
+
+ (void) mutex_lock(&cm->lock);
+ /* check one more time see if need to wait */
+ if (cm->state == NS_CONN_MT_CONNECTING) {
+ (void) conn_wait(cm, cu);
+
+ /* cm->lock is locked again at this point */
+
+ cu->state = NS_CONN_USER_WOKEUP;
+ }
+
+ if (cm->state == NS_CONN_MT_CONNECTED)
+ return (1);
+ else {
+ del_cu4cm(cu, cm);
+ cu->conn_mt = NULL;
+ cu->bad_mt_conn = B_FALSE;
+ return (0);
+ }
+}
+
+/*
+ * Check and see if the input MT connection '*cm' should be closed.
+ * In two cases, it should be closed. If a preferred server is
+ * found to be up when ldap_cachemgr is queried and reported back.
+ * Or when the server being used for the connection is found to
+ * be down. Return B_FALSE if the connection is not closed (or not marked
+ * to be closed), otherwise unlock mutex (*cm)->lock and return B_TRUE.
+ * This function assumes conn_mgmt cmg and conn_mt *cm are locked.
+ */
+static boolean_t
+check_and_close_conn(ns_conn_mgmt_t *cmg, ns_conn_mt_t **cm,
+ ns_conn_user_t *cu) {
+
+ int rc;
+ int j;
+ int svridx = -1;
+ int upidx = -1;
+ int free_cm;
+ ns_server_info_t sinfo;
+ ns_ldap_error_t *errorp = NULL;
+
+ /*
+ * check only if preferred servers are defined
+ */
+ if (cmg->pservers_loaded == B_FALSE)
+ get_preferred_servers(B_FALSE, B_FALSE, cmg);
+ if (cmg->pservers == NULL)
+ return (B_FALSE);
+
+ /*
+ * ask ldap_cachemgr for the first available server
+ */
+ rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
+ &sinfo, &errorp, NS_CACHE_ADDR_IP);
+ if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
+ (void) __ns_ldap_freeError(&errorp);
+ return (B_FALSE);
+ }
+
+ /*
+ * Did ldap_cachemgr return a preferred server ?
+ */
+ for (j = 0; cmg->pservers[j] != NULL; j++) {
+ if (strcasecmp(sinfo.server, cmg->pservers[j]) != 0)
+ continue;
+ upidx = j;
+ break;
+ }
+
+ /*
+ * Is the server being used a preferred one ?
+ */
+ for (j = 0; cmg->pservers[j] != NULL; j++) {
+ if (strcasecmp(cmg->pservers[j], (*cm)->conn->serverAddr) != 0)
+ continue;
+ svridx = j;
+ break;
+ }
+
+ /*
+ * Need to fall back to a down-but-now-up preferred server ?
+ * A preferred server falls back to a more preferred one.
+ * A regular one falls back to any preferred ones. So if
+ * both are preferred ones and same index, or both
+ * are not preferred ones, then no need to close the
+ * connection.
+ */
+ if ((upidx == -1 && svridx == -1) ||
+ (upidx != -1 && svridx != -1 && upidx == svridx)) {
+ __s_api_free_server_info(&sinfo);
+ return (B_FALSE);
+ }
+
+ /*
+ * otherwise, 4 cases, all may need to close the connection:
+ * For case 1 and 2, both servers are preferred ones:
+ * 1. ldap_cachemgr returned a better one to use (upidx < svridx)
+ * 2. the server being used is down (upidx > svridx)
+ * 3. ldap_cachemgr returned a preferred one, but the server
+ * being used is not, so need to fall back to the preferred server
+ * 4. ldap_cachemgr returned a non-preferred one, but the server
+ * being used is a preferred one, so it must be down (since
+ * ldap_cachemgr always returns a preferred one when possible).
+ * For case 1 & 3, close the READ connection when no user uses it.
+ * For 2 and 4, close the connection with error rc, LDAP_SERVER_DOWN.
+ */
+ if (upidx != -1 && (svridx == -1 || upidx < svridx)) { /* case 1 & 3 */
+ /* fallback does not make sense for WRITE/referred connection */
+ if ((*cm)->opened_for == NS_CONN_USER_WRITE ||
+ (*cm)->referral == B_TRUE) {
+ __s_api_free_server_info(&sinfo);
+ return (B_FALSE);
+ }
+ free_cm = close_conn_mt_when_nouser(*cm);
+ if (cmg->shutting_down == B_FALSE)
+ cu->retry = B_TRUE;
+ } else {
+ ns_ldap_error_t *ep;
+ ep = __s_api_make_error(LDAP_SERVER_DOWN,
+ NS_CONN_MSG_DOWN_FROM_CACHEMGR);
+ /* cu has not been attached to cm yet, use NULL as cu pointer */
+ free_cm = close_conn_mt(*cm, LDAP_SERVER_DOWN, &ep, NULL);
+ if (cmg->shutting_down == B_FALSE)
+ cu->retry = B_TRUE;
+ (void) __ns_ldap_freeError(&ep);
+ }
+
+ (void) mutex_unlock(&(*cm)->lock);
+ if (free_cm == 1) {
+ (void) free_conn_mt(*cm, 0);
+ *cm = NULL;
+ }
+
+ __s_api_free_server_info(&sinfo);
+
+ return (B_TRUE);
+}
+
+/*
+ * Check to see if a conn_mt matches the connection criteria from
+ * a conn_user. Return B_TRUE if yes, B_FALSE, otherwise. The input
+ * conn_mt pointer (*cmt) may be freed and *cmt will be set to NULL
+ * to indicate so.
+ * conn_mt *cmt and conn_mgmt cm->conn_mgmt are assumed locked.
+ * cm->lock is unlocked at exit if rc is B_FALSE.
+ */
+static boolean_t
+match_conn_mt(ns_conn_user_t *cu, ns_conn_mt_t **cmt,
+ ns_conn_mt_state_t st, const char *server,
+ const ns_cred_t *cred)
+{
+ boolean_t matched = B_FALSE;
+ boolean_t drop_conn;
+ int free_cm = 0;
+ ns_conn_mt_t *cm = *cmt;
+ ns_conn_mgmt_t *cmg = cm->conn_mgmt;
+
+ if (cm->state != st || cm->close_when_nouser == B_TRUE ||
+ cm->detached == B_TRUE || cm->pid != getpid() ||
+ cm->referral != cu->referral) {
+ (void) mutex_unlock(&cm->lock);
+ return (B_FALSE);
+ }
+
+ /*
+ * if a conn_mt opened for WRITE is idle
+ * long enough, then close it. To improve
+ * the performance of applications, such
+ * as ldapaddent, a WRITE connection is
+ * given a short time to live in the
+ * connection pool, expecting the write
+ * requests to come in a quick succession.
+ * To save resource, the connection will
+ * be closed if idle more than 60 seconds.
+ */
+ if (cm->opened_for == NS_CONN_USER_WRITE &&
+ cu->type != NS_CONN_USER_WRITE && cm->cu_cnt == 0 &&
+ ((time(NULL) - cm->access_time) > 60)) {
+ /*
+ * NS_LDAP_INTERNAL is irrelevant here. There no
+ * conn_user to consume the rc
+ */
+ free_cm = close_conn_mt(cm, NS_LDAP_INTERNAL, NULL, NULL);
+ (void) mutex_unlock(&cm->lock);
+ if (free_cm == 1) {
+ (void) free_conn_mt(cm, 0);
+ *cmt = NULL;
+ }
+ return (B_FALSE);
+ }
+
+ switch (cu->type) {
+ case NS_CONN_USER_SEARCH:
+ case NS_CONN_USER_GETENT:
+ if (cm->opened_for == NS_CONN_USER_SEARCH ||
+ cm->opened_for == NS_CONN_USER_GETENT)
+ matched = B_TRUE;
+ break;
+
+ case NS_CONN_USER_WRITE:
+ if (cm->opened_for == NS_CONN_USER_WRITE)
+ matched = B_TRUE;
+ break;
+
+ default:
+ matched = B_FALSE;
+ break;
+ }
+
+ if (matched == B_TRUE && ((server != NULL || cred != NULL) &&
+ is_server_cred_matched(server, cred, cm) == B_FALSE))
+ matched = B_FALSE;
+
+ if (matched != B_FALSE) {
+ /*
+ * Check and drop the 'connected' connection if
+ * necessary. Main nscd gets status changes from
+ * the ldap_cachemgr daemon directly via the
+ * GETSTATUSCHANGE door call, the standalone
+ * function works in a no ldap_cachemgr environment,
+ * so no need to check and drop connections.
+ */
+ if (cm->state == NS_CONN_MT_CONNECTED &&
+ cmg->is_nscd == B_FALSE && !__s_api_isStandalone()) {
+ drop_conn = check_and_close_conn(cmg, &cm, cu);
+ if (drop_conn == B_TRUE) {
+ if (cm == NULL)
+ *cmt = NULL;
+ return (B_FALSE);
+ }
+ }
+
+ /* check if max. users using or waiting for the connection */
+ if ((cm->state == NS_CONN_MT_CONNECTED &&
+ cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
+ cm->cu_cnt >= cm->cu_max) ||
+ (cm->state == NS_CONN_MT_CONNECTING &&
+ cm->cu_max != NS_CONN_MT_USER_NO_MAX &&
+ cm->waiter_cnt >= cm->cu_max - 1))
+ matched = B_FALSE;
+ }
+
+ if (matched == B_FALSE)
+ (void) mutex_unlock(&cm->lock);
+
+ return (matched);
+}
+
+/*
+ * obtain an MT connection from the connection management for a conn_user
+ *
+ * Input:
+ * server : server name or IP address
+ * flags : libsldap API flags
+ * cred : pointer to the user credential
+ * cu : pointer to the conn_user structure
+ * Output:
+ * session : hold pointer to the Connection structure
+ * errorp : hold pointer to error info (ns_ldap_error_t)
+ */
+int
+__s_api_conn_mt_get(const char *server, const int flags, const ns_cred_t *cred,
+ Connection **session, ns_ldap_error_t **errorp, ns_conn_user_t *cu)
+{
+ int rc;
+ int i;
+ ns_conn_mt_t *cn;
+ ns_conn_mt_state_t st;
+ ns_conn_mgmt_t *cmg;
+
+ if (errorp == NULL || cu == NULL || session == NULL)
+ return (NS_LDAP_INVALID_PARAM);
+
+ *session = NULL;
+ cmg = cu->conn_mgmt;
+
+ /*
+ * for pam_ldap, always try opening a new connection
+ */
+ if (cu->type == NS_CONN_USER_AUTH)
+ return (NS_LDAP_NOTFOUND);
+
+ /* if need a new conn, then don't reuse */
+ if (flags & NS_LDAP_NEW_CONN)
+ return (NS_LDAP_NOTFOUND);
+
+ if (flags & NS_LDAP_KEEP_CONN)
+ cu->keep_conn = B_TRUE;
+
+ /*
+ * We want to use MT connection only if keep-connection flag is
+ * set or if MT was requested (or active)
+ */
+ if (!((cmg->state == NS_CONN_MGMT_INACTIVE &&
+ cu->keep_conn == B_TRUE) || cmg->state == NS_CONN_MGMT_ACTIVE))
+ return (NS_LDAP_NOTFOUND);
+
+ /* MT connection will be used now (if possible/available) */
+ cu->use_mt_conn = B_TRUE;
+
+ NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errorp);
+
+ /* first look for a connection already open */
+ st = NS_CONN_MT_CONNECTED;
+ cu->state = NS_CONN_USER_FINDING;
+ for (i = 0; i < 2; i++) {
+ for (cn = cmg->cm_head; cn; cn = cn->next) {
+ (void) mutex_lock(&cn->lock);
+ rc = match_conn_mt(cu, &cn, st, server, cred);
+ if (rc == B_FALSE && cn != NULL) /* not found */
+ continue;
+ if (cn == NULL) { /* not found and cn freed */
+ /*
+ * as the conn_mt list could
+ * be different due to cn's
+ * deletion, scan the entire
+ * conn_mt list again
+ */
+ st = NS_CONN_MT_CONNECTED;
+ i = -1;
+ break;
+ }
+
+ /* return a connected one if found */
+ if (cn->state == NS_CONN_MT_CONNECTED) {
+ *session = cn->conn;
+ add_cu2cm(cu, cn);
+ cu->conn_mt = cn;
+ cu->state = NS_CONN_USER_CONNECTED;
+ (void) mutex_unlock(&cn->lock);
+ (void) mutex_unlock(&cmg->lock);
+ return (NS_LDAP_SUCCESS);
+ }
+
+ /*
+ * if cn is not connecting, or allow only
+ * one user, skip it
+ */
+ if (cn->state != NS_CONN_MT_CONNECTING ||
+ cn->cu_max == 1) {
+ (void) mutex_unlock(&cn->lock);
+ continue;
+ }
+
+ /* wait for the connecting conn_mt */
+ if (wait_for_conn_mt(cu, cn) != 1) {
+ /*
+ * NS_LDAP_NOTFOUND signals that the function
+ * __s_api_check_libldap_MT_conn_support()
+ * detected that the lower libldap library
+ * does not support MT connection, so return
+ * NS_LDAP_NOTFOUND to let the caller to
+ * open a non-MT conneciton. Otherwise,
+ * connect error occurred, return
+ * NS_CONN_USER_CONNECT_ERROR
+ */
+ if (cn->ns_rc != NS_LDAP_NOTFOUND)
+ cu->state = NS_CONN_USER_CONNECT_ERROR;
+ else {
+ cu->state = NS_CONN_USER_FINDING;
+ cu->use_mt_conn = B_FALSE;
+ }
+ (void) mutex_unlock(&cn->lock);
+
+ /* cmg->lock unlocked by wait_for_conn_mt() */
+
+ return (cn->ns_rc);
+ }
+
+ /* return the newly available conn_mt */
+ *session = cn->conn;
+ cu->state = NS_CONN_USER_CONNECTED;
+ (void) mutex_unlock(&cn->lock);
+
+ /* cmg->lock unlocked by wait_for_conn_mt() */
+
+ return (NS_LDAP_SUCCESS);
+ }
+
+ /* next, look for a connecting conn_mt */
+ if (i == 0)
+ st = NS_CONN_MT_CONNECTING;
+ }
+
+ /* no connection found, start opening one */
+ cn = init_conn_mt(cmg, errorp);
+ if (cn == NULL) {
+ (void) mutex_unlock(&cmg->lock);
+ return ((*errorp)->status);
+ }
+ cu->conn_mt = cn;
+ cn->opened_for = cu->type;
+ cn->referral = cu->referral;
+ if (cmg->ldap_mt == B_TRUE)
+ cn->cu_max = NS_CONN_MT_USER_MAX;
+ else
+ cn->cu_max = 1;
+ add_cm2cmg(cn, cmg);
+ (void) mutex_unlock(&cmg->lock);
+
+ return (NS_LDAP_NOTFOUND);
+}
+
+
+/*
+ * add an MT connection to the connection management
+ *
+ * Input:
+ * con : pointer to the Connection info
+ * cu : pointer to the conn_user structure
+ * Output:
+ * ep : hold pointer to error info (ns_ldap_error_t)
+ */
+int
+__s_api_conn_mt_add(Connection *con, ns_conn_user_t *cu, ns_ldap_error_t **ep)
+{
+ ns_conn_mgmt_t *cmg = cu->conn_mgmt;
+ ns_conn_mt_t *cm = cu->conn_mt;
+
+ /* if the conn_mgmt is being shut down, return error */
+ NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
+
+ /*
+ * start the change monitor thread only if it
+ * hasn't been started and the process is the
+ * main nscd (not peruser nscd)
+ */
+ if (cmg->procchg_started == B_FALSE && cmg->is_nscd == B_TRUE) {
+ start_thread(cmg);
+ cmg->procchg_started = B_TRUE;
+ }
+ (void) mutex_lock(&cm->lock);
+ cm->conn = con;
+ cm->state = NS_CONN_MT_CONNECTED;
+ cm->pid = getpid();
+ cm->create_time = time(NULL);
+ cm->access_time = cm->create_time;
+ cm->opened_for = cu->type;
+ add_cu2cm(cu, cm);
+ cu->conn_mt = cm;
+ cu->state = NS_CONN_USER_CONNECTED;
+ if (cmg->ldap_mt == B_TRUE)
+ cm->cu_max = NS_CONN_MT_USER_MAX;
+ else
+ cm->cu_max = 1;
+
+ /* wake up the waiters if any */
+ (void) conn_signal(cm);
+
+ (void) mutex_unlock(&cm->lock);
+ (void) mutex_unlock(&cmg->lock);
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * return an MT connection to the pool when a conn user is done usint it
+ *
+ * Input:
+ * cu : pointer to the conn_user structure
+ * Output: NONE
+ */
+void
+__s_api_conn_mt_return(ns_conn_user_t *cu)
+{
+ ns_conn_mt_t *cm;
+ ns_conn_mgmt_t *cmg;
+
+ if (cu == NULL || cu->use_mt_conn == B_FALSE)
+ return;
+ cm = cu->conn_mt;
+ if (cm == NULL)
+ return;
+ cmg = cu->conn_mgmt;
+
+ (void) mutex_lock(&cm->lock);
+ del_cu4cm(cu, cm);
+ cu->state = NS_CONN_USER_DISCONNECTED;
+ cu->conn_mt = NULL;
+ cu->bad_mt_conn = B_FALSE;
+
+ /*
+ * if this MT connection is no longer needed, or not usable, and
+ * no more conn_user uses it, then close it.
+ */
+
+ if ((cm->close_when_nouser == B_TRUE ||
+ cm->state != NS_CONN_MT_CONNECTED) && cm->cu_cnt == 0) {
+ (void) mutex_unlock(&cm->lock);
+ (void) mutex_lock(&cmg->lock);
+ (void) mutex_lock(&cm->lock);
+ del_cm4cmg(cm, cmg);
+ /* use ns_conn_free (instead of 1) to avoid lint warning */
+ NS_CONN_UNLOCK_AND_FREE(ns_conn_free, cm, cmg);
+ } else {
+ if (cm->state == NS_CONN_MT_CONNECTED && cm->cu_cnt == 0 &&
+ cm->conn != NULL && cm->conn->ld != NULL) {
+ struct timeval zerotime;
+ LDAPMessage *res;
+
+ zerotime.tv_sec = zerotime.tv_usec = 0L;
+ /* clean up remaining results just in case */
+ while (ldap_result(cm->conn->ld, LDAP_RES_ANY,
+ LDAP_MSG_ALL, &zerotime, &res) > 0) {
+ if (res != NULL)
+ (void) ldap_msgfree(res);
+ }
+ }
+ (void) mutex_unlock(&cm->lock);
+ }
+}
+
+/* save error info (rc and ns_ldap_error_t) in the conn_mt */
+static void
+err2cm(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp) {
+ ns_ldap_error_t *ep;
+
+ cm->ns_rc = rc;
+ cm->ns_error = NULL;
+ if (errorp != NULL && *errorp != NULL) {
+ ep = __s_api_copy_error(*errorp);
+ if (ep == NULL)
+ cm->ns_rc = NS_LDAP_MEMORY;
+ else
+ cm->ns_error = ep;
+ }
+}
+
+/* copy error info (rc and ns_ldap_error_t) from conn_mt to conn_user */
+static void
+err_from_cm(ns_conn_user_t *cu, ns_conn_mt_t *cm) {
+ ns_ldap_error_t *ep;
+
+ cu->ns_rc = cm->ns_rc;
+ if (cu->ns_error != NULL)
+ (void) __ns_ldap_freeError(&cu->ns_error);
+ cu->ns_error = NULL;
+ if (cm->ns_rc != NS_LDAP_SUCCESS && cm->ns_error != NULL) {
+ ep = __s_api_copy_error(cm->ns_error);
+ if (ep == NULL)
+ cu->ns_rc = NS_LDAP_MEMORY;
+ else
+ cu->ns_error = ep;
+ }
+}
+
+/* copy error info (rc and ns_ldap_error_t) from caller to conn_user */
+static void
+err_from_caller(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp) {
+
+ cu->ns_rc = rc;
+ if (errorp != NULL) {
+ if (cu->ns_error != NULL)
+ (void) __ns_ldap_freeError(&cu->ns_error);
+ cu->ns_error = *errorp;
+ *errorp = NULL;
+ } else
+ cu->ns_error = NULL;
+}
+
+/*
+ * remove an MT connection from the connection management when failed to open
+ *
+ * Input:
+ * cu : pointer to the conn_user structure
+ * rc : error code
+ * errorp : pointer to pointer to error info (ns_ldap_error_t)
+ * Output:
+ * errorp : set to NULL, if none NULL cm, callers do not need to free it
+ */
+void
+__s_api_conn_mt_remove(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
+{
+ ns_conn_mgmt_t *cmg;
+ ns_conn_mt_t *cm;
+ int free_cm = 0;
+
+ if (cu == NULL || cu->use_mt_conn == B_FALSE)
+ return;
+ if ((cm = cu->conn_mt) == NULL)
+ return;
+ cmg = cu->conn_mgmt;
+
+ (void) mutex_lock(&cmg->lock);
+ (void) mutex_lock(&cm->lock);
+ if (cm->state != NS_CONN_MT_CONNECT_ERROR) {
+ cm->state = NS_CONN_MT_CONNECT_ERROR;
+ cm->ns_rc = rc;
+ if (errorp != NULL) {
+ cm->ns_error = *errorp;
+ *errorp = NULL;
+ }
+ }
+
+ /* all the conn_users share the same error rc and ns_ldap_error_t */
+ err_from_cm(cu, cm);
+ /* wake up the waiters if any */
+ (void) conn_signal(cm);
+
+ del_cu4cm(cu, cm);
+ cu->conn_mt = NULL;
+ cu->bad_mt_conn = B_FALSE;
+ if (cm->cu_cnt == 0) {
+ del_cm4cmg(cm, cmg);
+ free_cm = 1;
+ }
+
+ NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
+}
+
+/*
+ * check to see if the underlying libldap supports multi-threaded client
+ * (MT connections)
+ */
+int
+__s_api_check_libldap_MT_conn_support(ns_conn_user_t *cu, LDAP *ld,
+ ns_ldap_error_t **ep)
+{
+ int rc;
+ ns_conn_mgmt_t *cmg;
+
+ /* if no need to check, just return success */
+ if (cu->conn_mt == NULL || cu->use_mt_conn == B_FALSE)
+ return (NS_LDAP_SUCCESS);
+
+ cmg = cu->conn_mgmt;
+ rc = setup_mt_ld(ld, cmg);
+
+ if (cmg->do_mt_conn == B_FALSE) {
+ /*
+ * If the conn_mgmt is being shut down, return error.
+ * if cmg is usable, cmg->lock will be locked. Otherwise,
+ * this function will return with rc NS_LDAP_OP_FAILED.
+ */
+ NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, ep);
+ if (cmg->do_mt_conn == B_FALSE) {
+ if (rc < 0)
+ cmg->ldap_mt = B_FALSE;
+ else {
+ cmg->ldap_mt = B_TRUE;
+ if (cmg->is_nscd == B_TRUE ||
+ cmg->is_peruser_nscd == B_TRUE) {
+ cmg->do_mt_conn = B_TRUE;
+ cmg->state = NS_CONN_MGMT_ACTIVE;
+ }
+ }
+ }
+ (void) mutex_unlock(&cmg->lock);
+ }
+
+ if (rc < 0)
+ __s_api_conn_mt_remove(cu, NS_LDAP_NOTFOUND, NULL);
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * Close an MT connection.
+ * Assume cm not null and locked, assume conn_mgmt is also locked.
+ * Return -1 if error, 1 if the cm should be freed, otherwise 0.
+ */
+static int
+close_conn_mt(ns_conn_mt_t *cm, int rc, ns_ldap_error_t **errorp,
+ ns_conn_user_t *cu)
+{
+ ns_conn_mgmt_t *cmg = cm->conn_mgmt;
+ ns_conn_mt_t *m;
+ ns_conn_user_t *u;
+
+ if ((cm->state != NS_CONN_MT_CONNECTED && cm->state !=
+ NS_CONN_MT_CLOSING) || cmg->cm_head == NULL || cmg->cm_cnt == 0)
+ return (-1);
+
+ /* if the conn_mt is not in the MT connection pool, nothing to do */
+ for (m = cmg->cm_head; m; m = m->next) {
+ if (cm == m)
+ break;
+ }
+ if (m == NULL)
+ return (-1);
+
+ if (cm->state == NS_CONN_MT_CONNECTED) { /* first time in here */
+ cm->state = NS_CONN_MT_CLOSING;
+ /*
+ * If more cu exist to consume the error info, copy
+ * it to the cm. If the caller calls on behalf of
+ * a cu, cu won't be NULL. Check to see if there's
+ * more cu that needs the error info. If caller does
+ * not have a specific cu attached to it (e.g.,
+ * shutdown_all_conn_mt()), cu is NULL, check if at
+ * least one cu exists.
+ */
+ if ((cu != NULL && cm->cu_cnt > 1) ||
+ (cu == NULL && cm->cu_cnt > 0)) {
+ err2cm(cm, rc, errorp);
+ /* wake up waiter (conn_user) if any */
+ (void) conn_signal(cm);
+ }
+
+ /* for each conn_user using the conn_mt, set bad_mt_conn flag */
+ if (cm->cu_head != NULL) {
+ for (u = cm->cu_head; u; u = u->next) {
+ u->bad_mt_conn = B_TRUE;
+ if (cmg->shutting_down == B_FALSE)
+ u->retry = B_TRUE;
+ }
+ }
+ }
+
+ /* detach the conn_mt if no more conn_user left */
+ if ((cu != NULL && cm->cu_cnt == 1) ||
+ (cu == NULL && cm->cu_cnt == 0)) {
+ del_cm4cmg(cm, cmg);
+ cm->detached = B_TRUE;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * An MT connection becomes bad, close it and free resources.
+ * This function is called with a ns_conn_user_t representing
+ * a user of the MT connection.
+ *
+ * Input:
+ * cu : pointer to the conn_user structure
+ * rc : error code
+ * errorp : pointer to pointer to error info (ns_ldap_error_t)
+ * Output:
+ * errorp : set to NULL (if no error), callers do not need to free it
+ */
+void
+__s_api_conn_mt_close(ns_conn_user_t *cu, int rc, ns_ldap_error_t **errorp)
+{
+ ns_conn_mgmt_t *cmg;
+ ns_conn_mt_t *cm;
+ int free_cm = 0;
+
+ if (cu == NULL || cu->use_mt_conn == B_FALSE)
+ return;
+
+ if (cu->state != NS_CONN_USER_CONNECTED || (cm = cu->conn_mt) == NULL)
+ return;
+ cmg = cu->conn_mgmt;
+
+ (void) mutex_lock(&cmg->lock);
+ (void) mutex_lock(&cm->lock);
+
+ /* close the MT connection if possible */
+ free_cm = close_conn_mt(cm, rc, errorp, cu);
+ if (free_cm == -1) { /* error case */
+ (void) mutex_unlock(&cm->lock);
+ (void) mutex_unlock(&cmg->lock);
+ return;
+ }
+
+ if (rc != NS_LDAP_SUCCESS) { /* error info passed in, use it */
+ err_from_caller(cu, rc, errorp);
+ } else { /* error not passed in, use those saved in the conn_mt */
+ err_from_cm(cu, cm);
+ }
+
+ /* detach the conn_user from the conn_mt */
+ del_cu4cm(cu, cm);
+ cu->conn_mt = NULL;
+ cu->bad_mt_conn = B_FALSE;
+ if (cmg->shutting_down == B_FALSE)
+ cu->retry = B_TRUE;
+ NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
+}
+
+/*
+ * Close an MT connection when the associated server is known to be
+ * down. This function is called with a ns_conn_mt_t representing
+ * the MT connection. That is, the caller is not a conn_user
+ * thread but rather the procchg thread.
+ */
+static void
+close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg)
+{
+ ns_conn_mgmt_t *cmg;
+ int free_cm = 0;
+ ns_ldap_error_t *ep;
+
+ if (cm == NULL)
+ return;
+ cmg = cm->conn_mgmt;
+
+ ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
+ if (ep != NULL) {
+ ep->status = rc;
+ if (errmsg != NULL)
+ ep->message = strdup(errmsg); /* OK if returns NULL */
+ }
+
+ (void) mutex_lock(&cmg->lock);
+ (void) mutex_lock(&cm->lock);
+
+ /* close the MT connection if possible */
+ free_cm = close_conn_mt(cm, LDAP_SERVER_DOWN, &ep, NULL);
+ if (free_cm == -1) { /* error case */
+ (void) mutex_unlock(&cm->lock);
+ (void) mutex_unlock(&cmg->lock);
+ return;
+ }
+ (void) __ns_ldap_freeError(&ep);
+
+ NS_CONN_UNLOCK_AND_FREE(free_cm, cm, cmg);
+}
+
+/*
+ * Close an MT connection when there is a better server to connect to.
+ * Mark the connection as to-be-closed-when-no-one-using so that
+ * any outstanding ldap operations can run to completion.
+ * Assume that both the conn_mt and conn_mgmt are locked.
+ * Return 1 if the conn_mt should be freed.
+ */
+static int
+close_conn_mt_when_nouser(ns_conn_mt_t *cm)
+{
+ int free_cm = 0;
+
+ if (cm->cu_cnt == 0) {
+ del_cm4cmg(cm, cm->conn_mgmt);
+ free_cm = 1;
+ } else {
+ cm->close_when_nouser = B_TRUE;
+ }
+
+ return (free_cm);
+}
+
+/*
+ * Retrieve the configured preferred server list.
+ * This function locked the conn_mgmt and does not
+ * unlock at exit.
+ */
+static void
+get_preferred_servers(boolean_t lock, boolean_t reload, ns_conn_mgmt_t *cmg)
+{
+ ns_ldap_error_t *errorp = NULL;
+ void **pservers = NULL;
+
+ if (lock == B_TRUE)
+ (void) mutex_lock(&cmg->lock);
+
+ /* if already done, and no reload, then return */
+ if (cmg->pservers_loaded == B_TRUE && reload == B_FALSE)
+ return;
+
+ if (cmg->pservers != NULL) {
+ (void) __ns_ldap_freeParam((void ***)&cmg->pservers);
+ cmg->pservers = NULL;
+ }
+
+ if (__ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
+ &pservers, &errorp) == NS_LDAP_SUCCESS) {
+ cmg->pservers = (char **)pservers;
+ cmg->pservers_loaded = B_TRUE;
+ } else {
+ (void) __ns_ldap_freeError(&errorp);
+ (void) __ns_ldap_freeParam(&pservers);
+ }
+}
+
+/*
+ * This function handles the config or server status change notification
+ * from the ldap_cachemgr.
+ */
+static ns_conn_mgmt_t *
+proc_server_change(ns_server_status_change_t *chg, ns_conn_mgmt_t *cmg)
+{
+ int cnt, i, j, k, n;
+ boolean_t loop = B_TRUE;
+ boolean_t cmg_locked = B_FALSE;
+ char *s;
+ ns_conn_mt_t *cm;
+ ns_conn_mgmt_t *ocmg;
+
+ /* if config changed, reload the configuration */
+ if (chg->config_changed == B_TRUE) {
+ /* reload the conn_mgmt and Native LDAP config */
+ ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_RELOAD_CONFIG);
+ shutdown_all_conn_mt(ocmg);
+ /* release the one obtained from access_conn_mgmt(RELOAD) */
+ (void) release_conn_mgmt(ocmg, B_FALSE);
+ /* release the one obtained when ocmg was created */
+ (void) release_conn_mgmt(ocmg, B_FALSE);
+ return (ocmg);
+ }
+
+ if ((cnt = chg->num_server) == 0)
+ return (cmg);
+
+ /* handle down servers first */
+ for (i = 0; i < cnt; i++) {
+
+ if (chg->changes[i] != NS_SERVER_DOWN)
+ continue;
+ s = chg->servers[i];
+
+ /*
+ * look for a CONNECTED MT connection using
+ * the same server s, and close it
+ */
+ while (loop) {
+ if (cmg_locked == B_FALSE) {
+ (void) mutex_lock(&cmg->lock);
+ cmg_locked = B_TRUE;
+ }
+ for (cm = cmg->cm_head; cm; cm = cm->next) {
+ (void) mutex_lock(&cm->lock);
+
+ if (cm->state == NS_CONN_MT_CONNECTED &&
+ cm->conn != NULL &&
+ strcasecmp(cm->conn->serverAddr, s) == 0) {
+ (void) mutex_unlock(&cm->lock);
+ break;
+ }
+
+ (void) mutex_unlock(&cm->lock);
+ }
+ if (cm != NULL) {
+ (void) mutex_unlock(&cmg->lock);
+ cmg_locked = B_FALSE;
+ close_conn_mt_by_procchg(cm, LDAP_SERVER_DOWN,
+ NS_CONN_MSG_DOWN_FROM_CACHEMGR);
+ /*
+ * Process the next cm using server s.
+ * Start from the head of the cm linked
+ * list again, as the cm list may change
+ * after close_conn_mt_by_procchg() is done.
+ */
+ continue;
+ }
+
+ /*
+ * No (more) MT connection using the down server s.
+ * Process the next server on the list.
+ */
+ break;
+ } /* while loop */
+ }
+
+ /*
+ * Next handle servers whose status changed to up.
+ * Get the preferred server list first if not done yet.
+ * get_preferred_servers() leaves conn_mgmt locked.
+ */
+ get_preferred_servers(cmg_locked == B_FALSE ? B_TRUE : B_FALSE,
+ B_FALSE, cmg);
+ cmg_locked = B_TRUE;
+ /*
+ * if no preferred server configured, we don't switch MT connection
+ * to a more preferred server (i.e., fallback), so just return
+ */
+ if (cmg->pservers == NULL) {
+ (void) mutex_unlock(&cmg->lock);
+ return (cmg);
+ }
+
+ /* for each server that is up now */
+ for (i = 0; i < cnt; i++) {
+ if (chg->changes[i] != NS_SERVER_UP)
+ continue;
+ s = chg->servers[i];
+
+ /*
+ * look for a CONNECTED MT connection which uses
+ * a server less preferred than s, and treat it
+ * as 'fallback needed' by calling
+ * close_conn_mt_when_nouser()
+ */
+ k = -1;
+ loop = B_TRUE;
+ while (loop) {
+ if (cmg_locked == B_FALSE) {
+ (void) mutex_lock(&cmg->lock);
+ cmg_locked = B_TRUE;
+ }
+
+ /* Is s a preferred server ? */
+ if (k == -1) {
+ for (j = 0; cmg->pservers[j] != NULL; j++) {
+ if (strcasecmp(cmg->pservers[j],
+ s) == 0) {
+ k = j;
+ break;
+ }
+ }
+ }
+ /* skip s if not a preferred server */
+ if (k == -1) {
+ break;
+ }
+
+ /* check each MT connection */
+ for (cm = cmg->cm_head; cm; cm = cm->next) {
+ (void) mutex_lock(&cm->lock);
+ /*
+ * Find an MT connection that is connected and
+ * not marked, but leave WRITE or REFERRAL
+ * connections alone, since fallback does not
+ * make sense for them.
+ */
+ if (cm->state == NS_CONN_MT_CONNECTED &&
+ cm->close_when_nouser == B_FALSE &&
+ cm->conn != NULL && cm->opened_for !=
+ NS_CONN_USER_WRITE &&
+ cm->referral == B_FALSE) {
+ n = -1;
+ /*
+ * j < k ??? should we close
+ * an active MT that is using s ?
+ * ie could s went down and up
+ * again, but cm is bound prior to
+ * the down ? Play safe here,
+ * and check j <= k.
+ */
+ for (j = 0; j <= k; j++) {
+ if (strcasecmp(
+ cm->conn->serverAddr,
+ cmg->pservers[j]) == 0) {
+ n = j;
+ break;
+ }
+ }
+ /*
+ * s is preferred, if its location
+ * in the preferred server list is
+ * ahead of that of the server
+ * used by the cm (i.e., no match
+ * found before s)
+ */
+ if (n == -1) { /* s is preferred */
+ int fr = 0;
+ fr = close_conn_mt_when_nouser(
+ cm);
+ NS_CONN_UNLOCK_AND_FREE(fr,
+ cm, cmg);
+ cmg_locked = B_FALSE;
+ /*
+ * break, not continue,
+ * because we need to
+ * check the entire cm
+ * list again. The call
+ * above may change the
+ * cm list.
+ */
+ break;
+ }
+ }
+ (void) mutex_unlock(&cm->lock);
+ }
+ /* if no (more) cm using s, check next server */
+ if (cm == NULL)
+ loop = B_FALSE;
+ } /* while loop */
+ }
+ if (cmg_locked == B_TRUE)
+ (void) mutex_unlock(&cmg->lock);
+ return (cmg);
+}
+
+/* Shut down all MT connection managed by the connection management */
+void
+shutdown_all_conn_mt(ns_conn_mgmt_t *cmg)
+{
+ ns_ldap_error_t *ep;
+ ns_conn_mt_t *cm;
+ int free_cm = 0;
+ boolean_t done = B_FALSE;
+
+ ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
+ if (ep != NULL) { /* if NULL, not a problem */
+ /* OK if returns NULL */
+ ep->message = strdup(NS_CONN_MSG_SHUTDOWN_RELOADED);
+ }
+
+ (void) mutex_lock(&cmg->lock);
+ while (cmg->cm_head != NULL && done == B_FALSE) {
+ for (cm = cmg->cm_head; cm; cm = cm->next) {
+ (void) mutex_lock(&cm->lock);
+ if (cm->next == NULL)
+ done = B_TRUE;
+ /* shut down each conn_mt, ignore errors */
+ free_cm = close_conn_mt(cm, LDAP_OTHER, &ep, NULL);
+ (void) mutex_unlock(&cm->lock);
+ if (free_cm == 1) {
+ (void) free_conn_mt(cm, 0);
+ /*
+ * conn_mt may change, so start from
+ * top of list again
+ */
+ break;
+ }
+ }
+ }
+ (void) mutex_unlock(&cmg->lock);
+ (void) __ns_ldap_freeError(&ep);
+}
+
+/* free all the resources used by the connection management */
+void
+__s_api_shutdown_conn_mgmt()
+{
+ ns_conn_mgmt_t *cmg;
+
+ cmg = access_conn_mgmt(NS_CONN_MGMT_OP_SHUTDOWN);
+ if (cmg == NULL) /* already being SHUT done */
+ return;
+
+ (void) shutdown_all_conn_mt(cmg);
+ (void) release_conn_mgmt(cmg, B_FALSE);
+
+ /* then destroy the conn_mgmt */
+ (void) release_conn_mgmt(cmg, B_FALSE);
+}
+
+
+/*
+ * reinitialize the libsldap connection management after
+ * receiving a new native LDAP configuration from ldap_cachemgr
+ */
+void
+__s_api_reinit_conn_mgmt_new_config(ns_config_t *new_cfg)
+{
+ ns_conn_mgmt_t *cmg;
+ ns_conn_mgmt_t *ocmg;
+
+ cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
+ if (cmg == NULL)
+ return;
+ if (cmg->config == new_cfg || cmg->state == NS_CONN_MGMT_DETACHED) {
+ (void) release_conn_mgmt(cmg, B_FALSE);
+ return;
+ }
+
+ /* reload the conn_mgmt and native LDAP config */
+ ocmg = access_conn_mgmt(NS_CONN_MGMT_OP_NEW_CONFIG);
+ if (ocmg == cmg)
+ shutdown_all_conn_mt(ocmg);
+ /* release the one obtained from access_conn_mgmt(RELOAD) */
+ (void) release_conn_mgmt(ocmg, B_FALSE);
+ /* release the one obtained when ocmg was created */
+ (void) release_conn_mgmt(ocmg, B_FALSE);
+ /* release the one obtained when this function is entered */
+ (void) release_conn_mgmt(cmg, B_FALSE);
+}
+
+/*
+ * Prepare to retry ldap search operation if needed.
+ * Return 1 if retry is needed, otherwise 0.
+ * If first time in, return 1. If not, return 1 if:
+ * - not a NS_CONN_USER_GETENT conn_user AND
+ * - have not retried 3 times yet AND
+ * - previous search failed AND
+ * - the retry flag is set in the ns_conn_user_t or config was reloaded
+ */
+int
+__s_api_setup_retry_search(ns_conn_user_t **conn_user,
+ ns_conn_user_type_t type, int *try_cnt, int *rc,
+ ns_ldap_error_t **errorp)
+{
+ boolean_t retry;
+ ns_conn_user_t *cu = *conn_user;
+ ns_conn_mgmt_t *cmg;
+
+ if (*try_cnt > 0 && cu != NULL) {
+ /*
+ * if called from firstEntry(), keep conn_mt for
+ * the subsequent getnext requests
+ */
+ if (cu->type == NS_CONN_USER_GETENT && *rc == NS_LDAP_SUCCESS)
+ return (0);
+ cmg = cu->conn_mgmt;
+ retry = cu->retry;
+ if (cu->conn_mt != NULL)
+ __s_api_conn_mt_return(cu);
+ if (cmg != NULL && cmg->cfg_reloaded == B_TRUE)
+ retry = B_TRUE;
+ __s_api_conn_user_free(cu);
+ *conn_user = NULL;
+
+ if (*rc == NS_LDAP_SUCCESS || retry != B_TRUE)
+ return (0);
+ }
+
+ *try_cnt = *try_cnt + 1;
+ if (*try_cnt > NS_LIST_TRY_MAX)
+ return (0);
+
+ *conn_user = __s_api_conn_user_init(type, NULL, B_FALSE);
+ if (*conn_user == NULL) {
+ if (*try_cnt == 1) { /* first call before any retry */
+ *rc = NS_LDAP_MEMORY;
+ *errorp = NULL;
+ }
+ /* for 1+ try, use previous rc and errorp */
+ return (0);
+ }
+
+ /* free ldap_error_t from previous search */
+ if (*try_cnt > 1 && rc != NS_LDAP_SUCCESS && *errorp != NULL)
+ (void) __ns_ldap_freeError(errorp);
+
+ return (1);
+}
+
+/* prepare to get the next entry for an enumeration */
+int
+__s_api_setup_getnext(ns_conn_user_t *cu, int *ns_err,
+ ns_ldap_error_t **errorp)
+{
+ int rc;
+ ns_conn_mgmt_t *cmg;
+
+ /*
+ * if using an MT connection, ensure the thread-specific data are set,
+ * but if the MT connection is no longer good, return the error saved.
+ */
+ if (cu->conn_mt != NULL && (cmg = cu->conn_mgmt) != NULL) {
+
+ if (cu->bad_mt_conn == B_TRUE) {
+ __s_api_conn_mt_close(cu, 0, NULL);
+ *ns_err = cu->ns_rc;
+ *errorp = cu->ns_error;
+ cu->ns_error = NULL;
+ return (*ns_err);
+ }
+
+ rc = conn_tsd_check(cmg);
+ if (rc != NS_LDAP_SUCCESS) {
+ *errorp = NULL;
+ return (rc);
+ }
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/* wait for an MT connection to become available */
+static int
+conn_wait(ns_conn_mt_t *conn_mt, ns_conn_user_t *conn_user)
+{
+ ns_conn_waiter_t mywait;
+ ns_conn_waiter_t *head = &conn_mt->waiter;
+
+ (void) cond_init(&(mywait.waitcv), USYNC_THREAD, 0);
+ mywait.key = conn_user;
+ mywait.signaled = 0;
+ mywait.next = head->next;
+ mywait.prev = head;
+ if (mywait.next)
+ mywait.next->prev = &mywait;
+ head->next = &mywait;
+ atomic_inc_uint(&conn_mt->waiter_cnt);
+
+ while (!mywait.signaled)
+ (void) cond_wait(&(mywait.waitcv), &conn_mt->lock);
+ if (mywait.prev)
+ mywait.prev->next = mywait.next;
+ if (mywait.next)
+ mywait.next->prev = mywait.prev;
+ return (0);
+}
+
+/* signal that an MT connection is now available */
+static int
+conn_signal(ns_conn_mt_t *conn_mt)
+{
+ int c = 0;
+ ns_conn_waiter_t *head = &conn_mt->waiter;
+ ns_conn_waiter_t *tmp = head->next;
+
+ while (tmp) {
+ (void) cond_signal(&(tmp->waitcv));
+ tmp->signaled = 1;
+ atomic_dec_uint(&conn_mt->waiter_cnt);
+ c++;
+ tmp = tmp->next;
+ }
+
+ return (c);
+}
+
+/*
+ * wait and process the server status and/or config change notification
+ * from ldap_cachemgr
+ */
+static void *
+get_server_change(void *arg)
+{
+ union {
+ ldap_data_t s_d;
+ char s_b[DOORBUFFERSIZE];
+ } space;
+ ldap_data_t *sptr = &space.s_d;
+ int ndata;
+ int adata;
+ char *ptr;
+ int ds_cnt;
+ int door_rc;
+ int which;
+ int retry = 0;
+ boolean_t loop = B_TRUE;
+ char *c, *oc;
+ int dslen = strlen(DOORLINESEP);
+ char dsep = DOORLINESEP_CHR;
+ char chg_data[DOORBUFFERSIZE];
+ char **servers = NULL;
+ boolean_t getchg_not_supported = B_FALSE;
+ ns_conn_mgmt_t *ocmg = (ns_conn_mgmt_t *)arg;
+ ns_conn_mgmt_t *cmg;
+ ns_server_status_t *status = NULL;
+ ns_server_status_change_t chg = { 0 };
+ ldap_get_change_out_t *get_chg;
+ ldap_get_chg_cookie_t cookie;
+ ldap_get_chg_cookie_t new_cookie;
+
+ cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
+ if (cmg != ocmg)
+ thr_exit(NULL);
+ /* cmg is locked before called */
+ cmg->procchg_tid = thr_self();
+
+ /* make sure the thread specific data are set */
+ (void) conn_tsd_setup(cmg);
+ cookie = cmg->cfg_cookie;
+
+ while (loop) {
+
+ if (chg.servers != NULL)
+ free(chg.servers);
+ if (chg.changes != NULL)
+ free(chg.changes);
+ if (sptr != &space.s_d)
+ (void) munmap((char *)sptr, sizeof (space));
+
+ /*
+ * If the attached conn_mgmt has been deleted,
+ * then exit. The new conn_mgmt will starts it
+ * own monitor thread later. If libsldap is being
+ * unloaded or configuration reloaded, OR
+ * ldap_cachemgr rejected the GETSTATUSCHANGE door
+ * call, then exit as well.
+ */
+ if (cmg == NULL || cmg->state == NS_CONN_MGMT_DETACHED ||
+ getchg_not_supported == B_TRUE) {
+
+ if (cmg != NULL) {
+ cmg->procchg_started = B_FALSE;
+ (void) release_conn_mgmt(cmg, B_FALSE);
+ }
+
+ conn_tsd_free();
+ thr_exit(NULL);
+ }
+
+ (void) memset(space.s_b, 0, DOORBUFFERSIZE);
+ (void) memset(&chg, 0, sizeof (chg));
+ adata = sizeof (ldap_call_t) + 1;
+ ndata = sizeof (space);
+ space.s_d.ldap_call.ldap_callnumber = GETSTATUSCHANGE;
+ space.s_d.ldap_call.ldap_u.get_change.op =
+ NS_STATUS_CHANGE_OP_START;
+ space.s_d.ldap_call.ldap_u.get_change.cookie = cookie;
+ sptr = &space.s_d;
+ door_rc = __ns_ldap_trydoorcall_getfd();
+ cmg->procchg_door_call = B_TRUE;
+ if (release_conn_mgmt(cmg, B_FALSE) == NULL) {
+ conn_tsd_free();
+ thr_exit(NULL);
+ }
+
+ if (door_rc == NS_CACHE_SUCCESS)
+ door_rc = __ns_ldap_trydoorcall_send(&sptr, &ndata,
+ &adata);
+
+ /*
+ * Check and see if the conn_mgmt is still current.
+ * If not, no need to continue.
+ */
+ cmg = access_conn_mgmt(NS_CONN_MGMT_OP_REF);
+ if (cmg != NULL)
+ cmg->procchg_door_call = B_FALSE;
+ if (cmg != ocmg) {
+ if (cmg != NULL) {
+ cmg->procchg_started = B_FALSE;
+ (void) release_conn_mgmt(cmg, B_FALSE);
+ }
+ conn_tsd_free();
+ thr_exit(NULL);
+ }
+
+ if (door_rc != NS_CACHE_SUCCESS) {
+ if (door_rc == NS_CACHE_NOSERVER) {
+ if (retry++ > 10)
+ getchg_not_supported = B_TRUE;
+ else {
+ /*
+ * ldap_cachemgr may be down, give
+ * it time to restart
+ */
+ (void) sleep(2);
+ }
+ } else if (door_rc == NS_CACHE_NOTFOUND)
+ getchg_not_supported = B_TRUE;
+ continue;
+ } else
+ retry = 0;
+
+ /* copy info from door call return structure */
+ get_chg = &sptr->ldap_ret.ldap_u.changes;
+ ptr = get_chg->data;
+ /* configuration change ? */
+ if (get_chg->type == NS_STATUS_CHANGE_TYPE_CONFIG) {
+ chg.config_changed = B_TRUE;
+ cmg = proc_server_change(&chg, cmg);
+ continue;
+ }
+
+ /* server status changes ? */
+ if (get_chg->type == NS_STATUS_CHANGE_TYPE_SERVER) {
+ /*
+ * first check cookies, if don't match, config
+ * has changed
+ */
+ new_cookie = get_chg->cookie;
+ if (new_cookie.mgr_pid != cookie.mgr_pid ||
+ new_cookie.seq_num != cookie.seq_num) {
+ chg.config_changed = B_TRUE;
+ cmg = proc_server_change(&chg, cmg);
+ continue;
+ }
+
+ (void) strlcpy(chg_data, ptr, sizeof (chg_data));
+ chg.num_server = get_chg->server_count;
+
+ servers = (char **)calloc(chg.num_server,
+ sizeof (char *));
+ if (servers == NULL) {
+ syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
+ continue;
+ }
+ status = (ns_server_status_t *)calloc(chg.num_server,
+ sizeof (int));
+ if (status == NULL) {
+ syslog(LOG_INFO, NS_CONN_MSG_MEMORY_ERROR);
+ free(servers);
+ continue;
+ }
+ ds_cnt = 0;
+ which = 0;
+ oc = ptr;
+ for (c = ptr; which != 2; c++) {
+ /* look for DOORLINESEP or end of string */
+ if (*c != dsep && *c != '\0')
+ continue;
+ if (*c == dsep) { /* DOORLINESEP */
+ *c = '\0'; /* current value */
+ c += dslen; /* skip to next value */
+ }
+ if (which == 0) { /* get server info */
+ servers[ds_cnt] = oc;
+ oc = c;
+ which = 1; /* get status next */
+ continue;
+ }
+ /* which == 1, get up/down status */
+ if (strcmp(NS_SERVER_CHANGE_UP, oc) == 0) {
+ status[ds_cnt] = NS_SERVER_UP;
+ } else if (strcmp(NS_SERVER_CHANGE_DOWN,
+ oc) == 0)
+ status[ds_cnt] = NS_SERVER_DOWN;
+ else {
+ syslog(LOG_INFO,
+ NS_CONN_MSG_BAD_CACHEMGR_DATA);
+ continue;
+ }
+ oc = c;
+ ds_cnt++;
+ if (*c == '\0')
+ which = 2; /* exit the loop */
+ else
+ which = 0; /* get server info next */
+ }
+ chg.servers = servers;
+ chg.changes = status;
+ cmg = proc_server_change(&chg, cmg);
+ continue;
+ }
+ }
+
+ return (NULL);
+}
+
+/* start the thread handling the change notification from ldap_cachemgr */
+static void
+start_thread(ns_conn_mgmt_t *cmg) {
+
+ int errnum;
+
+ /*
+ * start a thread to get and process config and server status changes
+ */
+ if (thr_create(NULL, NULL, get_server_change,
+ (void *)cmg, THR_DETACHED, NULL) != 0) {
+ errnum = errno;
+ syslog(LOG_WARNING, NS_CONN_MSG_NO_PROCCHG_THREAD,
+ strerror(errnum));
+ }
+}
diff --git a/usr/src/lib/libsldap/common/ns_connmgmt.h b/usr/src/lib/libsldap/common/ns_connmgmt.h
new file mode 100755
index 0000000000..bb7849bf86
--- /dev/null
+++ b/usr/src/lib/libsldap/common/ns_connmgmt.h
@@ -0,0 +1,305 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef _NS_CONNMGMT_H
+#define _NS_CONNMGMT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <thread.h>
+#include "ns_sldap.h"
+#include "ns_internal.h"
+#include "ns_cache_door.h"
+
+struct ns_conn_user; /* connection user, forward definition */
+struct ns_conn_mt; /* multi-threaded (MT) connection, forward definition */
+struct ns_conn_mgmt; /* connection management, forward definition */
+
+#define NS_CONN_MT_USER_NO_MAX -1
+#define NS_CONN_MT_USER_MAX NS_CONN_MT_USER_NO_MAX
+#define NS_LIST_TRY_MAX 3
+
+/*
+ * Structure for handling the waiter of a pending multi-threaded (MT) connection
+ */
+typedef struct ns_conn_waiter {
+ cond_t waitcv;
+ uint8_t signaled;
+ struct ns_conn_user *key;
+ struct ns_conn_waiter *next, *prev;
+} ns_conn_waiter_t;
+
+/*
+ * type of a connection user
+ */
+typedef enum {
+ NS_CONN_USER_SEARCH = 1,
+ NS_CONN_USER_WRITE = 2,
+ NS_CONN_USER_AUTH = 3,
+ NS_CONN_USER_GETENT = 4
+} ns_conn_user_type_t;
+
+/*
+ * state of a connection user
+ */
+typedef enum {
+ NS_CONN_USER_UNINITED = 0,
+ NS_CONN_USER_ALLOCATED = 1,
+ NS_CONN_USER_FINDING = 2, /* looking for an MT connection */
+ NS_CONN_USER_WAITING = 3, /* waiting for an MT connection */
+ NS_CONN_USER_WOKEUP = 4,
+ NS_CONN_USER_CONNECT_ERROR = 5,
+ NS_CONN_USER_CONNECTED = 6,
+ NS_CONN_USER_DISCONNECTED = 7,
+ NS_CONN_USER_FREED = 8
+} ns_conn_user_state_t;
+
+/*
+ * A connection user represents a request processed by libsldap. It
+ * usually is a thread using the same connection from start to end.
+ * Different connection users of the same type can share the same
+ * connection opened for that type. But search and getent users can
+ * share the same connection opened for either search or getent. AUTH
+ * connection are not shareable.
+ *
+ * A getent user may have a longer lifespan and live outside of libsldap.
+ * This is because the associated search cookie is passed back to the caller
+ * via the firstEntry call and used in the subsequent nextEntry or endEntry
+ * calls. Even though the firstEntry and the nextEntry/endEntry calls may
+ * be running in a different thread, the connection being used will be the
+ * same. It is the one assigend during the firstEntry call.
+ */
+struct ns_conn_user {
+ ns_conn_user_type_t type; /* search, write, auth, getent, ... */
+ ns_conn_user_state_t state;
+ thread_t tid; /* id of the thread starts the request */
+ struct ns_conn_user *next; /* next conn_user in the linked list */
+ struct ns_conn_mt *conn_mt; /* the MT connection being used */
+ struct ns_conn_mgmt *conn_mgmt; /* ref counted conn management */
+ void *userinfo; /* private data of the request */
+ ns_ldap_return_code ns_rc; /* error return code */
+ ns_ldap_error_t *ns_error; /* error info */
+ boolean_t referral; /* using a referred server ? */
+ boolean_t retry; /* retry the request on certain error? */
+ boolean_t keep_conn; /* keep the conn for reuse ? */
+ boolean_t use_mt_conn; /* using/used an MT connection ? */
+ boolean_t bad_mt_conn; /* MT connection is not usable ? */
+};
+
+/*
+ * state of an MT connection
+ */
+typedef enum {
+ NS_CONN_MT_UNINITED = 0,
+ NS_CONN_MT_CONNECTING = 1,
+ NS_CONN_MT_CONNECT_ERROR = 2,
+ NS_CONN_MT_CONNECTED = 3,
+ NS_CONN_MT_CLOSING = 4
+} ns_conn_mt_state_t;
+
+/*
+ * An ns_conn_mt (or MT connection) represents an ldap connection
+ * that can be shared among multiple threads. It also represents
+ * the set of connection users using the ldap connection. It contains
+ * a pointer to the Connection structure that has the physical info
+ * of the connection (server name, address, ldap handle, etc). It
+ * also contains a linked list of all the conn_user using the ldap
+ * connection. The connection users can wait on an MT connection
+ * to become available or be told to abort and clean up when one of
+ * the connection user detects an error and knows that the connection
+ * is no longer usable. The error info is then saved in the structure
+ * for other users to consume.
+ *
+ * An MT connection is meant to be shared concurrently and persistent.
+ * Even when there's no current user, it will be kept by the connection
+ * management, waiting for the next user. It will be closed when
+ * a connection error is detected, when a better server should be
+ * used, when the Native LDAP configuration change, or when the libsldap
+ * is being unloaded.
+ */
+typedef struct ns_conn_mt {
+ mutex_t lock;
+ ns_conn_mt_state_t state;
+ pid_t pid; /* process creates the connection */
+ thread_t tid; /* thread creates the connection */
+ struct ns_conn_mt *next; /* next conn_mt in the linked list */
+ ns_conn_user_t *cu_head; /* head of conn_user linked list */
+ ns_conn_user_t *cu_tail; /* tail of conn_user linked list */
+ struct ns_conn_mgmt *conn_mgmt; /* ref counted conn management */
+ ns_conn_waiter_t waiter; /* first of the connection waiters */
+ uint_t cu_cnt; /* number of the using conn_user */
+ int32_t cu_max; /* max. allowed number of conn_user */
+ uint_t waiter_cnt; /* number of waiters */
+ ns_conn_user_type_t opened_for; /* type of conn_user opened for */
+ Connection *conn; /* name, IP address, ldap handle, etc */
+ time_t create_time; /* time when connection created */
+ time_t access_time; /* time when last used */
+ ns_ldap_return_code ns_rc; /* saved error code */
+ ns_ldap_error_t *ns_error; /* saved error info */
+ boolean_t close_when_nouser; /* close connection when */
+ /* last user is done ? */
+ boolean_t detached; /* no longer in connection pool? */
+ boolean_t referral; /* using a referred server ? */
+} ns_conn_mt_t;
+
+/*
+ * state of a connection management
+ * (a connection pool sharing the same native LDAP configuration)
+ */
+typedef enum {
+ NS_CONN_MGMT_UNINITED = 0,
+ NS_CONN_MGMT_INACTIVE = 1, /* conn sharing not yet requested */
+ NS_CONN_MGMT_ACTIVE = 2, /* connection sharing required/requested */
+ NS_CONN_MGMT_DETACHED = 3 /* on the way down, no new user allowed */
+} ns_conn_mgmt_state_t;
+
+/*
+ * An ns_conn_mgmt (or connection management) represents the set of MT
+ * connections using the same native LDAP configuration. It is a connection
+ * pool that can adjust the MT connection status and usage based on the
+ * change notifications it receives from the ldap_cachemgr daemon, OR When
+ * the change is detected at config refresh time. When a server status
+ * change (up or down) notification is received or detected, it will
+ * close the MT connections using the server. Or mark them as to-be-closed
+ * and close them when all users are done using them. When a config change
+ * notice is received, it will detach itself and allow a new ns_conn_mgmt be
+ * created for the new configuration. The old config would still be used
+ * by the detached ns_conn_mgmt. Both will be destroyed when all existing
+ * conn_user are done. Any conn_user and MT connection created after the
+ * configuration switch will use the new configuration.
+ *
+ * Note that there's always just one current ns_conn_mgmt. Its usage is
+ * reference counted. Any new conn_user or MT connection referencing
+ * the ns_conn_mgmt adds 1 to the count, any release of the ns_conn_mgmt
+ * decrement the count by 1. The ns_conn_mgmt can not be freed until
+ * the reference count becomes zero.
+ *
+ * Each ns_conn_mgmt references a native LDAP configuration. The config
+ * component of this library always maintains a global configuration. It is
+ * referred to as the current global config. The current ns_conn_mgmt
+ * uses that global config. When an ns_conn_mgmt is detached, or not
+ * longer active/current, the config it uses is no longer the current global
+ * one, which is referred as the per connection management config. When
+ * the ns_conn_mgmt is freed, the config will also be destroyed.
+ */
+
+typedef struct ns_conn_mgmt {
+ mutex_t lock;
+ ns_conn_mgmt_state_t state;
+ pid_t pid; /* process creates the conn_mgmt */
+ thread_t procchg_tid; /* id of the change monitor thread */
+ ns_conn_mt_t *cm_head; /* head of the conn_mt linked list */
+ ns_conn_mt_t *cm_tail; /* tail of the conn_mt linked list */
+ mutex_t cfg_lock; /* lock serializes access to config */
+ ldap_get_chg_cookie_t cfg_cookie; /* used to detect if config changes */
+ ns_config_t *config; /* the native LDAP config being used */
+ char **pservers; /* preferred servers defined in config */
+ uint_t cm_cnt; /* number of MT connection in the pool */
+ uint_t ref_cnt; /* number of reference by conn_MT/conn_user */
+ boolean_t is_nscd; /* running in a nscd ? */
+ boolean_t is_peruser_nscd; /* running in a per-user nscd ? */
+ boolean_t ldap_mt; /* libldap supports multi-threaded client ? */
+ boolean_t do_mt_conn; /* need and able to do MT conn ? */
+ boolean_t shutting_down; /* on the way down ? */
+ boolean_t cfg_reloaded; /* config is not current ? */
+ boolean_t procchg_started; /* change monitor thread started ? */
+ boolean_t procchg_door_call; /* in door call and waiting ? */
+ boolean_t pservers_loaded; /* pservers array is set ? */
+} ns_conn_mgmt_t;
+
+/*
+ * For a connection management and the conn_mt connections it manages, it is
+ * very helpful to know exactly when the Native LDAP configuration changes
+ * and when the status of the configured servers change. If the config
+ * changes, new connection management will be created. If servers go up
+ * or down, conn_mt connections being used need to be dropped or switched.
+ * For processes other than the main nscd, the changes has to be detected
+ * in a less efficient way by libsldap. For the main nscd (not including
+ * peruser nscd), the connection management which has active conn_mt
+ * connections can rely on the ldap_cachemgr daemon to report if there's any
+ * change in servers' status or if the native LDAP configuration has changed.
+ *
+ * The mechanism for reporting of the changes is a door call sent from
+ * libsldap to ldap_cachemgr. The call will not be returned until changes
+ * detected by ldap_cachemgr. When the change info is passed back to
+ * libsldap, the change monitor thread will wake up from the door call
+ * and process the notification. For servers went from up to down, the
+ * associated MT connections will be closed, and then all conn_users'
+ * state will be marked as closing. When a conn_user notices it, the
+ * operations represented by that conn_user will be ended with error
+ * info. When a more preferred server is up, MT connections using
+ * less preferred servers will be marked as closed-when-all-user-done,
+ * so that new connection will be opened and using the preferred server.
+ * A configuration change causes the current connection management and
+ * the configuration it uses to become detached but continually being
+ * used by the old MT connections. Any new MT connection opened will
+ * be put in a new connection management and uses the new configuration
+ * immediately.
+ */
+typedef enum {
+ NS_SERVER_UP = 1,
+ NS_SERVER_DOWN = 2
+} ns_server_status_t;
+
+typedef struct ns_server_status_change {
+ int num_server;
+ boolean_t config_changed;
+ ns_server_status_t *changes; /* array of status change */
+ char **servers; /* array of server */
+} ns_server_status_change_t;
+
+/*
+ * connection management functions
+ */
+ns_conn_mgmt_t *__s_api_conn_mgmt_init();
+int __s_api_setup_mt_ld(LDAP *ld);
+int __s_api_check_mtckey();
+void __s_api_use_prev_conn_mgmt(int, ns_config_t *);
+ns_conn_user_t *__s_api_conn_user_init(int, void *, boolean_t);
+void __s_api_conn_mt_return(ns_conn_user_t *);
+void __s_api_conn_user_free(ns_conn_user_t *);
+int __s_api_conn_mt_add(Connection *con, ns_conn_user_t *, ns_ldap_error_t **);
+int __s_api_conn_mt_get(const char *, const int, const ns_cred_t *,
+ Connection **, ns_ldap_error_t **, ns_conn_user_t *);
+void __s_api_conn_mt_remove(ns_conn_user_t *, int, ns_ldap_error_t **);
+int __s_api_check_libldap_MT_conn_support(ns_conn_user_t *, LDAP *ld,
+ ns_ldap_error_t **);
+void __s_api_conn_mt_close(ns_conn_user_t *, int, ns_ldap_error_t **);
+void __s_api_reinit_conn_mgmt_new_config(ns_config_t *);
+int __s_api_setup_retry_search(ns_conn_user_t **, ns_conn_user_type_t, int *,
+ int *, ns_ldap_error_t **);
+int __s_api_setup_getnext(ns_conn_user_t *, int *, ns_ldap_error_t **);
+void __s_api_shutdown_conn_mgmt();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NS_CONNMGMT_H */
diff --git a/usr/src/lib/libsldap/common/ns_init.c b/usr/src/lib/libsldap/common/ns_init.c
index e9b6842210..58ef523c1f 100644
--- a/usr/src/lib/libsldap/common/ns_init.c
+++ b/usr/src/lib/libsldap/common/ns_init.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -27,25 +27,15 @@
#include "ns_sldap.h"
#include "ns_internal.h"
+#include "ns_connmgmt.h"
#include <syslog.h>
#pragma init(ns_ldap_init)
-thread_key_t ns_mtckey;
-
static void
ns_ldap_init()
{
get_environment(); /* load environment debugging options */
- /*
- * ns_mtckey is needed to allow the sharing of an
- * ldap connection among multiple threads. Used
- * mainly in ns_connect.c.
- */
- if (thr_keycreate(&ns_mtckey, ns_tsd_cleanup) != 0) {
- syslog(LOG_ERR, "libsldap: unable to create the thread "
- "key needed for sharing ldap connections");
- MTperConn = 0;
- }
+ (void) __s_api_conn_mgmt_init();
}
diff --git a/usr/src/lib/libsldap/common/ns_internal.h b/usr/src/lib/libsldap/common/ns_internal.h
index fcf337410b..a7dbf4e6de 100644
--- a/usr/src/lib/libsldap/common/ns_internal.h
+++ b/usr/src/lib/libsldap/common/ns_internal.h
@@ -40,6 +40,7 @@ extern "C" {
#include <lber.h>
#include <ldap.h>
#include "ns_sldap.h"
+#include "ns_cache_door.h"
/*
* INTERNALLY USED CONSTANTS
@@ -90,6 +91,7 @@ extern "C" {
#define CPARATOK ')'
#define BSLTOK '\\'
#define DOORLINESEP "\07"
+#define DOORLINESEP_CHR 0x7
#define COMMASEP ", "
#define SPACESEP " "
#define SEMISEP ";"
@@ -431,6 +433,7 @@ typedef struct ns_config {
boolean_t delete;
mutex_t config_mutex;
int nUse;
+ ldap_get_chg_cookie_t config_cookie;
} ns_config_t;
/*
@@ -515,7 +518,8 @@ typedef enum {
ERROR = 19,
LDAP_ERROR = 20,
GET_ACCT_MGMT_INFO = 21,
- CLEAR_RESULTS = 22
+ CLEAR_RESULTS = 22,
+ REINIT = 23
} ns_state_t;
/*
@@ -553,10 +557,6 @@ typedef struct connection {
boolean_t usedBit; /* true if only used by */
/* one thread and not shared */
/* by other threads */
- boolean_t notAvail; /* not sharable, delete */
- /* when shared == 0 */
- int shared; /* number of threads */
- /* using this connection */
pid_t pid; /* process id */
char *serverAddr;
ns_cred_t *auth;
@@ -593,6 +593,9 @@ struct ns_ldap_list_batch {
struct ns_ldap_cookie *cookie_list;
};
+struct ns_conn_user;
+typedef struct ns_conn_user ns_conn_user_t;
+
/*
* This structure used internally in searches
*/
@@ -670,10 +673,13 @@ typedef struct ns_ldap_cookie {
/* Flag to indicate password less account management is required */
int nopasswd_acct_mgmt;
int err_from_result;
+ ns_conn_user_t *conn_user;
/* BATCH PROCESSING */
ns_ldap_list_batch_t *batch;
boolean_t no_wait;
+ boolean_t reinit_on_retriable_err;
+ int retries;
ns_ldap_result_t **caller_result;
ns_ldap_error_t **caller_errorp;
int *caller_rc;
@@ -731,6 +737,9 @@ char *__s_get_scope_name(ns_config_t *ptr, ScopeType_t type);
char *__s_get_pref_name(PrefOnly_t type);
char *__s_get_searchref_name(ns_config_t *ptr, SearchRef_t type);
char *__s_get_hostcertpath(void);
+void __s_api_free_sessionPool();
+int __s_api_requestServer(const char *request, const char *server,
+ ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType);
/* ************ internal sldap-api functions *********** */
@@ -744,17 +753,38 @@ int __s_api_get_search_DNs_v1(char ***, const char *,
ns_ldap_error_t **);
int __s_api_getConnection(const char *, const int,
const ns_cred_t *, int *,
- Connection **, ns_ldap_error_t **, int, int);
+ Connection **, ns_ldap_error_t **, int, int, ns_conn_user_t *);
char **__s_api_cp2dArray(char **);
void __s_api_free2dArray(char **);
int __s_api_isCtrlSupported(Connection *, char *);
ns_config_t *__ns_ldap_make_config(ns_ldap_result_t *result);
ns_auth_t *__s_api_AuthEnumtoStruct(const EnumAuthType_t i);
-int __s_api_peruser_proc(void);
-int __s_api_nscd_proc(void);
+boolean_t __s_api_peruser_proc(void);
+boolean_t __s_api_nscd_proc(void);
char *dvalue(char *);
char *evalue(char *);
+ns_ldap_error_t *__s_api_make_error(int, char *);
+ns_ldap_error_t *__s_api_copy_error(ns_ldap_error_t *);
+
+/* ************ specific 'Standalone' functions ********** */
+ns_ldap_return_code __s_api_ip2hostname(char *ipaddr, char **hostname);
+struct hostent *__s_api_hostname2ip(const char *name,
+ struct hostent *result,
+ char *buffer,
+ int buflen,
+ int *h_errnop);
+void __s_api_setInitMode();
+void __s_api_unsetInitMode();
+int __s_api_isStandalone(void);
+int __s_api_isInitializing();
+ns_ldap_return_code __s_api_findRootDSE(const char *request,
+ const char *server,
+ const char *addrType,
+ ns_server_info_t *ret,
+ ns_ldap_error_t **error);
+ns_config_t *__s_api_create_config_door_str(char *config,
+ ns_ldap_error_t **errorp);
extern void get_environment();
@@ -767,11 +797,14 @@ int __s_api_get_versiontype(ns_config_t *ptr, char *value,
ParamIndexType *type);
int __s_api_get_profiletype(char *value, ParamIndexType *type);
void __s_api_init_config(ns_config_t *ptr);
+void __s_api_init_config_global(ns_config_t *ptr);
ns_parse_status __s_api_crosscheck(ns_config_t *domainptr, char *errstr,
int check_dn);
ns_config_t *__s_api_create_config(void);
ns_config_t *__s_api_get_default_config(void);
+ns_config_t *__s_api_get_default_config_global(void);
ns_config_t *__s_api_loadrefresh_config();
+ns_config_t *__s_api_loadrefresh_config_global();
void __s_api_destroy_config(ns_config_t *ptr);
int __s_api_get_configtype(ParamIndexType type);
const char *__s_api_get_configname(ParamIndexType type);
@@ -792,7 +825,8 @@ char **__ns_ldap_mapAttributeList(const char *service,
/* internal configuration APIs */
void __ns_ldap_setServer(int set);
ns_ldap_error_t *__ns_ldap_LoadConfiguration();
-ns_ldap_error_t *__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname);
+ns_ldap_error_t *__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname,
+ ns_config_t *new);
ns_ldap_error_t *__ns_ldap_DumpConfiguration(char *filename);
ns_ldap_error_t *__ns_ldap_DumpLdif(char *filename);
int __ns_ldap_cache_ping();
@@ -822,6 +856,8 @@ __ns_ldap_check_all_preq(int foreground,
/* internal un-exposed APIs */
ns_cred_t *__ns_ldap_dupAuth(const ns_cred_t *authp);
+boolean_t __s_api_is_auth_matched(const ns_cred_t *auth1,
+ const ns_cred_t *auth2);
int __s_api_get_SSD_from_SSDtoUse_service(const char *service,
ns_ldap_search_desc_t ***SSDlist,
ns_ldap_error_t **errorp);
@@ -844,6 +880,7 @@ int __s_api_append_default_basedn(
int __s_api_removeServer(const char *server);
void __s_api_removeBadServers(char **server);
void __s_api_free_server_info(ns_server_info_t *sinfo);
+void __s_api_freeConnection(Connection *con);
/* internal referrals APIs */
int __s_api_toFollowReferrals(const int flags,
@@ -885,10 +922,6 @@ int __s_api_sasl_bind_callback(
int __s_api_self_gssapi_only_get(void);
int __s_api_sasl_gssapi_init(void);
-int __s_api_check_MTC_tsd();
-
-/* Multiple threads per connection functions */
-void ns_tsd_cleanup(void *);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libsldap/common/ns_reads.c b/usr/src/lib/libsldap/common/ns_reads.c
index 4d2476fb27..a2d658252d 100644
--- a/usr/src/lib/libsldap/common/ns_reads.c
+++ b/usr/src/lib/libsldap/common/ns_reads.c
@@ -37,10 +37,10 @@
#include <string.h>
#include <strings.h>
-
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
+#include "ns_connmgmt.h"
#define _NIS_FILTER "nisdomain=*"
#define _NIS_DOMAIN "nisdomain"
@@ -862,6 +862,13 @@ __s_api_get_cachemgr_data(const char *type,
#ifdef DEBUG
(void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
#endif
+ /*
+ * We are not going to perform DN to domain mapping
+ * in the Standalone mode
+ */
+ if (__s_api_isStandalone()) {
+ return (-1);
+ }
if (from == NULL || from[0] == '\0' || to == NULL)
return (-1);
@@ -882,7 +889,7 @@ __s_api_get_cachemgr_data(const char *type,
sptr = &space.s_d;
rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
- if (rc != SUCCESS)
+ if (rc != NS_CACHE_SUCCESS)
return (-1);
else
*to = strdup(sptr->ldap_ret.ldap_u.buff);
@@ -905,6 +912,13 @@ __s_api_set_cachemgr_data(const char *type,
#ifdef DEBUG
(void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
#endif
+ /*
+ * We are not going to perform DN to domain mapping
+ * in the Standalone mode
+ */
+ if (__s_api_isStandalone()) {
+ return (-1);
+ }
if ((from == NULL) || (from[0] == '\0') ||
(to == NULL) || (to[0] == '\0'))
@@ -928,7 +942,7 @@ __s_api_set_cachemgr_data(const char *type,
sptr = &space.s_d;
rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
- if (rc != SUCCESS)
+ if (rc != NS_CACHE_SUCCESS)
return (-1);
return (NS_LDAP_SUCCESS);
@@ -1591,7 +1605,7 @@ get_current_session(ns_ldap_cookie_t *cookie)
rc = __s_api_getConnection(NULL, cookie->i_flags,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd,
- cookie->nopasswd_acct_mgmt);
+ cookie->nopasswd_acct_mgmt, cookie->conn_user);
/*
* If password control attached in *cookie->errorp,
@@ -1630,10 +1644,15 @@ get_next_session(ns_ldap_cookie_t *cookie)
cookie->connectionId = -1;
}
+ /* If using a MT connection, return it. */
+ if (cookie->conn_user != NULL &&
+ cookie->conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(cookie->conn_user);
+
rc = __s_api_getConnection(NULL, cookie->i_flags,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd,
- cookie->nopasswd_acct_mgmt);
+ cookie->nopasswd_acct_mgmt, cookie->conn_user);
/*
* If password control attached in *cookie->errorp,
@@ -1671,10 +1690,18 @@ get_referral_session(ns_ldap_cookie_t *cookie)
cookie->connectionId = -1;
}
+ /* set it up to use a connection opened for referral */
+ if (cookie->conn_user != NULL) {
+ /* If using a MT connection, return it. */
+ if (cookie->conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(cookie->conn_user);
+ cookie->conn_user->referral = B_TRUE;
+ }
+
rc = __s_api_getConnection(cookie->refpos->refHost, 0,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd,
- cookie->nopasswd_acct_mgmt);
+ cookie->nopasswd_acct_mgmt, cookie->conn_user);
/*
* If password control attached in *cookie->errorp,
@@ -2057,7 +2084,10 @@ clear_results(ns_ldap_cookie_t *cookie)
{
int rc;
if (cookie->conn != NULL && cookie->conn->ld != NULL &&
- cookie->connectionId != -1 && cookie->msgId != 0) {
+ (cookie->connectionId != -1 ||
+ (cookie->conn_user != NULL &&
+ cookie->conn_user->conn_mt != NULL)) &&
+ cookie->msgId != 0) {
/*
* We need to cleanup the rest of response (if there is such)
* and LDAP abandon is too heavy for LDAP servers, so we will
@@ -2109,6 +2139,7 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
char errstr[MAXERROR];
char *err;
int rc, ret;
+ int rc_save;
ns_ldap_entry_t *nextEntry;
ns_ldap_error_t *error = NULL;
ns_ldap_error_t **errorp;
@@ -2157,6 +2188,45 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
cookie->i_attr);
}
break;
+ case REINIT:
+ /* Check if we've reached MAX retries. */
+ cookie->retries++;
+ if (cookie->retries > NS_LIST_TRY_MAX - 1) {
+ cookie->new_state = LDAP_ERROR;
+ break;
+ }
+
+ /*
+ * Even if we still have retries left, check
+ * if retry is possible.
+ */
+ if (cookie->conn_user != NULL) {
+ int retry;
+ ns_conn_mgmt_t *cmg;
+ cmg = cookie->conn_user->conn_mgmt;
+ retry = cookie->conn_user->retry;
+ if (cmg != NULL && cmg->cfg_reloaded == 1)
+ retry = 1;
+ if (retry == 0) {
+ cookie->new_state = LDAP_ERROR;
+ break;
+ }
+ }
+ /*
+ * Free results if any, reset to the first
+ * search descriptor and start a new session.
+ */
+ if (cookie->resultMsg != NULL) {
+ (void) ldap_msgfree(cookie->resultMsg);
+ cookie->resultMsg = NULL;
+ }
+ (void) __ns_ldap_freeError(&cookie->errorp);
+ (void) __ns_ldap_freeResult(&cookie->result);
+ cookie->sdpos = cookie->sdlist;
+ cookie->err_from_result = 0;
+ cookie->err_rc = 0;
+ cookie->new_state = NEXT_SESSION;
+ break;
case NEXT_SEARCH_DESCRIPTOR:
/* get next search descriptor */
if (cookie->sdpos == NULL) {
@@ -2263,7 +2333,12 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
rc == LDAP_CONNECT_ERROR ||
rc == LDAP_SERVER_DOWN) {
- cookie->new_state = NEXT_SESSION;
+ if (cookie->reinit_on_retriable_err) {
+ cookie->err_rc = rc;
+ cookie->new_state = REINIT;
+ } else
+ cookie->new_state =
+ NEXT_SESSION;
/*
* If not able to reach the
@@ -2279,11 +2354,14 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
* find out sooner when the server
* is up again.
*/
- if (rc == LDAP_CONNECT_ERROR ||
- rc == LDAP_SERVER_DOWN) {
+ if ((rc == LDAP_CONNECT_ERROR ||
+ rc == LDAP_SERVER_DOWN) &&
+ (cookie->conn_user == NULL ||
+ cookie->conn_user->conn_mt ==
+ NULL)) {
ret = __s_api_removeServer(
cookie->conn->serverAddr);
- if (ret == NOSERVER &&
+ if (ret == NS_CACHE_NOSERVER &&
cookie->conn_auth_type
== NS_LDAP_AUTH_NONE) {
/*
@@ -2313,6 +2391,19 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
cookie->connectionId =
-1;
}
+ } else if ((rc == LDAP_CONNECT_ERROR ||
+ rc == LDAP_SERVER_DOWN) &&
+ cookie->conn_user != NULL &&
+ cookie->reinit_on_retriable_err) {
+ /*
+ * MT connection not usable,
+ * close it before REINIT.
+ * rc has already been saved
+ * in cookie->err_rc above.
+ */
+ __s_api_conn_mt_close(
+ cookie->conn_user,
+ rc, &cookie->errorp);
}
break;
}
@@ -2375,8 +2466,10 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
cookie->resultMsg, 1);
break;
}
- if (rc == LDAP_TIMEOUT ||
- rc == LDAP_SERVER_DOWN) {
+ if ((rc == LDAP_TIMEOUT ||
+ rc == LDAP_SERVER_DOWN) &&
+ (cookie->conn_user == NULL ||
+ cookie->conn_user->conn_mt == NULL)) {
if (rc == LDAP_TIMEOUT)
(void) __s_api_removeServer(
cookie->conn->serverAddr);
@@ -2393,7 +2486,31 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
if (rc == LDAP_BUSY ||
rc == LDAP_UNAVAILABLE ||
rc == LDAP_UNWILLING_TO_PERFORM) {
- cookie->new_state = NEXT_SESSION;
+ if (cookie->reinit_on_retriable_err) {
+ cookie->err_rc = rc;
+ cookie->err_from_result = 1;
+ cookie->new_state = REINIT;
+ } else
+ cookie->new_state =
+ NEXT_SESSION;
+ break;
+ }
+ if ((rc == LDAP_CONNECT_ERROR ||
+ rc == LDAP_SERVER_DOWN) &&
+ cookie->reinit_on_retriable_err) {
+ ns_ldap_error_t *errorp = NULL;
+ cookie->err_rc = rc;
+ cookie->err_from_result = 1;
+ cookie->new_state = REINIT;
+ if (cookie->conn_user != NULL)
+ __s_api_conn_mt_close(
+ cookie->conn_user,
+ rc, &errorp);
+ if (errorp != NULL) {
+ (void) __ns_ldap_freeError(
+ &cookie->errorp);
+ cookie->errorp = errorp;
+ }
break;
}
cookie->err_rc = rc;
@@ -2473,8 +2590,10 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
cookie->resultMsg, 1);
break;
}
- if (rc == LDAP_TIMEOUT ||
- rc == LDAP_SERVER_DOWN) {
+ if ((rc == LDAP_TIMEOUT ||
+ rc == LDAP_SERVER_DOWN) &&
+ (cookie->conn_user == NULL ||
+ cookie->conn_user->conn_mt == NULL)) {
if (rc == LDAP_TIMEOUT)
(void) __s_api_removeServer(
cookie->conn->serverAddr);
@@ -2491,7 +2610,31 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
if (rc == LDAP_BUSY ||
rc == LDAP_UNAVAILABLE ||
rc == LDAP_UNWILLING_TO_PERFORM) {
- cookie->new_state = NEXT_SESSION;
+ if (cookie->reinit_on_retriable_err) {
+ cookie->err_rc = rc;
+ cookie->err_from_result = 1;
+ cookie->new_state = REINIT;
+ } else
+ cookie->new_state =
+ NEXT_SESSION;
+ break;
+ }
+ if ((rc == LDAP_CONNECT_ERROR ||
+ rc == LDAP_SERVER_DOWN) &&
+ cookie->reinit_on_retriable_err) {
+ ns_ldap_error_t *errorp = NULL;
+ cookie->err_rc = rc;
+ cookie->err_from_result = 1;
+ cookie->new_state = REINIT;
+ if (cookie->conn_user != NULL)
+ __s_api_conn_mt_close(
+ cookie->conn_user,
+ rc, &errorp);
+ if (errorp != NULL) {
+ (void) __ns_ldap_freeError(
+ &cookie->errorp);
+ cookie->errorp = errorp;
+ }
break;
}
cookie->err_rc = rc;
@@ -2578,6 +2721,8 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
cookie->reflist = NULL;
cookie->new_state =
NEXT_SEARCH_DESCRIPTOR;
+ if (cookie->conn_user != NULL)
+ cookie->conn_user->referral = B_FALSE;
}
break;
case GET_REFERRAL_SESSION:
@@ -2588,6 +2733,7 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
}
break;
case LDAP_ERROR:
+ rc_save = cookie->err_rc;
if (cookie->err_from_result) {
if (cookie->err_rc == LDAP_SERVER_DOWN) {
(void) sprintf(errstr,
@@ -2625,6 +2771,18 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
}
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
+ if (cookie->conn_user != NULL) {
+ if (rc_save == LDAP_SERVER_DOWN ||
+ rc_save == LDAP_CONNECT_ERROR) {
+ /*
+ * MT connection is not usable,
+ * close it.
+ */
+ __s_api_conn_mt_close(cookie->conn_user,
+ rc_save, &cookie->errorp);
+ return (ERROR);
+ }
+ }
return (ERROR);
default:
case ERROR:
@@ -2639,6 +2797,15 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
return (ERROR);
}
+ if (cookie->conn_user != NULL &&
+ cookie->conn_user->bad_mt_conn == B_TRUE) {
+ __s_api_conn_mt_close(cookie->conn_user, 0, NULL);
+ cookie->err_rc = cookie->conn_user->ns_rc;
+ cookie->errorp = cookie->conn_user->ns_error;
+ cookie->conn_user->ns_error = NULL;
+ return (ERROR);
+ }
+
if (cycle == ONE_STEP) {
return (cookie->new_state);
}
@@ -2656,7 +2823,6 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
#endif
}
-
/*
* internal function for __ns_ldap_list
*/
@@ -2674,7 +2840,7 @@ ldap_list(
ns_ldap_error_t **errorp,
int *rcp,
int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
- const void *userdata)
+ const void *userdata, ns_conn_user_t *conn_user)
{
ns_ldap_cookie_t *cookie;
ns_ldap_search_desc_t **sdlist = NULL;
@@ -2695,6 +2861,7 @@ ldap_list(
*rcp = NS_LDAP_MEMORY;
return (NS_LDAP_MEMORY);
}
+ cookie->conn_user = conn_user;
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
@@ -2797,6 +2964,7 @@ ldap_list(
if (batch != NULL) {
cookie->batch = batch;
+ cookie->reinit_on_retriable_err = B_TRUE;
cookie->no_wait = B_TRUE;
(void) search_state_machine(cookie, INIT, 0);
cookie->no_wait = B_FALSE;
@@ -2828,8 +2996,13 @@ ldap_list(
/* Copy results back to user */
rc = cookie->err_rc;
- if (rc != NS_LDAP_SUCCESS)
- *errorp = cookie->errorp;
+ if (rc != NS_LDAP_SUCCESS) {
+ if (conn_user != NULL && conn_user->ns_error != NULL) {
+ *errorp = conn_user->ns_error;
+ conn_user->ns_error = NULL;
+ } else
+ *errorp = cookie->errorp;
+ }
*rResult = cookie->result;
from_result = cookie->err_from_result;
@@ -2848,7 +3021,8 @@ ldap_list(
/*
* __ns_ldap_list performs one or more LDAP searches to a given
* directory server using service search descriptors and schema
- * mapping as appropriate.
+ * mapping as appropriate. The operation may be retried a
+ * couple of times in error situations.
*/
int
@@ -2865,12 +3039,20 @@ __ns_ldap_list(
int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
const void *userdata)
{
- int rc;
- return (ldap_list(NULL, service, filter,
- init_filter_cb, attribute, auth, flags, rResult, errorp, &rc,
- callback, userdata));
-}
+ ns_conn_user_t *cu = NULL;
+ int try_cnt = 0;
+ int rc = NS_LDAP_SUCCESS, trc;
+
+ for (;;) {
+ if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
+ &try_cnt, &rc, errorp) == 0)
+ break;
+ rc = ldap_list(NULL, service, filter, init_filter_cb, attribute,
+ auth, flags, rResult, errorp, &trc, callback, userdata, cu);
+ }
+ return (rc);
+}
/*
* Create and initialize batch for native LDAP lookups
@@ -2904,9 +3086,31 @@ __ns_ldap_list_batch_add(
int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
const void *userdata)
{
- return (ldap_list(batch, service, filter,
- init_filter_cb, attribute, auth, flags, rResult, errorp, rcp,
- callback, userdata));
+ ns_conn_user_t *cu;
+ int rc;
+
+ cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, 0);
+ if (cu == NULL) {
+ if (rcp != NULL)
+ *rcp = NS_LDAP_MEMORY;
+ return (NS_LDAP_MEMORY);
+ }
+
+ rc = ldap_list(batch, service, filter, init_filter_cb, attribute, auth,
+ flags, rResult, errorp, rcp, callback, userdata, cu);
+
+ /*
+ * Free the conn_user if the cookie was not batched. If the cookie
+ * was batched then __ns_ldap_list_batch_end or release will free the
+ * conn_user. The batch API instructs the search_state_machine
+ * to reinit and retry (max 3 times) on retriable LDAP errors.
+ */
+ if (rc != NS_LDAP_SUCCESS && cu != NULL) {
+ if (cu->conn_mt != NULL)
+ __s_api_conn_mt_return(cu);
+ __s_api_conn_user_free(cu);
+ }
+ return (rc);
}
@@ -2920,11 +3124,19 @@ __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
for (c = batch->cookie_list; c != NULL; c = next) {
next = c->next_cookie_in_batch;
+ if (c->conn_user != NULL) {
+ if (c->conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(c->conn_user);
+ __s_api_conn_user_free(c->conn_user);
+ c->conn_user = NULL;
+ }
delete_search_cookie(c);
}
free(batch);
}
+#define LD_USING_STATE(st) \
+ ((st == DO_SEARCH) || (st == MULTI_RESULT) || (st == NEXT_RESULT))
/*
* Process batch. Everytime this function is called it selects an
@@ -2948,6 +3160,7 @@ __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
{
ns_ldap_cookie_t *c, *ptr, **prev;
ns_state_t state;
+ ns_ldap_error_t *errorp = NULL;
int rc;
/* Check if are already done */
@@ -2960,11 +3173,47 @@ __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
batch->next_cookie = c->next_cookie_in_batch;
- if (c->conn->shared > 0) {
- rc = __s_api_check_MTC_tsd();
- if (rc != NS_LDAP_SUCCESS) {
+ /*
+ * Checks the status of the cookie's connection if it needs
+ * to use that connection for ldap_search_ext or ldap_result.
+ * If the connection is no longer good but worth retrying
+ * then reinit the search_state_machine for this cookie
+ * starting from the first search descriptor. REINIT will
+ * clear any leftover results if max retries have not been
+ * reached and redo the search (which may also involve
+ * following referrals again).
+ *
+ * Note that each cookie in the batch will make this
+ * determination when it reaches one of the LD_USING_STATES.
+ */
+ if (LD_USING_STATE(c->new_state) && c->conn_user != NULL) {
+ rc = __s_api_setup_getnext(c->conn_user, &c->err_rc, &errorp);
+ if (rc == LDAP_BUSY || rc == LDAP_UNAVAILABLE ||
+ rc == LDAP_UNWILLING_TO_PERFORM) {
+ if (errorp != NULL) {
+ (void) __ns_ldap_freeError(&c->errorp);
+ c->errorp = errorp;
+ }
+ c->new_state = REINIT;
+ } else if (rc == LDAP_CONNECT_ERROR ||
+ rc == LDAP_SERVER_DOWN) {
+ if (errorp != NULL) {
+ (void) __ns_ldap_freeError(&c->errorp);
+ c->errorp = errorp;
+ }
+ c->new_state = REINIT;
+ /*
+ * MT connection is not usable,
+ * close it before REINIT.
+ */
+ __s_api_conn_mt_close(
+ c->conn_user, rc, NULL);
+ } else if (rc != NS_LDAP_SUCCESS) {
if (rcp != NULL)
*rcp = rc;
+ *c->caller_result = NULL;
+ *c->caller_errorp = errorp;
+ *c->caller_rc = rc;
return (-1);
}
}
@@ -2998,6 +3247,12 @@ __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
ptr = ptr->next_cookie_in_batch;
}
/* Delete cookie and decrement active cookie count */
+ if (c->conn_user != NULL) {
+ if (c->conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(c->conn_user);
+ __s_api_conn_user_free(c->conn_user);
+ c->conn_user = NULL;
+ }
delete_search_cookie(c);
batch->nactive--;
break;
@@ -3045,19 +3300,15 @@ __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
return (rc);
}
-
/*
- * __s_api_find_domainname performs one or more LDAP searches to
+ * find_domainname performs one or more LDAP searches to
* find the value of the nisdomain attribute associated with
- * the input DN
+ * the input DN (with no retry).
*/
static int
-__s_api_find_domainname(
- const char *dn,
- char **domainname,
- const ns_cred_t *cred,
- ns_ldap_error_t **errorp)
+find_domainname(const char *dn, char **domainname, const ns_cred_t *cred,
+ ns_ldap_error_t **errorp, ns_conn_user_t *conn_user)
{
ns_ldap_cookie_t *cookie;
@@ -3075,6 +3326,7 @@ __s_api_find_domainname(
if (cookie == NULL) {
return (NS_LDAP_MEMORY);
}
+ cookie->conn_user = conn_user;
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
@@ -3122,8 +3374,13 @@ __s_api_find_domainname(
/* Copy domain name if found */
rc = cookie->err_rc;
- if (rc != NS_LDAP_SUCCESS)
- *errorp = cookie->errorp;
+ if (rc != NS_LDAP_SUCCESS) {
+ if (conn_user != NULL && conn_user->ns_error != NULL) {
+ *errorp = conn_user->ns_error;
+ conn_user->ns_error = NULL;
+ } else
+ *errorp = cookie->errorp;
+ }
if (cookie->result == NULL)
rc = NS_LDAP_NOTFOUND;
if (rc == NS_LDAP_SUCCESS) {
@@ -3142,19 +3399,44 @@ __s_api_find_domainname(
return (rc);
}
-int
-__ns_ldap_firstEntry(
- const char *service,
- const char *filter,
- int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
- char **realfilter, const void *userdata),
- const char * const *attribute,
- const ns_cred_t *auth,
- const int flags,
- void **vcookie,
- ns_ldap_result_t **result,
- ns_ldap_error_t ** errorp,
- const void *userdata)
+/*
+ * __s_api_find_domainname performs one or more LDAP searches to
+ * find the value of the nisdomain attribute associated with
+ * the input DN (with retry).
+ */
+
+static int
+__s_api_find_domainname(const char *dn, char **domainname,
+ const ns_cred_t *cred, ns_ldap_error_t **errorp)
+{
+ ns_conn_user_t *cu = NULL;
+ int try_cnt = 0;
+ int rc = NS_LDAP_SUCCESS;
+
+ for (;;) {
+ if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
+ &try_cnt, &rc, errorp) == 0)
+ break;
+ rc = find_domainname(dn, domainname, cred, errorp, cu);
+ }
+
+ return (rc);
+}
+
+static int
+firstEntry(
+ const char *service,
+ const char *filter,
+ int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
+ char **realfilter, const void *userdata),
+ const char * const *attribute,
+ const ns_cred_t *auth,
+ const int flags,
+ void **vcookie,
+ ns_ldap_result_t **result,
+ ns_ldap_error_t ** errorp,
+ const void *userdata,
+ ns_conn_user_t *conn_user)
{
ns_ldap_cookie_t *cookie = NULL;
ns_ldap_error_t *error = NULL;
@@ -3235,6 +3517,9 @@ __ns_ldap_firstEntry(
return (NS_LDAP_MEMORY);
}
+ /* identify self as a getent user */
+ cookie->conn_user = conn_user;
+
cookie->sdlist = sdlist;
/* see if need to follow referrals */
@@ -3289,8 +3574,13 @@ __ns_ldap_firstEntry(
/* FALLTHROUGH */
case ERROR:
rc = cookie->err_rc;
- *errorp = cookie->errorp;
- cookie->errorp = NULL;
+ if (conn_user != NULL && conn_user->ns_error != NULL) {
+ *errorp = conn_user->ns_error;
+ conn_user->ns_error = NULL;
+ } else {
+ *errorp = cookie->errorp;
+ cookie->errorp = NULL;
+ }
delete_search_cookie(cookie);
return (rc);
case EXIT:
@@ -3311,12 +3601,38 @@ __ns_ldap_firstEntry(
}
}
+int
+__ns_ldap_firstEntry(
+ const char *service,
+ const char *filter,
+ int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
+ char **realfilter, const void *userdata),
+ const char * const *attribute,
+ const ns_cred_t *auth,
+ const int flags,
+ void **vcookie,
+ ns_ldap_result_t **result,
+ ns_ldap_error_t ** errorp,
+ const void *userdata)
+{
+ ns_conn_user_t *cu = NULL;
+ int try_cnt = 0;
+ int rc = NS_LDAP_SUCCESS;
+
+ for (;;) {
+ if (__s_api_setup_retry_search(&cu, NS_CONN_USER_GETENT,
+ &try_cnt, &rc, errorp) == 0)
+ break;
+ rc = firstEntry(service, filter, init_filter_cb, attribute,
+ auth, flags, vcookie, result, errorp, userdata, cu);
+ }
+ return (rc);
+}
+
/*ARGSUSED2*/
int
-__ns_ldap_nextEntry(
- void *vcookie,
- ns_ldap_result_t **result,
- ns_ldap_error_t ** errorp)
+__ns_ldap_nextEntry(void *vcookie, ns_ldap_result_t **result,
+ ns_ldap_error_t ** errorp)
{
ns_ldap_cookie_t *cookie;
ns_state_t state;
@@ -3326,19 +3642,15 @@ __ns_ldap_nextEntry(
cookie->result = NULL;
*result = NULL;
+ if (cookie->conn_user != NULL) {
+ rc = __s_api_setup_getnext(cookie->conn_user,
+ &cookie->err_rc, errorp);
+ if (rc != NS_LDAP_SUCCESS)
+ return (rc);
+ }
+
state = END_PROCESS_RESULT;
for (;;) {
- /*
- * if the ldap connection being used is shared,
- * ensure the thread-specific data area for setting
- * status is allocated
- */
- if (cookie->conn->shared > 0) {
- rc = __s_api_check_MTC_tsd();
- if (rc != NS_LDAP_SUCCESS)
- return (rc);
- }
-
state = search_state_machine(cookie, state, ONE_STEP);
switch (state) {
case PROCESS_RESULT:
@@ -3384,6 +3696,11 @@ __ns_ldap_endEntry(
*errorp = cookie->errorp;
cookie->errorp = NULL;
+ if (cookie->conn_user != NULL) {
+ if (cookie->conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(cookie->conn_user);
+ __s_api_conn_user_free(cookie->conn_user);
+ }
delete_search_cookie(cookie);
cookie = NULL;
*vcookie = NULL;
@@ -3437,6 +3754,7 @@ __ns_ldap_auth(const ns_cred_t *auth,
int rc = 0;
int do_not_fail_if_new_pwd_reqd = 0;
int nopasswd_acct_mgmt = 0;
+ ns_conn_user_t *conn_user;
#ifdef DEBUG
@@ -3447,9 +3765,17 @@ __ns_ldap_auth(const ns_cred_t *auth,
if (!auth)
return (NS_LDAP_INVALID_PARAM);
+ conn_user = __s_api_conn_user_init(NS_CONN_USER_AUTH,
+ NULL, B_FALSE);
+
rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
auth, &connectionId, &conp, errorp,
- do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt);
+ do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
+ conn_user);
+
+ if (conn_user != NULL)
+ __s_api_conn_user_free(conn_user);
+
if (rc == NS_LDAP_OP_FAILED && *errorp)
(void) __ns_ldap_freeError(errorp);
@@ -5003,18 +5329,11 @@ parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
}
/*
- * __ns_ldap_getAcctMgmt() is called from pam account management stack
- * for retrieving accounting information of users with no user password -
- * eg. rlogin, rsh, etc. This function uses the account management control
- * request to do a search on the server for the user in question. The
- * response control returned from the server is got from the cookie.
- * Input params: username of whose account mgmt information is to be got
- * pointer to hold the parsed account management information
- * Return values: NS_LDAP_SUCCESS on success or appropriate error
- * code on failure
+ * internal function for __ns_ldap_getAcctMgmt()
*/
-int
-__ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
+static int
+getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp,
+ ns_conn_user_t *conn_user)
{
int scope, rc;
char ldapfilter[1024];
@@ -5032,6 +5351,7 @@ __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
cookie = init_search_state_machine();
if (cookie == NULL)
return (NS_LDAP_MEMORY);
+ cookie->conn_user = conn_user;
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(0,
@@ -5139,3 +5459,31 @@ out:
return (rc);
}
+
+/*
+ * __ns_ldap_getAcctMgmt() is called from pam account management stack
+ * for retrieving accounting information of users with no user password -
+ * eg. rlogin, rsh, etc. This function uses the account management control
+ * request to do a search on the server for the user in question. The
+ * response control returned from the server is got from the cookie.
+ * Input params: username of whose account mgmt information is to be got
+ * pointer to hold the parsed account management information
+ * Return values: NS_LDAP_SUCCESS on success or appropriate error
+ * code on failure
+ */
+int
+__ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
+{
+ ns_conn_user_t *cu = NULL;
+ int try_cnt = 0;
+ int rc = NS_LDAP_SUCCESS;
+ ns_ldap_error_t *error = NULL;
+
+ for (;;) {
+ if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
+ &try_cnt, &rc, &error) == 0)
+ break;
+ rc = getAcctMgmt(user, acctResp, cu);
+ }
+ return (rc);
+}
diff --git a/usr/src/lib/libsldap/common/ns_sldap.h b/usr/src/lib/libsldap/common/ns_sldap.h
index 20254cbf86..b5ed277201 100644
--- a/usr/src/lib/libsldap/common/ns_sldap.h
+++ b/usr/src/lib/libsldap/common/ns_sldap.h
@@ -487,6 +487,169 @@ typedef struct ns_ldap_objectclass_map {
typedef struct ns_ldap_list_batch ns_ldap_list_batch_t;
/*
+ * The type of standalone configuration specified by a client application.
+ * The meaning of the requests is as follows:
+ *
+ * NS_CACHEMGR: libsldap will request all the configuration via door_call(3C)
+ * to ldap_cachemgr.
+ * NS_LDAP_SERVER: the consumer application has specified a directory server
+ * to communicate to.
+ * NS_PREDEFINED: reserved for internal use
+ */
+typedef enum {
+ NS_CACHEMGR = 0,
+ NS_LDAP_SERVER
+} ns_standalone_request_type_t;
+
+/*
+ * This structure describes an LDAP server specified by a client application.
+ */
+typedef struct ns_dir_server {
+ char *server; /* A directory server's IP */
+ uint16_t port; /* A directory server's port. */
+ /* Default value is 389 */
+ char *domainName; /* A domain name being served */
+ /* by the specified server. */
+ /* Default value is the local */
+ /* domain's name */
+ char *profileName; /* A DUAProfile's name. */
+ /* Default value is 'default' */
+ ns_auth_t *auth; /* Authentication information used */
+ /* during subsequent connections */
+ char *cred; /* A credential level to be used */
+ /* along with the authentication info */
+ char *host_cert_path; /* A path to the certificate database */
+ /* Default is '/vat/ldap' */
+ char *bind_dn; /* A bind DN to be used during */
+ /* subsequent LDAP Bind requests */
+ char *bind_passwd; /* A bind password to be used during */
+ /* subsequent LDAP Bind requests */
+} ns_dir_server_t;
+
+/*
+ * This structure contains information describing an LDAP server.
+ */
+typedef struct ns_standalone_conf {
+ union {
+ ns_dir_server_t server;
+ void *predefined_conf; /* Reserved for internal use */
+ } ds_profile; /* A type of the configuration */
+
+#define SA_SERVER ds_profile.server.server
+#define SA_PORT ds_profile.server.port
+#define SA_DOMAIN ds_profile.server.domainName
+#define SA_PROFILE_NAME ds_profile.server.profileName
+#define SA_AUTH ds_profile.server.auth
+#define SA_CRED ds_profile.server.cred
+#define SA_CERT_PATH ds_profile.server.host_cert_path
+#define SA_BIND_DN ds_profile.server.bind_dn
+#define SA_BIND_PWD ds_profile.server.bind_passwd
+
+ ns_standalone_request_type_t type;
+} ns_standalone_conf_t;
+
+/*
+ * This function "informs" libsldap that a client application has specified
+ * a directory to use. The function obtains a DUAProfile, credentials,
+ * and naming context. During all further operations on behalf
+ * of the application requested a standalone schema libsldap will use
+ * the information obtained by __ns_ldap_initStandalone() instead of
+ * door_call(3C)ing ldap_cachemgr(1M).
+ *
+ * conf
+ * A structure describing where and in which way to obtain all the
+ * configuration describing how to communicate to a choosen LDAP directory.
+ *
+ * errorp
+ * An error object describing an error occured.
+ */
+ns_ldap_return_code __ns_ldap_initStandalone(
+ const ns_standalone_conf_t *conf,
+ ns_ldap_error_t **errorp);
+
+/*
+ * This function obtains the directory's base DN and a DUAProfile
+ * from a specified server.
+ *
+ * server
+ * Specifies the selected directory sever.
+ *
+ * cred
+ * Contains an authentication information and credential required to
+ * establish a connection.
+ *
+ * config
+ * If not NULL, a new configuration basing on a DUAProfile specified in the
+ * server parameter will be create and returned.
+ *
+ * baseDN
+ * If not NULL, the directory's base DN will be returned.
+ *
+ * error
+ * Describes an error, if any.
+ */
+ns_ldap_return_code __ns_ldap_getConnectionInfoFromDUA(
+ const ns_dir_server_t *server,
+ const ns_cred_t *cred,
+ char **config, char **baseDN,
+ ns_ldap_error_t **error);
+
+#define SA_PROHIBIT_FALLBACK 0
+#define SA_ALLOW_FALLBACK 1
+
+#define DONT_SAVE_NSCONF 0
+#define SAVE_NSCONF 1
+
+/*
+ * This function obtains the root DSE from a specified server.
+ *
+ * server_addr
+ * An adress of a server to be connected to.
+ *
+ * rootDSE
+ * A buffer containing the root DSE in the ldap_cachmgr door call format.
+ *
+ * errorp
+ * Describes an error, if any.
+ *
+ * anon_fallback
+ * If set to 1 and establishing a connection fails, __s_api_getRootDSE()
+ * will try once again using anonymous credentials.
+ */
+ns_ldap_return_code __ns_ldap_getRootDSE(
+ const char *server_addr,
+ char **rootDSE,
+ ns_ldap_error_t **errorp,
+ int anon_fallback);
+
+/*
+ * This function iterates through the list of the configured LDAP servers
+ * and "pings" those which are marked as removed or if any error occurred
+ * during the previous receiving of the server's root DSE. If the
+ * function is able to reach such a server and get its root DSE, it
+ * marks the server as on-line. Otherwise, the server's status is set
+ * to "Error".
+ * For each server the function tries to connect to, it fires up
+ * a separate thread and then waits until all the threads finish.
+ * The function returns NS_LDAP_INTERNAL if the Standalone mode was not
+ * initialized or was canceled prior to an invocation of
+ * __ns_ldap_pingOfflineServers().
+ */
+ns_ldap_return_code __ns_ldap_pingOfflineServers(void);
+
+/*
+ * This function cancels the Standalone mode and destroys the list of root DSEs.
+ */
+void __ns_ldap_cancelStandalone(void);
+/*
+ * This function initializes an ns_auth_t structure provided by a caller
+ * according to a specified authentication mechanism.
+ */
+ns_ldap_return_code __ns_ldap_initAuth(const char *auth_mech,
+ ns_auth_t *auth,
+ ns_ldap_error_t **errorp);
+
+/*
* Simplified LDAP Naming APIs
*/
int __ns_ldap_list(
diff --git a/usr/src/lib/libsldap/common/ns_standalone.c b/usr/src/lib/libsldap/common/ns_standalone.c
new file mode 100644
index 0000000000..eb49756c33
--- /dev/null
+++ b/usr/src/lib/libsldap/common/ns_standalone.c
@@ -0,0 +1,2451 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define __STANDALONE_MODULE__
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <strings.h>
+
+#include <thread.h>
+
+#include <nsswitch.h>
+#include <nss_dbdefs.h>
+#include <nss.h>
+
+#include "ns_cache_door.h"
+#include "ns_internal.h"
+
+typedef enum {
+ INFO_SERVER_JUST_INITED = -1,
+ INFO_SERVER_UNKNOWN = 0,
+ INFO_SERVER_CONNECTING = 1,
+ INFO_SERVER_UP = 2,
+ INFO_SERVER_ERROR = 3,
+ INFO_SERVER_REMOVED = 4
+} dir_server_status_t;
+
+typedef enum {
+ INFO_STATUS_NEW = 2,
+ INFO_STATUS_OLD = 3
+} dir_server_info_t;
+
+typedef struct dir_server {
+ char *ip;
+ char **controls;
+ char **saslMech;
+ dir_server_status_t status;
+ mutex_t updateStatus;
+ dir_server_info_t info;
+} dir_server_t;
+
+typedef struct dir_server_list {
+ dir_server_t **nsServers;
+
+ rwlock_t listDestroyLock;
+} dir_server_list_t;
+
+struct {
+ /* The local list of the directory servers' root DSEs. */
+ dir_server_list_t *list;
+ /* The flag indicating if libsldap is in the 'Standalone' mode. */
+ int standalone;
+ /*
+ * The mutex ensuring that only one thread performs
+ * the initialization of the list.
+ */
+ mutex_t listReplaceLock;
+ /*
+ * A flag indicating that a particular thread is
+ * in the 'ldap_cachemgr' mode. It is stored by thread as
+ * a thread specific data.
+ */
+ const int initFlag;
+ /*
+ * A thread specific key storing
+ * the the 'ldap_cachemgr' mode indicator.
+ */
+ thread_key_t standaloneInitKey;
+} dir_servers = {NULL, 0, DEFAULTMUTEX, '1'};
+
+typedef struct switchDatabase {
+ char *conf;
+ uint32_t alloced;
+} switch_database_t;
+
+static thread_key_t switchConfigKey;
+
+#pragma init(createStandaloneKey)
+
+#define DONT_INCLUDE_ATTR_NAMES 0
+#define INCLUDE_ATTR_NAMES 1
+#define IS_PROFILE 1
+#define NOT_PROFILE 0
+/* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */
+#define MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12)
+
+static
+void
+switch_conf_disposer(void *data)
+{
+ switch_database_t *localData = (switch_database_t *)data;
+
+ free(localData->conf);
+ free(localData);
+}
+
+/*
+ * This function initializes an indication that a thread obtaining a root DSE
+ * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap
+ * will not invoke the __s_api_requestServer function. Instead, the library
+ * will establish a connection to the server specified by
+ * the __ns_ldap_getRootDSE function.
+ * Since ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time
+ * and we do not want to affect a thread obtaining a DUAProfile,
+ * the 'ldap_cachemgr' mode is thread private.
+ * In addition, this function creates a key holding temporary configuration
+ * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB"
+ * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()).
+ */
+static
+void
+createStandaloneKey()
+{
+ if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) {
+ syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
+ "key needed for sharing ldap connections"));
+ }
+ if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) {
+ syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
+ "key containing current nsswitch configuration"));
+ }
+}
+
+/*
+ * This function sets the 'ldap_cachemgr' mode indication.
+ */
+void
+__s_api_setInitMode()
+{
+ (void) thr_setspecific(dir_servers.standaloneInitKey,
+ (void *) &dir_servers.initFlag);
+}
+
+/*
+ * This function unset the 'ldap_cachemgr' mode indication.
+ */
+void
+__s_api_unsetInitMode()
+{
+ (void) thr_setspecific(dir_servers.standaloneInitKey, NULL);
+}
+
+/*
+ * This function checks if the 'ldap_cachemgr' mode indication is set.
+ */
+int
+__s_api_isInitializing() {
+ int *flag = NULL;
+
+ (void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag);
+
+ return (flag != NULL && *flag == dir_servers.initFlag);
+}
+
+/*
+ * This function checks if the process runs in the 'Standalone' mode.
+ * In this mode libsldap will check the local, process private list of root DSEs
+ * instead of requesting them via a door call to ldap_cachemgr.
+ */
+int
+__s_api_isStandalone()
+{
+ int mode;
+
+ (void) mutex_lock(&dir_servers.listReplaceLock);
+ mode = dir_servers.standalone;
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+
+ return (mode);
+}
+
+
+static
+int
+remove_ldap(char *dst, char *src, int dst_buf_len)
+{
+ int i = 0;
+
+ if (strlen(src) >= dst_buf_len)
+ return (0);
+
+ while (*src != '\0') {
+ /* Copy up to one space from source. */
+ if (isspace(*src)) {
+ dst[i++] = *src;
+ while (isspace(*src))
+ src++;
+ }
+
+ /* If not "ldap", just copy. */
+ if (strncmp(src, "ldap", 4) != 0) {
+ while (!isspace(*src)) {
+ dst[i++] = *src++;
+ /* At the end of string? */
+ if (dst[i-1] == '\0')
+ return (1);
+ }
+ /* Copy up to one space from source. */
+ if (isspace(*src)) {
+ dst[i++] = *src;
+ while (isspace(*src))
+ src++;
+ }
+ /* Copy also the criteria section */
+ if (*src == '[')
+ while (*src != ']') {
+ dst[i++] = *src++;
+ /* Shouln't happen if format is right */
+ if (dst[i-1] == '\0')
+ return (1);
+ }
+ }
+
+ /* If next part is ldap, skip over it ... */
+ if (strncmp(src, "ldap", 4) == 0) {
+ if (isspace(*(src+4)) || *(src+4) == '\0') {
+ src += 4;
+ while (isspace(*src))
+ src++;
+ if (*src == '[') {
+ while (*src++ != ']') {
+ /*
+ * See comment above about
+ * correct format.
+ */
+ if (*src == '\0') {
+ dst[i++] = '\0';
+ return (1);
+ }
+ }
+ }
+ while (isspace(*src))
+ src++;
+ }
+ }
+ if (*src == '\0')
+ dst[i++] = '\0';
+ }
+
+ return (1);
+}
+
+static
+char *
+get_db(const char *db_name)
+{
+ char *ptr;
+ switch_database_t *hostService = NULL;
+ FILE *fp = fopen(__NSW_CONFIG_FILE, "rF");
+ char *linep, line[NSS_BUFSIZ];
+
+ if (fp == NULL) {
+ syslog(LOG_WARNING, gettext("libsldap: can not read %s"),
+ __NSW_CONFIG_FILE);
+ return (NULL);
+ }
+
+ while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) {
+ while (isspace(*linep)) {
+ ++linep;
+ }
+ if (*linep == '#') {
+ continue;
+ }
+ if (strncmp(linep, db_name, strlen(db_name)) != 0) {
+ continue;
+ }
+ if ((linep = strchr(linep, ':')) != NULL) {
+ if (linep[strlen(linep) - 1] == '\n') {
+ linep[strlen(linep) - 1] = '\0';
+ }
+
+ while (isspace(*++linep))
+ ;
+
+ if ((ptr = strchr(linep, '#')) != NULL) {
+ while (--ptr >= linep && isspace(*ptr))
+ ;
+ *(ptr + 1) = '\0';
+ }
+
+ if (strlen(linep) == 0) {
+ continue;
+ }
+ break;
+ }
+ }
+
+ (void) fclose(fp);
+
+ if (linep == NULL) {
+ syslog(LOG_WARNING,
+ gettext("libsldap: the %s database "
+ "is missing from %s"),
+ db_name,
+ __NSW_CONFIG_FILE);
+ return (NULL);
+ }
+
+ (void) thr_getspecific(switchConfigKey, (void **) &hostService);
+ if (hostService == NULL) {
+ hostService = calloc(1, sizeof (switch_database_t));
+ if (hostService == NULL) {
+ return (NULL);
+ }
+ (void) thr_setspecific(switchConfigKey, hostService);
+ }
+
+ /*
+ * In a long-living process threads can perform several
+ * getXbyY requests. And the windows between those requests
+ * can be long. The nsswitch configuration can change from time
+ * to time. So instead of allocating/freeing memory every time
+ * the API is called, reallocate memory only when the current
+ * configuration for the database being used is longer than
+ * the previous one.
+ */
+ if (strlen(linep) >= hostService->alloced) {
+ ptr = (char *)realloc((void *)hostService->conf,
+ strlen(linep) + 1);
+ if (ptr == NULL) {
+ free((void *)hostService->conf);
+ hostService->conf = NULL;
+ hostService->alloced = 0;
+ return (NULL);
+ }
+ bzero(ptr, strlen(linep) + 1);
+ hostService->conf = ptr;
+ hostService->alloced = strlen(linep) + 1;
+ }
+
+ if (remove_ldap(hostService->conf, linep, hostService->alloced))
+ return (hostService->conf);
+ else
+ return (NULL);
+}
+
+static
+void
+_initf_ipnodes(nss_db_params_t *p)
+{
+ char *services = get_db("ipnodes");
+
+ p->name = NSS_DBNAM_IPNODES;
+ p->flags |= NSS_USE_DEFAULT_CONFIG;
+ p->default_config = services == NULL ? "" : services;
+}
+
+static
+void
+_initf_hosts(nss_db_params_t *p)
+{
+ char *services = get_db("hosts");
+
+ p->name = NSS_DBNAM_HOSTS;
+ p->flags |= NSS_USE_DEFAULT_CONFIG;
+ p->default_config = services == NULL ? "" : services;
+}
+
+/*
+ * This function is an analog of the standard gethostbyaddr_r()
+ * function with an exception that it removes the 'ldap' back-end
+ * (if any) from the host/ipnodes nsswitch's databases and then
+ * looks up using remaining back-ends.
+ */
+static
+struct hostent *
+_filter_gethostbyaddr_r(const char *addr, int len, int type,
+ struct hostent *result, char *buffer, int buflen,
+ int *h_errnop)
+{
+ DEFINE_NSS_DB_ROOT(db_root_hosts);
+ DEFINE_NSS_DB_ROOT(db_root_ipnodes);
+ nss_XbyY_args_t arg;
+ nss_status_t res;
+ int (*str2ent)();
+ void (*nss_initf)();
+ nss_db_root_t *nss_db_root;
+ int dbop;
+
+ switch (type) {
+ case AF_INET:
+ str2ent = str2hostent;
+ nss_initf = _initf_hosts;
+ nss_db_root = &db_root_hosts;
+ dbop = NSS_DBOP_HOSTS_BYADDR;
+ break;
+ case AF_INET6:
+ str2ent = str2hostent6;
+ nss_initf = _initf_ipnodes;
+ nss_db_root = &db_root_ipnodes;
+ dbop = NSS_DBOP_IPNODES_BYADDR;
+ default:
+ return (NULL);
+ }
+
+ NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
+
+ arg.key.hostaddr.addr = addr;
+ arg.key.hostaddr.len = len;
+ arg.key.hostaddr.type = type;
+ arg.stayopen = 0;
+ arg.h_errno = NETDB_SUCCESS;
+
+ res = nss_search(nss_db_root, nss_initf, dbop, &arg);
+ arg.status = res;
+ *h_errnop = arg.h_errno;
+ return (struct hostent *)NSS_XbyY_FINI(&arg);
+}
+
+/*
+ * This routine is an analog of gethostbyaddr_r().
+ * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity
+ * prior to querying the name services.
+ * If the buffer is not big enough to accommodate a returning data,
+ * NULL is returned and h_errnop is set to TRY_AGAIN.
+ */
+struct hostent *
+__s_api_hostname2ip(const char *name,
+ struct hostent *result, char *buffer, int buflen,
+ int *h_errnop)
+{
+ DEFINE_NSS_DB_ROOT(db_root_ipnodes);
+ DEFINE_NSS_DB_ROOT(db_root_hosts);
+ nss_XbyY_args_t arg;
+ nss_status_t res;
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ if (inet_pton(AF_INET, name, &addr) > 0) {
+ if (buflen < strlen(name) + 1 +
+ sizeof (char *) * 2 + /* The h_aliases member */
+ sizeof (struct in_addr) +
+ sizeof (struct in_addr *) * 2) {
+ *h_errnop = TRY_AGAIN;
+ return (NULL);
+ }
+
+ result->h_addrtype = AF_INET;
+ result->h_length = sizeof (struct in_addr);
+ (void) strncpy(buffer, name, buflen);
+
+ result->h_addr_list = (char **)ROUND_UP(
+ buffer + strlen(name) + 1,
+ sizeof (char *));
+ result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
+ sizeof (char *));
+ result->h_aliases[0] = buffer;
+ result->h_aliases[1] = NULL;
+ bcopy(&addr,
+ buffer + buflen - sizeof (struct in_addr),
+ sizeof (struct in_addr));
+ result->h_addr_list[0] = buffer + buflen -
+ sizeof (struct in_addr);
+ result->h_addr_list[1] = NULL;
+ result->h_aliases = result->h_addr_list;
+ result->h_name = buffer;
+
+ *h_errnop = NETDB_SUCCESS;
+ return (result);
+ }
+ if (inet_pton(AF_INET6, name, &addr6) > 0) {
+ if (buflen < strlen(name) + 1 +
+ sizeof (char *) * 2 + /* The h_aliases member */
+ sizeof (struct in6_addr) +
+ sizeof (struct in6_addr *) * 2) {
+ *h_errnop = TRY_AGAIN;
+ return (NULL);
+ }
+
+ result->h_addrtype = AF_INET6;
+ result->h_length = sizeof (struct in6_addr);
+ (void) strncpy(buffer, name, buflen);
+
+ result->h_addr_list = (char **)ROUND_UP(
+ buffer + strlen(name) + 1,
+ sizeof (char *));
+ result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
+ sizeof (char *));
+ result->h_aliases[0] = buffer;
+ result->h_aliases[1] = NULL;
+ bcopy(&addr6,
+ buffer + buflen - sizeof (struct in6_addr),
+ sizeof (struct in6_addr));
+ result->h_addr_list[0] = buffer + buflen -
+ sizeof (struct in6_addr);
+ result->h_addr_list[1] = NULL;
+ result->h_aliases = result->h_addr_list;
+ result->h_name = buffer;
+
+ *h_errnop = NETDB_SUCCESS;
+ return (result);
+ }
+
+ NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
+
+ arg.key.name = name;
+ arg.stayopen = 0;
+ arg.h_errno = NETDB_SUCCESS;
+
+ res = nss_search(&db_root_ipnodes, _initf_ipnodes,
+ NSS_DBOP_IPNODES_BYNAME, &arg);
+ if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) {
+ arg.h_errno = NETDB_SUCCESS;
+ res = nss_search(&db_root_hosts, _initf_hosts,
+ NSS_DBOP_HOSTS_BYNAME, &arg);
+ }
+ arg.status = res;
+ *h_errnop = arg.h_errno;
+ return ((struct hostent *)NSS_XbyY_FINI(&arg));
+}
+
+/*
+ * Convert an IP to a host name.
+ */
+ns_ldap_return_code
+__s_api_ip2hostname(char *ipaddr, char **hostname) {
+ struct in_addr in;
+ struct in6_addr in6;
+ struct hostent *hp = NULL, hostEnt;
+ char buffer[NSS_BUFLEN_HOSTS];
+ int buflen = NSS_BUFLEN_HOSTS;
+ char *start = NULL,
+ *end = NULL,
+ delim = '\0';
+ char *port = NULL,
+ *addr = NULL;
+ int errorNum = 0,
+ len = 0;
+
+ if (ipaddr == NULL || hostname == NULL)
+ return (NS_LDAP_INVALID_PARAM);
+ *hostname = NULL;
+ if ((addr = strdup(ipaddr)) == NULL)
+ return (NS_LDAP_MEMORY);
+
+ if (addr[0] == '[') {
+ /*
+ * Assume it's [ipv6]:port
+ * Extract ipv6 IP
+ */
+ start = &addr[1];
+ if ((end = strchr(addr, ']')) != NULL) {
+ *end = '\0';
+ delim = ']';
+ if (*(end + 1) == ':')
+ /* extract port */
+ port = end + 2;
+ } else {
+ free(addr);
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ } else if ((end = strchr(addr, ':')) != NULL) {
+ /* assume it's ipv4:port */
+ *end = '\0';
+ delim = ':';
+ start = addr;
+ port = end + 1;
+ } else
+ /* No port */
+ start = addr;
+
+
+ if (inet_pton(AF_INET, start, &in) == 1) {
+ /* IPv4 */
+ hp = _filter_gethostbyaddr_r((char *)&in,
+ sizeof (in.s_addr),
+ AF_INET,
+ &hostEnt,
+ buffer,
+ buflen,
+ &errorNum);
+ if (hp && hp->h_name) {
+ /* hostname + '\0' */
+ len = strlen(hp->h_name) + 1;
+ if (port)
+ /* ':' + port */
+ len += strlen(port) + 1;
+ if ((*hostname = malloc(len)) == NULL) {
+ free(addr);
+ return (NS_LDAP_MEMORY);
+ }
+
+ if (port)
+ (void) snprintf(*hostname, len, "%s:%s",
+ hp->h_name, port);
+ else
+ (void) strlcpy(*hostname, hp->h_name, len);
+
+ free(addr);
+ return (NS_LDAP_SUCCESS);
+ } else {
+ free(addr);
+ return (NS_LDAP_NOTFOUND);
+ }
+ } else if (inet_pton(AF_INET6, start, &in6) == 1) {
+ /* IPv6 */
+ hp = _filter_gethostbyaddr_r((char *)&in6,
+ sizeof (in6.s6_addr),
+ AF_INET6,
+ &hostEnt,
+ buffer,
+ buflen,
+ &errorNum);
+ if (hp && hp->h_name) {
+ /* hostname + '\0' */
+ len = strlen(hp->h_name) + 1;
+ if (port)
+ /* ':' + port */
+ len += strlen(port) + 1;
+ if ((*hostname = malloc(len)) == NULL) {
+ free(addr);
+ return (NS_LDAP_MEMORY);
+ }
+
+ if (port)
+ (void) snprintf(*hostname, len, "%s:%s",
+ hp->h_name, port);
+ else
+ (void) strlcpy(*hostname, hp->h_name, len);
+
+ free(addr);
+ return (NS_LDAP_SUCCESS);
+ } else {
+ free(addr);
+ return (NS_LDAP_NOTFOUND);
+ }
+ } else {
+ /*
+ * A hostname
+ * Return it as is
+ */
+ if (end)
+ *end = delim;
+ *hostname = addr;
+ return (NS_LDAP_SUCCESS);
+ }
+}
+
+/*
+ * This function obtains data returned by an LDAP search request and puts it
+ * in a string in the ldap_cachmgr(1) door call format.
+ *
+ * INPUT:
+ * ld - a pointer to an LDAP structure used for a search operation,
+ * result_msg - a pointer to an LDAPMessage returned by the search,
+ * include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will
+ * contain attribute names.
+ * Otherwise, only values will be return.
+ *
+ * OUTPUT:
+ * a buffer containing server info in the following format:
+ * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
+ * Should be free'ed by the caller.
+ */
+static
+ns_ldap_return_code
+convert_to_door_line(LDAP* ld,
+ LDAPMessage *result_msg,
+ int include_names,
+ int is_profile,
+ char **door_line)
+{
+ uint32_t total_length = 0, attr_len = 0, i;
+ LDAPMessage *e;
+ char *a, **vals;
+ BerElement *ber;
+ int seen_objectclass = 0, rewind = 0;
+
+ if (!door_line) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ *door_line = NULL;
+
+ if ((e = ldap_first_entry(ld, result_msg)) == NULL) {
+ return (NS_LDAP_NOTFOUND);
+ }
+
+ /* calculate length of received data */
+ for (a = ldap_first_attribute(ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(ld, e, ber)) {
+
+ if ((vals = ldap_get_values(ld, e, a)) != NULL) {
+ for (i = 0; vals[i] != NULL; i++) {
+ total_length += (include_names ?
+ strlen(a) : 0) +
+ strlen(vals[i]) +
+ strlen(DOORLINESEP) +1;
+ }
+ ldap_value_free(vals);
+ }
+ ldap_memfree(a);
+ }
+ if (ber != NULL) {
+ ber_free(ber, 0);
+ }
+
+ if (total_length == 0) {
+ return (NS_LDAP_NOTFOUND);
+ }
+
+ /* copy the data */
+ /* add 1 for the last '\0' */
+ *door_line = (char *)malloc(total_length + 1);
+ if (*door_line == NULL) {
+ return (NS_LDAP_MEMORY);
+ }
+
+ /* make it an empty string first */
+ **door_line = '\0';
+ a = ldap_first_attribute(ld, e, &ber);
+ while (a != NULL) {
+ if (is_profile) {
+ /*
+ * If we're processing DUAConfigProfile, we need to make
+ * sure we put objectclass attribute first.
+ * __s_api_create_config_door_str depends on that.
+ */
+ if (seen_objectclass) {
+ if (strcasecmp(a, "objectclass") == 0) {
+ /* Skip objectclass now. */
+ a = ldap_next_attribute(ld, e, ber);
+ continue;
+ }
+ } else {
+ if (strcasecmp(a, "objectclass") == 0) {
+ seen_objectclass = 1;
+ rewind = 1;
+ } else {
+ /* Skip all but objectclass first. */
+ a = ldap_next_attribute(ld, e, ber);
+ continue;
+ }
+ }
+ }
+
+ if ((vals = ldap_get_values(ld, e, a)) != NULL) {
+ for (i = 0; vals[i] != NULL; i++) {
+ if (include_names) {
+ attr_len += strlen(a);
+ }
+ attr_len += strlen(vals[i]) +
+ strlen(DOORLINESEP) + 2;
+ if (include_names) {
+ (void) snprintf(*door_line +
+ strlen(*door_line),
+ attr_len,
+ "%s=%s%s",
+ a, vals[i],
+ DOORLINESEP);
+ } else {
+ (void) snprintf(*door_line +
+ strlen(*door_line),
+ attr_len,
+ "%s%s",
+ vals[i],
+ DOORLINESEP);
+ }
+ }
+ ldap_value_free(vals);
+ }
+ ldap_memfree(a);
+
+ /* Rewind */
+ if (rewind) {
+ if (ber != NULL) {
+ ber_free(ber, 0);
+ }
+ a = ldap_first_attribute(ld, e, &ber);
+ rewind = 0;
+ } else {
+ a = ldap_next_attribute(ld, e, ber);
+ }
+ }
+ if (ber != NULL) {
+ ber_free(ber, 0);
+ }
+
+ if (e != result_msg) {
+ (void) ldap_msgfree(e);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This function looks up the base DN of a directory serving
+ * a specified domain name.
+ *
+ * INPUT:
+ * ld - a pointer to an LDAP structure used for the search operation,
+ * domain_name - the name of a domain.
+ *
+ * OUTPUT:
+ * a buffer containing a directory's base DN found.
+ * Should be free'ed by the caller.
+ */
+static
+ns_ldap_return_code
+getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn)
+{
+ struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
+ char *attrs[2], *DNlist, *rest, *ptr;
+ char filter[BUFSIZ], *a = NULL;
+ int ldap_rc;
+ LDAPMessage *resultMsg = NULL;
+ ns_ldap_return_code ret_code;
+
+ /* Get the whole list of naming contexts residing on the server */
+ attrs[0] = "namingcontexts";
+ attrs[1] = NULL;
+ ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
+ attrs, 0, NULL, NULL, &tv, 0, &resultMsg);
+ switch (ldap_rc) {
+ /* If successful, the root DSE was found. */
+ case LDAP_SUCCESS:
+ break;
+ /*
+ * If the root DSE was not found, the server does
+ * not comply with the LDAP v3 protocol.
+ */
+ default:
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ return (NS_LDAP_OP_FAILED);
+ break;
+ }
+
+ if ((ret_code = convert_to_door_line(ld,
+ resultMsg,
+ DONT_INCLUDE_ATTR_NAMES,
+ NOT_PROFILE,
+ &DNlist)) != NS_LDAP_SUCCESS) {
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+ return (ret_code);
+ }
+
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ if (DNlist == NULL ||
+ (ptr = strtok_r(DNlist, DOORLINESEP, &rest)) == NULL) {
+ return (NS_LDAP_NOTFOUND);
+ }
+ attrs[0] = "dn";
+ do {
+ /*
+ * For each context try to find a NIS domain object
+ * which 'nisdomain' attribute's value matches the domain name
+ */
+ (void) snprintf(filter,
+ BUFSIZ,
+ "(&(objectclass=nisDomainObject)"
+ "(nisdomain=%s))",
+ domain_name);
+ ldap_rc = ldap_search_ext_s(ld,
+ ptr,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ attrs,
+ 0,
+ NULL,
+ NULL,
+ &tv,
+ 0,
+ &resultMsg);
+ if (ldap_rc != LDAP_SUCCESS) {
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+ continue;
+ }
+ if ((a = ldap_get_dn(ld, resultMsg)) != NULL) {
+ *dir_base_dn = strdup(a);
+ ldap_memfree(a);
+
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ if (!*dir_base_dn) {
+ free(DNlist);
+ return (NS_LDAP_MEMORY);
+ }
+ break;
+ }
+
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+ } while (ptr = strtok_r(NULL, DOORLINESEP, &rest));
+
+ free(DNlist);
+
+ if (!*dir_base_dn) {
+ return (NS_LDAP_NOTFOUND);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This function parses the results of a search operation
+ * requesting a DUAProfile.
+ *
+ * INPUT:
+ * ld - a pointer to an LDAP structure used for the search operation,
+ * dir_base_dn - the name of a directory's base DN,
+ * profile_name - the name of a DUAProfile to be obtained.
+ *
+ * OUTPUT:
+ * a buffer containing the DUAProfile in the following format:
+ * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
+ * Should be free'ed by the caller.
+ */
+static
+ns_ldap_return_code
+getDUAProfile(LDAP *ld,
+ const char *dir_base_dn,
+ const char *profile_name,
+ char **profile)
+{
+ char searchBaseDN[BUFSIZ], filter[BUFSIZ];
+ LDAPMessage *resultMsg = NULL;
+ struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
+ int ldap_rc;
+ ns_ldap_return_code ret_code;
+
+ (void) snprintf(searchBaseDN, BUFSIZ, "ou=profile,%s", dir_base_dn);
+ (void) snprintf(filter,
+ BUFSIZ,
+ _PROFILE_FILTER,
+ _PROFILE1_OBJECTCLASS,
+ _PROFILE2_OBJECTCLASS,
+ profile_name);
+ ldap_rc = ldap_search_ext_s(ld,
+ searchBaseDN,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ &tv,
+ 0,
+ &resultMsg);
+
+ switch (ldap_rc) {
+ /* If successful, the DUA profile was found. */
+ case LDAP_SUCCESS:
+ break;
+ /*
+ * If the root DSE was not found, the server does
+ * not comply with the LDAP v3 protocol.
+ */
+ default:
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ return (NS_LDAP_OP_FAILED);
+ break;
+ }
+
+ ret_code = convert_to_door_line(ld,
+ resultMsg,
+ INCLUDE_ATTR_NAMES,
+ IS_PROFILE,
+ profile);
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+ return (ret_code);
+}
+
+/*
+ * This function derives the directory's base DN from a provided domain name.
+ *
+ * INPUT:
+ * domain_name - the name of a domain to be converted into a base DN,
+ * buffer - contains the derived base DN,
+ * buf_len - the length of the buffer.
+ *
+ * OUTPUT:
+ * The function returns the address of the buffer or NULL.
+ */
+static
+char *
+domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len)
+{
+ char *nextDC, *chr;
+ uint16_t i, length;
+
+ if (!domain_name || !buffer || buf_len == 0) {
+ return (NULL);
+ }
+
+ buffer[0] = '\0';
+ nextDC = chr = domain_name;
+ length = strlen(domain_name);
+ for (i = 0; i < length + 1; ++i, ++chr) {
+ /* Simply replace dots with "dc=" */
+ if (*chr != '.' && *chr != '\0') {
+ continue;
+ }
+ *chr = '\0';
+ if (strlcat(buffer, "dc=", buf_len) >= buf_len)
+ return (NULL);
+ if (strlcat(buffer, nextDC, buf_len) >= buf_len)
+ return (NULL);
+ if (i < length) {
+ /*
+ * The end of the domain name
+ * has not been reached yet
+ */
+ if (strlcat(buffer, ",", buf_len) >= buf_len)
+ return (NULL);
+ nextDC = chr + 1;
+ *chr = '.';
+ }
+ }
+
+ return (buffer);
+}
+
+/*
+ * This function obtains the directory's base DN and a DUAProfile
+ * from a specified server.
+ *
+ * INPUT:
+ * server - a structure describing a server to connect to and
+ * a DUAProfile to be obtained from the server,
+ * cred - credentials to be used during establishing connections to
+ * the server.
+ *
+ * OUTPUT:
+ * dua_profile - a buffer containing the DUAProfile in the following format:
+ * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
+ * dir_base_dn - a buffer containing the base DN,
+ * errorp - an error object describing an error, if any.
+ *
+ * All the output data structures should be free'ed by the caller.
+ */
+ns_ldap_return_code
+__ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server,
+ const ns_cred_t *cred,
+ char **dua_profile,
+ char **dir_base_dn,
+ ns_ldap_error_t **errorp)
+{
+ char serverAddr[MAX_HOSTADDR_LEN];
+ char *dirBaseDN = NULL, *duaProfile = NULL;
+ ns_cred_t default_cred;
+ ns_ldap_return_code ret_code;
+
+ ns_config_t *config_struct = __s_api_create_config();
+ ConnectionID sessionId = 0;
+ Connection *session = NULL;
+ char errmsg[MAXERROR];
+ char buffer[NSS_BUFLEN_HOSTS];
+
+ if (errorp == NULL) {
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ *errorp = NULL;
+
+ if (server == NULL) {
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ if (config_struct == NULL) {
+ return (NS_LDAP_MEMORY);
+ }
+
+ /*
+ * If no credentials are specified, try to establish a connection
+ * as anonymous.
+ */
+ if (!cred) {
+ default_cred.cred.unix_cred.passwd = NULL;
+ default_cred.cred.unix_cred.userID = NULL;
+ default_cred.auth.type = NS_LDAP_AUTH_NONE;
+ }
+
+ /* Now create a default LDAP configuration */
+
+ (void) strncpy(buffer, server->server, sizeof (buffer));
+ if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer,
+ errorp) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_CONFIG);
+ }
+
+ /* Put together the address and the port specified by the user app. */
+ if (server->port > 0) {
+ (void) snprintf(serverAddr,
+ sizeof (serverAddr),
+ "%s:%hu",
+ buffer,
+ server->port);
+ } else {
+ (void) strncpy(serverAddr, buffer, sizeof (serverAddr));
+ }
+
+ /*
+ * There is no default value for the 'Default Search Base DN' attribute.
+ * Derive one from the domain name to make __s_api_crosscheck() happy.
+ */
+ if (domainname2baseDN(server->domainName ?
+ server->domainName : config_struct->domainName,
+ buffer, NSS_BUFLEN_HOSTS) == NULL) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Can not convert %s into a base DN name"),
+ server->domainName ?
+ server->domainName : config_struct->domainName);
+ MKERROR(LOG_ERR,
+ *errorp,
+ NS_LDAP_INTERNAL,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_INTERNAL);
+ }
+ if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P,
+ buffer, errorp) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_CONFIG);
+ }
+
+ if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) {
+ __s_api_destroy_config(config_struct);
+ return (NS_LDAP_CONFIG);
+ }
+
+ __s_api_init_config(config_struct);
+
+ __s_api_setInitMode();
+
+ if ((ret_code = __s_api_getConnection(serverAddr,
+ NS_LDAP_NEW_CONN,
+ cred ? cred : &default_cred,
+ &sessionId,
+ &session,
+ errorp,
+ 0,
+ 0,
+ NULL)) != NS_LDAP_SUCCESS) {
+ __s_api_unsetInitMode();
+ return (ret_code);
+ }
+
+ __s_api_unsetInitMode();
+
+ if ((ret_code = getDirBaseDN(session->ld,
+ server->domainName ?
+ server->domainName :
+ config_struct->domainName,
+ &dirBaseDN)) != NS_LDAP_SUCCESS) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Can not find the "
+ "nisDomainObject for domain %s\n"),
+ server->domainName ?
+ server->domainName : config_struct->domainName);
+ MKERROR(LOG_ERR,
+ *errorp,
+ ret_code,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ DropConnection(sessionId, NS_LDAP_NEW_CONN);
+ return (ret_code);
+ }
+
+ /*
+ * And here obtain a DUAProfile which will be used
+ * as a real configuration.
+ */
+ if ((ret_code = getDUAProfile(session->ld,
+ dirBaseDN,
+ server->profileName ?
+ server->profileName : "default",
+ &duaProfile)) != NS_LDAP_SUCCESS) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Can not find the "
+ "%s DUAProfile\n"),
+ server->profileName ?
+ server->profileName : "default");
+ MKERROR(LOG_ERR,
+ *errorp,
+ ret_code,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ DropConnection(sessionId, NS_LDAP_NEW_CONN);
+ return (ret_code);
+ }
+
+ if (dir_base_dn) {
+ *dir_base_dn = dirBaseDN;
+ } else {
+ free(dirBaseDN);
+ }
+
+ if (dua_profile) {
+ *dua_profile = duaProfile;
+ } else {
+ free(duaProfile);
+ }
+
+ DropConnection(sessionId, NS_LDAP_NEW_CONN);
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This function obtains the root DSE from a specified server.
+ *
+ * INPUT:
+ * server_addr - an adress of a server to be connected to.
+ *
+ * OUTPUT:
+ * root_dse - a buffer containing the root DSE in the following format:
+ * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
+ * For example: ( here | used as DOORLINESEP for visual purposes)
+ * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL
+ * Should be free'ed by the caller.
+ */
+ns_ldap_return_code
+__ns_ldap_getRootDSE(const char *server_addr,
+ char **root_dse,
+ ns_ldap_error_t **errorp,
+ int anon_fallback)
+{
+ char errmsg[MAXERROR];
+ ns_ldap_return_code ret_code;
+
+ ConnectionID sessionId = 0;
+ Connection *session = NULL;
+
+ struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
+ char *attrs[3];
+ int ldap_rc, ldaperrno = 0;
+ LDAPMessage *resultMsg = NULL;
+ void **paramVal = NULL;
+
+ ns_cred_t anon;
+
+ if (errorp == NULL) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ *errorp = NULL;
+
+ if (!root_dse) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ if (!server_addr) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ __s_api_setInitMode();
+
+ /*
+ * All the credentials will be taken from the current
+ * libsldap configuration.
+ */
+ if ((ret_code = __s_api_getConnection(server_addr,
+ NS_LDAP_NEW_CONN,
+ NULL,
+ &sessionId,
+ &session,
+ errorp,
+ 0,
+ 0,
+ NULL)) != NS_LDAP_SUCCESS) {
+ /* Fallback to anonymous mode is disabled. Stop. */
+ if (anon_fallback == 0) {
+ syslog(LOG_WARNING,
+ gettext("libsldap: can not get the root DSE from "
+ " the %s server: %s. "
+ "Falling back to anonymous disabled.\n"),
+ server_addr,
+ errorp && *errorp && (*errorp)->message ?
+ (*errorp)->message : "");
+ if (errorp != NULL && *errorp != NULL) {
+ (void) __ns_ldap_freeError(errorp);
+ }
+ __s_api_unsetInitMode();
+ return (ret_code);
+ }
+
+ /*
+ * Fallback to anonymous, non-SSL mode for backward
+ * compatibility reasons. This mode should only be used when
+ * this function (__ns_ldap_getRootDSE) is called from
+ * ldap_cachemgr(1M).
+ */
+ syslog(LOG_WARNING,
+ gettext("libsldap: Falling back to anonymous, non-SSL"
+ " mode for __ns_ldap_getRootDSE. %s\n"),
+ errorp && *errorp && (*errorp)->message ?
+ (*errorp)->message : "");
+
+ /* Setup the anon credential for anonymous connection. */
+ (void) memset(&anon, 0, sizeof (ns_cred_t));
+ anon.auth.type = NS_LDAP_AUTH_NONE;
+
+ if (*errorp != NULL) {
+ (void) __ns_ldap_freeError(errorp);
+ }
+ *errorp = NULL;
+
+ ret_code = __s_api_getConnection(server_addr,
+ NS_LDAP_NEW_CONN,
+ &anon,
+ &sessionId,
+ &session,
+ errorp,
+ 0,
+ 0,
+ NULL);
+
+ if (ret_code != NS_LDAP_SUCCESS) {
+ __s_api_unsetInitMode();
+ return (ret_code);
+ }
+ }
+
+ __s_api_unsetInitMode();
+
+ /* get search timeout value */
+ (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, &paramVal, errorp);
+ if (paramVal != NULL && *paramVal != NULL) {
+ tv.tv_sec = **((int **)paramVal);
+ (void) __ns_ldap_freeParam(&paramVal);
+ }
+ if (*errorp != NULL) {
+ (void) __ns_ldap_freeError(errorp);
+ }
+
+ /* Get root DSE from the server specified by the caller. */
+ attrs[0] = "supportedControl";
+ attrs[1] = "supportedsaslmechanisms";
+ attrs[2] = NULL;
+ ldap_rc = ldap_search_ext_s(session->ld,
+ "",
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ attrs,
+ 0,
+ NULL,
+ NULL,
+ &tv,
+ 0,
+ &resultMsg);
+
+ if (ldap_rc != LDAP_SUCCESS) {
+ /*
+ * If the root DSE was not found, the server does
+ * not comply with the LDAP v3 protocol.
+ */
+ (void) ldap_get_option(session->ld,
+ LDAP_OPT_ERROR_NUMBER,
+ &ldaperrno);
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext(ldap_err2string(ldaperrno)));
+ MKERROR(LOG_ERR,
+ *errorp,
+ NS_LDAP_OP_FAILED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ return (NS_LDAP_OP_FAILED);
+ }
+
+ ret_code = convert_to_door_line(session->ld,
+ resultMsg,
+ INCLUDE_ATTR_NAMES,
+ NOT_PROFILE,
+ root_dse);
+ if (ret_code == NS_LDAP_NOTFOUND) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("No root DSE data "
+ "for server %s returned."),
+ server_addr);
+ MKERROR(LOG_ERR,
+ *errorp,
+ NS_LDAP_NOTFOUND,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ }
+
+ if (resultMsg) {
+ (void) ldap_msgfree(resultMsg);
+ resultMsg = NULL;
+ }
+
+ DropConnection(sessionId, NS_LDAP_NEW_CONN);
+
+ return (ret_code);
+}
+
+/*
+ * This function destroys the local list of root DSEs. The input parameter is
+ * a pointer to the list to be erased.
+ * The type of the pointer passed to this function should be
+ * (dir_server_list_t *).
+ */
+static
+void *
+disposeOfOldList(void *param)
+{
+ dir_server_list_t *old_list = (dir_server_list_t *)param;
+ long i = 0, j;
+
+ (void) rw_wrlock(&old_list->listDestroyLock);
+ /* Destroy the old list */
+ while (old_list->nsServers[i]) {
+ free(old_list->nsServers[i]->ip);
+ j = 0;
+ while (old_list->nsServers[i]->controls &&
+ old_list->nsServers[i]->controls[j]) {
+ free(old_list->nsServers[i]->controls[j]);
+ ++j;
+ }
+ free(old_list->nsServers[i]->controls);
+ j = 0;
+ while (old_list->nsServers[i]->saslMech &&
+ old_list->nsServers[i]->saslMech[j]) {
+ free(old_list->nsServers[i]->saslMech[j]);
+ ++j;
+ }
+ free(old_list->nsServers[i]->saslMech);
+ ++i;
+ }
+ /*
+ * All the structures pointed by old_list->nsServers were allocated
+ * in one chunck. The nsServers[0] pointer points to the beginning
+ * of that chunck.
+ */
+ free(old_list->nsServers[0]);
+ free(old_list->nsServers);
+ (void) rw_unlock(&old_list->listDestroyLock);
+ (void) rwlock_destroy(&old_list->listDestroyLock);
+ free(old_list);
+
+ return (NULL);
+}
+
+/*
+ * This function cancels the Standalone mode and destroys the list of root DSEs.
+ */
+void
+__ns_ldap_cancelStandalone(void)
+{
+ dir_server_list_t *old_list;
+
+ (void) mutex_lock(&dir_servers.listReplaceLock);
+ dir_servers.standalone = 0;
+ if (!dir_servers.list) {
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+ return;
+ }
+ old_list = dir_servers.list;
+ dir_servers.list = NULL;
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+
+ (void) disposeOfOldList(old_list);
+}
+
+
+static
+void*
+create_ns_servers_entry(void *param)
+{
+#define CHUNK_SIZE 16
+
+ dir_server_t *server = (dir_server_t *)param;
+ ns_ldap_return_code *retCode = calloc(1,
+ sizeof (ns_ldap_return_code));
+ uint32_t sc_counter = 0, sm_counter = 0;
+ uint32_t sc_mem_blocks = 1, sm_mem_blocks = 1;
+ char *rootDSE = NULL, *attr, *val, *rest, **ptr;
+ ns_ldap_error_t *error = NULL;
+
+ if (retCode == NULL) {
+ return (NULL);
+ }
+
+ /*
+ * We call this function in non anon-fallback mode because we
+ * want the whole procedure to fail as soon as possible to
+ * indicate there are problems with connecting to the server.
+ */
+ *retCode = __ns_ldap_getRootDSE(server->ip,
+ &rootDSE,
+ &error,
+ SA_ALLOW_FALLBACK);
+
+ if (*retCode == NS_LDAP_MEMORY) {
+ free(retCode);
+ return (NULL);
+ }
+
+ /*
+ * If the root DSE can not be obtained, log an error and keep the
+ * server.
+ */
+ if (*retCode != NS_LDAP_SUCCESS) {
+ server->status = INFO_SERVER_ERROR;
+ syslog(LOG_WARNING,
+ gettext("libsldap (\"standalone\" mode): "
+ "can not obtain the root DSE from %s. %s"),
+ server->ip,
+ error && error->message ? error->message : "");
+ if (error) {
+ (void) __ns_ldap_freeError(&error);
+ }
+ return (retCode);
+ }
+
+ /* Get the first attribute of the root DSE. */
+ attr = strtok_r(rootDSE, DOORLINESEP, &rest);
+ if (attr == NULL) {
+ free(rootDSE);
+ server->status = INFO_SERVER_ERROR;
+ syslog(LOG_WARNING,
+ gettext("libsldap (\"standalone\" mode): "
+ "the root DSE from %s is empty or corrupted."),
+ server->ip);
+ *retCode = NS_LDAP_INTERNAL;
+ return (retCode);
+ }
+
+ server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *));
+ server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *));
+ if (server->controls == NULL || server->saslMech == NULL) {
+ free(rootDSE);
+ free(retCode);
+ return (NULL);
+ }
+
+ do {
+ if ((val = strchr(attr, '=')) == NULL) {
+ continue;
+ }
+ ++val;
+
+ if (strncasecmp(attr,
+ _SASLMECHANISM,
+ _SASLMECHANISM_LEN) == 0) {
+ if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) {
+ ptr = (char **)realloc(server->saslMech,
+ CHUNK_SIZE *
+ ++sm_mem_blocks *
+ sizeof (char *));
+ if (ptr == NULL) {
+ *retCode = NS_LDAP_MEMORY;
+ break;
+ }
+ bzero((char *)ptr +
+ (sm_counter + 1) *
+ sizeof (char *),
+ CHUNK_SIZE *
+ sm_mem_blocks *
+ sizeof (char *) -
+ (sm_counter + 1) *
+ sizeof (char *));
+ server->saslMech = ptr;
+ }
+ server->saslMech[sm_counter] = strdup(val);
+ if (server->saslMech[sm_counter] == NULL) {
+ *retCode = NS_LDAP_MEMORY;
+ break;
+ }
+ ++sm_counter;
+ continue;
+ }
+ if (strncasecmp(attr,
+ _SUPPORTEDCONTROL,
+ _SUPPORTEDCONTROL_LEN) == 0) {
+ if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) {
+ ptr = (char **)realloc(server->controls,
+ CHUNK_SIZE *
+ ++sc_mem_blocks *
+ sizeof (char *));
+ if (ptr == NULL) {
+ *retCode = NS_LDAP_MEMORY;
+ break;
+ }
+ bzero((char *)ptr +
+ (sc_counter + 1) *
+ sizeof (char *),
+ CHUNK_SIZE *
+ sc_mem_blocks *
+ sizeof (char *) -
+ (sc_counter + 1) *
+ sizeof (char *));
+ server->controls = ptr;
+ }
+
+ server->controls[sc_counter] = strdup(val);
+ if (server->controls[sc_counter] == NULL) {
+ *retCode = NS_LDAP_MEMORY;
+ break;
+ }
+ ++sc_counter;
+ continue;
+ }
+
+ } while (attr = strtok_r(NULL, DOORLINESEP, &rest));
+
+ free(rootDSE);
+
+ if (*retCode == NS_LDAP_MEMORY) {
+ free(retCode);
+ return (NULL);
+ }
+
+ server->controls[sc_counter] = NULL;
+ server->saslMech[sm_counter] = NULL;
+
+ server->status = INFO_SERVER_UP;
+
+ return (retCode);
+#undef CHUNK_SIZE
+}
+
+
+/*
+ * This function creates a new local list of root DSEs from all the servers
+ * mentioned in the DUAProfile (or local NS BEC) and returns
+ * a pointer to the list.
+ */
+static
+ns_ldap_return_code
+createDirServerList(dir_server_list_t **new_list,
+ ns_ldap_error_t **errorp)
+{
+ char **serverList;
+ ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
+ dir_server_t *tmpSrvArray;
+ long srvListLength, i;
+ thread_t *thrPool, thrID;
+ void *status = NULL;
+
+ if (errorp == NULL) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ *errorp = NULL;
+
+ if (new_list == NULL) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ retCode = __s_api_getServers(&serverList, errorp);
+ if (retCode != NS_LDAP_SUCCESS || serverList == NULL) {
+ return (retCode);
+ }
+
+ for (i = 0; serverList[i]; ++i) {
+ ;
+ }
+ srvListLength = i;
+
+ thrPool = calloc(srvListLength, sizeof (thread_t));
+ if (thrPool == NULL) {
+ __s_api_free2dArray(serverList);
+ return (NS_LDAP_MEMORY);
+ }
+
+ *new_list = (dir_server_list_t *)calloc(1,
+ sizeof (dir_server_list_t));
+ if (*new_list == NULL) {
+ __s_api_free2dArray(serverList);
+ free(thrPool);
+ return (NS_LDAP_MEMORY);
+ }
+ (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL);
+
+ (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1,
+ sizeof (dir_server_t *));
+ if ((*new_list)->nsServers == NULL) {
+ free(*new_list);
+ *new_list = NULL;
+ __s_api_free2dArray(serverList);
+ free(thrPool);
+ return (NS_LDAP_MEMORY);
+ }
+
+ /*
+ * Allocate a set of dir_server_t structures as an array,
+ * with one alloc call and then initialize the nsServers pointers
+ * with the addresses of the array's members.
+ */
+ tmpSrvArray = (dir_server_t *)calloc(srvListLength,
+ sizeof (dir_server_t));
+ for (i = 0; i < srvListLength; ++i) {
+ (*new_list)->nsServers[i] = &tmpSrvArray[i];
+
+ (*new_list)->nsServers[i]->info = INFO_STATUS_NEW;
+ (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus,
+ USYNC_THREAD,
+ NULL);
+
+ (*new_list)->nsServers[i]->ip = strdup(serverList[i]);
+ if ((*new_list)->nsServers[i]->ip == NULL) {
+ retCode = NS_LDAP_MEMORY;
+ break;
+ }
+
+ (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING;
+
+ switch (thr_create(NULL,
+ 0,
+ create_ns_servers_entry,
+ (*new_list)->nsServers[i],
+ 0,
+ &thrID)) {
+ case EAGAIN:
+ (*new_list)->nsServers[i]->status =
+ INFO_SERVER_ERROR;
+ continue;
+ break;
+ case ENOMEM:
+ (*new_list)->nsServers[i]->status =
+ INFO_SERVER_ERROR;
+ continue;
+ break;
+ default:
+ thrPool[i] = thrID;
+ continue;
+ break;
+ }
+ }
+
+ for (i = 0; i < srvListLength; ++i) {
+ if (thrPool[i] != 0 &&
+ thr_join(thrPool[i], NULL, &status) == 0) {
+ if (status == NULL) {
+ /*
+ * Some memory allocation problems occured. Just
+ * ignore the server and hope there will be some
+ * other good ones.
+ */
+ (*new_list)->nsServers[i]->status =
+ INFO_SERVER_ERROR;
+ }
+ free(status);
+ }
+ }
+
+ __s_api_free2dArray(serverList);
+ free(thrPool);
+
+ if (retCode == NS_LDAP_MEMORY) {
+ (void) disposeOfOldList(*new_list);
+ return (NS_LDAP_MEMORY);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This functions replaces the local list of root DSEs with a new one and starts
+ * a thread destroying the old list. There is no need for other threads to wait
+ * until the old list will be destroyed.
+ * Since it is possible that more than one thread can start creating the list,
+ * this function should be protected by mutexes to be sure that only one thread
+ * performs the initialization.
+ */
+static
+ns_ldap_return_code
+initGlobalList(ns_ldap_error_t **error)
+{
+ dir_server_list_t *new_list, *old_list;
+ ns_ldap_return_code ret_code;
+ thread_t tid;
+
+ ret_code = createDirServerList(&new_list, error);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ return (ret_code);
+ }
+
+ old_list = dir_servers.list;
+ dir_servers.list = new_list;
+
+ if (old_list) {
+ (void) thr_create(NULL,
+ 0,
+ disposeOfOldList,
+ old_list,
+ THR_DETACHED,
+ &tid);
+ }
+
+ return (NS_LDAP_SUCCESS);
+}
+
+static
+struct {
+ char *authMech;
+ ns_auth_t auth;
+} authArray[] = {{"none", {NS_LDAP_AUTH_NONE,
+ NS_LDAP_TLS_NONE,
+ NS_LDAP_SASL_NONE,
+ NS_LDAP_SASLOPT_NONE}},
+ {"simple", {NS_LDAP_AUTH_SIMPLE,
+ NS_LDAP_TLS_NONE,
+ NS_LDAP_SASL_NONE,
+ NS_LDAP_SASLOPT_NONE}},
+ {"tls:simple", {NS_LDAP_AUTH_TLS,
+ NS_LDAP_TLS_SIMPLE,
+ NS_LDAP_SASL_NONE,
+ NS_LDAP_SASLOPT_NONE}},
+ {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS,
+ NS_LDAP_TLS_SASL,
+ NS_LDAP_SASL_CRAM_MD5,
+ NS_LDAP_SASLOPT_NONE}},
+ {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS,
+ NS_LDAP_TLS_SASL,
+ NS_LDAP_SASL_DIGEST_MD5,
+ NS_LDAP_SASLOPT_NONE}},
+ {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL,
+ NS_LDAP_TLS_SASL,
+ NS_LDAP_SASL_CRAM_MD5,
+ NS_LDAP_SASLOPT_NONE}},
+ {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL,
+ NS_LDAP_TLS_SASL,
+ NS_LDAP_SASL_DIGEST_MD5,
+ NS_LDAP_SASLOPT_NONE}},
+ {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL,
+ NS_LDAP_TLS_SASL,
+ NS_LDAP_SASL_GSSAPI,
+ NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}},
+ {NULL, {NS_LDAP_AUTH_NONE,
+ NS_LDAP_TLS_NONE,
+ NS_LDAP_SASL_NONE,
+ NS_LDAP_SASLOPT_NONE}}};
+
+ns_ldap_return_code
+__ns_ldap_initAuth(const char *auth_mech,
+ ns_auth_t *auth,
+ ns_ldap_error_t **errorp)
+{
+ uint32_t i;
+ char errmsg[MAXERROR];
+
+ if (auth_mech == NULL) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Invalid authentication method specified\n"));
+ MKERROR(LOG_WARNING,
+ *errorp,
+ NS_LDAP_INTERNAL,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ for (i = 0; authArray[i].authMech != NULL; ++i) {
+ if (strcasecmp(auth_mech, authArray[i].authMech) == 0) {
+ *auth = authArray[i].auth;
+ return (NS_LDAP_SUCCESS);
+ }
+ }
+
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Invalid authentication method specified\n"));
+ MKERROR(LOG_WARNING,
+ *errorp,
+ NS_LDAP_INTERNAL,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+}
+
+/*
+ * This function "informs" libsldap that a client application has specified
+ * a directory to use. The function obtains a DUAProfile, credentials,
+ * and naming context. During all further operations on behalf
+ * of the application requested a standalone schema libsldap will use
+ * the information obtained by __ns_ldap_initStandalone() instead of
+ * door_call(3C)ing ldap_cachemgr(1M).
+ *
+ * INPUT:
+ * sa_conf - a structure describing where and in which way to obtain all
+ * the configuration describing how to communicate to
+ * a choosen LDAP directory,
+ * errorp - an error object describing an error occured.
+ */
+ns_ldap_return_code
+__ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf,
+ ns_ldap_error_t **errorp) {
+
+ ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE,
+ NS_LDAP_TLS_NONE,
+ NS_LDAP_SASL_NONE,
+ NS_LDAP_SASLOPT_NONE},
+ NULL,
+ {NULL, NULL}};
+ char *dua_profile = NULL;
+ char errmsg[MAXERROR];
+ ns_config_t *cfg;
+ int ret_code;
+
+ if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL ||
+ sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("Bind DN and bind password"
+ " must both be provided\n"));
+ MKERROR(LOG_ERR,
+ *errorp,
+ NS_CONFIG_NOTLOADED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ switch (sa_conf->type) {
+ case NS_LDAP_SERVER:
+ if (sa_conf->SA_BIND_DN != NULL) {
+ user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN;
+ user_cred.auth.type = NS_LDAP_AUTH_SIMPLE;
+ }
+
+ if (sa_conf->SA_BIND_PWD != NULL) {
+ user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD;
+ }
+
+ if (sa_conf->SA_AUTH != NULL) {
+ user_cred.auth.type = sa_conf->SA_AUTH->type;
+ user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype;
+ user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech;
+ user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt;
+ }
+
+ if (sa_conf->SA_CERT_PATH != NULL) {
+ user_cred.hostcertpath = sa_conf->SA_CERT_PATH;
+ }
+
+ ret_code = __ns_ldap_getConnectionInfoFromDUA(
+ &sa_conf->ds_profile.server,
+ &user_cred,
+ &dua_profile,
+ NULL,
+ errorp);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ return (ret_code);
+ }
+
+ cfg = __s_api_create_config_door_str(dua_profile, errorp);
+ if (cfg == NULL) {
+ free(dua_profile);
+ return (NS_LDAP_CONFIG);
+ }
+
+ if (sa_conf->SA_CERT_PATH != NULL) {
+ char *certPathAttr;
+ ParamIndexType type;
+
+ switch (cfg->version) {
+ case NS_LDAP_V1:
+ certPathAttr = "NS_LDAP_CERT_PATH";
+ break;
+ default: /* Version 2 */
+ certPathAttr = "NS_LDAP_HOST_CERTPATH";
+ break;
+ }
+
+ if (__s_api_get_versiontype(cfg,
+ certPathAttr,
+ &type) == 0 &&
+ (ret_code = __ns_ldap_setParamValue(cfg,
+ type,
+ sa_conf->SA_CERT_PATH,
+ errorp)) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(cfg);
+ return (ret_code);
+ }
+ }
+
+ if (sa_conf->SA_BIND_DN != NULL &&
+ sa_conf->SA_BIND_PWD != NULL) {
+ char buffer[BUFSIZE], *authMethods;
+
+ authMethods = __s_api_strValue(cfg,
+ buffer,
+ BUFSIZE,
+ NS_LDAP_AUTH_P,
+ NS_FILE_FMT);
+ if (authMethods != NULL &&
+ strstr(authMethods, "sasl/GSSAPI") != NULL) {
+ /*
+ * The received DUAProfile specifies
+ * sasl/GSSAPI as an auth. mechanism.
+ * The bind DN and password will be
+ * ignored.
+ */
+ syslog(LOG_INFO, gettext("sasl/GSSAPI will be "
+ "used as an authentication method. "
+ "The bind DN and password will "
+ "be ignored.\n"));
+ break;
+ }
+
+ if (__ns_ldap_setParamValue(cfg,
+ NS_LDAP_BINDDN_P,
+ sa_conf->SA_BIND_DN,
+ errorp) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(cfg);
+ return (NS_LDAP_CONFIG);
+ }
+
+ if (__ns_ldap_setParamValue(cfg,
+ NS_LDAP_BINDPASSWD_P,
+ sa_conf->SA_BIND_PWD,
+ errorp) != NS_LDAP_SUCCESS) {
+ __s_api_destroy_config(cfg);
+ return (NS_LDAP_CONFIG);
+ }
+ }
+
+ break;
+ default: /* NS_CACHEMGR */
+ return (NS_LDAP_SUCCESS);
+ }
+
+ __s_api_init_config(cfg);
+ __ns_ldap_setServer(TRUE);
+
+ (void) mutex_lock(&dir_servers.listReplaceLock);
+ if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) {
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+ return (ret_code);
+ }
+ dir_servers.standalone = 1;
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * INPUT:
+ * serverAddr is the address of a server and
+ * request is one of the following:
+ * NS_CACHE_NEW: get a new server address, addr is ignored.
+ * NS_CACHE_NORESP: get the next one, remove addr from list.
+ * NS_CACHE_NEXT: get the next one, keep addr on list.
+ * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same
+ * as NS_CACHE_NEXT.
+ * addrType:
+ * NS_CACHE_ADDR_IP: return server address as is, this is default.
+ * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only
+ * self credential case requires such format.
+ * OUTPUT:
+ * ret
+ *
+ * a structure of type ns_server_info_t containing the server address
+ * or name, server controls and supported SASL mechanisms.
+ * NOTE: Caller should allocate space for the structure and free
+ * all the space allocated by the function for the information contained
+ * in the structure.
+ *
+ * error - an error object describing an error, if any.
+ */
+ns_ldap_return_code
+__s_api_findRootDSE(const char *request,
+ const char *serverAddr,
+ const char *addrType,
+ ns_server_info_t *ret,
+ ns_ldap_error_t **error)
+{
+ dir_server_list_t *current_list = NULL;
+ ns_ldap_return_code ret_code;
+ long i = 0;
+ int matched = FALSE;
+ dir_server_t *server = NULL;
+ char errmsg[MAXERROR];
+
+ (void) mutex_lock(&dir_servers.listReplaceLock);
+ if (dir_servers.list == NULL) {
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("The list of root DSEs is empty: "
+ "the Standalone mode was not properly initialized"));
+ MKERROR(LOG_ERR,
+ *error,
+ NS_CONFIG_NOTLOADED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ current_list = dir_servers.list;
+ (void) rw_rdlock(&current_list->listDestroyLock);
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+
+ /*
+ * The code below is mostly the clone of the
+ * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function.
+ * Currently we have two different server lists: one is maintained
+ * by libsldap ('standalone' mode), the other is in ldap_cachemgr
+ * (a part of its standard functionality).
+ */
+
+ /*
+ * If NS_CACHE_NEW, or the server info is new,
+ * starts from the beginning of the list.
+ */
+ (void) mutex_lock(&current_list->nsServers[0]->updateStatus);
+ if (strcmp(request, NS_CACHE_NEW) == 0 ||
+ current_list->nsServers[0]->info == INFO_STATUS_NEW) {
+ matched = TRUE;
+ }
+ (void) mutex_unlock(&current_list->nsServers[i]->updateStatus);
+
+ for (i = 0; current_list->nsServers[i]; ++i) {
+ /*
+ * Lock the updateStatus mutex to
+ * make sure the server status stays the same
+ * while the data is being processed.
+ */
+ if (matched == FALSE &&
+ strcmp(current_list->nsServers[i]->ip,
+ serverAddr) == 0) {
+ matched = TRUE;
+ if (strcmp(request, NS_CACHE_NORESP) == 0) {
+
+ /*
+ * if the server has already been removed,
+ * don't bother.
+ */
+ (void) mutex_lock(&current_list->
+ nsServers[i]->updateStatus);
+ if (current_list->nsServers[i]->status ==
+ INFO_SERVER_REMOVED) {
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ continue;
+ }
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+
+ /*
+ * if the information is new,
+ * give this server one more chance.
+ */
+ (void) mutex_lock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ if (current_list->nsServers[i]->info ==
+ INFO_STATUS_NEW &&
+ current_list->nsServers[i]->status ==
+ INFO_SERVER_UP) {
+ server = current_list->nsServers[i];
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ break;
+ } else {
+ /*
+ * it is recommended that
+ * before removing the
+ * server from the list,
+ * the server should be
+ * contacted one more time
+ * to make sure that it is
+ * really unavailable.
+ * For now, just trust the client
+ * (i.e., the sldap library)
+ * that it knows what it is
+ * doing and would not try
+ * to mess up the server
+ * list.
+ */
+ current_list->nsServers[i]->status =
+ INFO_SERVER_REMOVED;
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ continue;
+ }
+ } else {
+ /*
+ * req == NS_CACHE_NEXT or NS_CACHE_WRITE
+ */
+ continue;
+ }
+ }
+
+ if (matched) {
+ if (strcmp(request, NS_CACHE_WRITE) == 0) {
+ /*
+ * ldap_cachemgr checks here if the server
+ * is not a non-replica server (a server
+ * of type INFO_RW_WRITEABLE). But currently
+ * it considers all the servers in its list
+ * as those.
+ */
+ (void) mutex_lock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ if (current_list->nsServers[i]->status ==
+ INFO_SERVER_UP) {
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ server = current_list->nsServers[i];
+ break;
+ }
+ } else {
+ (void) mutex_lock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ if (current_list->nsServers[i]->status ==
+ INFO_SERVER_UP) {
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ server = current_list->nsServers[i];
+ break;
+ }
+ }
+
+ (void) mutex_unlock(&current_list->
+ nsServers[i]->
+ updateStatus);
+ }
+ }
+
+ if (server == NULL) {
+ (void) rw_unlock(&current_list->listDestroyLock);
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("No servers are available"));
+ MKERROR(LOG_ERR,
+ *error,
+ NS_CONFIG_NOTLOADED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_NOTFOUND);
+ }
+
+ (void) mutex_lock(&server->updateStatus);
+ server->info = INFO_STATUS_OLD;
+ (void) mutex_unlock(&server->updateStatus);
+
+ if (ret == NULL) {
+ (void) rw_unlock(&current_list->listDestroyLock);
+ return (NS_LDAP_SUCCESS);
+ }
+
+ if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
+ ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN);
+ if (ret_code != NS_LDAP_SUCCESS) {
+ (void) snprintf(errmsg,
+ sizeof (errmsg),
+ gettext("The %s address "
+ "can not be resolved into "
+ "a host name. Returning "
+ "the address as it is."),
+ server->ip);
+ MKERROR(LOG_ERR,
+ *error,
+ NS_CONFIG_NOTLOADED,
+ strdup(errmsg),
+ NS_LDAP_MEMORY);
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+
+ ret->server = strdup(server->ip);
+
+ ret->controls = __s_api_cp2dArray(server->controls);
+ ret->saslMechanisms = __s_api_cp2dArray(server->saslMech);
+
+ (void) rw_unlock(&current_list->listDestroyLock);
+
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * This function iterates through the list of the configured LDAP servers
+ * and "pings" those which are marked as removed or if any error occurred
+ * during the previous receiving of the server's root DSE. If the
+ * function is able to reach such a server and get its root DSE, it
+ * marks the server as on-line. Otherwise, the server's status is set
+ * to "Error".
+ * For each server the function tries to connect to, it fires up
+ * a separate thread and then waits until all the treads finish.
+ * The function returns NS_LDAP_INTERNAL if the Standalone mode was not
+ * initialized or was canceled prior to an invocation of
+ * __ns_ldap_pingOfflineServers().
+ */
+ns_ldap_return_code
+__ns_ldap_pingOfflineServers(void)
+{
+ dir_server_list_t *current_list = NULL;
+ ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
+ long srvListLength, i = 0;
+ thread_t *thrPool, thrID;
+ void *status = NULL;
+
+ (void) mutex_lock(&dir_servers.listReplaceLock);
+ if (dir_servers.list == NULL) {
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ current_list = dir_servers.list;
+ (void) rw_wrlock(&current_list->listDestroyLock);
+ (void) mutex_unlock(&dir_servers.listReplaceLock);
+
+ while (current_list->nsServers[i] != NULL) {
+ ++i;
+ }
+ srvListLength = i;
+
+ thrPool = calloc(srvListLength, sizeof (thread_t));
+ if (thrPool == NULL) {
+ (void) rw_unlock(&current_list->listDestroyLock);
+ return (NS_LDAP_MEMORY);
+ }
+
+ for (i = 0; i < srvListLength; ++i) {
+ if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED &&
+ current_list->nsServers[i]->status != INFO_SERVER_ERROR) {
+ continue;
+ }
+ current_list->nsServers[i]->status = INFO_SERVER_CONNECTING;
+ current_list->nsServers[i]->info = INFO_STATUS_NEW;
+
+ __s_api_free2dArray(current_list->nsServers[i]->controls);
+ current_list->nsServers[i]->controls = NULL;
+ __s_api_free2dArray(current_list->nsServers[i]->saslMech);
+ current_list->nsServers[i]->saslMech = NULL;
+
+ switch (thr_create(NULL,
+ 0,
+ create_ns_servers_entry,
+ current_list->nsServers[i],
+ 0,
+ &thrID)) {
+ case EAGAIN:
+ current_list->nsServers[i]->status = INFO_SERVER_ERROR;
+ continue;
+ break;
+ case ENOMEM:
+ current_list->nsServers[i]->status = INFO_SERVER_ERROR;
+ retCode = NS_LDAP_MEMORY;
+ break;
+ default:
+ thrPool[i] = thrID;
+ continue;
+ break;
+ }
+ /* A memory allocation error has occured */
+ break;
+
+ }
+
+ for (i = 0; i < srvListLength; ++i) {
+ if (thrPool[i] != 0 &&
+ thr_join(thrPool[i], NULL, &status) == 0) {
+ if (status == NULL) {
+ current_list->nsServers[i]->status =
+ INFO_SERVER_ERROR;
+ retCode = NS_LDAP_MEMORY;
+ }
+ free(status);
+ }
+ }
+
+ (void) rw_unlock(&current_list->listDestroyLock);
+
+ free(thrPool);
+
+ return (retCode);
+}
diff --git a/usr/src/lib/libsldap/common/ns_writes.c b/usr/src/lib/libsldap/common/ns_writes.c
index 4ac8c16c13..1e29fbaf7f 100644
--- a/usr/src/lib/libsldap/common/ns_writes.c
+++ b/usr/src/lib/libsldap/common/ns_writes.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,9 +41,11 @@
#include "ns_sldap.h"
#include "ns_internal.h"
+#include "ns_connmgmt.h"
/* Additional headers for addTypedEntry Conversion routines */
#include <pwd.h>
+#include <project.h>
#include <shadow.h>
#include <grp.h>
#include <netinet/in.h>
@@ -168,15 +170,13 @@ init_bval_mod(
* assume single value,
* since only one value/length pair passed in
*/
- bmodval = (struct berval **)calloc(2,
- sizeof (struct berval *));
+ bmodval = (struct berval **)calloc(2, sizeof (struct berval *));
if (bmodval == NULL) {
free(mod->mod_type);
mod->mod_type = NULL;
return (-1);
}
- bmodval[0] = (struct berval *)calloc(1,
- sizeof (struct berval));
+ bmodval[0] = (struct berval *)calloc(1, sizeof (struct berval));
if (bmodval[0] == NULL) {
free(mod->mod_type);
mod->mod_type = NULL;
@@ -215,8 +215,7 @@ freeModList(LDAPMod **mods)
/* free attribute name */
name_is_oc = FALSE;
if (mods[i]->mod_type) {
- if (strcasecmp(mods[i]->mod_type,
- "objectclass") == 0)
+ if (strcasecmp(mods[i]->mod_type, "objectclass") == 0)
name_is_oc = TRUE;
free(mods[i]->mod_type);
}
@@ -313,8 +312,7 @@ __s_api_makeModListCount(
/*
* Perform attribute mapping if necessary.
*/
- if (schema_mapping_existed &&
- (flags & NS_LDAP_NOMAP) == 0) {
+ if (schema_mapping_existed && (flags & NS_LDAP_NOMAP) == 0) {
mapping = __ns_ldap_getMappedAttributes(service,
attr[k]->attrname);
} else
@@ -333,10 +331,9 @@ __s_api_makeModListCount(
}
if (mapping == NULL) {
- mods[i]->mod_type = strdup(attr[k]->attrname);
- if (mods[i]->mod_type == NULL) {
- goto free_memory;
- }
+ mods[i]->mod_type = strdup(attr[k]->attrname);
+ if (mods[i]->mod_type == NULL)
+ goto free_memory;
} else {
/*
* 1 to N attribute mapping is only done for "gecos",
@@ -378,11 +375,11 @@ __s_api_makeModListCount(
*
* notes: in case c2 and c3, ... could still contain ","
*/
- if (strcasecmp(service, "passwd") == 0 &&
- strcasecmp(attr[k]->attrname, "gecos") == 0 &&
- mapping[1] && attr[k]->attrvalue[0] &&
- (comma1 = strchr(attr[k]->attrvalue[0],
- COMMATOK)) != NULL) {
+ if (strcasecmp(service, "passwd") == 0 &&
+ strcasecmp(attr[k]->attrname, "gecos") == 0 &&
+ mapping[1] && attr[k]->attrvalue[0] &&
+ (comma1 = strchr(attr[k]->attrvalue[0],
+ COMMATOK)) != NULL) {
/* is there a second comma? */
if (*(comma1 + 1) != '\0')
@@ -393,8 +390,7 @@ __s_api_makeModListCount(
* case c2: mapped to two attributes or just
* one comma
*/
- if (mapping[2] == NULL ||
- comma2 == NULL) {
+ if (mapping[2] == NULL || comma2 == NULL) {
/* case c2 */
/*
@@ -405,7 +401,7 @@ __s_api_makeModListCount(
if (vlen > 0 && c) {
rc = init_bval_mod(mods[i], mod_op,
- mapping[0], c, vlen);
+ mapping[0], c, vlen);
if (rc != 0)
goto free_memory;
} else {
@@ -431,15 +427,17 @@ __s_api_makeModListCount(
* get pointer to data.
* Skip leading spaces.
*/
- for (c = comma1 + 1; *c == SPACETOK; c++);
+ for (c = comma1 + 1; *c == SPACETOK; c++) {
+ /* empty */
+ }
/* get data length */
vlen = strlen(attr[k]->attrvalue[0]) -
- (c - attr[k]->attrvalue[0]);
+ (c - attr[k]->attrvalue[0]);
if (vlen > 0 && c) {
rc = init_bval_mod(mods[i], mod_op,
- mapping[1], c, vlen);
+ mapping[1], c, vlen);
if (rc != 0)
goto free_memory;
} else {
@@ -464,7 +462,7 @@ __s_api_makeModListCount(
if (vlen > 0 && c) {
rc = init_bval_mod(mods[i], mod_op,
- mapping[0], c, vlen);
+ mapping[0], c, vlen);
if (rc != 0)
goto free_memory;
} else {
@@ -483,14 +481,16 @@ __s_api_makeModListCount(
* get pointer to data.
* Skip leading spaces.
*/
- for (c = comma1 + 1; *c == SPACETOK; c++);
+ for (c = comma1 + 1; *c == SPACETOK; c++) {
+ /* empty */
+ };
/* get data length */
vlen = comma2 - c;
if (vlen > 0 && c) {
rc = init_bval_mod(mods[i], mod_op,
- mapping[1], c, vlen);
+ mapping[1], c, vlen);
if (rc != 0)
goto free_memory;
} else {
@@ -514,15 +514,17 @@ __s_api_makeModListCount(
* get pointer to data.
* Skip leading spaces.
*/
- for (c = comma2 + 1; *c == SPACETOK; c++);
+ for (c = comma2 + 1; *c == SPACETOK; c++) {
+ /* empty */
+ }
/* get data length */
vlen = strlen(attr[k]->attrvalue[0]) -
- (c - attr[k]->attrvalue[0]);
+ (c - attr[k]->attrvalue[0]);
if (vlen > 0 && c) {
rc = init_bval_mod(mods[i], mod_op,
- mapping[2], c, vlen);
+ mapping[2], c, vlen);
if (rc != 0)
goto free_memory;
} else {
@@ -536,20 +538,20 @@ __s_api_makeModListCount(
mapping = NULL;
continue;
+ }
}
- }
- /* case c1 */
- mods[i]->mod_type = strdup(mapping[0]);
- if (mods[i]->mod_type == NULL) {
+ /* case c1 */
+ mods[i]->mod_type = strdup(mapping[0]);
+ if (mods[i]->mod_type == NULL) {
goto free_memory;
- }
- __s_api_free2dArray(mapping);
- mapping = NULL;
+ }
+ __s_api_free2dArray(mapping);
+ mapping = NULL;
}
modval = (char **)calloc(attr[k]->value_count+1,
- sizeof (char *));
+ sizeof (char *));
if (modval == NULL)
goto free_memory;
/*
@@ -562,30 +564,30 @@ __s_api_makeModListCount(
if (strcasecmp(mods[i]->mod_type, "objectclass") == 0) {
for (j = 0; j < attr[k]->value_count; j++) {
if (schema_mapping_existed &&
- (flags & NS_LDAP_NOMAP) == 0)
+ (flags & NS_LDAP_NOMAP) == 0)
mapping =
- __ns_ldap_getMappedObjectClass(
- service, attr[k]->attrvalue[j]);
+ __ns_ldap_getMappedObjectClass(
+ service, attr[k]->attrvalue[j]);
else
mapping = NULL;
if (mapping == NULL && auto_service &&
- (flags & NS_LDAP_NOMAP) == 0)
+ (flags & NS_LDAP_NOMAP) == 0)
/*
* if service == auto_xxx and
* no mapped objectclass is found
* then try automount
*/
mapping =
- __ns_ldap_getMappedObjectClass(
- "automount", attr[k]->attrvalue[j]);
+ __ns_ldap_getMappedObjectClass(
+ "automount", attr[k]->attrvalue[j]);
if (mapping && mapping[0]) {
/* assume single mapping */
modval[j] = strdup(mapping[0]);
} else {
modval[j] = strdup(attr[k]->
- attrvalue[j]);
+ attrvalue[j]);
}
if (modval[j] == NULL)
goto free_memory;
@@ -686,10 +688,26 @@ write_state_machine(
int i = 0;
int ldap_error;
int nopasswd_acct_mgmt = 0;
+ ns_conn_user_t *conn_user = NULL;
while (always) {
switch (state) {
case W_EXIT:
+ /* return the MT connection and free the conn user */
+ if (conn_user != NULL) {
+ if (conn_user->use_mt_conn == B_TRUE) {
+ if (conn_user->ns_error != NULL) {
+ *errorp = conn_user->ns_error;
+ conn_user->ns_error = NULL;
+ return_rc = conn_user->ns_rc;
+ }
+ if (conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(
+ conn_user);
+ }
+ __s_api_conn_user_free(conn_user);
+ }
+
if (connectionId > -1)
DropConnection(connectionId, NS_LDAP_NEW_CONN);
if (ref_list)
@@ -700,7 +718,7 @@ write_state_machine(
case W_INIT:
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
- &followRef, errorp);
+ &followRef, errorp);
if (rc != NS_LDAP_SUCCESS) {
return_rc = rc;
new_state = W_ERROR;
@@ -709,9 +727,8 @@ write_state_machine(
len = strlen(dn);
if (dn[len-1] == COMMATOK)
rc = __s_api_append_default_basedn(
- dn, &target_dn,
- &target_dn_allocated,
- errorp);
+ dn, &target_dn, &target_dn_allocated,
+ errorp);
else
target_dn = dn;
if (rc != NS_LDAP_SUCCESS) {
@@ -722,14 +739,13 @@ write_state_machine(
new_state = GET_CONNECTION;
break;
case GET_CONNECTION:
+ /* identify self as a write user */
+ conn_user = __s_api_conn_user_init(NS_CONN_USER_WRITE,
+ NULL, B_FALSE);
rc = __s_api_getConnection(NULL,
- flags | NS_LDAP_NEW_CONN,
- cred,
- &connectionId,
- &conp,
- errorp,
- do_not_fail_if_new_pwd_reqd,
- nopasswd_acct_mgmt);
+ flags, cred, &connectionId, &conp, errorp,
+ do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
+ conn_user);
/*
* If password control attached
@@ -740,8 +756,7 @@ write_state_machine(
* Reset rc to NS_LDAP_SUCCESS.
*/
if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
- (void) __ns_ldap_freeError(
- errorp);
+ (void) __ns_ldap_freeError(errorp);
*errorp = NULL;
rc = NS_LDAP_SUCCESS;
}
@@ -774,39 +789,39 @@ write_state_machine(
break;
case DO_ADD_SYNC:
rc = ldap_add_ext_s(conp->ld, target_dn,
- mods, NULL, NULL);
+ mods, NULL, NULL);
new_state = GET_RESULT_SYNC;
break;
case DO_DELETE_SYNC:
rc = ldap_delete_ext_s(conp->ld, target_dn,
- NULL, NULL);
+ NULL, NULL);
new_state = GET_RESULT_SYNC;
break;
case DO_MODIFY_SYNC:
rc = ldap_modify_ext_s(conp->ld, target_dn,
- mods, NULL, NULL);
+ mods, NULL, NULL);
new_state = GET_RESULT_SYNC;
break;
case DO_ADD_ASYNC:
rc = ldap_add_ext(conp->ld, target_dn,
- mods, NULL, NULL, &msgid);
+ mods, NULL, NULL, &msgid);
new_state = GET_RESULT_ASYNC;
break;
case DO_DELETE_ASYNC:
rc = ldap_delete_ext(conp->ld, target_dn,
- NULL, NULL, &msgid);
+ NULL, NULL, &msgid);
new_state = GET_RESULT_ASYNC;
break;
case DO_MODIFY_ASYNC:
rc = ldap_modify_ext(conp->ld, target_dn,
- mods, NULL, NULL, &msgid);
+ mods, NULL, NULL, &msgid);
new_state = GET_RESULT_ASYNC;
break;
case GET_RESULT_SYNC:
if (rc != LDAP_SUCCESS) {
Errno = rc;
(void) ldap_get_lderrno(conp->ld,
- NULL, &errmsg);
+ NULL, &errmsg);
/*
* free errmsg if it is an empty string
*/
@@ -822,7 +837,7 @@ write_state_machine(
break;
case GET_RESULT_ASYNC:
rc = ldap_result(conp->ld, msgid, 1,
- (struct timeval *)NULL, &res);
+ (struct timeval *)NULL, &res);
/* if no server response, set Errno */
if (rc == -1) {
(void) ldap_get_option(conp->ld,
@@ -830,9 +845,8 @@ write_state_machine(
new_state = W_LDAP_ERROR;
break;
}
- if (rc == LDAP_RES_ADD ||
- rc == LDAP_RES_MODIFY ||
- rc == LDAP_RES_DELETE) {
+ if (rc == LDAP_RES_ADD || rc == LDAP_RES_MODIFY ||
+ rc == LDAP_RES_DELETE) {
new_state = PARSE_RESULT;
break;
} else {
@@ -846,10 +860,8 @@ write_state_machine(
* and the last "1" is to free
* the result (res)
*/
- rc = ldap_parse_result(conp->ld,
- res, &Errno,
- NULL, &errmsg,
- &referrals, NULL, 1);
+ rc = ldap_parse_result(conp->ld, res, &Errno,
+ NULL, &errmsg, &referrals, NULL, 1);
/*
* free errmsg if it is an empty string
*/
@@ -871,9 +883,8 @@ write_state_machine(
for (i = 0; referrals[i] != NULL; i++) {
/* add to referral list */
rc = __s_api_addRefInfo(&ref_list,
- referrals[i],
- NULL, NULL, NULL,
- conp->ld);
+ referrals[i], NULL, NULL, NULL,
+ conp->ld);
if (rc != NS_LDAP_SUCCESS) {
__s_api_deleteRefInfo(ref_list);
ref_list = NULL;
@@ -913,14 +924,22 @@ write_state_machine(
(void) __ns_ldap_freeError(errorp);
if (connectionId > -1)
DropConnection(connectionId, NS_LDAP_NEW_CONN);
+
+ /* set it up to use a referral connection */
+ if (conn_user != NULL) {
+ /*
+ * If an MT connection is being used,
+ * return it to the pool.
+ */
+ if (conn_user->conn_mt != NULL)
+ __s_api_conn_mt_return(conn_user);
+
+ conn_user->referral = B_TRUE;
+ }
rc = __s_api_getConnection(current_ref->refHost,
- 0,
- cred,
- &connectionId,
- &conp,
- errorp,
- do_not_fail_if_new_pwd_reqd,
- nopasswd_acct_mgmt);
+ 0, cred, &connectionId, &conp, errorp,
+ do_not_fail_if_new_pwd_reqd,
+ nopasswd_acct_mgmt, conn_user);
/*
* If password control attached
@@ -931,8 +950,7 @@ write_state_machine(
* Reset rc to NS_LDAP_SUCCESS.
*/
if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
- (void) __ns_ldap_freeError(
- errorp);
+ (void) __ns_ldap_freeError(errorp);
*errorp = NULL;
rc = NS_LDAP_SUCCESS;
}
@@ -946,28 +964,28 @@ write_state_machine(
* Get LDAP error code from errorp.
*/
if (*errorp != NULL) {
+ ns_write_state_t get_ref =
+ GET_REFERRAL_CONNECTION;
+
ldap_error = (*errorp)->status;
if (ldap_error == LDAP_BUSY ||
ldap_error == LDAP_UNAVAILABLE ||
ldap_error ==
- LDAP_UNWILLING_TO_PERFORM ||
+ LDAP_UNWILLING_TO_PERFORM ||
ldap_error == LDAP_CONNECT_ERROR ||
ldap_error == LDAP_SERVER_DOWN) {
current_ref = current_ref->next;
if (current_ref == NULL) {
- /* no more referral */
- /* to follow */
- new_state = W_ERROR;
- } else {
- new_state =
- GET_REFERRAL_CONNECTION;
- }
+ /* no more referral to follow */
+ new_state = W_ERROR;
+ } else
+ new_state = get_ref;
/*
* free errorp before going to
* next referral
*/
(void) __ns_ldap_freeError(
- errorp);
+ errorp);
*errorp = NULL;
break;
}
@@ -981,6 +999,8 @@ write_state_machine(
__s_api_deleteRefInfo(ref_list);
ref_list = NULL;
new_state = W_ERROR;
+ if (conn_user != NULL)
+ conn_user->referral = B_FALSE;
break;
}
/* target DN may changed due to referrals */
@@ -1008,34 +1028,39 @@ write_state_machine(
* password management
*/
passwd_mgmt =
- __s_api_contain_passwd_control_oid(
- conp->controls);
+ __s_api_contain_passwd_control_oid(
+ conp->controls);
if (passwd_mgmt)
pwd_status =
- __s_api_set_passwd_status(
- Errno, errmsg);
+ __s_api_set_passwd_status(
+ Errno, errmsg);
ldap_memfree(errmsg);
errmsg = NULL;
}
(void) sprintf(errstr,
- gettext(ldap_err2string(Errno)));
+ gettext(ldap_err2string(Errno)));
err = strdup(errstr);
if (pwd_status != NS_PASSWD_GOOD) {
MKERROR_PWD_MGMT(*errorp, Errno, err,
- pwd_status, 0, NULL);
+ pwd_status, 0, NULL);
} else {
MKERROR(LOG_INFO, *errorp, Errno, err, NULL);
}
+ if (conn_user != NULL &&
+ (Errno == LDAP_SERVER_DOWN ||
+ Errno == LDAP_CONNECT_ERROR)) {
+ __s_api_conn_mt_close(conn_user, Errno, errorp);
+ }
return_rc = NS_LDAP_INTERNAL;
new_state = W_EXIT;
break;
case W_ERROR:
default:
(void) sprintf(errstr,
- gettext("Internal write State machine exit"
- " (state = %d, rc = %d)."),
- err_state, return_rc);
+ gettext("Internal write State machine exit"
+ " (state = %d, rc = %d)."),
+ err_state, return_rc);
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, return_rc, err, NULL);
new_state = W_EXIT;
@@ -1044,6 +1069,12 @@ write_state_machine(
if (new_state == W_ERROR)
err_state = state;
+
+ if (conn_user != NULL && conn_user->bad_mt_conn == B_TRUE) {
+ __s_api_conn_mt_close(conn_user, 0, NULL);
+ new_state = W_EXIT;
+ }
+
state = new_state;
}
@@ -1391,7 +1422,7 @@ __s_cvt_passwd(const void *data, char **rdn,
}
if (ptr->pw_passwd != NULL &&
- ptr->pw_passwd[0] != '\0') {
+ ptr->pw_passwd[0] != '\0') {
rc = __s_add_attr(e, "userPassword", ptr->pw_passwd);
if (rc != NS_LDAP_SUCCESS) {
__s_cvt_freeEntryRdn(entry, rdn);
@@ -1413,7 +1444,7 @@ __s_cvt_passwd(const void *data, char **rdn,
return (rc);
}
if (ptr->pw_gecos != NULL &&
- ptr->pw_gecos[0] != '\0') {
+ ptr->pw_gecos[0] != '\0') {
rc = __s_add_attr(e, "gecos", ptr->pw_gecos);
if (rc != NS_LDAP_SUCCESS) {
__s_cvt_freeEntryRdn(entry, rdn);
@@ -1427,7 +1458,7 @@ __s_cvt_passwd(const void *data, char **rdn,
return (rc);
}
if (ptr->pw_shell != NULL &&
- ptr->pw_shell[0] != '\0') {
+ ptr->pw_shell[0] != '\0') {
rc = __s_add_attr(e, "loginShell", ptr->pw_shell);
if (rc != NS_LDAP_SUCCESS) {
__s_cvt_freeEntryRdn(entry, rdn);
@@ -1439,6 +1470,118 @@ __s_cvt_passwd(const void *data, char **rdn,
}
/*
+ * Conversion: project
+ * Input format: struct project
+ * Exported objectclass: SolarisProject
+ */
+static int
+__s_cvt_project(const void *data, char **rdn,
+ ns_ldap_entry_t **entry, ns_ldap_error_t **errorp)
+{
+ ns_ldap_entry_t *e;
+ int rc;
+ char trdn[RDNSIZE];
+
+ /* routine specific */
+ struct project *ptr;
+ int max_attr = 9;
+ char ibuf[11];
+ static char *oclist[] = {
+ "SolarisProject",
+ "top",
+ NULL
+ };
+
+ if (data == NULL || rdn == NULL || entry == NULL || errorp == NULL)
+ return (NS_LDAP_OP_FAILED);
+
+ *entry = e = __s_mk_entry(oclist, max_attr);
+ if (e == NULL)
+ return (NS_LDAP_MEMORY);
+
+ /* Convert the structure */
+ ptr = (struct project *)data;
+
+ if (ptr->pj_name == NULL || ptr->pj_projid > MAXUID) {
+ __ns_ldap_freeEntry(e);
+ *entry = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ /* Create an appropriate rdn */
+ (void) snprintf(trdn, RDNSIZE, "SolarisProjectName=%s", ptr->pj_name);
+ *rdn = strdup(trdn);
+ if (*rdn == NULL) {
+ __ns_ldap_freeEntry(e);
+ *entry = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+
+ /* Error check the data and add the attributes */
+
+ /* Project name */
+ rc = __s_add_attr(e, "SolarisProjectName", ptr->pj_name);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+
+ /*
+ * Project ID:
+ * ibuf is 11 chars big, which should be enough for string
+ * representation of 32bit number + nul-car
+ */
+ if (snprintf(ibuf, sizeof (ibuf), "%u", ptr->pj_projid) < 0) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ rc = __s_add_attr(e, "SolarisProjectID", ibuf);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+
+ /* Comment/Description */
+ if (ptr->pj_comment != NULL && ptr->pj_comment[0] != '\0') {
+ rc = __s_add_attr(e, "description", ptr->pj_comment);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+ }
+
+ /* Attributes */
+ if (ptr->pj_attr != NULL && ptr->pj_attr[0] != '\0') {
+ rc = __s_add_attr(e, "SolarisProjectAttr", ptr->pj_attr);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+ }
+
+ /* Users */
+ if (ptr->pj_users != NULL) {
+ rc = __s_add_attrlist(e, "memberUid", ptr->pj_users);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+ }
+
+ /* Groups */
+ if (ptr->pj_groups != NULL) {
+ rc = __s_add_attrlist(e, "memberGid", ptr->pj_groups);
+ if (rc != NS_LDAP_SUCCESS) {
+ __s_cvt_freeEntryRdn(entry, rdn);
+ return (rc);
+ }
+ }
+
+
+
+ return (NS_LDAP_SUCCESS);
+}
+/*
* Conversion: shadow
* Input format: struct shadow
* Exported objectclass: shadowAccount
@@ -2019,7 +2162,7 @@ __s_cvt_services(const void *data, char **rdn,
/* Create an appropriate rdn */
(void) snprintf(trdn, RDNSIZE, "cn=%s+ipServiceProtocol=%s",
- ptr->s_name, ptr->s_proto);
+ ptr->s_name, ptr->s_proto);
*rdn = strdup(trdn);
if (*rdn == NULL) {
__ns_ldap_freeEntry(e);
@@ -2111,10 +2254,10 @@ __s_cvt_networks(const void *data, char **rdn,
}
(void) snprintf(cp, sizeof (cp), "%d.%d.%d.%d",
- (ptr->n_net & 0xFF000000) >> 24,
- (ptr->n_net & 0x00FF0000) >> 16,
- (ptr->n_net & 0x0000FF00) >> 8,
- (ptr->n_net & 0x000000FF));
+ (ptr->n_net & 0xFF000000) >> 24,
+ (ptr->n_net & 0x00FF0000) >> 16,
+ (ptr->n_net & 0x0000FF00) >> 8,
+ (ptr->n_net & 0x000000FF));
/* Create an appropriate rdn */
(void) snprintf(trdn, RDNSIZE, "ipNetworkNumber=%s", cp);
@@ -2505,7 +2648,7 @@ modify_ethers_bootp(
ns_ldap_error_t *new_errorp = NULL;
if (rdn == NULL || fulldn == NULL || attrlist == NULL ||
- errorp == NULL || service == NULL)
+ errorp == NULL || service == NULL)
return (NS_LDAP_OP_FAILED);
bzero(&new_attrlist, sizeof (new_attrlist));
@@ -2517,19 +2660,17 @@ modify_ethers_bootp(
new_attrlist[0]->value_count = 1;
if (strcasecmp(service, "ethers") == NULL) {
(void) snprintf(&filter[0], sizeof (filter),
- "(&(objectClass=ieee802Device)(%s))",
- rdn);
+ "(&(objectClass=ieee802Device)(%s))", rdn);
new_attrlist[0]->attrvalue[0] = "ieee802Device";
} else {
(void) snprintf(&filter[0], sizeof (filter),
- "(&(objectClass=bootableDevice)(%s))",
- rdn);
+ "(&(objectClass=bootableDevice)(%s))", rdn);
new_attrlist[0]->attrvalue[0] = "bootableDevice";
}
rc = __ns_ldap_list(service, filter, NULL, (const char **)NULL,
- NULL, NS_LDAP_SCOPE_SUBTREE, &resultp, &new_errorp,
- NULL, NULL);
+ NULL, NS_LDAP_SCOPE_SUBTREE, &resultp, &new_errorp,
+ NULL, NULL);
switch (rc) {
case NS_LDAP_SUCCESS:
@@ -2549,9 +2690,9 @@ modify_ethers_bootp(
/* aptr2 needed here to avoid lint warning */
aptr2 = (ns_ldap_attr_t *)*aptr++;
if ((strcasecmp(aptr2->attrname, "cn") != 0) &&
- (strcasecmp(aptr2->attrname,
- "objectclass") != 0)) {
- new_attrlist[i++] = (ns_ldap_attr_t *)aptr2;
+ (strcasecmp(aptr2->attrname,
+ "objectclass") != 0)) {
+ new_attrlist[i++] = (ns_ldap_attr_t *)aptr2;
}
}
@@ -2565,7 +2706,7 @@ modify_ethers_bootp(
/* clean errorp first */
(void) __ns_ldap_freeError(errorp);
rc = __ns_ldap_addAttr(service, fulldn, aptr, cred, flags,
- errorp);
+ errorp);
break;
default:
/*
@@ -2753,7 +2894,7 @@ __s_cvt_auto_mount(const void *data, char **rdn,
/* determine profile version number */
rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, &paramVal, errorp);
if (paramVal && *paramVal &&
- strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0)
+ strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0)
version1 = 1;
if (paramVal)
(void) __ns_ldap_freeParam(&paramVal);
@@ -2781,7 +2922,7 @@ __s_cvt_auto_mount(const void *data, char **rdn,
/* Create an appropriate rdn */
(void) snprintf(trdn, RDNSIZE, version1 ? "cn=%s" : "automountKey=%s",
- ptr->key);
+ ptr->key);
*rdn = strdup(trdn);
if (*rdn == NULL) {
__ns_ldap_freeEntry(e);
@@ -2791,7 +2932,7 @@ __s_cvt_auto_mount(const void *data, char **rdn,
if (ptr->key != '\0') {
rc = __s_add_attr(e, version1 ? "cn" : "automountKey",
- (char *)ptr->key);
+ (char *)ptr->key);
if (rc != NS_LDAP_SUCCESS) {
__s_cvt_freeEntryRdn(entry, rdn);
return (rc);
@@ -2799,7 +2940,7 @@ __s_cvt_auto_mount(const void *data, char **rdn,
}
rc = __s_add_attr(e, version1 ? "nisMapEntry" : "automountInformation",
- (char *)ptr->value);
+ (char *)ptr->value);
if (rc != NS_LDAP_SUCCESS) {
__s_cvt_freeEntryRdn(entry, rdn);
return (rc);
@@ -2811,7 +2952,7 @@ __s_cvt_auto_mount(const void *data, char **rdn,
*/
mappedschema = __ns_ldap_getMappedObjectClass("automount", "automount");
if (mappedschema && mappedschema[0] &&
- strcasecmp(mappedschema[0], "nisObject") == 0)
+ strcasecmp(mappedschema[0], "nisObject") == 0)
version1 = 1;
if (mappedschema)
__s_api_free2dArray(mappedschema);
@@ -3411,6 +3552,7 @@ static __ns_cvt_type_t __s_cvtlist[] = {
{ NS_LDAP_TYPE_AUUSER, AE, __s_cvt_audituser },
{ NS_LDAP_TYPE_TNRHTP, 0, __s_cvt_tnrhtp },
{ NS_LDAP_TYPE_TNRHDB, 0, __s_cvt_tnrhdb },
+ { NS_LDAP_TYPE_PROJECT, 0, __s_cvt_project },
{ NULL, 0, NULL },
};
@@ -3570,15 +3712,15 @@ int __ns_ldap_addTypedEntry(
cred, flags, errorp);
else {
rc = __ns_ldap_repAttr(service, fulldn, modattrlist,
- cred, flags, errorp);
+ cred, flags, errorp);
if (rc == NS_LDAP_INTERNAL && *errorp &&
(*errorp)->status == LDAP_NO_SUCH_OBJECT) {
(void) __ns_ldap_freeError(errorp);
rc = __ns_ldap_addEntry(service, fulldn,
entry, cred, flags, errorp);
if (rc == NS_LDAP_INTERNAL && *errorp &&
- (*errorp)->status ==
- LDAP_OBJECT_CLASS_VIOLATION)
+ (*errorp)->status ==
+ LDAP_OBJECT_CLASS_VIOLATION)
(*errorp)->status = LDAP_NO_SUCH_OBJECT;
}
}