diff options
Diffstat (limited to 'usr/src/lib/libsldap')
| -rw-r--r-- | usr/src/lib/libsldap/Makefile.com | 12 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/llib-lsldap | 53 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/mapfile-vers | 6 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_cache_door.h | 9 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_common.c | 16 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_config.c | 49 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_connect.c | 935 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_init.c | 21 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_internal.h | 65 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_reads.c | 28 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_sasl.c | 580 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_sldap.h | 29 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/common/ns_writes.c | 6 | ||||
| -rw-r--r-- | usr/src/lib/libsldap/req.flg | 1 |
14 files changed, 1615 insertions, 195 deletions
diff --git a/usr/src/lib/libsldap/Makefile.com b/usr/src/lib/libsldap/Makefile.com index ce3a899fec..244c974085 100644 --- a/usr/src/lib/libsldap/Makefile.com +++ b/usr/src/lib/libsldap/Makefile.com @@ -22,8 +22,9 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +#ident "%Z%%M% %I% %E% SMI" # +# lib/libsldap/Makefile.com LIBRARY= libsldap.a VERS= .1 @@ -32,7 +33,7 @@ SLDAPOBJ= ns_common.o ns_reads.o ns_writes.o \ ns_connect.o ns_config.o ns_error.o \ ns_cache_door.o ns_getalias.o ns_trace.o \ ns_init.o ns_crypt.o ns_confmgr.o \ - ns_mapping.o ns_wrapper.o + ns_mapping.o ns_wrapper.o ns_sasl.o OBJECTS= $(SLDAPOBJ) @@ -41,13 +42,14 @@ include ../../Makefile.lib SRCS = $(SLDAPOBJ:%.o=../common/%.c) LIBS = $(DYNLIB) $(LINTLIB) $(LINTLIB):= SRCS=../common/llib-lsldap -LDLIBS += -lnsl -lldap -lc +LDLIBS += -lnsl -lldap -lscf -lc SRCDIR = ../common CFLAGS += $(CCVERBOSE) -LOCFLAGS += -D_REENTRANT -DSUNW_OPTIONS -DTHREAD_SUNOS5_LWP -CPPFLAGS += -I../common -I$(SRC)/lib/libldap5/include/ldap -I/usr/include/mps $(LOCFLAGS) +LOCFLAGS += -D_REENTRANT -DSUNW_OPTIONS +CPPFLAGS += -I../common -I$(SRC)/lib/libldap5/include/ldap \ + -I/usr/include/mps $(LOCFLAGS) LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN diff --git a/usr/src/lib/libsldap/common/llib-lsldap b/usr/src/lib/libsldap/common/llib-lsldap index 6114809d89..466116a856 100644 --- a/usr/src/lib/libsldap/common/llib-lsldap +++ b/usr/src/lib/libsldap/common/llib-lsldap @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -23,7 +22,7 @@ /* PROTOLIB1 */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -186,3 +185,49 @@ char *__s_api_get_canonical_name( ns_ldap_entry_t *entry, ns_ldap_attr_t *attrptr, int case_ignore); + +void __ns_ldap_setServer( + int set); + +ns_ldap_error_t *__ns_ldap_LoadConfiguration( + void); + +ns_ldap_error_t *__ns_ldap_DumpConfiguration( + char *file); + +ns_ldap_error_t *__ns_ldap_DumpLdif( + char *filename); + +ns_ldap_error_t *__ns_ldap_print_config( + int verbose); + +void __ns_ldap_default_config( + void); + +int __ns_ldap_download( + const char *profile, + char *addr, + char *baseDN, + ns_ldap_error_t **errorp); + +int __ns_ldap_check_dns_preq( + int foreground, + int mode_verbose, + int mode_quiet, + const char *fname, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); + +int __ns_ldap_check_gssapi_preq( + int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); + +int __ns_ldap_check_all_preq( + int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); diff --git a/usr/src/lib/libsldap/common/mapfile-vers b/usr/src/lib/libsldap/common/mapfile-vers index 37fd9dd2be..18a1ddd867 100644 --- a/usr/src/lib/libsldap/common/mapfile-vers +++ b/usr/src/lib/libsldap/common/mapfile-vers @@ -32,6 +32,12 @@ SUNWprivate_1.1 { global: __ns_ldap_getAcctMgmt; __s_api_get_canonical_name; + __ns_ldap_getAttrStruct; + __ns_ldap_self_gssapi_config; + __ns_ldap_self_gssapi_only_set; + __ns_ldap_check_dns_preq; + __ns_ldap_check_gssapi_preq; + __ns_ldap_check_all_preq; } SUNWprivate_1.0; SUNWprivate_1.0 { diff --git a/usr/src/lib/libsldap/common/ns_cache_door.h b/usr/src/lib/libsldap/common/ns_cache_door.h index 21d32b5292..d6c9e3a56e 100644 --- a/usr/src/lib/libsldap/common/ns_cache_door.h +++ b/usr/src/lib/libsldap/common/ns_cache_door.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -164,6 +163,8 @@ typedef union { #define NS_CACHE_NORESP "1" #define NS_CACHE_NEXT "2" #define NS_CACHE_WRITE "3" +#define NS_CACHE_ADDR_HOSTNAME "H" +#define NS_CACHE_ADDR_IP "I" /* * GETCACHE/SETCACHE data flags diff --git a/usr/src/lib/libsldap/common/ns_common.c b/usr/src/lib/libsldap/common/ns_common.c index 2089509afc..1244b22710 100644 --- a/usr/src/lib/libsldap/common/ns_common.c +++ b/usr/src/lib/libsldap/common/ns_common.c @@ -2223,16 +2223,24 @@ __s_api_removeServer(const char *server) (void) memset(ret, 0, sizeof (ns_server_info_t)); (void) memset(space.s_b, 0, DOORBUFFERSIZE); - adata = (sizeof (ldap_call_t) + strlen(ireq) +1); + adata = (sizeof (ldap_call_t) + strlen(ireq) + + strlen(NS_CACHE_ADDR_IP) + 1); adata += strlen(DOORLINESEP) + 1; adata += strlen(server) + 1; ndata = sizeof (space); space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber); - (void) strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len); - (void) strlcat(space.s_d.ldap_call.ldap_u.domainname, DOORLINESEP, len); - (void) strlcat(space.s_d.ldap_call.ldap_u.domainname, server, len); + if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len) + return (-1); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, + NS_CACHE_ADDR_IP, len) >= len) + return (-1); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, DOORLINESEP, len) >= + len) + return (-1); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, len) >= len) + return (-1); sptr = &space.s_d; /* try to remove the server via the door interface */ diff --git a/usr/src/lib/libsldap/common/ns_config.c b/usr/src/lib/libsldap/common/ns_config.c index 1e52104c6c..fb24f7721b 100644 --- a/usr/src/lib/libsldap/common/ns_config.c +++ b/usr/src/lib/libsldap/common/ns_config.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -140,6 +139,7 @@ static ns_enum_map ns_auth_enum_v2[] = { { ENUM2INT(NS_LDAP_EA_SASL_DIGEST_MD5_CONF), "sasl/DIGEST-MD5:auth-conf" }, { ENUM2INT(NS_LDAP_EA_SASL_EXTERNAL), "sasl/EXTERNAL" }, + { ENUM2INT(NS_LDAP_EA_SASL_GSSAPI), "sasl/GSSAPI" }, { ENUM2INT(NS_LDAP_EA_TLS_NONE), "tls:none" }, { ENUM2INT(NS_LDAP_EA_TLS_SIMPLE), "tls:simple" }, { ENUM2INT(NS_LDAP_EA_TLS_SASL_CRAM_MD5), "tls:sasl/CRAM-MD5" }, @@ -1005,7 +1005,7 @@ __s_api_crosscheck(ns_config_t *ptr, char *errstr, int check_dn) int i, len, cnt; const char *begin; char **ppc; - int *pi; + int *pi, self, gssapi; if (ptr == NULL) @@ -1171,7 +1171,38 @@ __s_api_crosscheck(ns_config_t *ptr, char *errstr, int check_dn) } ptr->paramList[NS_LDAP_EXP_P].ns_tm = tm; } - + /* + * If credential level self is defined, there should be + * at least an auth method sasl/GSSAPI and vice versa. + */ + self = 0; + cnt = ptr->paramList[NS_LDAP_CREDENTIAL_LEVEL_P].ns_acnt; + for (i = 0; i < cnt; i++) { + if (ptr->paramList[NS_LDAP_CREDENTIAL_LEVEL_P].ns_pi[i] == + NS_LDAP_CRED_SELF) + self++; + } + gssapi = 0; + cnt = ptr->paramList[NS_LDAP_AUTH_P].ns_acnt; + for (i = 0; i < cnt; i++) { + if (ptr->paramList[NS_LDAP_AUTH_P].ns_pi[i] == + NS_LDAP_EA_SASL_GSSAPI) + gssapi++; + } + if (gssapi == 0 && self > 0) { + (void) snprintf(errstr, MAXERROR, + gettext("Configuration Error: " + "Credential level self requires " + "authentication method sasl/GSSAPI")); + return (NS_PARSE_ERR); + } + if (gssapi > 0 && self == 0) { + (void) snprintf(errstr, MAXERROR, + gettext("Configuration Error: " + "Authentication method sasl/GSSAPI " + "requires credential level self")); + return (NS_PARSE_ERR); + } return (NS_SUCCESS); } @@ -3453,6 +3484,12 @@ __s_api_AuthEnumtoStruct(const EnumAuthType_t i) ap->type = NS_LDAP_AUTH_SASL; ap->saslmech = NS_LDAP_SASL_EXTERNAL; break; + case NS_LDAP_EA_SASL_GSSAPI: + ap->type = NS_LDAP_AUTH_SASL; + ap->saslmech = NS_LDAP_SASL_GSSAPI; + ap->saslopt = NS_LDAP_SASLOPT_INT | + NS_LDAP_SASLOPT_PRIV; + break; case NS_LDAP_EA_TLS_NONE: ap->type = NS_LDAP_AUTH_TLS; ap->tlstype = NS_LDAP_TLS_NONE; diff --git a/usr/src/lib/libsldap/common/ns_connect.c b/usr/src/lib/libsldap/common/ns_connect.c index ccf75a9963..514c802b34 100644 --- a/usr/src/lib/libsldap/common/ns_connect.c +++ b/usr/src/lib/libsldap/common/ns_connect.c @@ -38,6 +38,7 @@ #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" @@ -54,8 +55,18 @@ 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); - -static mutex_t sessionPoolLock = DEFAULTMUTEX; +/* + * 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. + */ +static mutex_t sessionLock = DEFAULTMUTEX; +static int wait4session = 0; +static thread_t sessionTid = 0; +int MTperConn = 1; +static rwlock_t sessionPoolLock = DEFAULTRWLOCK; static Connection **sessionPool = NULL; static int sessionPoolSize = 0; @@ -65,10 +76,239 @@ static mutex_t nscdLock = DEFAULTMUTEX; static int nscdChecked = 0; static pid_t checkedPid = -1; 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) { + 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; + } + 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; +} +/* 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); + } + 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 + */ +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 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 /proc/PID/psinfo to see if this process is nscd @@ -126,7 +366,7 @@ nscd_proc() static int __s_api_requestServer(const char *request, const char *server, - ns_server_info_t *ret, ns_ldap_error_t **error) + ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType) { union { ldap_data_t s_d; @@ -142,7 +382,7 @@ __s_api_requestServer(const char *request, const char *server, char **mptr, **mptr1, **cptr, **cptr1; int mcnt, ccnt; char **servers; - int rc; + int rc, len; if (ret == NULL || error == NULL) { return (NS_LDAP_OP_FAILED); @@ -157,18 +397,26 @@ __s_api_requestServer(const char *request, const char *server, else ireq = request; - adata = (sizeof (ldap_call_t) + strlen(ireq) +1); + adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1); if (server != NULL) { adata += strlen(DOORLINESEP) + 1; adata += strlen(server) + 1; } ndata = sizeof (space); + len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber); space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; - (void) strcpy(space.s_d.ldap_call.ldap_u.domainname, ireq); + if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len) + return (NS_LDAP_MEMORY); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >= + len) + return (NS_LDAP_MEMORY); if (server != NULL) { - (void) strcat(space.s_d.ldap_call.ldap_u.domainname, - DOORLINESEP); - (void) strcat(space.s_d.ldap_call.ldap_u.domainname, server); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, + DOORLINESEP, len) >= len) + return (NS_LDAP_MEMORY); + if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, + len) >= len) + return (NS_LDAP_MEMORY); } sptr = &space.s_d; @@ -327,86 +575,120 @@ __s_api_requestServer(const char *request, const char *server, return (NS_LDAP_SUCCESS); } -#ifdef DEBUG /* * printCred(): prints the credential structure */ static void -printCred(FILE *fp, const ns_cred_t *cred) +printCred(int pri, const ns_cred_t *cred) { - if (fp == NULL) { - (void) fprintf(fp, "printCred: fp is NULL\n"); - return; - } + thread_t t = thr_self(); + if (cred == NULL) { - (void) fprintf(fp, "printCred: cred is NULL\n"); + syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t); return; } - (void) fprintf(fp, "AuthType=%d\n", cred->auth.type); - (void) fprintf(fp, "TlsType=%d\n", cred->auth.tlstype); - (void) fprintf(fp, "SaslMech=%d\n", cred->auth.saslmech); - (void) fprintf(fp, "SaslOpt=%d\n", cred->auth.saslopt); + 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); if (cred->hostcertpath) - (void) fprintf(fp, "hostCertPath=%s\n", cred->hostcertpath); + syslog(pri, "tid= %d: hostCertPath=%s\n", + t, cred->hostcertpath); if (cred->cred.unix_cred.userID) - (void) fprintf(fp, "userID=%s\n", cred->cred.unix_cred.userID); + syslog(pri, "tid= %d: userID=%s\n", + t, cred->cred.unix_cred.userID); if (cred->cred.unix_cred.passwd) - (void) fprintf(fp, "passwd=%s\n", cred->cred.unix_cred.passwd); + syslog(pri, "tid= %d: passwd=%s\n", + t, cred->cred.unix_cred.passwd); } /* * printConnection(): prints the connection structure */ static void -printConnection(FILE *fp, Connection *con) +printConnection(int pri, Connection *con) { - if (fp == NULL || con == NULL) + thread_t t = thr_self(); + + if (con == NULL) return; - (void) fprintf(fp, "connectionID=%d\n", con->connectionId); - (void) fprintf(fp, "usedBit=%d\n", con->usedBit); - (void) fprintf(fp, "threadID=%d\n", con->threadID); + 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); if (con->serverAddr) { - (void) fprintf(fp, "serverAddr=%s\n", con->serverAddr); + syslog(pri, "tid= %d: serverAddr=%s\n", + t, con->serverAddr); } - printCred(fp, con->auth); - (void) fprintf(fp, "-----------------------------------------------\n"); - fflush(fp); + printCred(pri, con->auth); } -#endif /* DEBUG */ /* - * 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. + * 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 addConnection(Connection *con) { - int i; + int i, noMTperC = 0; + thread_t t = thr_self(); + struct ldap_thread_fns tfns; + void *tsd; if (!con) return (-1); -#ifdef DEBUG - (void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID); -#endif /* DEBUG */ - (void) mutex_lock(&sessionPoolLock); + + 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); if (sessionPool == NULL) { sessionPoolSize = SESSION_CACHE_INC; sessionPool = calloc(sessionPoolSize, sizeof (struct connection **)); if (!sessionPool) { - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); return (-1); } -#ifdef DEBUG - (void) fprintf(stderr, "Initialized sessionPool\n"); -#endif /* DEBUG */ + + syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t); } for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i) ; @@ -417,26 +699,44 @@ addConnection(Connection *con) (sessionPoolSize + SESSION_CACHE_INC) * sizeof (Connection *)); if (!cl) { - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); return (-1); } (void) memset(cl + sessionPoolSize, 0, SESSION_CACHE_INC * sizeof (struct connection *)); sessionPool = cl; sessionPoolSize += SESSION_CACHE_INC; -#ifdef DEBUG - (void) fprintf(stderr, "Increased sessionPoolSize to: %d\n", - sessionPoolSize); -#endif /* DEBUG */ + syslog(LOG_DEBUG, "tid: %d: Increased " + "sessionPoolSize to: %d\n", + t, sessionPoolSize); } sessionPool[i] = con; - con->usedBit = B_TRUE; - (void) mutex_unlock(&sessionPoolLock); + if (noMTperC == 0) + con->shared++; + else + con->usedBit = B_TRUE; + + (void) rw_unlock(&sessionPoolLock); + con->connectionId = i + CONID_OFFSET; -#ifdef DEBUG - (void) fprintf(stderr, "Connection added [%d]\n", i); - printConnection(stderr, con); -#endif /* DEBUG */ + + 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); + } + return (i + CONID_OFFSET); } @@ -453,6 +753,11 @@ findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET) return (-1); + + /* if a new connection is requested, no need to continue */ + if (flags & NS_LDAP_NEW_CONN) + return (-1); + *conp = NULL; if (sessionPool == NULL) return (-1); @@ -460,9 +765,9 @@ findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, if (id < 0 || id >= sessionPoolSize) return (-1); - (void) mutex_lock(&sessionPoolLock); + (void) rw_rdlock(&sessionPoolLock); if (sessionPool[id] == NULL) { - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); return (-1); } cp = sessionPool[id]; @@ -471,11 +776,12 @@ findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, * 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) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); return (-1); } if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) && @@ -485,18 +791,19 @@ findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, ((cp->auth->cred.unix_cred.userID == NULL) || (strcasecmp(cp->auth->cred.unix_cred.userID, auth->cred.unix_cred.userID) != 0))) { - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); return (-1); } + /* An existing connection is found but it needs to be reset */ if (flags & NS_LDAP_NEW_CONN) { - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); DropConnection(cID, 0); return (-1); } /* found an available connection */ cp->usedBit = B_TRUE; - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); cp->threadID = thr_self(); *conp = cp; return (cID); @@ -508,39 +815,111 @@ findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, * 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(const char *serverAddr, const ns_cred_t *auth, Connection **conp) +findConnection(int flags, const char *serverAddr, + const ns_cred_t *auth, Connection **conp) { Connection *cp; int i; + int rc; + int try; +#ifdef DEBUG + thread_t t = thr_self(); +#endif /* DEBUG */ if (auth == NULL || conp == NULL) return (-1); *conp = NULL; + /* no need to find connection if anonymous */ + if (auth->auth.type == NS_LDAP_AUTH_NONE) + return (-1); + + /* if a new connection is requested, no need to continue */ + if (flags & NS_LDAP_NEW_CONN) + return (-1); + #ifdef DEBUG - (void) fprintf(stderr, "Find connection\n"); - (void) fprintf(stderr, "Looking for ....\n"); + (void) fprintf(stderr, "tid= %d: Find connection\n", t); + (void) fprintf(stderr, "tid= %d: Looking for ....\n", t); if (serverAddr && *serverAddr) - (void) fprintf(stderr, "serverAddr=%s\n", serverAddr); + (void) fprintf(stderr, "tid= %d: serverAddr=%s\n", + t, serverAddr); else - (void) fprintf(stderr, "serverAddr=NULL\n"); + (void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t); printCred(stderr, auth); fflush(stderr); #endif /* DEBUG */ - if (sessionPool == NULL) + + /* + * 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); return (-1); - (void) mutex_lock(&sessionPoolLock); + } + + /* + * If no connection 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) + */ + if (sessionPool == NULL) { + (void) rw_unlock(&sessionPoolLock); + (void) mutex_lock(&sessionLock); + if (sessionPool == NULL) { + 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: session pool not empty\n", t); + fflush(stderr); +#endif /* DEBUG */ + /* + * connection pool is not empty, check to see if + * one can be shared. + */ + (void) mutex_unlock(&sessionLock); + (void) rw_rdlock(&sessionPoolLock); + } + try = 0; + check_again: + for (i = 0; i < sessionPoolSize; ++i) { if (sessionPool[i] == NULL) continue; cp = sessionPool[i]; #ifdef DEBUG - (void) fprintf(stderr, "checking connection [%d] ....\n", i); + (void) fprintf(stderr, "tid= %d: checking connection " + "[%d] ....\n", t, i); printConnection(stderr, cp); #endif /* DEBUG */ - if ((cp->usedBit) || + 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) || @@ -560,18 +939,89 @@ findConnection(const char *serverAddr, const ns_cred_t *auth, Connection **conp) auth->cred.unix_cred.passwd) != 0)))) continue; /* found an available connection */ - cp->usedBit = B_TRUE; - (void) mutex_unlock(&sessionPoolLock); - cp->threadID = thr_self(); + if (MTperConn == 0) + cp->usedBit = B_TRUE; + else { + /* 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); + *conp = cp; #ifdef DEBUG - (void) fprintf(stderr, "Connection found cID=%d\n", i); + (void) fprintf(stderr, "tid= %d: Connection found " + "cID=%d, shared =%d\n", t, i, cp->shared); fflush(stderr); #endif /* DEBUG */ return (i + CONID_OFFSET); } - (void) mutex_unlock(&sessionPoolLock); - return (-1); + (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); + 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", rc); + return (-1); + } } /* @@ -607,7 +1057,7 @@ 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, char ***badsrvrs) + int nopasswd_acct_mgmt, int flags, char ***badsrvrs) { Connection *con = NULL; ConnectionID id; @@ -618,7 +1068,9 @@ makeConnection(Connection **conp, const char *serverAddr, LDAP *ld = NULL; int passwd_mgmt = 0; int totalbad = 0; /* Number of servers contacted unsuccessfully */ - short memerr = 0; /* Variable for tracking memory allocation errors */ + short memerr = 0; /* Variable for tracking memory allocation */ + char *serverAddrType = NULL; + if (conp == NULL || errorp == NULL || auth == NULL) return (NS_LDAP_INVALID_PARAM); @@ -628,10 +1080,11 @@ makeConnection(Connection **conp, const char *serverAddr, sinfo.controls = NULL; sinfo.saslMechanisms = NULL; - if ((id = findConnection(serverAddr, auth, &con)) != -1) { + if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) { /* connection found in cache */ #ifdef DEBUG - (void) fprintf(stderr, "connection found in cache %d\n", id); + (void) fprintf(stderr, "tid= %d: connection found in " + "cache %d\n", thr_self(), id); fflush(stderr); #endif /* DEBUG */ *cID = id; @@ -639,21 +1092,26 @@ makeConnection(Connection **conp, const char *serverAddr, return (NS_LDAP_SUCCESS); } + if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) + serverAddrType = NS_CACHE_ADDR_HOSTNAME; + else + serverAddrType = NS_CACHE_ADDR_IP; + if (serverAddr) { - rc = openConnection(&ld, serverAddr, auth, timeoutSec, errorp, + rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr, + &sinfo, errorp, serverAddrType); + if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) { + (void) snprintf(errmsg, sizeof (errmsg), + gettext("makeConnection: unable to get " + "server information for %s"), serverAddr); + syslog(LOG_ERR, "libsldap: %s", errmsg); + return (NS_LDAP_OP_FAILED); + } + rc = openConnection(&ld, sinfo.server, auth, timeoutSec, errorp, fail_if_new_pwd_reqd, passwd_mgmt); if (rc == NS_LDAP_SUCCESS || rc == NS_LDAP_SUCCESS_WITH_INFO) { exit_rc = rc; - rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr, - &sinfo, errorp); - if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) { - (void) snprintf(errmsg, sizeof (errmsg), - gettext("makeConnection: unable to get " - "server information for %s"), serverAddr); - syslog(LOG_ERR, "libsldap: %s", errmsg); - return (NS_LDAP_OP_FAILED); - } goto create_con; } else { return (rc); @@ -666,7 +1124,8 @@ makeConnection(Connection **conp, const char *serverAddr, hReq = NS_CACHE_NEW; else hReq = NS_CACHE_NEXT; - rc = __s_api_requestServer(hReq, host, &sinfo, errorp); + rc = __s_api_requestServer(hReq, host, &sinfo, errorp, + serverAddrType); if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) || (host && (strcasecmp(host, sinfo.server) == 0))) { /* Log the error */ @@ -851,6 +1310,7 @@ create_con: } con->threadID = thr_self(); + con->ld = ld; if ((id = addConnection(con)) == -1) { freeConnection(con); @@ -866,7 +1326,8 @@ create_con: return (NS_LDAP_MEMORY); } #ifdef DEBUG - (void) fprintf(stderr, "connection added into cache %d\n", id); + (void) fprintf(stderr, "tid= %d: connection added into " + "cache %d\n", thr_self(), id); fflush(stderr); #endif /* DEBUG */ *cID = id; @@ -884,42 +1345,86 @@ _DropConnection(ConnectionID cID, int flag, int fini) { Connection *cp; int id; - int use_mutex = !fini; + int use_lock = !fini; +#ifdef DEBUG + thread_t t = thr_self(); +#endif /* DEBUG */ id = cID - CONID_OFFSET; if (id < 0 || id >= sessionPoolSize) return; #ifdef DEBUG - (void) fprintf(stderr, - "Dropping connection cID=%d flag=0x%x\n", cID, flag); + (void) fprintf(stderr, "tid= %d: " + "Dropping connection cID=%d flag=0x%x, fini = %d\n", + t, cID, flag, fini); fflush(stderr); #endif /* DEBUG */ - if (use_mutex) - (void) mutex_lock(&sessionPoolLock); + if (use_lock) + (void) rw_wrlock(&sessionPoolLock); cp = sessionPool[id]; /* sanity check before removing */ - if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) { - if (use_mutex) - (void) mutex_unlock(&sessionPoolLock); + if (!cp || (!fini && !cp->shared && (!cp->usedBit || + cp->threadID != thr_self()))) { +#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); return; } if (!fini && - ((flag & NS_LDAP_NEW_CONN) == 0) && - ((flag & NS_LDAP_KEEP_CONN) || nscd_proc())) { + ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail && + ((flag & NS_LDAP_KEEP_CONN) || + (MTperConn == 0 && nscd_proc()) || + MTperConn)) { +#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 */ - if (use_mutex) - (void) mutex_unlock(&sessionPoolLock); + if (use_lock) + (void) rw_unlock(&sessionPoolLock); } else { /* delete Connection (disconnect) */ - sessionPool[id] = NULL; - if (use_mutex) - (void) mutex_unlock(&sessionPoolLock); - (void) ldap_unbind(cp->ld); - freeConnection(cp); + 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--; + cp->notAvail = 1; + } + + 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); } } @@ -1189,16 +1694,17 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, int errnum = 0; LDAPMessage *resultMsg; int msgId; - int useSSL = 0; + int useSSL = 0, port = 0; struct timeval tv; AuthType_t bindType; int timeoutMilliSec = timeoutSec * 1000; struct berval cred; char *sslServerAddr; char *s1; - char *errmsg; + char *errmsg, *end = NULL; LDAPControl **controls; - int pwd_rc; + int pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF; + ns_sasl_cb_param_t sasl_param; *errorp = NULL; *ldp = NULL; @@ -1246,7 +1752,8 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, const char *hostcertpath; char *alloc_hcp = NULL; #ifdef DEBUG - (void) fprintf(stderr, "+++TLS transport\n"); + (void) fprintf(stderr, "tid= %d: +++TLS transport\n", + thr_self()); #endif /* DEBUG */ if (prldap_set_session_option(NULL, NULL, @@ -1324,15 +1831,40 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, } } else { #ifdef DEBUG - (void) fprintf(stderr, "+++Unsecure transport\n"); + (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n", + thr_self()); #endif /* DEBUG */ - /* Warning message IF cannot connect to host(s) */ - if ((ld = ldap_init((char *)serverAddr, LDAP_PORT)) == NULL) { + 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); + } + + /* 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) && @@ -1340,21 +1872,22 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, /* 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); + (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); } } } } + 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); /* @@ -1377,7 +1910,8 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, switch (bindType) { case NS_LDAP_AUTH_NONE: #ifdef DEBUG - (void) fprintf(stderr, "+++Anonymous bind\n"); + (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n", + thr_self()); #endif /* DEBUG */ break; case NS_LDAP_AUTH_SIMPLE: @@ -1394,7 +1928,8 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, } #ifdef DEBUG - (void) fprintf(stderr, "+++Simple bind\n"); + (void) fprintf(stderr, "tid= %d: +++Simple bind\n", + thr_self()); #endif /* DEBUG */ msgId = ldap_simple_bind(ld, binddn, passwd); @@ -1462,31 +1997,35 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, break; case NS_LDAP_AUTH_SASL: - /* We don't support any sasl options yet */ - if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE) { + 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)"), auth->auth.saslopt); + "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); } - binddn = auth->cred.unix_cred.userID; - passwd = auth->cred.unix_cred.passwd; - if (passwd == NULL || *passwd == '\0' || - binddn == NULL || *binddn == '\0') { - (void) sprintf(errstr, + 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); + 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); } - cred.bv_val = passwd; - cred.bv_len = strlen(passwd); switch (auth->auth.saslmech) { case NS_LDAP_SASL_CRAM_MD5: @@ -1584,6 +2123,47 @@ openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 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, @@ -1648,12 +2228,11 @@ __s_api_getDefaultAuth( return (NS_LDAP_INVALID_PARAM); } - /* - * Do not differentiate between (proxy/self) at this time, but - * reject self credential levels at this time + * credential level "self" can work with auth method sasl/GSSAPI only */ - if (cLevel && *cLevel == NS_LDAP_CRED_SELF) + if (cLevel && *cLevel == NS_LDAP_CRED_SELF && + aMethod->saslmech != NS_LDAP_SASL_GSSAPI) return (NS_LDAP_INVALID_PARAM); *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); @@ -1674,7 +2253,7 @@ __s_api_getDefaultAuth( (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { getUid++; getPasswd++; - } else { + } else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) { (void) __ns_ldap_freeCred(authp); *authp = NULL; return (NS_LDAP_INVALID_PARAM); @@ -1845,7 +2424,7 @@ __s_api_getConnection( int sec = 1; ns_cred_t *authp = NULL; ns_cred_t anon; - int version = NS_LDAP_V2; + int version = NS_LDAP_V2, self_gssapi_only = 0; void **paramVal = NULL; char **badSrvrs = NULL; /* List of problem hostnames */ @@ -1941,16 +2520,20 @@ __s_api_getConnection( /* using specified auth method */ rc = makeConnection(&con, server, cred, sessionId, timeoutSec, errorp, - fail_if_new_pwd_reqd, nopasswd_acct_mgmt, - &badSrvrs); + fail_if_new_pwd_reqd, + nopasswd_acct_mgmt, flags, &badSrvrs); if (rc == NS_LDAP_SUCCESS || rc == NS_LDAP_SUCCESS_WITH_INFO) { *session = con; break; } } else { + self_gssapi_only = __s_api_self_gssapi_only_get(); /* for every cred level */ for (cNext = cLevel; *cNext != NULL; cNext++) { + if (self_gssapi_only && + **cNext != NS_LDAP_CRED_SELF) + continue; if (**cNext == NS_LDAP_CRED_ANON) { /* * make connection anonymously @@ -1964,7 +2547,8 @@ __s_api_getConnection( rc = makeConnection(&con, server, &anon, sessionId, timeoutSec, errorp, fail_if_new_pwd_reqd, - nopasswd_acct_mgmt, &badSrvrs); + nopasswd_acct_mgmt, flags, + &badSrvrs); if (rc == NS_LDAP_SUCCESS || rc == NS_LDAP_SUCCESS_WITH_INFO) { @@ -1975,6 +2559,22 @@ __s_api_getConnection( } /* for each cred level */ for (aNext = aMethod; *aNext != NULL; aNext++) { + if (self_gssapi_only && + (*aNext)->saslmech != + NS_LDAP_SASL_GSSAPI) + continue; + /* + * self coexists with sasl/GSSAPI only + * and non-self coexists with non-gssapi + * only + */ + if ((**cNext == NS_LDAP_CRED_SELF && + (*aNext)->saslmech != + NS_LDAP_SASL_GSSAPI) || + (**cNext != NS_LDAP_CRED_SELF && + (*aNext)->saslmech == + NS_LDAP_SASL_GSSAPI)) + continue; /* make connection and authenticate */ /* with default credentials */ authp = NULL; @@ -1994,7 +2594,8 @@ __s_api_getConnection( rc = makeConnection(&con, server, authp, sessionId, timeoutSec, errorp, fail_if_new_pwd_reqd, - nopasswd_acct_mgmt, &badSrvrs); + nopasswd_acct_mgmt, flags, + &badSrvrs); (void) __ns_ldap_freeCred(&authp); if (rc == NS_LDAP_SUCCESS || rc == @@ -2015,6 +2616,30 @@ __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 + * configured + */ + rc = NS_LDAP_CONFIG; + } + (void) __ns_ldap_freeParam((void ***)&aMethod); (void) __ns_ldap_freeParam((void ***)&cLevel); @@ -2038,7 +2663,7 @@ _free_sessionPool() { int id; - (void) mutex_lock(&sessionPoolLock); + (void) rw_wrlock(&sessionPoolLock); if (sessionPool != NULL) { for (id = 0; id < sessionPoolSize; id++) _DropConnection(id + CONID_OFFSET, 0, 1); @@ -2046,5 +2671,5 @@ _free_sessionPool() sessionPool = NULL; sessionPoolSize = 0; } - (void) mutex_unlock(&sessionPoolLock); + (void) rw_unlock(&sessionPoolLock); } diff --git a/usr/src/lib/libsldap/common/ns_init.c b/usr/src/lib/libsldap/common/ns_init.c index e95a99375f..e9b6842210 100644 --- a/usr/src/lib/libsldap/common/ns_init.c +++ b/usr/src/lib/libsldap/common/ns_init.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,11 +27,25 @@ #include "ns_sldap.h" #include "ns_internal.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; + } } diff --git a/usr/src/lib/libsldap/common/ns_internal.h b/usr/src/lib/libsldap/common/ns_internal.h index 07fc60149e..699cb1dfa0 100644 --- a/usr/src/lib/libsldap/common/ns_internal.h +++ b/usr/src/lib/libsldap/common/ns_internal.h @@ -474,7 +474,7 @@ typedef enum EnumAuthType { NS_LDAP_EA_SASL_DIGEST_MD5_INT = 5, NS_LDAP_EA_SASL_DIGEST_MD5_CONF = 6, NS_LDAP_EA_SASL_EXTERNAL = 7, - NS_LDAP_EA_SASL_GSSAPI = 8, /* unsupported */ + NS_LDAP_EA_SASL_GSSAPI = 8, NS_LDAP_EA_SASL_SPNEGO = 9, /* unsupported */ NS_LDAP_EA_TLS_NONE = 10, NS_LDAP_EA_TLS_SIMPLE = 11, @@ -549,9 +549,15 @@ typedef int ConnectionID; */ typedef struct connection { ConnectionID connectionId; - boolean_t usedBit; + 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 */ char *serverAddr; - ns_cred_t *auth; + ns_cred_t *auth; LDAP *ld; thread_t threadID; /* thread ID using it */ struct ns_ldap_cookie *cookieInfo; @@ -664,6 +670,23 @@ typedef struct ns_server_info { } ns_server_info_t; /* + * sasl callback function parameters + */ +typedef struct ns_sasl_cb_param { + char *mech; + char *authid; + char *authzid; + char *passwd; + char *realm; +} ns_sasl_cb_param_t; + +/* self/sasl/gssapi variable */ +extern int sasl_gssapi_inited; + +/* Multiple threads per connection variable */ +extern int MTperConn; + +/* * INTERNAL GLOBAL DEFINITIONS AND FUNCTION DECLARATIONS */ @@ -748,6 +771,29 @@ ns_ldap_error_t *__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname); ns_ldap_error_t *__ns_ldap_DumpConfiguration(char *filename); ns_ldap_error_t *__ns_ldap_DumpLdif(char *filename); int __ns_ldap_cache_ping(); +ns_ldap_error_t *__ns_ldap_print_config(int); +void __ns_ldap_default_config(); +int __ns_ldap_download(const char *, char *, char *, + ns_ldap_error_t **); +int +__ns_ldap_check_dns_preq(int foreground, + int mode_verbose, + int mode_quiet, + const char *fname, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); +int +__ns_ldap_check_gssapi_preq(int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); +int +__ns_ldap_check_all_preq(int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp); /* internal un-exposed APIs */ ns_cred_t *__ns_ldap_dupAuth(const ns_cred_t *authp); @@ -804,6 +850,19 @@ int __s_api_contain_account_usable_control_oid(char **oids); char *__s_api_get_canonical_name(ns_ldap_entry_t *entry, ns_ldap_attr_t *attrptr, int case_ignore); +/* self/sasl/gssapi functions */ +int __s_api_sasl_bind_callback( + LDAP *ld, + unsigned flags, + void *defaults, + void *in); + +int __s_api_self_gssapi_only_get(void); +int __s_api_sasl_gssapi_init(void); + +/* Multiple threads per connection functions */ +void ns_tsd_cleanup(void *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libsldap/common/ns_reads.c b/usr/src/lib/libsldap/common/ns_reads.c index 8db18695c4..3239ab9a55 100644 --- a/usr/src/lib/libsldap/common/ns_reads.c +++ b/usr/src/lib/libsldap/common/ns_reads.c @@ -2220,10 +2220,18 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle) LDAP_ERROR; } if (cookie->connectionId > -1) { - DropConnection( + /* + * NS_LDAP_NEW_CONN + * indicates that the + * connection should + * be deleted, not + * kept alive + */ + DropConnection( cookie->connectionId, NS_LDAP_NEW_CONN); - cookie->connectionId = -1; + cookie->connectionId = + -1; } } break; @@ -3022,7 +3030,7 @@ __ns_ldap_auth(const ns_cred_t *auth, if (!auth) return (NS_LDAP_INVALID_PARAM); - rc = __s_api_getConnection(NULL, flags, + rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN, auth, &connectionId, &conp, errorp, do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt); if (rc == NS_LDAP_OP_FAILED && *errorp) @@ -3047,6 +3055,20 @@ __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname) return (NULL); } +ns_ldap_attr_t * +__ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname) +{ + int i; + + if (entry == NULL) + return (NULL); + for (i = 0; i < entry->attr_count; i++) { + if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL) + return (entry->attr_pair[i]); + } + return (NULL); +} + /*ARGSUSED*/ int diff --git a/usr/src/lib/libsldap/common/ns_sasl.c b/usr/src/lib/libsldap/common/ns_sasl.c new file mode 100644 index 0000000000..fc708b735a --- /dev/null +++ b/usr/src/lib/libsldap/common/ns_sasl.c @@ -0,0 +1,580 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <thread.h> +#include <synch.h> +#include <sasl/sasl.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <ctype.h> +#include <libscf.h> +#include <libintl.h> +#include <locale.h> +#include "ns_sldap.h" +#include "ns_internal.h" + +static int self_gssapi_only = 0; +static mutex_t self_gssapi_only_lock = DEFAULTMUTEX; + +#define DNS_FMRI "svc:/network/dns/client:default" +#define MSGSIZE 256 + +#define NSSWITCH_CONF "/etc/nsswitch.conf" + +/* + * Error Handling + */ +#define CLIENT_FPRINTF if (mode_verbose && !mode_quiet) (void) fprintf + +/* + * One time initializtion + */ +int sasl_gssapi_inited = 0; +static mutex_t sasl_gssapi_lock = DEFAULTMUTEX; +int +__s_api_sasl_gssapi_init(void) { + int rc = NS_LDAP_SUCCESS; + (void) mutex_lock(&sasl_gssapi_lock); + if (!sasl_gssapi_inited) { + if (getuid() == 0) { + if (system( + "/usr/sbin/cryptoadm disable metaslot") + == 0) { + syslog(LOG_WARNING, + "libsldap: Metaslot disabled " + "for self credential mode"); + sasl_gssapi_inited = 1; + } else { + syslog(LOG_ERR, + "libsldap: Can't disable " + "Metaslot for self credential " + "mode"); + rc = NS_LDAP_INTERNAL; + } + } + } + (void) mutex_unlock(&sasl_gssapi_lock); + + return (rc); +} + +/* + * nscd calls this function to set self_gssapi_only flag so libsldap performs + * sasl/GSSAPI bind only. Also see comments of __ns_ldap_self_gssapi_config. + * + * Input: flag 0 use any kind of connection + * 1 use self/gssapi connection only + */ +void +__ns_ldap_self_gssapi_only_set(int flag) { + (void) mutex_lock(&self_gssapi_only_lock); + self_gssapi_only = flag; + (void) mutex_unlock(&self_gssapi_only_lock); +} +/* + * Get the flag value of self_gssapi_only + */ +int +__s_api_self_gssapi_only_get(void) { + int flag; + (void) mutex_lock(&self_gssapi_only_lock); + flag = self_gssapi_only; + (void) mutex_unlock(&self_gssapi_only_lock); + return (flag); +} +/* + * nscd calls this function to detect the current native ldap configuration. + * The output are + * NS_LDAP_SELF_GSSAPI_CONFIG_NONE: No credential level self and + * no authentication method sasl/GSSAPI is + * configured. + * NS_LDAP_SELF_GSSAPI_CONFIG_ONLY: Only credential level self and + * authentication method sasl/GSSAPI are + * configured. + * NS_LDAP_SELF_GSSAPI_CONFIG_MIXED: More than one credential level are + * configured, including self. + * More than one authentication method + * are configured, including sasl/GSSAPI. + * + * __s_api_crosscheck makes sure self and sasl/GSSAPI pair up if they do + * get configured. + * + * When nscd detects it's MIXED case, it calls __ns_ldap_self_gssapi_only_set + * to force libsldap to do sasl/GSSAPI bind only for per-user lookup. + * + * Return: NS_LDAP_SUCCESS + * OTHERWISE - FAILURE + * + * Output: config. See comments above. + * + */ +int +__ns_ldap_self_gssapi_config(ns_ldap_self_gssapi_config_t *config) { + int self = 0, other_level = 0, gssapi = 0, other_method = 0; + ns_auth_t **aMethod = NULL, **aNext = NULL; + int **cLevel = NULL, **cNext = NULL, rc; + ns_ldap_error_t *errp = NULL; + + if (config == NULL) + return (NS_LDAP_INVALID_PARAM); + else + *config = NS_LDAP_SELF_GSSAPI_CONFIG_NONE; + + /* Get the credential level list */ + if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, + (void ***)&cLevel, &errp)) != NS_LDAP_SUCCESS) { + if (errp) + (void) __ns_ldap_freeError(&errp); + if (cLevel) + (void) __ns_ldap_freeParam((void ***)&cLevel); + return (rc); + } + if (errp) + (void) __ns_ldap_freeError(&errp); + /* Get the authentication method list */ + if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, + (void ***)&aMethod, &errp)) != NS_LDAP_SUCCESS) { + if (errp) + (void) __ns_ldap_freeError(&errp); + if (cLevel) + (void) __ns_ldap_freeParam((void ***)&cLevel); + if (aMethod) + (void) __ns_ldap_freeParam((void ***)&aMethod); + return (rc); + } + if (errp) + (void) __ns_ldap_freeError(&errp); + + if (cLevel == NULL || aMethod == NULL) { + if (cLevel) + (void) __ns_ldap_freeParam((void ***)&cLevel); + if (aMethod) + (void) __ns_ldap_freeParam((void ***)&aMethod); + return (NS_LDAP_SUCCESS); + } + + for (cNext = cLevel; *cNext != NULL; cNext++) { + if (**cNext == NS_LDAP_CRED_SELF) + self++; + else + other_level++; + } + for (aNext = aMethod; *aNext != NULL; aNext++) { + if ((*aNext)->saslmech == NS_LDAP_SASL_GSSAPI) + gssapi++; + else + other_method++; + } + + if (self > 0 && gssapi > 0) { + if (other_level == 0 && other_method == 0) + *config = NS_LDAP_SELF_GSSAPI_CONFIG_ONLY; + else + *config = NS_LDAP_SELF_GSSAPI_CONFIG_MIXED; + } + + if (cLevel) + (void) __ns_ldap_freeParam((void ***)&cLevel); + if (aMethod) + (void) __ns_ldap_freeParam((void ***)&aMethod); + return (NS_LDAP_SUCCESS); +} + +int +__s_api_sasl_bind_callback( + /* LINTED E_FUNC_ARG_UNUSED */ + LDAP *ld, + /* LINTED E_FUNC_ARG_UNUSED */ + unsigned flags, + void *defaults, + void *in) +{ + char *ret = NULL; + sasl_interact_t *interact = in; + ns_sasl_cb_param_t *cred = (ns_sasl_cb_param_t *)defaults; + + + while (interact->id != SASL_CB_LIST_END) { + + switch (interact->id) { + + case SASL_CB_GETREALM: + ret = cred->realm; + break; + case SASL_CB_AUTHNAME: + ret = cred->authid; + break; + case SASL_CB_PASS: + ret = cred->passwd; + break; + case SASL_CB_USER: + ret = cred->authzid; + break; + case SASL_CB_NOECHOPROMPT: + case SASL_CB_ECHOPROMPT: + default: + break; + } + + if (ret) { + interact->result = strdup(ret); + if (interact->result == NULL) + return (LDAP_NO_MEMORY); + + interact->len = strlen(ret); + } else { + interact->result = NULL; + interact->len = 0; + } + interact++; + } + + return (LDAP_SUCCESS); +} + +/* + * Find "dbase: service1 [...] services2" in fname and return + * " service1 [...] services2" + * e.g. + * Find "hosts: files dns" and return " files dns" + */ +static char * +__ns_nsw_getconfig(const char *dbase, const char *fname, int *errp) +{ + FILE *fp = NULL; + char *linep, *retp = NULL; + char lineq[BUFSIZ], db_colon[BUFSIZ]; + + if ((fp = fopen(fname, "rF")) == NULL) { + *errp = NS_LDAP_CONFIG; + return (NULL); + } + *errp = NS_LDAP_SUCCESS; + + while (linep = fgets(lineq, BUFSIZ, fp)) { + char *tokenp, *comment; + + /* + * Ignore portion of line following the comment character '#'. + */ + if ((comment = strchr(linep, '#')) != NULL) { + *comment = '\0'; + } + if ((*linep == '\0') || isspace(*linep)) { + continue; + } + (void) snprintf(db_colon, BUFSIZ, "%s:", dbase); + if ((tokenp = strstr(linep, db_colon)) == NULL) { + continue; /* ignore this line */ + } else { + /* skip "dbase:" */ + retp = strdup(tokenp + strlen(db_colon)); + if (retp == NULL) + *errp = NS_LDAP_MEMORY; + } + } + + (void) fclose(fp); + return (retp); +} +/* + * Test the configurations of the "hosts" and "ipnodes" + * dns has to be present and appear before ldap + * e.g. + * "dns" , "dns files" "dns ldap files", "files dns" are allowed. + * + * Kerberos requires dns or it'd fail. + */ +static int +test_dns_nsswitch(int foreground, + const char *fname, + ns_ldap_error_t **errpp) { + int ldap, dns, i, pserr, rc = NS_LDAP_SUCCESS; + char *db[3] = {"hosts", "ipnodes", NULL}; + char buf[MSGSIZE], *conf = NULL, *token = NULL, *last = NULL; + + for (i = 0; db[i] != NULL; i++) { + conf = __ns_nsw_getconfig(db[i], fname, &pserr); + + if (conf == NULL) { + (void) snprintf(buf, MSGSIZE, + gettext("Parsing %s to find \"%s:\" " + "failed. err: %d"), + fname, db[i], pserr); + if (foreground) { + (void) fprintf(stderr, "%s\n", buf); + } else { + MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG, + strdup(buf), NS_LDAP_MEMORY); + } + return (pserr); + } + ldap = dns = 0; + token = strtok_r(conf, " ", &last); + while (token != NULL) { + if (strncmp(token, "dns", 3) == 0) { + if (ldap) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: ldap can't appear " + "before dns"), db[i]); + if (foreground) { + (void) fprintf(stderr, + "start: %s\n", + buf); + } else { + MKERROR(LOG_ERR, *errpp, + NS_LDAP_CONFIG, + strdup(buf), + NS_LDAP_MEMORY); + } + free(conf); + return (NS_LDAP_CONFIG); + } else { + dns++; + } + } else if (strncmp(token, "ldap", 4) == 0) { + ldap++; + } + /* next token */ + token = strtok_r(NULL, " ", &last); + } + if (conf) { + free(conf); + conf = NULL; + } + if (!dns) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: dns is not defined in " + "%s"), db[i], fname); + if (foreground) { + (void) fprintf(stderr, "start: %s\n", buf); + } else { + MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG, + strdup(buf), NS_LDAP_MEMORY); + } + rc = NS_LDAP_CONFIG; + break; + } + } + return (rc); +} + +static boolean_t +is_service(const char *fmri, const char *state) { + char *st; + boolean_t result = B_FALSE; + + if ((st = smf_get_state(fmri)) != NULL) { + if (strcmp(st, state) == 0) + result = B_TRUE; + free(st); + } + return (result); +} + + +/* + * This function checks dns prerequisites for sasl/GSSAPI bind. + * It's called only if config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY || + * config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED. + */ +int +__ns_ldap_check_dns_preq(int foreground, + int mode_verbose, + int mode_quiet, + const char *fname, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp) { + + char buf[MSGSIZE]; + int retcode = NS_LDAP_SUCCESS; + int loglevel; + + if (errpp) + *errpp = NULL; + else + return (NS_LDAP_INVALID_PARAM); + + if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE) + /* Shouldn't happen. Check this value just in case */ + return (NS_LDAP_SUCCESS); + + if ((retcode = test_dns_nsswitch(foreground, fname, errpp)) != + NS_LDAP_SUCCESS) + return (retcode); + + if (is_service(DNS_FMRI, SCF_STATE_STRING_ONLINE)) { + if (foreground) { + CLIENT_FPRINTF(stdout, "start: %s\n", + gettext("DNS client is enabled")); + } else { + syslog(LOG_INFO, "%s", + gettext("DNS client is enabled")); + } + return (NS_LDAP_SUCCESS); + } else { + if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: DNS client is not enabled. " + "Run \"svcadm enable %s\". %s."), + "Error", DNS_FMRI, "Abort"); + loglevel = LOG_ERR; + retcode = NS_LDAP_CONFIG; + } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: DNS client is not enabled. " + "Run \"svcadm enable %s\". %s." + "Fall back to other cred level/bind. "), + "Warning", DNS_FMRI, "Continue"); + loglevel = LOG_INFO; + retcode = NS_LDAP_SUCCESS; + } + + if (foreground) { + (void) fprintf(stderr, "start: %s\n", buf); + } else { + MKERROR(loglevel, *errpp, retcode, strdup(buf), + NS_LDAP_MEMORY); + } + return (retcode); + } +} + +/* + * Check if sasl/GSSAPI works + */ +int +__ns_ldap_check_gssapi_preq(int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp) { + + int rc; + char *attr[2] = {"dn", NULL}, buf[MSGSIZE]; + ns_cred_t cred; + ns_ldap_result_t *result = NULL; + int loglevel; + + if (errpp) + *errpp = NULL; + else + return (NS_LDAP_INVALID_PARAM); + + if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE) + /* Don't need to check */ + return (NS_LDAP_SUCCESS); + + (void) memset(&cred, 0, sizeof (ns_cred_t)); + + cred.auth.type = NS_LDAP_AUTH_SASL; + cred.auth.tlstype = NS_LDAP_TLS_NONE; + cred.auth.saslmech = NS_LDAP_SASL_GSSAPI; + + rc = __ns_ldap_list(NULL, (const char *)"objectclass=*", + NULL, (const char **)attr, &cred, + NS_LDAP_SCOPE_BASE, &result, errpp, NULL, NULL); + if (result) + (void) __ns_ldap_freeResult(&result); + + if (rc == NS_LDAP_SUCCESS) { + if (foreground) { + CLIENT_FPRINTF(stdout, "start: %s\n", + gettext("sasl/GSSAPI bind works")); + } else { + syslog(LOG_INFO, "%s", + gettext("sasl/GSSAPI bind works")); + } + return (NS_LDAP_SUCCESS); + } else { + if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: sasl/GSSAPI bind is not " + "working. %s."), + "Error", "Abort"); + loglevel = LOG_ERR; + } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) { + (void) snprintf(buf, MSGSIZE, + gettext("%s: sasl/GSSAPI bind is not " + "working. Fall back to other cred " + "level/bind. %s."), + "Warning", "Continue"); + loglevel = LOG_INFO; + /* reset return code */ + rc = NS_LDAP_SUCCESS; + } + + if (foreground) { + (void) fprintf(stderr, "start: %s\n", buf); + } else { + MKERROR(loglevel, *errpp, rc, strdup(buf), + NS_LDAP_MEMORY); + } + return (rc); + } +} +/* + * This is called by ldap_cachemgr to check dns and gssapi prequisites. + */ +int +__ns_ldap_check_all_preq(int foreground, + int mode_verbose, + int mode_quiet, + ns_ldap_self_gssapi_config_t config, + ns_ldap_error_t **errpp) { + + int rc; + + if (errpp) + *errpp = NULL; + else + return (NS_LDAP_INVALID_PARAM); + + if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE) + /* Don't need to check */ + return (NS_LDAP_SUCCESS); + + if ((rc = __ns_ldap_check_dns_preq(foreground, + mode_verbose, mode_quiet, NSSWITCH_CONF, + config, errpp)) != NS_LDAP_SUCCESS) + return (rc); + if ((rc = __ns_ldap_check_gssapi_preq(foreground, + mode_verbose, mode_quiet, config, errpp)) != + NS_LDAP_SUCCESS) + return (rc); + + return (NS_LDAP_SUCCESS); +} diff --git a/usr/src/lib/libsldap/common/ns_sldap.h b/usr/src/lib/libsldap/common/ns_sldap.h index 0138c1e3d5..59d7ae516a 100644 --- a/usr/src/lib/libsldap/common/ns_sldap.h +++ b/usr/src/lib/libsldap/common/ns_sldap.h @@ -91,7 +91,7 @@ typedef enum ScopeType { typedef enum CredLevel { NS_LDAP_CRED_ANON = 0, NS_LDAP_CRED_PROXY = 1, - NS_LDAP_CRED_SELF = 2 /* currently not supported */ + NS_LDAP_CRED_SELF = 2 } CredLevel_t; typedef enum AuthType { @@ -113,14 +113,14 @@ typedef enum SaslMech { NS_LDAP_SASL_CRAM_MD5 = 1, NS_LDAP_SASL_DIGEST_MD5 = 2, NS_LDAP_SASL_EXTERNAL = 3, /* currently not supported */ - NS_LDAP_SASL_GSSAPI = 4, /* currently not supported */ + NS_LDAP_SASL_GSSAPI = 4, NS_LDAP_SASL_SPNEGO = 5 /* currently not supported */ } SaslMech_t; typedef enum SaslOpt { NS_LDAP_SASLOPT_NONE = 0, - NS_LDAP_SASLOPT_INT = 1, /* currently not supported */ - NS_LDAP_SASLOPT_PRIV = 2 /* currently not supported */ + NS_LDAP_SASLOPT_INT = 1, + NS_LDAP_SASLOPT_PRIV = 2 } SaslOpt_t; typedef enum PrefOnly { @@ -211,6 +211,17 @@ typedef enum { } ParamIndexType; /* + * NONE - No self / SASL/GSSAPI configured + * ONLY - Only self / SASL/GSSAPI configured + * MIXED - self / SASL/GSSAPI is mixed with other types of configuration + */ +typedef enum { + NS_LDAP_SELF_GSSAPI_CONFIG_NONE = 0, + NS_LDAP_SELF_GSSAPI_CONFIG_ONLY = 1, + NS_LDAP_SELF_GSSAPI_CONFIG_MIXED = 2 +} ns_ldap_self_gssapi_config_t; + +/* * __ns_ldap_*() return codes */ typedef enum { @@ -614,6 +625,10 @@ char **__ns_ldap_getAttr( const ns_ldap_entry_t *entry, const char *attrname); +ns_ldap_attr_t *__ns_ldap_getAttrStruct( + const ns_ldap_entry_t *entry, + const char *attrname); + int __ns_ldap_getServiceAuthMethods( const char *service, ns_auth_t ***auth, @@ -666,6 +681,12 @@ int __ns_ldap_getParamType( int __ns_ldap_getAcctMgmt( const char *user, AcctUsableResponse_t *acctResp); +void +__ns_ldap_self_gssapi_only_set( + int flag); +int +__ns_ldap_self_gssapi_config( + ns_ldap_self_gssapi_config_t *config); #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libsldap/common/ns_writes.c b/usr/src/lib/libsldap/common/ns_writes.c index dadec06207..474db98161 100644 --- a/usr/src/lib/libsldap/common/ns_writes.c +++ b/usr/src/lib/libsldap/common/ns_writes.c @@ -691,7 +691,7 @@ write_state_machine( switch (state) { case W_EXIT: if (connectionId > -1) - DropConnection(connectionId, 0); + DropConnection(connectionId, NS_LDAP_NEW_CONN); if (ref_list) __s_api_deleteRefInfo(ref_list); if (target_dn && target_dn_allocated) @@ -723,7 +723,7 @@ write_state_machine( break; case GET_CONNECTION: rc = __s_api_getConnection(NULL, - flags, + flags | NS_LDAP_NEW_CONN, cred, &connectionId, &conp, @@ -912,7 +912,7 @@ write_state_machine( if (*errorp) (void) __ns_ldap_freeError(errorp); if (connectionId > -1) - DropConnection(connectionId, 0); + DropConnection(connectionId, NS_LDAP_NEW_CONN); rc = __s_api_getConnection(current_ref->refHost, 0, cred, diff --git a/usr/src/lib/libsldap/req.flg b/usr/src/lib/libsldap/req.flg index 6ced3afe87..25da177a24 100644 --- a/usr/src/lib/libsldap/req.flg +++ b/usr/src/lib/libsldap/req.flg @@ -28,3 +28,4 @@ echo_file usr/src/lib/libldap5/include/ldap/solaris-priv.h echo_file usr/src/lib/libldap5/include/ldap/ldappr.h +echo_file usr/src/lib/libldap5/include/ldap/solaris-int.h |
