1e1dd0a2th/*
2e1dd0a2th * CDDL HEADER START
3e1dd0a2th *
4e1dd0a2th * The contents of this file are subject to the terms of the
5e1dd0a2th * Common Development and Distribution License (the "License").
6e1dd0a2th * You may not use this file except in compliance with the License.
7e1dd0a2th *
8e1dd0a2th * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e1dd0a2th * or http://www.opensolaris.org/os/licensing.
10e1dd0a2th * See the License for the specific language governing permissions
11e1dd0a2th * and limitations under the License.
12e1dd0a2th *
13e1dd0a2th * When distributing Covered Code, include this CDDL HEADER in each
14e1dd0a2th * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e1dd0a2th * If applicable, add the following below this CDDL HEADER, with the
16e1dd0a2th * fields enclosed by brackets "[]" replaced with your own identifying
17e1dd0a2th * information: Portions Copyright [yyyy] [name of copyright owner]
18e1dd0a2th *
19e1dd0a2th * CDDL HEADER END
20e1dd0a2th */
21e1dd0a2th/*
22e1dd0a2th * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23e1dd0a2th * Use is subject to license terms.
24695ef82Gordon Ross *
25695ef82Gordon Ross * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26e1dd0a2th */
27e1dd0a2th
28e1dd0a2th#include <string.h>
29e1dd0a2th#include <errno.h>
30e1dd0a2th#include <syslog.h>
31e1dd0a2th#include <procfs.h>
32e1dd0a2th#include <unistd.h>
33e1dd0a2th#include <fcntl.h>
34e1dd0a2th#include <libintl.h>
35e1dd0a2th#include <atomic.h>
36e1dd0a2th#include <pthread.h>
37e1dd0a2th#include <sys/mman.h>
38e1dd0a2th#include <time.h>
39e1dd0a2th#include "solaris-int.h"
40e1dd0a2th#include "ns_connmgmt.h"
41e1dd0a2th#include "ns_cache_door.h"
42e1dd0a2th#include "ns_internal.h"
43e1dd0a2th
44e1dd0a2th/*
45e1dd0a2th * Access (reference, shutdown, or reload) the current connection
46e1dd0a2th * management control structure conn_mgmt_t.
47e1dd0a2th */
48e1dd0a2th#define	NS_CONN_MGMT_OP_REF		1
49e1dd0a2th#define	NS_CONN_MGMT_OP_SHUTDOWN	2
50e1dd0a2th#define	NS_CONN_MGMT_OP_RELOAD_CONFIG	3
51e1dd0a2th#define	NS_CONN_MGMT_OP_NEW_CONFIG	4
52e1dd0a2th#define	NS_CONN_MGMT_OP_LIB_INIT	5
53e1dd0a2th
54e1dd0a2thstatic ns_conn_mgmt_t *access_conn_mgmt(int);
55e1dd0a2thstatic ns_conn_mgmt_t *release_conn_mgmt(ns_conn_mgmt_t *, boolean_t);
56e1dd0a2thstatic int close_conn_mt(ns_conn_mt_t *, int, ns_ldap_error_t **,
57e1dd0a2th	ns_conn_user_t *);
58e1dd0a2thstatic int close_conn_mt_when_nouser(ns_conn_mt_t *cm);
59e1dd0a2thvoid shutdown_all_conn_mt(ns_conn_mgmt_t *cmg);
60e1dd0a2thstatic int conn_signal(ns_conn_mt_t *);
61e1dd0a2thstatic int conn_wait(ns_conn_mt_t *, ns_conn_user_t *);
62e1dd0a2thstatic void close_conn_mt_by_procchg(ns_conn_mt_t *cm, int rc, char *errmsg);
63e1dd0a2thstatic ns_conn_mgmt_t *proc_server_change(ns_server_status_change_t *chg,
64e1dd0a2th	ns_conn_mgmt_t  *cmg);
65e1dd0a2thstatic void get_preferred_servers(boolean_t, boolean_t, ns_conn_mgmt_t *);
66e1dd0a2thstatic void start_thread();
67e1dd0a2th
68e1dd0a2thstatic ns_conn_mgmt_t	*ns_connmgmt = NULL;
69e1dd0a2thstatic ns_conn_mgmt_t	*ns_connmgmt_parent = NULL;
70e1dd0a2thstatic mutex_t		ns_connmgmt_lock = DEFAULTMUTEX;
71e1dd0a2thstatic boolean_t	ns_connmgmt_shutting_down = B_FALSE;
72e1dd0a2th
73e1dd0a2th#define	NS_CONN_MSG_NO_CONN_MGMT gettext( \
74e1dd0a2th	"libsldap: unable to allocate the connection management control")
75e1dd0a2th#define	NS_CONN_MSG_NO_MTC_KEY gettext( \
76e1dd0a2th	"libsldap: unable to allocate the TSD key for per-thread ldap error")
77e1dd0a2th#define	NS_CONN_MSG_NO_CMG_KEY gettext( \
78e1dd0a2th	"libsldap: unable to allocate the TSD key for connection management")
79e1dd0a2th#define	NS_CONN_MSG_SHUTDOWN gettext("libsldap: library is being unloaded")
80e1dd0a2th#define	NS_CONN_MSG_RELOADED gettext( \
81e1dd0a2th	"libsldap: configuration has been reloaded")
82e1dd0a2th#define	NS_CONN_MSG_SHUTDOWN_RELOADED gettext( \
83e1dd0a2th	"libsldap: library unloaded or configuration has been reloaded")
84e1dd0a2th#define	NS_CONN_MSG_BAD_CACHEMGR_DATA gettext( \
85e1dd0a2th	"libsldap: received incorrect data from ldap_cachemgr")
86e1dd0a2th#define	NS_CONN_MSG_MEMORY_ERROR gettext( \
87e1dd0a2th	"libsldap: unable to allocate memory")
88e1dd0a2th#define	NS_CONN_MSG_NO_PROCCHG_THREAD gettext( \
89e1dd0a2th	"libsldap: unable to start the server monitor thread (%s)")
90e1dd0a2th#define	NS_CONN_MSG_DOWN_FROM_CACHEMGR gettext( \
91e1dd0a2th	"libsldap: server down reported by ldap_cachemgr")
92e1dd0a2th
93e1dd0a2thstatic int ns_conn_free = 1;
94e1dd0a2th#define	NS_CONN_UNLOCK_AND_FREE(free, cm, cmg)  \
95e1dd0a2th{ \
96e1dd0a2th	(void) mutex_unlock(&(cm)->lock);	\
97e1dd0a2th	if (free == 1)	\
98e1dd0a2th		cmg = free_conn_mt((cm), 1); \
99e1dd0a2th	if (cmg != NULL) \
100e1dd0a2th		(void) mutex_unlock(&(cmg)->lock); \
101e1dd0a2th}
102e1dd0a2th
103e1dd0a2th#define	NS_CONN_CHECK_ABORT_AND_LOCK(cmg, cu, errp) \
104e1dd0a2th{ \
105e1dd0a2th	char *msg = NULL; \
106e1dd0a2th	(void) mutex_lock(&(cmg)->lock); \
107e1dd0a2th	if ((cmg)->shutting_down == B_TRUE) \
108e1dd0a2th		msg = NS_CONN_MSG_SHUTDOWN; \
109e1dd0a2th	else if ((cmg)->cfg_reloaded == B_TRUE)  \
110e1dd0a2th		msg = NS_CONN_MSG_RELOADED; \
111e1dd0a2th	if (msg != NULL) { \
112e1dd0a2th		(*errp) = __s_api_make_error(NS_LDAP_OP_FAILED, msg); \
113e1dd0a2th		(void) mutex_unlock(&(cmg)->lock); \
114e1dd0a2th		return (NS_LDAP_OP_FAILED); \
115e1dd0a2th	} \
116e1dd0a2th}
117e1dd0a2th
118e1dd0a2th/*
119e1dd0a2th * TSD keys ns_mtckey and ns_cmgkey are for sharing ldap connections
120e1dd0a2th * and their associated connection management structure among
121e1dd0a2th * multiple threads. The pointers to the per-thread ldap error
122e1dd0a2th * information and the connection management structure are
123e1dd0a2th * saved in ns_mtckey and ns_cmgkey.
124e1dd0a2th */
125e1dd0a2ththread_key_t ns_mtckey = THR_ONCE_KEY;
126e1dd0a2ththread_key_t ns_cmgkey = THR_ONCE_KEY;
127e1dd0a2th
128e1dd0a2th/* Per thread LDAP error resides in thread-specific data (ns_mtckey) */
129e1dd0a2thstruct ldap_error {
130e1dd0a2th	int	le_errno;
131e1dd0a2th	char	*le_matched;
132e1dd0a2th	char	*le_errmsg;
133e1dd0a2th};
134e1dd0a2th
135e1dd0a2th/* NULL struct ldap_error */
136e1dd0a2thstatic struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
137e1dd0a2th
138e1dd0a2th/* destructor: free the ldap error data in the thread specific area */
139e1dd0a2thstatic void
140cc98049Toomas Soomens_mtckey_cleanup(void *key)
141cc98049Toomas Soome{
142e1dd0a2th	struct ldap_error *le = (struct ldap_error *)key;
143e1dd0a2th
144e1dd0a2th	if (le == NULL)
145e1dd0a2th		return;
146e1dd0a2th	if (le->le_matched != NULL) {
147e1dd0a2th		ldap_memfree(le->le_matched);
148e1dd0a2th	}
149e1dd0a2th	if (le->le_errmsg != NULL) {
150e1dd0a2th		ldap_memfree(le->le_errmsg);
151e1dd0a2th	}
152e1dd0a2th	free(le);
153e1dd0a2th}
154e1dd0a2th
155e1dd0a2th/* Free/detach the thread specific data structures */
156e1dd0a2thstatic void
157cc98049Toomas Soomeconn_tsd_free(void)
158cc98049Toomas Soome{
159e1dd0a2th	void	*tsd = NULL;
160e1dd0a2th	int	rc;
161e1dd0a2th
162e1dd0a2th	/* free the per-thread ldap error info */
163e1dd0a2th	rc = thr_getspecific(ns_mtckey, &tsd);
164e1dd0a2th	if (rc == 0 && tsd != NULL)
165e1dd0a2th		ns_mtckey_cleanup(tsd);
166e1dd0a2th	(void) thr_setspecific(ns_mtckey, NULL);
167e1dd0a2th
168e1dd0a2th	/* detach the connection management control */
169e1dd0a2th	(void) thr_setspecific(ns_cmgkey, NULL);
170e1dd0a2th}
171e1dd0a2th
172e1dd0a2th/* per-thread callback function for allocating a mutex */
173e1dd0a2thstatic void *
174e1dd0a2thns_mutex_alloc(void)
175e1dd0a2th{
176e1dd0a2th	mutex_t *mutexp = NULL;
177e1dd0a2th
178e1dd0a2th	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
179e1dd0a2th		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
180e1dd0a2th			free(mutexp);
181e1dd0a2th			mutexp = NULL;
182e1dd0a2th		}
183e1dd0a2th	}
184e1dd0a2th	return (mutexp);
185e1dd0a2th}
186e1dd0a2th
187e1dd0a2th/* per-thread callback function for freeing a mutex */
188e1dd0a2thstatic void
189e1dd0a2thns_mutex_free(void *mutexp)
190e1dd0a2th{
191e1dd0a2th	(void) mutex_destroy((mutex_t *)mutexp);
192e1dd0a2th	free(mutexp);
193e1dd0a2th}
194e1dd0a2th
195e1dd0a2th/*
196e1dd0a2th * Function for setting up thread-specific data
197e1dd0a2th * where per thread LDAP error and the pointer
198e1dd0a2th * to the active connection management control
199e1dd0a2th * are stored.
200e1dd0a2th */
201e1dd0a2thstatic int
202e1dd0a2thconn_tsd_setup(ns_conn_mgmt_t *cmg)
203e1dd0a2th{
204e1dd0a2th	void	*tsd;
205e1dd0a2th	int	rc;
206e1dd0a2th
207e1dd0a2th	rc = thr_setspecific(ns_cmgkey, cmg);
208e1dd0a2th	if (rc != 0) /* must be ENOMEM */
209e1dd0a2th		return (-1);
210e1dd0a2th
211e1dd0a2th	/* return success if the ns_mtckey TSD is already set */
212e1dd0a2th	rc = thr_getspecific(ns_mtckey, &tsd);
213e1dd0a2th	if (rc == 0 && tsd != NULL)
214e1dd0a2th		return (0);
215e1dd0a2th
216e1dd0a2th	/* allocate and set the ns_mtckey TSD */
217e1dd0a2th	tsd = (void *) calloc(1, sizeof (struct ldap_error));
218e1dd0a2th	if (tsd == NULL)
219e1dd0a2th		return (-1);
220e1dd0a2th	rc = thr_setspecific(ns_mtckey, tsd);
221e1dd0a2th	if (rc != 0) { /* must be ENOMEM */
222e1dd0a2th		free(tsd);
223e1dd0a2th		return (-1);
224e1dd0a2th	}
225e1dd0a2th	return (0);
226e1dd0a2th}
227e1dd0a2th
228e1dd0a2th/* Callback function for setting the per thread LDAP error */
229e1dd0a2th/*ARGSUSED*/
230e1dd0a2thstatic void
231e1dd0a2thset_ld_error(int err, char *matched, char *errmsg, void *dummy)
232e1dd0a2th{
233e1dd0a2th	struct ldap_error	*le;
234e1dd0a2th	int			eno;
235e1dd0a2th
236e1dd0a2th	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
237e1dd0a2th		syslog(LOG_ERR, gettext(
238e1dd0a2th		    "libsldap: set_ld_error: thr_getspecific failed (%s)."),
239e1dd0a2th		    strerror(eno));
240e1dd0a2th		return;
241e1dd0a2th	}
242e1dd0a2th
243e1dd0a2th	/* play safe, do nothing if TSD pointer is NULL */
244e1dd0a2th	if (le == NULL) {
245e1dd0a2th		syslog(LOG_INFO, gettext(
246e1dd0a2th		    "libsldap: set_ld_error: TSD pointer is NULL."));
247e1dd0a2th		return;
248e1dd0a2th	}
249e1dd0a2th
250e1dd0a2th	le->le_errno = err;
251e1dd0a2th
252e1dd0a2th	if (le->le_matched != NULL) {
253e1dd0a2th		ldap_memfree(le->le_matched);
254e1dd0a2th		le->le_matched = NULL;
255e1dd0a2th	}
256e1dd0a2th	le->le_matched = matched;
257e1dd0a2th
258e1dd0a2th	if (le->le_errmsg != NULL) {
259e1dd0a2th		ldap_memfree(le->le_errmsg);
260e1dd0a2th		le->le_errmsg = NULL;
261e1dd0a2th	}
262e1dd0a2th	le->le_errmsg = errmsg;
263e1dd0a2th}
264e1dd0a2th
265e1dd0a2th/* check and allocate the thread-specific data for using a MT connection */
266e1dd0a2thstatic int
267e1dd0a2thconn_tsd_check(ns_conn_mgmt_t *cmg)
268e1dd0a2th{
269e1dd0a2th	if (conn_tsd_setup(cmg) != 0)
270e1dd0a2th		return (NS_LDAP_MEMORY);
271e1dd0a2th
272e1dd0a2th	return (NS_LDAP_SUCCESS);
273e1dd0a2th}
274e1dd0a2th
275e1dd0a2th/* Callback function for getting the per thread LDAP error */
276e1dd0a2th/*ARGSUSED*/
277e1dd0a2thstatic int
278e1dd0a2thget_ld_error(char **matched, char **errmsg, void *dummy)
279e1dd0a2th{
280e1dd0a2th	struct ldap_error	*le;
281e1dd0a2th	int			eno;
282e1dd0a2th
283e1dd0a2th	if ((eno = thr_getspecific(ns_mtckey, (void **)&le)) != 0) {
284e1dd0a2th		syslog(LOG_ERR, gettext(
285e1dd0a2th		    "libsldap: get_ld_error: thr_getspecific failed (%s)"),
286e1dd0a2th		    strerror(eno));
287e1dd0a2th		return (eno);
288e1dd0a2th	}
289e1dd0a2th
290e1dd0a2th	/* play safe, return NULL error data, if TSD pointer is NULL */
291e1dd0a2th	if (le == NULL)
292e1dd0a2th		le = &ldap_error_NULL;
293e1dd0a2th
294e1dd0a2th	if (matched != NULL) {
295e1dd0a2th		*matched = le->le_matched;
296e1dd0a2th	}
297e1dd0a2th	if (errmsg != NULL) {
298e1dd0a2th		*errmsg = le->le_errmsg;
299e1dd0a2th	}
300e1dd0a2th	return (le->le_errno);
301e1dd0a2th}
302e1dd0a2th
303e1dd0a2th/* Callback function for setting per thread errno */
304e1dd0a2thstatic void
305e1dd0a2thset_errno(int err)
306e1dd0a2th{
307e1dd0a2th	errno = err;
308e1dd0a2th}
309e1dd0a2th
310e1dd0a2th/* Callback function for getting per thread errno */
311e1dd0a2thstatic int
312e1dd0a2thget_errno(void)
313e1dd0a2th{
314e1dd0a2th	return (errno);
315e1dd0a2th}
316e1dd0a2th
317cc98049Toomas Soomestatic void *
318cc98049Toomas Soomeltf_threadid(void)
319cc98049Toomas Soome{
320cc98049Toomas Soome	return ((void *)(uintptr_t)thr_self());
321cc98049Toomas Soome}
322cc98049Toomas Soome
323e1dd0a2th/* set up an ldap session 'ld' for sharing among multiple threads */
324e1dd0a2thstatic int
325e1dd0a2thsetup_mt_conn(LDAP *ld)
326e1dd0a2th{
327e1dd0a2th
328e1dd0a2th	struct ldap_thread_fns		tfns;
329e1dd0a2th	struct ldap_extra_thread_fns	extrafns;
330e1dd0a2th	int				rc;
331e1dd0a2th
332e1dd0a2th	/*
333e1dd0a2th	 * Set the function pointers for dealing with mutexes
334e1dd0a2th	 * and error information
335e1dd0a2th	 */
336e1dd0a2th	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
337e1dd0a2th	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
338e1dd0a2th	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
339e1dd0a2th	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
340e1dd0a2th	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
341e1dd0a2th	tfns.ltf_get_errno = get_errno;
342e1dd0a2th	tfns.ltf_set_errno = set_errno;
343e1dd0a2th	tfns.ltf_get_lderrno = get_ld_error;
344e1dd0a2th	tfns.ltf_set_lderrno = set_ld_error;
345e1dd0a2th	tfns.ltf_lderrno_arg = NULL;
346e1dd0a2th
347e1dd0a2th	/*
348e1dd0a2th	 * Set up the ld to use those function pointers
349e1dd0a2th	 */
350e1dd0a2th	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
351e1dd0a2th	    (void *) &tfns);
352e1dd0a2th	if (rc < 0) {
353e1dd0a2th		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
354e1dd0a2th		"(LDAP_OPT_THREAD_FN_PTRS)"));
355e1dd0a2th		return (0);
356e1dd0a2th	}
357e1dd0a2th
358e1dd0a2th	/*
359e1dd0a2th	 * Set the function pointers for working with semaphores
360e1dd0a2th	 */
361e1dd0a2th	(void) memset(&extrafns, '\0',
362e1dd0a2th	    sizeof (struct ldap_extra_thread_fns));
363cc98049Toomas Soome	extrafns.ltf_threadid_fn = ltf_threadid;
364e1dd0a2th	extrafns.ltf_mutex_trylock = NULL;
365e1dd0a2th	extrafns.ltf_sema_alloc = NULL;
366e1dd0a2th	extrafns.ltf_sema_free = NULL;
367e1dd0a2th	extrafns.ltf_sema_wait = NULL;
368e1dd0a2th	extrafns.ltf_sema_post = NULL;
369e1dd0a2th
370e1dd0a2th	/* Set up the ld to use those function pointers */
371e1dd0a2th	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
372e1dd0a2th	    (void *) &extrafns);
373e1dd0a2th	if (rc < 0) {
374e1dd0a2th		syslog(LOG_INFO, gettext("libsldap: ldap_set_option "
375e1dd0a2th		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"));
376e1dd0a2th		return (0);
377e1dd0a2th	}
378e1dd0a2th
379e1dd0a2th	return (1);
380e1dd0a2th}
381e1dd0a2th
382e1dd0a2th/* set up an MT connection for sharing among multiple threads */
383e1dd0a2thstatic int
384e1dd0a2thsetup_mt_ld(LDAP *ld, ns_conn_mgmt_t *cmg)
385e1dd0a2th{
386e1dd0a2th	thread_t	t = thr_self();
387e1dd0a2th
388e1dd0a2th	/* set up the per-thread data for using the MT connection */
389e1dd0a2th	if (conn_tsd_setup(cmg) == -1) {
390e1dd0a2th		syslog(LOG_WARNING,
391e1dd0a2th		    gettext("libsldap: tid= %d: unable to set up TSD\n"), t);
392e1dd0a2th		return (-1);
393e1dd0a2th	}
394e1dd0a2th
395e1dd0a2th	if (setup_mt_conn(ld) == 0) {
396e1dd0a2th		/* multiple threads per connection not supported */
397e1dd0a2th		syslog(LOG_WARNING, gettext("libsldap: tid= %d: multiple "
398e1dd0a2th		    "threads per connection not supported\n"), t);
399e1dd0a2th		conn_tsd_free();
400e1dd0a2th		return (-1);
401e1dd0a2th	}
402e1dd0a2th	return (0);
403e1dd0a2th}
404e1dd0a2th
405e1dd0a2th/*
406e1dd0a2th * Check name and UID of process, if it is nscd.
407e1dd0a2th *
408e1dd0a2th * Input:
409e1dd0a2th *   pid	: PID of checked process
410e1dd0a2th *   check_uid	: check if UID == 0
411e1dd0a2th * Output:
412e1dd0a2th *   B_TRUE	: nscd detected
413e1dd0a2th *   B_FALSE	: nscd not confirmed
414e1dd0a2th */
415e1dd0a2thstatic boolean_t
416e1dd0a2thcheck_nscd_proc(pid_t pid, boolean_t check_uid)
417e1dd0a2