diff options
Diffstat (limited to 'usr/src/lib/libsldap/common')
| -rw-r--r-- | usr/src/lib/libsldap/common/mapfile-vers | 8 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_cache_door.c | 56 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_cache_door.h | 68 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_common.c | 114 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_config.c | 349 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_confmgr.c | 173 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_connect.c | 2755 | ||||
| -rwxr-xr-x | usr/src/lib/libsldap/common/ns_connmgmt.c | 2631 | ||||
| -rwxr-xr-x | usr/src/lib/libsldap/common/ns_connmgmt.h | 305 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_init.c | 16 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_internal.h | 59 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_reads.c | 518 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_sldap.h | 163 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_standalone.c | 2451 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_writes.c | 402 |
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, ¶m) == -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, - ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { - (void) __ns_ldap_freeError(&errorp); - (void) __ns_ldap_freeParam(¶mVal); - (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, ¶mVal, errorp); + if (paramVal != NULL && *paramVal != NULL) { + tv.tv_sec = **((int **)paramVal); + (void) __ns_ldap_freeParam(¶mVal); + } + 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(¤t_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(¤t_list->nsServers[0]->updateStatus); + if (strcmp(request, NS_CACHE_NEW) == 0 || + current_list->nsServers[0]->info == INFO_STATUS_NEW) { + matched = TRUE; + } + (void) mutex_unlock(¤t_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(¤t_list-> + nsServers[i]->updateStatus); + if (current_list->nsServers[i]->status == + INFO_SERVER_REMOVED) { + (void) mutex_unlock(¤t_list-> + nsServers[i]-> + updateStatus); + continue; + } + (void) mutex_unlock(¤t_list-> + nsServers[i]-> + updateStatus); + + /* + * if the information is new, + * give this server one more chance. + */ + (void) mutex_lock(¤t_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(¤t_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(¤t_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(¤t_list-> + nsServers[i]-> + updateStatus); + if (current_list->nsServers[i]->status == + INFO_SERVER_UP) { + (void) mutex_unlock(¤t_list-> + nsServers[i]-> + updateStatus); + server = current_list->nsServers[i]; + break; + } + } else { + (void) mutex_lock(¤t_list-> + nsServers[i]-> + updateStatus); + if (current_list->nsServers[i]->status == + INFO_SERVER_UP) { + (void) mutex_unlock(¤t_list-> + nsServers[i]-> + updateStatus); + server = current_list->nsServers[i]; + break; + } + } + + (void) mutex_unlock(¤t_list-> + nsServers[i]-> + updateStatus); + } + } + + if (server == NULL) { + (void) rw_unlock(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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, ¶mVal, 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(¶mVal); @@ -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; } } |
