ns_connect.c revision 8142c2b2a11acff39ec7e0576c566a751eb30ba6
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <synch.h>
#include <time.h>
#include <libintl.h>
#include <thread.h>
#include <syslog.h>
#include <nsswitch.h>
#include <nss_dbdefs.h>
#include "solaris-priv.h"
#include "solaris-int.h"
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
#include "ldappr.h"
#include <fcntl.h>
#include <procfs.h>
#include <unistd.h>
extern unsigned int _sleep(unsigned int);
LDAPControl **, LDAPControl **);
int, ns_ldap_error_t **, int, int);
static void
/*
* sessionLock, wait4session, sessionTid
* 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 int wait4session = 0;
static thread_t sessionTid = 0;
int MTperConn = 1;
static int sessionPoolSize = 0;
static int sharedConnNumber = 0;
static int nscdChecked = 0;
static int isNscd = 0;
/*
* SSF values are for SASL integrity & privacy.
* JES DS5.2 does not support this feature but DS6 does.
* The values between 0 and 65535 can work with both server versions.
*/
#define MAX_SASL_SSF 65535
#define MIN_SASL_SSF 0
/* 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;
};
/* destructor */
void
ns_tsd_cleanup(void *key) {
return;
}
}
}
/* Callback function for allocating a mutex */
static void *
ns_mutex_alloc(void)
{
}
}
return (mutexp);
}
/* Callback function for freeing a mutex */
static void
ns_mutex_free(void *mutexp)
{
}
/*
* Function for setting up thread-specific data
* where per thread LDAP error is stored
*/
static int
{
void *tsd;
int rc;
/* return success if TSD already set */
return (0);
/* allocate and set TSD */
return (-1);
if (rc != 0) { /* must be ENOMEM */
return (-1);
}
return (0);
}
/* Callback function for setting the per thread LDAP error */
/*ARGSUSED*/
static void
{
struct ldap_error *le;
" %d", errno);
return;
}
/* play safe, do nothing if TSD pointer is NULL */
return;
}
}
}
int
/* check and allocate the thread-specific data for using a shared connection */
{
if (tsd_setup() != 0)
return (NS_LDAP_MEMORY);
return (NS_LDAP_SUCCESS);
}
/* Callback function for getting the per thread LDAP error */
/*ARGSUSED*/
static int
{
struct ldap_error *le;
" %d", errno);
return (errno);
}
/* play safe, return NULL error data, if TSD pointer is NULL */
le = &ldap_error_NULL;
}
}
}
/* Callback function for setting per thread errno */
static void
{
}
/* 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
*/
static int
{
struct ldap_thread_fns tfns;
struct ldap_extra_thread_fns extrafns;
int rc;
/*
* Set the function pointers for dealing with mutexes
* and error information
*/
/*
* Set up this session to use those function pointers
*/
(void *) &tfns);
if (rc < 0) {
"(LDAP_OPT_THREAD_FN_PTRS)");
return (-1);
}
/*
* Set the function pointers for working with semaphores
*/
sizeof (struct ldap_extra_thread_fns));
/* Set up this session to use those function pointers */
(void *) &extrafns);
if (rc < 0) {
"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
return (-1);
}
return (0);
}
static void
void *tsd;
/* set up to share this connection among threads */
if (MTperConn == 1) {
if (tsd_setup() == -1) {
"to set up TSD\n", t);
} else {
/* multiple threads per connection not supported */
"threads per connection not "
"supported\n", t);
MTperConn = 0;
}
}
}
}
/*
* If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce
* constant reconnects for many operations.
* A more complete solution is to develop true connection pooling.
* However, this is much better than a new connection for every request.
*/
int
__s_api_nscd_proc(void)
{
int ret;
int fd;
/* Don't bother checking if this process isn't root. */
/* It can't be nscd */
if (getuid() != 0)
return (0);
return (isNscd);
}
(void) mutex_lock(&nscdLock);
(void) mutex_unlock(&nscdLock);
return (isNscd);
}
nscdChecked = 1;
checkedPid = my_pid;
isNscd = 0;
/* process runs as root and is named nscd */
/* that's good enough for now */
isNscd = 1;
}
}
}
(void) mutex_unlock(&nscdLock);
return (isNscd);
}
/*
* This function requests a server from the cache manager through
* the door functionality
*/
static int
{
union {
char s_b[DOORBUFFERSIZE];
} space;
int ndata;
int adata;
const char *ireq;
char *dptr;
char **servers;
return (NS_LDAP_OP_FAILED);
}
ireq = NS_CACHE_NEW;
else
}
return (NS_LDAP_MEMORY);
len)
return (NS_LDAP_MEMORY);
return (NS_LDAP_MEMORY);
return (NS_LDAP_MEMORY);
}
case 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 */
if (rc != NS_LDAP_SUCCESS) {
}
return (rc);
}
gettext("No server found in configuration"));
return (NS_LDAP_CONFIG);
}
return (NS_LDAP_MEMORY);
}
return (NS_LDAP_SUCCESS);
case NOTFOUND:
default:
return (NS_LDAP_OP_FAILED);
}
/* copy info from door call return structure here */
/* Get the host */
"ldap_cachemgr"));
return (NS_LDAP_OP_FAILED);
}
return (NS_LDAP_MEMORY);
}
/* Get the host FQDN format */
"returned from ldap_cachemgr"));
return (NS_LDAP_OP_FAILED);
}
return (NS_LDAP_MEMORY);
}
}
mcnt = 0;
ccnt = 0;
for (; ; ) {
break;
_SASLMECHANISM_LEN) == 0) {
continue;
dptr++;
sizeof (char *) * (mcnt+2));
}
return (NS_LDAP_MEMORY);
}
}
return (NS_LDAP_MEMORY);
}
mcnt++;
}
_SUPPORTEDCONTROL_LEN) == 0) {
continue;
dptr++;
sizeof (char *) * (ccnt+2));
}
return (NS_LDAP_MEMORY);
}
}
return (NS_LDAP_MEMORY);
}
ccnt++;
}
}
}
}
/* clean up door call */
}
return (NS_LDAP_SUCCESS);
}
/*
* printCred(): prints the credential structure
*/
static void
{
return;
}
if (cred->hostcertpath)
t, cred->hostcertpath);
#ifdef DEBUG
#endif
}
/*
* printConnection(): prints the connection structure
*/
static void
{
return;
if (con->serverAddr) {
t, con->serverAddr);
}
}
/*
* addConnection(): set up a connection so that it can be shared
* among multiple threads and then insert the connection in the
* connection list.
* 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
{
int i, noMTperC = 0;
struct ldap_thread_fns tfns;
void *tsd;
if (!con)
return (-1);
t, con->serverAddr);
if (MTperConn == 1) {
/*
* Make sure ld has proper thread functions and tsd
* is set up.
*/
/*
* 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.
*/
(void *)&tfns) != 0 ||
MTperConn = 0;
noMTperC = 1;
} else {
noMTperC = 1;
}
} else {
noMTperC = 1;
}
(void) rw_wrlock(&sessionPoolLock);
if (sessionPool == NULL) {
sizeof (struct connection **));
if (!sessionPool) {
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
}
;
if (i == sessionPoolSize) {
/* run out of array, need to increase sessionPool */
Connection **cl;
sizeof (Connection *));
if (!cl) {
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
SESSION_CACHE_INC * sizeof (struct connection *));
sessionPool = cl;
"sessionPoolSize to: %d\n",
t, sessionPoolSize);
}
sessionPool[i] = con;
if (noMTperC == 0) {
(void) mutex_lock(&sharedConnNumberLock);
(void) mutex_unlock(&sharedConnNumberLock);
} else
(void) rw_unlock(&sessionPoolLock);
t, i);
/*
* A connection can be shared now, unlock
* the session mutex and let other
* threads try to use this connection or
* get their own.
*/
wait4session = 0;
sessionTid = 0;
(void) mutex_unlock(&sessionLock);
}
return (i + CONID_OFFSET);
}
/*
* See if the specified session matches a currently available
*/
static int
Connection **conp)
{
Connection *cp;
int id;
return (-1);
/* if a new connection is requested, no need to continue */
if (flags & NS_LDAP_NEW_CONN)
return (-1);
if (sessionPool == NULL)
return (-1);
return (-1);
(void) rw_rdlock(&sessionPoolLock);
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
/*
* Make sure the connection has the same type of authentication method
*/
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
(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 */
(void) rw_unlock(&sessionPoolLock);
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
{
Connection *cp;
int rc;
int try;
char **servers;
#ifdef DEBUG
#endif /* DEBUG */
return (-1);
/* if a new connection is requested, no need to continue */
if (flags & NS_LDAP_NEW_CONN)
return (-1);
#ifdef DEBUG
if (serverAddr && *serverAddr)
t, serverAddr);
else
#endif /* DEBUG */
/*
* If multiple threads per connection not supported,
* no sessionPool means no connection
*/
(void) rw_rdlock(&sessionPoolLock);
(void) rw_unlock(&sessionPoolLock);
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);
(void) mutex_unlock(&sharedConnNumberLock);
(void) rw_unlock(&sessionPoolLock);
(void) mutex_lock(&sessionLock);
(void) mutex_lock(&sharedConnNumberLock);
MTperConn == 1)) {
(void) mutex_unlock(&sharedConnNumberLock);
wait4session = 1;
sessionTid = thr_self();
#ifdef DEBUG
"connection ... \n", t);
#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
"exist\n", t);
#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;
for (i = 0; i < sessionPoolSize; ++i) {
if (sessionPool[i] == NULL)
continue;
cp = sessionPool[i];
#ifdef DEBUG
"[%d] ....\n", t, i);
#endif /* DEBUG */
(serverAddr && *serverAddr &&
continue;
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.
*/
(void) __ns_ldap_freeError(&errorp);
(void) __ns_ldap_freeParam(¶mVal);
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
/*
* Do fallback only if preferred servers are defined.
*/
/*
* Find the 1st available server
*/
if (rc != NS_LDAP_SUCCESS) {
/*
* Drop the connection.
* Pass 1 to fini so it won't be locked
* inside _DropConnection
*/
NS_LDAP_NEW_CONN, 1);
(void) rw_unlock(
(void) __ns_ldap_freeError(&errorp);
(void) __ns_ldap_freeParam(
(void ***)&servers);
return (-1);
}
/*
* Test if cp->serverAddr is a
* preferred server.
*/
conn_server_index = -1;
if (strcasecmp(servers[j],
cp->serverAddr) == 0) {
conn_server_index = j;
break;
}
}
/*
* Test if sinfo.server is a preferred
* server.
*/
up_server_index = -1;
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 ==
/*
* 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
*/
NS_LDAP_NEW_CONN, 1);
(void) rw_unlock(
(void) __ns_ldap_freeParam(
(void ***)&servers);
&sinfo);
return (-1);
} else {
/*
* Keep the connection
*/
(void) __ns_ldap_freeParam(
(void ***)&servers);
&sinfo);
}
} else {
(void) rw_unlock(&sessionPoolLock);
"sinfo.server from "
"__s_api_requestServer");
(void) __ns_ldap_freeParam(
(void ***)&servers);
return (-1);
}
}
}
/* found an available connection */
if (MTperConn == 0)
else {
/*
* if connection was established in a different
* process, drop it and get a new one
*/
(void) rw_unlock(&sessionPoolLock);
goto get_conn;
}
/* allocate TSD for per thread ldap error */
/* if we got TSD, this connection is shared */
if (rc != -1)
(void) rw_unlock(&sessionPoolLock);
return (-1);
}
}
(void) rw_unlock(&sessionPoolLock);
#ifdef DEBUG
#endif /* DEBUG */
return (i + CONID_OFFSET);
}
(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.
*/
(void) mutex_lock(&sessionLock);
(void) mutex_unlock(&sessionLock);
(void) rw_rdlock(&sessionPoolLock);
#ifdef DEBUG
"pool again\n", t);
#endif /* DEBUG */
try++;
goto check_again;
} else {
"%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
"none being opened, get connection ...\n", t);
#endif /* DEBUG */
return (-1);
} else {
"error %d", rc);
return (-1);
}
}
/*
* Free a Connection structure
*/
static void
{
return;
if (con->serverAddr)
if (con->saslMechanisms) {
}
}
}
/*
* Find a connection matching the passed in criteria. If an open
* connection with that criteria exists use it, otherwise open a
* new connection.
* Success: returns the pointer to the Connection structure
* Failure: returns NULL, error code and message should be in errorp
*/
static int
{
int passwd_mgmt = 0;
int totalbad = 0; /* Number of servers contacted unsuccessfully */
short memerr = 0; /* Variable for tracking memory allocation */
return (NS_LDAP_INVALID_PARAM);
/* connection found in cache */
#ifdef DEBUG
#endif /* DEBUG */
return (NS_LDAP_SUCCESS);
}
} else {
}
if (serverAddr) {
gettext("makeConnection: unable to get "
"server information for %s"), serverAddr);
return (NS_LDAP_OP_FAILED);
}
goto create_con;
} else {
return (rc);
}
}
/* No cached connection, create one */
for (; ; ) {
hReq = NS_CACHE_NEW;
else
/* Log the error */
if (*errorp) {
"unable to make LDAP connection, "
"request for a server failed"),
}
if (host)
return (NS_LDAP_OP_FAILED);
}
if (host)
return (NS_LDAP_MEMORY);
}
/* check if server supports password management */
/* check if server supports password less account mgmt */
if (nopasswd_acct_mgmt &&
"provide account information without password",
host);
return (NS_LDAP_OP_FAILED);
}
/* make the connection */
/* if success, go to create connection structure */
if (rc == NS_LDAP_SUCCESS ||
rc == NS_LDAP_SUCCESS_WITH_INFO) {
break;
}
/*
* If not able to reach the server, inform the ldap
* cache manager that the server should be removed
* from its server list. Thus, the manager will not
* return this server on the next get-server request
* and will also reduce the server list refresh TTL,
* so that it will find out sooner when the server
* is up again.
*/
/* Reset memory allocation error */
memerr = 0;
/*
* We contacted a server that we could
* not either authenticate to or contact.
* If it is due to authentication, then
* we need to try the server again. So,
* do not remove the server yet, but
* add it to the bad server list.
* The caller routine will remove
* the servers if:
* a). A good server is found or
* b). All the possible methods
* are tried without finding
* a good server
*/
(sizeof (char *) * NUMTOMALLOC))) {
memerr = 1;
}
/* Allocate memory in chunks of NUMTOMALLOC */
} else if ((totalbad % NUMTOMALLOC) ==
NUMTOMALLOC - 1) {
char **tmpptr;
*badsrvrs,
(sizeof (char *) * NUMTOMALLOC *
memerr = 1;
} else {
}
}
/*
* Store host only if there were no unsuccessful
* memory allocations above
*/
if (!memerr &&
memerr = 1;
totalbad--;
}
}
}
/* else, cleanup and go for the next server */
/* Return if we had memory allocation errors */
if (memerr)
return (NS_LDAP_MEMORY);
if (*errorp) {
/*
* If openConnection() failed due to
* password policy, or invalid credential,
* keep *errorp and exit
*/
return (rc);
} else {
(void) __ns_ldap_freeError(errorp);
}
}
}
/* we have created ld, setup con structure */
if (host)
/*
* If password control attached in **errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure
*/
if (*errorp) {
(void) __ns_ldap_freeError(errorp);
}
return (NS_LDAP_MEMORY);
}
}
/*
* If password control attached in **errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure
*/
if (*errorp) {
(void) __ns_ldap_freeError(errorp);
}
return (NS_LDAP_MEMORY);
}
/*
* If password control attached in **errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure
*/
if (*errorp) {
(void) __ns_ldap_freeError(errorp);
}
return (NS_LDAP_MEMORY);
}
#ifdef DEBUG
#endif /* DEBUG */
return (exit_rc);
}
/*
* Return the specified connection to the pool. If necessary
* delete the connection.
*/
static void
{
Connection *cp;
int id;
#ifdef DEBUG
#endif /* DEBUG */
return;
#ifdef DEBUG
"Dropping connection cID=%d flag=0x%x, fini = %d\n",
#endif /* DEBUG */
if (use_lock)
(void) rw_wrlock(&sessionPoolLock);
/* sanity check before removing */
#ifdef DEBUG
"need to remove (fini = %d, cp = %p)\n", t,
else
"need to remove (fini = %d, cp = %p, shared = %d)"
#endif /* DEBUG */
if (use_lock)
(void) rw_unlock(&sessionPoolLock);
return;
}
if (!fini &&
((flag & NS_LDAP_KEEP_CONN) ||
(MTperConn == 0 && __s_api_nscd_proc()) ||
MTperConn)) {
#ifdef DEBUG
#endif /* DEBUG */
/* release Connection (keep alive) */
if (use_lock)
(void) rw_unlock(&sessionPoolLock);
} else {
/* delete Connection (disconnect) */
#ifdef DEBUG
"longer available (fini = %d, shared = %d)\n",
#endif /* DEBUG */
/*
* Mark this connection not available and decrement
* sharedConnNumber. There could be multiple threads
* sharing this connection so decrement
* sharedConnNumber only once per connection.
*/
(void) mutex_lock(&sharedConnNumberLock);
(void) mutex_unlock(&sharedConnNumberLock);
}
}
#ifdef DEBUG
"(fini = %d, shared = %d)\n",
#endif /* DEBUG */
}
if (use_lock)
(void) rw_unlock(&sessionPoolLock);
}
}
void
{
}
/*
* This routine is called after a bind operation is
* done in openConnection() to process the password
* management information, if any.
*
* Input:
* bind_type: "simple" or "sasl/DIGEST-MD5"
* ldaprc : ldap rc from the ldap bind operation
* controls : controls returned by the server
* errmsg : error message from the server
* fail_if_new_pwd_reqd:
* flag indicating if connection should be open
* when password needs to change immediately
* passwd_mgmt:
* flag indicating if server supports password
*
* Output : ns_ldap_error structure, which may contain
* password status and number of seconds until
* expired
*
* return rc:
* NS_LDAP_EXTERNAL: error, connection should not open
* NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
* NS_LDAP_SUCCESS: OK to open connection
*
*/
static int
int fail_if_new_pwd_reqd,
int passwd_mgmt)
{
int exit_rc;
int sec_until_exp = 0;
/*
* errmsg may be an empty string,
* even if ldaprc is LDAP_SUCCESS,
* free the empty string if that's the case
*/
if (errmsg &&
}
if (ldaprc != LDAP_SUCCESS) {
/*
* try to map ldap rc and error message to
* a password status
*/
if (errmsg) {
if (passwd_mgmt)
}
gettext("openConnection: "
"%s bind failed "
if (pwd_status != NS_PASSWD_GOOD) {
pwd_status, 0, NULL);
} else {
NULL);
}
if (controls)
return (NS_LDAP_INTERNAL);
}
/*
* ldaprc is LDAP_SUCCESS,
* process the password management controls, if any
*/
if (controls && passwd_mgmt) {
/*
* The control with the OID
* 2.16.840.1.113730.3.4.4 (or
* LDAP_CONTROL_PWEXPIRED, as defined
* in the ldap.h header file) is the
* expired password control.
*
* This control is used if the server
* is configured to require users to
* change their passwords when first
* logging in and whenever the
* passwords are reset.
*
* If the user is logging in for the
* first time or if the user's
* password has been reset, the
* server sends this control to
* indicate that the client needs to
* change the password immediately.
*
* At this point, the only operation
* that the client can perform is to
* change the user's password. If the
* client requests any other LDAP
* operation, the server sends back
* an LDAP_UNWILLING_TO_PERFORM
* result code with an expired
* password control.
*
* The control with the OID
* 2.16.840.1.113730.3.4.5 (or
* LDAP_CONTROL_PWEXPIRING, as
* defined in the ldap.h header file)
* is the password expiration warning
* control.
*
* This control is used if the server
* is configured to expire user
* passwords after a certain amount
* of time.
*
* The server sends this control back
* to the client if the client binds
* using a password that will soon
* expire. The ldctl_value field of
* the LDAPControl structure
* specifies the number of seconds
* before the password will expire.
*/
LDAP_CONTROL_PWEXPIRED) == 0) {
/*
* if the caller wants this bind
* to fail, set up the error info.
* If call to this function is
* for searching the LDAP directory,
* e.g., __ns_ldap_list(),
* there's really no sense to
* let a connection open and
* then fail immediately afterward
* on the LDAP search operation with
* the LDAP_UNWILLING_TO_PERFORM rc
*/
if (fail_if_new_pwd_reqd) {
sizeof (errstr),
"openConnection: "
"%s bind "
"failed "
"- password "
"expired. It "
" needs to change "
"immediately!"),
0,
NULL);
} else {
NULL,
0,
NULL);
exit_rc =
}
break;
LDAP_CONTROL_PWEXPIRING) == 0) {
if ((*ctrl)->
ldctl_value.bv_len > 0 &&
(*ctrl)->
NULL,
NULL);
exit_rc =
break;
}
}
}
if (controls)
return (exit_rc);
}
static int
{
enum __nsw_parse_err pserr;
struct __nsw_switchconfig *conf;
struct __nsw_lookup *lkp;
const char *name;
int found = 0;
return (-1);
}
/* check for skip and count other backends */
found = 1;
break;
}
}
return (found);
}
static int
int fail_if_new_pwd_reqd, int passwd_mgmt)
{
char *digest_md5_name;
const char *s;
int ldapVersion = LDAP_VERSION3;
int derefOption = LDAP_DEREF_ALWAYS;
int zero = 0;
int rc;
int errnum = 0;
int msgId;
char *sslServerAddr;
char *s1;
case NS_LDAP_AUTH_NONE:
case NS_LDAP_AUTH_SIMPLE:
case NS_LDAP_AUTH_SASL:
break;
case NS_LDAP_AUTH_TLS:
useSSL = 1;
case NS_LDAP_TLS_NONE:
break;
case NS_LDAP_TLS_SIMPLE:
break;
case NS_LDAP_TLS_SASL:
break;
default:
gettext("openConnection: unsupported "
"TLS authentication method "
return (NS_LDAP_INTERNAL);
}
break;
default:
gettext("openConnection: unsupported "
NULL);
return (NS_LDAP_INTERNAL);
}
if (useSSL) {
const char *hostcertpath;
#ifdef DEBUG
thr_self());
#endif /* DEBUG */
timeoutMilliSec) != LDAP_SUCCESS) {
gettext("openConnection: failed to initialize "
"TLS security"));
return (NS_LDAP_INTERNAL);
}
if (hostcertpath == NULL) {
}
if (hostcertpath == NULL)
return (NS_LDAP_MEMORY);
if (alloc_hcp)
gettext("openConnection: failed to initialize "
"TLS security (%s)"),
return (NS_LDAP_INTERNAL);
}
if (alloc_hcp)
/* determine if the host name contains a port number */
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.
*/
if (sslServerAddr == NULL)
return (NS_LDAP_MEMORY);
*s1 = '\0';
gettext("openConnection: cannot use tls with %s. "
"Trying %s"),
} else
sslServerAddr = (char *)serverAddr;
if (sslServerAddr != serverAddr)
gettext("openConnection: failed to connect "
return (NS_LDAP_INTERNAL);
}
} else {
#ifdef DEBUG
thr_self());
#endif /* DEBUG */
/*
* 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
* will fail. So it needs to be parsed to construct
*
* For useSSL case above, it already parses port so
* no need to parse serverAddr
*/
*end = '\0';
}
/* Warning message IF cannot connect to host(s) */
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 */
"ldap") != 0) {
gettext("openConnection: "
"unrecoverable gethostname "
"recursion detected "
"in /etc/nsswitch.conf"));
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
}
}
}
/*
* set LDAP_OPT_REFERRALS to OFF.
* This library will handle the referral itself
* based on API flags or configuration file
* specification. If this option is not set
* to OFF, libldap will never pass the
* referral info up to this library
*/
/* retry if LDAP I/O was interrupted */
switch (bindType) {
case NS_LDAP_AUTH_NONE:
#ifdef DEBUG
thr_self());
#endif /* DEBUG */
break;
case NS_LDAP_AUTH_SIMPLE:
"missing credentials for Simple bind"));
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
#ifdef DEBUG
thr_self());
#endif /* DEBUG */
if (msgId == -1) {
(void *)&errnum);
gettext("openConnection: simple bind failed "
(void) ldap_unbind(ld);
NULL);
return (NS_LDAP_INTERNAL);
}
(void *)&errnum);
gettext("openConnection: simple bind failed "
(void) ldap_msgfree(resultMsg);
(void) ldap_unbind(ld);
NULL);
return (NS_LDAP_INTERNAL);
}
/*
* get ldaprc, controls, and error msg
*/
if (rc != LDAP_SUCCESS) {
gettext("openConnection: simple bind failed "
"- unable to parse result"));
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
/* process the password management info, if any */
if (pwd_rc == NS_LDAP_INTERNAL) {
(void) ldap_unbind(ld);
return (pwd_rc);
}
if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
return (pwd_rc);
}
break;
case NS_LDAP_AUTH_SASL:
gettext("openConnection: SASL options are "
"not supported (%d) for non-GSSAPI sasl bind"),
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
gettext("openConnection: missing "
"credentials for SASL bind"));
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
}
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
* 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.
*/
(void) ldap_get_option(ld,
LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
gettext("openConnection: "
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
break;
case NS_LDAP_SASL_DIGEST_MD5:
/* 5 = strlen("dn: ") + 1 */
if (digest_md5_name == NULL) {
(void) ldap_unbind(ld);
return (NS_LDAP_MEMORY);
}
(void) ldap_get_option(ld,
LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
gettext("openConnection: "
"DIGEST-MD5 bind failed - %s"),
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
/*
* get ldaprc, controls, and error msg
*/
if (rc != LDAP_SUCCESS) {
gettext("openConnection: "
"DIGEST-MD5 bind failed "
"- unable to parse result"));
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
/* process the password management info, if any */
if (pwd_rc == NS_LDAP_INTERNAL) {
(void) ldap_unbind(ld);
return (pwd_rc);
}
if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
return (pwd_rc);
}
break;
case NS_LDAP_SASL_GSSAPI:
if (sasl_gssapi_inited == 0) {
if (rc != NS_LDAP_SUCCESS) {
gettext("openConnection: "
"GSSAPI initialization "
"failed"));
(void) ldap_unbind(ld);
return (rc);
}
}
(void) memset(&sasl_param, 0,
sizeof (ns_sasl_cb_param_t));
(void *)&min_ssf);
(void *)&max_ssf);
&sasl_param);
if (rc != LDAP_SUCCESS) {
gettext("openConnection: "
"GSSAPI bind failed "
(void) ldap_unbind(ld);
return (NS_LDAP_INTERNAL);
}
break;
default:
(void) ldap_unbind(ld);
gettext("openConnection: unsupported SASL "
NULL);
return (NS_LDAP_INTERNAL);
}
}
return (NS_LDAP_SUCCESS);
}
/*
* FUNCTION: __s_api_getDefaultAuth
*
* Constructs a credential for authentication using the config module.
*
* RETURN VALUES:
*
* NS_LDAP_SUCCESS If successful
* NS_LDAP_CONFIG If there are any config errors.
* NS_LDAP_MEMORY Memory errors.
* NS_LDAP_OP_FAILED If there are no more authentication methods so can
* not build a new authp.
* NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
* necessary fields of a cred for a given auth method
* are not provided.
* INPUT:
*
* cLevel Currently requested credential level to be tried
*
* aMethod Currently requested authentication method to be tried
*
* OUTPUT:
*
* authp authentication method to use.
*/
static int
int *cLevel,
{
char *modparamVal = NULL;
int getUid = 0;
int getPasswd = 0;
int getCertpath = 0;
int rc = 0;
#ifdef DEBUG
#endif
/* Require an Auth */
return (NS_LDAP_INVALID_PARAM);
}
/*
*/
return (NS_LDAP_INVALID_PARAM);
return (NS_LDAP_MEMORY);
case NS_LDAP_AUTH_NONE:
return (NS_LDAP_SUCCESS);
case NS_LDAP_AUTH_SIMPLE:
getUid++;
getPasswd++;
break;
case NS_LDAP_AUTH_SASL:
getUid++;
getPasswd++;
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_INVALID_PARAM);
}
break;
case NS_LDAP_AUTH_TLS:
getUid++;
getPasswd++;
getCertpath++;
getCertpath++;
} else {
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_INVALID_PARAM);
}
break;
}
if (getUid) {
(void) __ns_ldap_freeCred(authp);
(void) __ns_ldap_freeError(&errorp);
return (rc);
}
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_INVALID_PARAM);
}
(void) __ns_ldap_freeParam(¶mVal);
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_MEMORY);
}
}
if (getPasswd) {
(void) __ns_ldap_freeCred(authp);
(void) __ns_ldap_freeError(&errorp);
return (rc);
}
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_INVALID_PARAM);
}
(void) __ns_ldap_freeParam(¶mVal);
(void) __ns_ldap_freeCred(authp);
if (modparamVal != NULL)
return (NS_LDAP_INVALID_PARAM);
}
}
if (getCertpath) {
(void) __ns_ldap_freeCred(authp);
(void) __ns_ldap_freeError(&errorp);
return (rc);
}
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_INVALID_PARAM);
}
(void) __ns_ldap_freeParam(¶mVal);
(void) __ns_ldap_freeCred(authp);
return (NS_LDAP_MEMORY);
}
}
return (NS_LDAP_SUCCESS);
}
/*
* 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
const char *server,
const int flags,
int fail_if_new_pwd_reqd,
int nopasswd_acct_mgmt)
{
int rc;
int sec = 1;
return (NS_LDAP_INVALID_PARAM);
}
/* if we already have a session id try to reuse connection */
if (*sessionId > 0) {
return (NS_LDAP_SUCCESS);
}
*sessionId = 0;
}
/* get profile version number */
return (rc);
"version"));
return (NS_LDAP_CONFIG);
}
(void) __ns_ldap_freeParam((void ***)¶mVal);
/* Get the bind timeout value */
timeoutSec = **((int **)paramVal);
(void) __ns_ldap_freeParam(¶mVal);
}
if (*errorp)
(void) __ns_ldap_freeError(errorp);
/* Get the authentication method list */
return (rc);
return (NS_LDAP_MEMORY);
return (NS_LDAP_MEMORY);
}
if (version == NS_LDAP_V1)
else {
}
}
/* Get the credential level list */
(void) __ns_ldap_freeParam((void ***)&aMethod);
return (rc);
}
return (NS_LDAP_MEMORY);
return (NS_LDAP_MEMORY);
if (version == NS_LDAP_V1)
*(cLevel[0]) = NS_LDAP_CRED_PROXY;
else
*(cLevel[0]) = NS_LDAP_CRED_ANON;
}
}
/* setup the anon credential for anonymous connection */
for (; ; ) {
/* using specified auth method */
/* not using bad server if credentials were supplied */
}
if (rc == NS_LDAP_SUCCESS ||
rc == NS_LDAP_SUCCESS_WITH_INFO) {
break;
}
} else {
/* for every cred level */
if (self_gssapi_only &&
**cNext != NS_LDAP_CRED_SELF)
continue;
if (**cNext == NS_LDAP_CRED_ANON) {
/*
* make connection anonymously
* Free the down server list before
* looping through
*/
}
&badSrvrs);
if (rc == NS_LDAP_SUCCESS ||
rc ==
goto done;
}
continue;
}
/* for each cred level */
if (self_gssapi_only &&
continue;
/*
* and non-self coexists with non-gssapi
* only
*/
if ((**cNext == NS_LDAP_CRED_SELF &&
(**cNext != NS_LDAP_CRED_SELF &&
continue;
/* make connection and authenticate */
/* with default credentials */
if (rc != NS_LDAP_SUCCESS) {
continue;
}
/*
* Free the down server list before
* looping through
*/
}
&badSrvrs);
(void) __ns_ldap_freeCred(&authp);
if (rc == NS_LDAP_SUCCESS ||
rc ==
goto done;
}
}
}
}
if (flags & NS_LDAP_HARD) {
if (sec < LDAPMAXHARDLOOKUPTIME)
sec *= 2;
} else {
break;
}
}
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.
*/
wait4session = 0;
sessionTid = 0;
#ifdef DEBUG
"unlocking sessionLock \n", thr_self());
#endif /* DEBUG */
(void) mutex_unlock(&sessionLock);
}
/*
* configured
*/
rc = NS_LDAP_CONFIG;
}
(void) __ns_ldap_freeParam((void ***)&aMethod);
(void) __ns_ldap_freeParam((void ***)&cLevel);
/*
* At this point, either we have a successful
* connection or exhausted all the possible auths.
* and creds. Mark the problem servers as down
* so that the problem servers are not contacted
* again until the refresh_ttl expires.
*/
(void) __s_api_removeBadServers(badSrvrs);
}
return (rc);
}
#pragma fini(_free_sessionPool)
static void
{
int id;
(void) rw_wrlock(&sessionPoolLock);
if (sessionPool != NULL) {
sessionPool = NULL;
sessionPoolSize = 0;
}
(void) rw_unlock(&sessionPoolLock);
}