1e1dd0a2fSth /*
2e1dd0a2fSth  * CDDL HEADER START
3e1dd0a2fSth  *
4e1dd0a2fSth  * The contents of this file are subject to the terms of the
5e1dd0a2fSth  * Common Development and Distribution License (the "License").
6e1dd0a2fSth  * You may not use this file except in compliance with the License.
7e1dd0a2fSth  *
8e1dd0a2fSth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e1dd0a2fSth  * or http://www.opensolaris.org/os/licensing.
10e1dd0a2fSth  * See the License for the specific language governing permissions
11e1dd0a2fSth  * and limitations under the License.
12e1dd0a2fSth  *
13e1dd0a2fSth  * When distributing Covered Code, include this CDDL HEADER in each
14e1dd0a2fSth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e1dd0a2fSth  * If applicable, add the following below this CDDL HEADER, with the
16e1dd0a2fSth  * fields enclosed by brackets "[]" replaced with your own identifying
17e1dd0a2fSth  * information: Portions Copyright [yyyy] [name of copyright owner]
18e1dd0a2fSth  *
19e1dd0a2fSth  * CDDL HEADER END
20e1dd0a2fSth  */
21e1dd0a2fSth /*
22434c5a06SMilan Jurik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23e1dd0a2fSth  * Use is subject to license terms.
2433f5ff17SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
25b3b48d8eSHans Rosenfeld  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
26e1dd0a2fSth  */
27e1dd0a2fSth 
28e1dd0a2fSth #define	__STANDALONE_MODULE__
29e1dd0a2fSth 
30e1dd0a2fSth #include <stdio.h>
31e1dd0a2fSth #include <sys/types.h>
32e1dd0a2fSth #include <stdlib.h>
33e1dd0a2fSth #include <libintl.h>
34e1dd0a2fSth #include <string.h>
35e1dd0a2fSth #include <ctype.h>
36e1dd0a2fSth 
37e1dd0a2fSth #include <sys/stat.h>
38e1dd0a2fSth #include <fcntl.h>
39e1dd0a2fSth #include <unistd.h>
40e1dd0a2fSth #include <syslog.h>
41e1dd0a2fSth #include <locale.h>
42e1dd0a2fSth #include <errno.h>
43e1dd0a2fSth #include <sys/time.h>
44e1dd0a2fSth 
45e1dd0a2fSth #include <arpa/inet.h>
46e1dd0a2fSth #include <netdb.h>
47e1dd0a2fSth #include <strings.h>
48e1dd0a2fSth 
49e1dd0a2fSth #include <thread.h>
50e1dd0a2fSth 
51e1dd0a2fSth #include <nsswitch.h>
52e1dd0a2fSth #include <nss_dbdefs.h>
53e1dd0a2fSth #include <nss.h>
54e1dd0a2fSth 
55e1dd0a2fSth #include "ns_cache_door.h"
56e1dd0a2fSth #include "ns_internal.h"
57ca190d8dSmichen #include "ns_connmgmt.h"
58e1dd0a2fSth 
59e1dd0a2fSth typedef enum {
60e1dd0a2fSth 	INFO_SERVER_JUST_INITED	= -1,
61e1dd0a2fSth 	INFO_SERVER_UNKNOWN	= 0,
62e1dd0a2fSth 	INFO_SERVER_CONNECTING	= 1,
63e1dd0a2fSth 	INFO_SERVER_UP		= 2,
64e1dd0a2fSth 	INFO_SERVER_ERROR 	= 3,
65e1dd0a2fSth 	INFO_SERVER_REMOVED	= 4
66e1dd0a2fSth } dir_server_status_t;
67e1dd0a2fSth 
68e1dd0a2fSth typedef enum {
69e1dd0a2fSth 	INFO_STATUS_NEW   	= 2,
70e1dd0a2fSth 	INFO_STATUS_OLD		= 3
71e1dd0a2fSth } dir_server_info_t;
72e1dd0a2fSth 
73e1dd0a2fSth typedef struct dir_server {
74e1dd0a2fSth 	char			*ip;
75e1dd0a2fSth 	char			**controls;
76e1dd0a2fSth 	char			**saslMech;
77e1dd0a2fSth 	dir_server_status_t	status;
78e1dd0a2fSth 	mutex_t			updateStatus;
79e1dd0a2fSth 	dir_server_info_t	info;
80e1dd0a2fSth } dir_server_t;
81e1dd0a2fSth 
82e1dd0a2fSth typedef struct dir_server_list {
83e1dd0a2fSth 	dir_server_t	**nsServers;
84e1dd0a2fSth 
85e1dd0a2fSth 	rwlock_t	listDestroyLock;
86e1dd0a2fSth } dir_server_list_t;
87e1dd0a2fSth 
88e1dd0a2fSth struct {
89e1dd0a2fSth 	/* The local list of the directory servers' root DSEs. */
90e1dd0a2fSth 	dir_server_list_t	*list;
91e1dd0a2fSth 	/* The flag indicating if libsldap is in the 'Standalone' mode. */
92e1dd0a2fSth 	int			standalone;
93e1dd0a2fSth 	/*
94e1dd0a2fSth 	 * The mutex ensuring that only one thread performs
95e1dd0a2fSth 	 * the initialization of the list.
96e1dd0a2fSth 	 */
97e1dd0a2fSth 	mutex_t			listReplaceLock;
98e1dd0a2fSth 	/*
99e1dd0a2fSth 	 * A flag indicating that a particular thread is
100e1dd0a2fSth 	 * in the 'ldap_cachemgr' mode. It is stored by thread as
101e1dd0a2fSth 	 * a thread specific data.
102e1dd0a2fSth 	 */
103e1dd0a2fSth 	const int		initFlag;
104e1dd0a2fSth 	/*
105e1dd0a2fSth 	 * A thread specific key storing
106e1dd0a2fSth 	 * the the 'ldap_cachemgr' mode indicator.
107e1dd0a2fSth 	 */
108e1dd0a2fSth 	thread_key_t		standaloneInitKey;
109e1dd0a2fSth } dir_servers = {NULL, 0, DEFAULTMUTEX, '1'};
110e1dd0a2fSth 
111e1dd0a2fSth typedef struct switchDatabase {
112e1dd0a2fSth 	char *conf;
113e1dd0a2fSth 	uint32_t alloced;
114e1dd0a2fSth } switch_database_t;
115e1dd0a2fSth 
116e1dd0a2fSth static thread_key_t switchConfigKey;
117e1dd0a2fSth 
118e1dd0a2fSth #pragma init(createStandaloneKey)
119e1dd0a2fSth 
120e1dd0a2fSth #define	DONT_INCLUDE_ATTR_NAMES	0
121e1dd0a2fSth #define	INCLUDE_ATTR_NAMES	1
122e1dd0a2fSth #define	IS_PROFILE		1
123e1dd0a2fSth #define	NOT_PROFILE		0
124e1dd0a2fSth /* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */
125e1dd0a2fSth #define	MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12)
126e1dd0a2fSth 
127e1dd0a2fSth static
128e1dd0a2fSth void
switch_conf_disposer(void * data)129e1dd0a2fSth switch_conf_disposer(void *data)
130e1dd0a2fSth {
131e1dd0a2fSth 	switch_database_t *localData = (switch_database_t *)data;
132e1dd0a2fSth 
133e1dd0a2fSth 	free(localData->conf);
134e1dd0a2fSth 	free(localData);
135e1dd0a2fSth }
136e1dd0a2fSth 
137e1dd0a2fSth /*
138e1dd0a2fSth  * This function initializes an indication that a thread obtaining a root DSE
139e1dd0a2fSth  * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap
140e1dd0a2fSth  * will not invoke the __s_api_requestServer function. Instead, the library
141e1dd0a2fSth  * will establish a connection to the server specified by
142e1dd0a2fSth  * the __ns_ldap_getRootDSE function.
143e1dd0a2fSth  * Since  ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time
144e1dd0a2fSth  * and we do not want to affect a thread obtaining a DUAProfile,
145e1dd0a2fSth  * the 'ldap_cachemgr' mode is thread private.
146e1dd0a2fSth  * In addition, this function creates a key holding temporary configuration
147e1dd0a2fSth  * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB"
148e1dd0a2fSth  * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()).
149e1dd0a2fSth  */
150e1dd0a2fSth static
151e1dd0a2fSth void
createStandaloneKey()152e1dd0a2fSth createStandaloneKey()
153e1dd0a2fSth {
154e1dd0a2fSth 	if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) {
155e1dd0a2fSth 		syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
156e1dd0a2fSth 		"key needed for sharing ldap connections"));
157e1dd0a2fSth 	}
158e1dd0a2fSth 	if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) {
159e1dd0a2fSth 		syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
160e1dd0a2fSth 		    "key containing current nsswitch configuration"));
161e1dd0a2fSth 	}
162e1dd0a2fSth }
163e1dd0a2fSth 
164e1dd0a2fSth /*
165e1dd0a2fSth  * This function sets the 'ldap_cachemgr' mode indication.
166e1dd0a2fSth  */
167e1dd0a2fSth void
__s_api_setInitMode()168e1dd0a2fSth __s_api_setInitMode()
169e1dd0a2fSth {
170e1dd0a2fSth 	(void) thr_setspecific(dir_servers.standaloneInitKey,
171e1dd0a2fSth 	    (void *) &dir_servers.initFlag);
172e1dd0a2fSth }
173e1dd0a2fSth 
174e1dd0a2fSth /*
175e1dd0a2fSth  * This function unset the 'ldap_cachemgr' mode indication.
176e1dd0a2fSth  */
177e1dd0a2fSth void
__s_api_unsetInitMode()178e1dd0a2fSth __s_api_unsetInitMode()
179e1dd0a2fSth {
180e1dd0a2fSth 	(void) thr_setspecific(dir_servers.standaloneInitKey, NULL);
181e1dd0a2fSth }
182e1dd0a2fSth 
183e1dd0a2fSth /*
184e1dd0a2fSth  * This function checks if the 'ldap_cachemgr' mode indication is set.
185e1dd0a2fSth  */
186e1dd0a2fSth int
__s_api_isInitializing()187e1dd0a2fSth __s_api_isInitializing() {
188e1dd0a2fSth 	int *flag = NULL;
189e1dd0a2fSth 
190e1dd0a2fSth 	(void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag);
191e1dd0a2fSth 
192e1dd0a2fSth 	return (flag != NULL && *flag == dir_servers.initFlag);
193e1dd0a2fSth }
194e1dd0a2fSth 
195e1dd0a2fSth /*
196e1dd0a2fSth  * This function checks if the process runs in the 'Standalone' mode.
197e1dd0a2fSth  * In this mode libsldap will check the local, process private list of root DSEs
198e1dd0a2fSth  * instead of requesting them via a door call to ldap_cachemgr.
199e1dd0a2fSth  */
200e1dd0a2fSth int
__s_api_isStandalone()201e1dd0a2fSth __s_api_isStandalone()
202e1dd0a2fSth {
203e1dd0a2fSth 	int	mode;
204e1dd0a2fSth 
205e1dd0a2fSth 	(void) mutex_lock(&dir_servers.listReplaceLock);
206e1dd0a2fSth 	mode = dir_servers.standalone;
207e1dd0a2fSth 	(void) mutex_unlock(&dir_servers.listReplaceLock);
208e1dd0a2fSth 
209e1dd0a2fSth 	return (mode);
210e1dd0a2fSth }
211e1dd0a2fSth 
212e1dd0a2fSth 
213e1dd0a2fSth static
214e1dd0a2fSth int
remove_ldap(char * dst,char * src,int dst_buf_len)215e1dd0a2fSth remove_ldap(char *dst, char *src, int dst_buf_len)
216e1dd0a2fSth {
217e1dd0a2fSth 	int i = 0;
218e1dd0a2fSth 
219e1dd0a2fSth 	if (strlen(src) >= dst_buf_len)
220e1dd0a2fSth 		return (0);
221e1dd0a2fSth 
222e1dd0a2fSth 	while (*src != '\0') {
223e1dd0a2fSth 		/* Copy up to one space from source. */
224e1dd0a2fSth 		if (isspace(*src)) {
225e1dd0a2fSth 			dst[i++] = *src;
226e1dd0a2fSth 			while (isspace(*src))
227e1dd0a2fSth 				src++;
228e1dd0a2fSth 		}
229e1dd0a2fSth 
230e1dd0a2fSth 		/* If not "ldap", just copy. */
231e1dd0a2fSth 		if (strncmp(src, "ldap", 4) != 0) {
232e1dd0a2fSth 			while (!isspace(*src)) {
233e1dd0a2fSth 				dst[i++] = *src++;
234e1dd0a2fSth 				/* At the end of string? */
235e1dd0a2fSth 				if (dst[i-1] == '\0')
236e1dd0a2fSth 					return (1);
237e1dd0a2fSth 			}
238e1dd0a2fSth 			/* Copy up to one space from source. */
239e1dd0a2fSth 			if (isspace(*src)) {
240e1dd0a2fSth 				dst[i++] = *src;
241e1dd0a2fSth 				while (isspace(*src))
242e1dd0a2fSth 					src++;
243e1dd0a2fSth 			}
244e1dd0a2fSth 			/* Copy also the criteria section */
245e1dd0a2fSth 			if (*src == '[')
246e1dd0a2fSth 				while (*src != ']') {
247e1dd0a2fSth 					dst[i++] = *src++;
248e1dd0a2fSth 					/* Shouln't happen if format is right */
249e1dd0a2fSth 					if (dst[i-1] == '\0')
250e1dd0a2fSth 						return (1);
251e1dd0a2fSth 				}
252e1dd0a2fSth 		}
253e1dd0a2fSth 
254e1dd0a2fSth 		/* If next part is ldap, skip over it ... */
255e1dd0a2fSth 		if (strncmp(src, "ldap", 4) == 0) {
256e1dd0a2fSth 			if (isspace(*(src+4)) || *(src+4) == '\0') {
257e1dd0a2fSth 				src += 4;
258e1dd0a2fSth 				while (isspace(*src))
259e1dd0a2fSth 					src++;
260e1dd0a2fSth 				if (*src == '[') {
261e1dd0a2fSth 					while (*src++ != ']') {
262e1dd0a2fSth 						/*
263e1dd0a2fSth 						 * See comment above about
264e1dd0a2fSth 						 * correct format.
265e1dd0a2fSth 						 */
266e1dd0a2fSth 						if (*src == '\0') {
267e1dd0a2fSth 							dst[i++] = '\0';
268e1dd0a2fSth 							return (1);
269e1dd0a2fSth 						}
270e1dd0a2fSth 					}
271e1dd0a2fSth 				}
272e1dd0a2fSth 				while (isspace(*src))
273e1dd0a2fSth 					src++;
274e1dd0a2fSth 			}
275e1dd0a2fSth 		}
276e1dd0a2fSth 		if (*src == '\0')
277e1dd0a2fSth 			dst[i++] = '\0';
278e1dd0a2fSth 	}
279e1dd0a2fSth 
280e1dd0a2fSth 	return (1);
281e1dd0a2fSth }
282e1dd0a2fSth 
283e1dd0a2fSth static
284e1dd0a2fSth char *
get_db(const char * db_name)285e1dd0a2fSth get_db(const char *db_name)
286e1dd0a2fSth {
287e1dd0a2fSth 	char			*ptr;
288e1dd0a2fSth 	switch_database_t	*hostService = NULL;
289e1dd0a2fSth 	FILE			*fp = fopen(__NSW_CONFIG_FILE, "rF");
290e1dd0a2fSth 	char			*linep, line[NSS_BUFSIZ];
291e1dd0a2fSth 
292e1dd0a2fSth 	if (fp == NULL) {
293e1dd0a2fSth 		syslog(LOG_WARNING, gettext("libsldap: can not read %s"),
294e1dd0a2fSth 		    __NSW_CONFIG_FILE);
295e1dd0a2fSth 		return (NULL);
296e1dd0a2fSth 	}
297e1dd0a2fSth 
298e1dd0a2fSth 	while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) {
299e1dd0a2fSth 		while (isspace(*linep)) {
300e1dd0a2fSth 			++linep;
301e1dd0a2fSth 		}
302e1dd0a2fSth 		if (*linep == '#') {
303e1dd0a2fSth 			continue;
304e1dd0a2fSth 		}
305e1dd0a2fSth 		if (strncmp(linep, db_name, strlen(db_name)) != 0) {
306e1dd0a2fSth 			continue;
307e1dd0a2fSth 		}
308e1dd0a2fSth 		if ((linep = strchr(linep, ':')) != NULL) {
309e1dd0a2fSth 			if (linep[strlen(linep) - 1] == '\n') {
310e1dd0a2fSth 				linep[strlen(linep) - 1] = '\0';
311e1dd0a2fSth 			}
312e1dd0a2fSth 
313e1dd0a2fSth 			while (isspace(*++linep))
314e1dd0a2fSth 				;
315e1dd0a2fSth 
316e1dd0a2fSth 			if ((ptr = strchr(linep, '#')) != NULL) {
317e1dd0a2fSth 				while (--ptr >= linep && isspace(*ptr))
318e1dd0a2fSth 					;
319e1dd0a2fSth 				*(ptr + 1) = '\0';
320e1dd0a2fSth 			}
321e1dd0a2fSth 
322e1dd0a2fSth 			if (strlen(linep) == 0) {
323e1dd0a2fSth 				continue;
324e1dd0a2fSth 			}
325e1dd0a2fSth 			break;
326e1dd0a2fSth 		}
327e1dd0a2fSth 	}
328e1dd0a2fSth 
329e1dd0a2fSth 	(void) fclose(fp);
330e1dd0a2fSth 
331e1dd0a2fSth 	if (linep == NULL) {
332e1dd0a2fSth 		syslog(LOG_WARNING,
333e1dd0a2fSth 		    gettext("libsldap: the %s database "
334e1dd0a2fSth 		    "is missing from %s"),
335e1dd0a2fSth 		    db_name,
336e1dd0a2fSth 		    __NSW_CONFIG_FILE);
337e1dd0a2fSth 		return (NULL);
338e1dd0a2fSth 	}
339e1dd0a2fSth 
340e1dd0a2fSth 	(void) thr_getspecific(switchConfigKey, (void **) &hostService);
341e1dd0a2fSth 	if (hostService == NULL) {
342e1dd0a2fSth 		hostService = calloc(1, sizeof (switch_database_t));
343e1dd0a2fSth 		if (hostService == NULL) {
344e1dd0a2fSth 			return (NULL);
345e1dd0a2fSth 		}
346e1dd0a2fSth 		(void) thr_setspecific(switchConfigKey, hostService);
347e1dd0a2fSth 	}
348e1dd0a2fSth 
349e1dd0a2fSth 	/*
350e1dd0a2fSth 	 * In a long-living process threads can perform several
351e1dd0a2fSth 	 * getXbyY requests. And the windows between those requests
352e1dd0a2fSth 	 * can be long. The nsswitch configuration can change from time
353e1dd0a2fSth 	 * to time. So instead of allocating/freeing memory every time
354e1dd0a2fSth 	 * the API is called, reallocate memory only when the current
355e1dd0a2fSth 	 * configuration for the database being used is longer than
356e1dd0a2fSth 	 * the previous one.
357e1dd0a2fSth 	 */
358e1dd0a2fSth 	if (strlen(linep) >= hostService->alloced) {
359e1dd0a2fSth 		ptr = (char *)realloc((void *)hostService->conf,
360e1dd0a2fSth 		    strlen(linep) + 1);
361e1dd0a2fSth 		if (ptr == NULL) {
362e1dd0a2fSth 			free((void *)hostService->conf);
363e1dd0a2fSth 			hostService->conf = NULL;
364e1dd0a2fSth 			hostService->alloced = 0;
365e1dd0a2fSth 			return (NULL);
366e1dd0a2fSth 		}
367e1dd0a2fSth 		bzero(ptr, strlen(linep) + 1);
368e1dd0a2fSth 		hostService->conf = ptr;
369e1dd0a2fSth 		hostService->alloced = strlen(linep) + 1;
370e1dd0a2fSth 	}
371e1dd0a2fSth 
372e1dd0a2fSth 	if (remove_ldap(hostService->conf, linep, hostService->alloced))
373e1dd0a2fSth 		return (hostService->conf);
374e1dd0a2fSth 	else
375e1dd0a2fSth 		return (NULL);
376e1dd0a2fSth }
377e1dd0a2fSth 
378e1dd0a2fSth static
379e1dd0a2fSth void
_initf_ipnodes(nss_db_params_t * p)380e1dd0a2fSth _initf_ipnodes(nss_db_params_t *p)
381e1dd0a2fSth {
382