diff options
author | djl <none@none> | 2006-09-29 06:00:17 -0700 |
---|---|---|
committer | djl <none@none> | 2006-09-29 06:00:17 -0700 |
commit | cb5caa98562cf06753163f558cbcfe30b8f4673a (patch) | |
tree | 7a24623821583899295e29553207e69701b471ff /usr/src/lib/libsldap/common | |
parent | 350f572a3fa518fc3690d53066c2c54fd03b5a08 (diff) | |
download | illumos-joyent-cb5caa98562cf06753163f558cbcfe30b8f4673a.tar.gz |
PSARC 2005/133 Sparks: Name Service Switch 2
4406529 artificial limit of 10 threads per backend
4516075 LDAP connections could be reused more
4696964 LDAP naming services should support Kerberos authentication
4740951 Need host based authentication options in Native LDAP
4952533 Some backends of gethostby* do not set h_errno correctly
4979596 getXbyY calls should have better buffer mechanism
5028908 /usr/bin/logins accesses free memory deep in nss_getent_u().
5046881 nscd: old-data-ok parameter is not useful, should go away
6225323 NSS/nscd Enhancements (Sparks Project)
--HG--
rename : usr/src/cmd/nscd/attrstr.c => deleted_files/usr/src/cmd/nscd/attrstr.c
rename : usr/src/cmd/nscd/hash.c => deleted_files/usr/src/cmd/nscd/hash.c
rename : usr/src/cmd/nscd/nscd_parse.c => deleted_files/usr/src/cmd/nscd/nscd_parse.c
rename : usr/src/cmd/nscd/nscd.h => usr/src/cmd/nscd/cache.h
Diffstat (limited to 'usr/src/lib/libsldap/common')
-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 |
12 files changed, 1607 insertions, 190 deletions
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, |