xref: /illumos-gate/usr/src/cmd/ldapcachemgr/cachemgr_getldap.c (revision 306990466301759c88d84c51c57b57ca930a3635)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  *
24  * Copyright 2018 Joyent, Inc.
25  */
26 
27 #include <assert.h>
28 #include <errno.h>
29 #include <memory.h>
30 #include <signal.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <libintl.h>
35 #include <syslog.h>
36 #include <sys/door.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <synch.h>
42 #include <pthread.h>
43 #include <unistd.h>
44 #include <lber.h>
45 #include <ldap.h>
46 #include <ctype.h>	/* tolower */
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <ucred.h>
51 #include "cachemgr.h"
52 #include "solaris-priv.h"
53 #include "ns_connmgmt.h"
54 
55 static rwlock_t	ldap_lock = DEFAULTRWLOCK;
56 static int	sighup_update = FALSE;
57 extern admin_t	current_admin;
58 
59 extern int is_root_or_all_privs(char *dc_str, ucred_t **ucp);
60 
61 /* variables used for SIGHUP wakeup on sleep */
62 static mutex_t			sighuplock;
63 static cond_t			cond;
64 
65 /* refresh time statistics */
66 static time_t	prev_refresh_time = 0;
67 
68 /* variables used for signaling parent process */
69 static mutex_t	sig_mutex;
70 static int	signal_done = FALSE;
71 
72 /* TCP connection timeout (in milliseconds) */
73 static int tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
74 
75 #ifdef SLP
76 extern int	use_slp;
77 #endif /* SLP */
78 
79 /* nis domain information */
80 #define	_NIS_FILTER		"objectclass=nisDomainObject"
81 #define	_NIS_DOMAIN		"nisdomain"
82 
83 #define	CACHESLEEPTIME		600
84 /*
85  * server list refresh delay when in "no server" mode
86  * (1 second)
87  */
88 #define	REFRESH_DELAY_WHEN_NO_SERVER	1
89 
90 typedef enum {
91 	INFO_OP_CREATE		= 0,
92 	INFO_OP_DELETE		= 1,
93 	INFO_OP_REFRESH		= 2,
94 	INFO_OP_REFRESH_WAIT	= 3,
95 	INFO_OP_GETSERVER	= 4,
96 	INFO_OP_GETSTAT		= 5,
97 	INFO_OP_REMOVESERVER	= 6
98 } info_op_t;
99 
100 typedef enum {
101 	INFO_RW_UNKNOWN		= 0,
102 	INFO_RW_READONLY	= 1,
103 	INFO_RW_WRITEABLE	= 2
104 } info_rw_t;
105 
106 typedef enum {
107 	INFO_SERVER_JUST_INITED	= -1,
108 	INFO_SERVER_UNKNOWN	= 0,
109 	INFO_SERVER_CONNECTING	= 1,
110 	INFO_SERVER_UP		= 2,
111 	INFO_SERVER_ERROR	= 3,
112 	INFO_SERVER_REMOVED	= 4
113 } info_server_t;
114 
115 typedef enum {
116 	INFO_STATUS_UNKNOWN	= 0,
117 	INFO_STATUS_ERROR	= 1,
118 	INFO_STATUS_NEW		= 2,
119 	INFO_STATUS_OLD		= 3
120 } info_status_t;
121 
122 typedef enum {
123 	CACHE_OP_CREATE		= 0,
124 	CACHE_OP_DELETE		= 1,
125 	CACHE_OP_FIND		= 2,
126 	CACHE_OP_ADD		= 3,
127 	CACHE_OP_GETSTAT	= 4
128 } cache_op_t;
129 
130 typedef enum {
131 	CACHE_MAP_UNKNOWN	= 0,
132 	CACHE_MAP_DN2DOMAIN	= 1
133 } cache_type_t;
134 
135 typedef struct server_info_ext {
136 	char			*addr;
137 	char			*hostname;
138 	char			*rootDSE_data;
139 	char			*errormsg;
140 	info_rw_t		type;
141 	info_server_t		server_status;
142 	info_server_t		prev_server_status;
143 	info_status_t		info_status;
144 	ns_server_status_t	change;
145 } server_info_ext_t;
146 
147 typedef struct server_info {
148 	struct server_info	*next;
149 	mutex_t			mutex[2];	/* 0: current copy lock */
150 						/* 1: update copy lock */
151 	server_info_ext_t	sinfo[2]; /* 0: current, 1:  update copy */
152 } server_info_t;
153 
154 typedef struct cache_hash {
155 	cache_type_t		type;
156 	char			*from;
157 	char			*to;
158 	struct cache_hash	*next;
159 } cache_hash_t;
160 
161 /*
162  * The status of a server to be removed. It can be up or down.
163  */
164 typedef struct rm_svr {
165 	char	*addr;
166 	int	up; /* 1: up, 0: down */
167 } rm_svr_t;
168 
169 static int getldap_destroy_serverInfo(server_info_t *head);
170 static void test_server_change(server_info_t *head);
171 static void remove_server(char *addr);
172 static ns_server_status_t set_server_status(char *input, server_info_t *head);
173 static void create_buf_and_notify(char *input, ns_server_status_t st);
174 
175 /*
176  * Load configuration
177  * The code was in signal handler getldap_revalidate
178  * It's moved out of the handler because it could cause deadlock
179  * return: 1 SUCCESS
180  *         0 FAIL
181  */
182 static int
load_config(void)183 load_config(void)
184 {
185 	ns_ldap_error_t *error;
186 	int		rc = 1;
187 
188 	(void) __ns_ldap_setServer(TRUE);
189 
190 	(void) rw_wrlock(&ldap_lock);
191 	if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
192 		logit("Error: Unable to read '%s': %s\n",
193 		    NSCONFIGFILE, error->message);
194 		__ns_ldap_freeError(&error);
195 		rc = 0; /* FAIL */
196 	} else
197 		sighup_update = TRUE;
198 
199 	(void) rw_unlock(&ldap_lock);
200 
201 	return (rc);
202 }
203 
204 /*
205  * Calculate a hash for a string
206  * Based on elf_hash algorithm, hash is case insensitive
207  * Uses tolower instead of _tolower because of I18N
208  */
209 
210 static unsigned long
getldap_hash(const char * str)211 getldap_hash(const char *str)
212 {
213 	unsigned int	hval = 0;
214 
215 	while (*str) {
216 		unsigned int	g;
217 
218 		hval = (hval << 4) + tolower(*str++);
219 		if ((g = (hval & 0xf0000000)) != 0)
220 			hval ^= g >> 24;
221 		hval &= ~g;
222 	}
223 	return ((unsigned long)hval);
224 }
225 
226 /*
227  * Remove a hash table entry.
228  * This function expects a lock in place when called.
229  */
230 
231 static cache_hash_t *
getldap_free_hash(cache_hash_t * p)232 getldap_free_hash(cache_hash_t *p)
233 {
234 	cache_hash_t	*next;
235 
236 	p->type = CACHE_MAP_UNKNOWN;
237 	if (p->from)
238 		free(p->from);
239 	if (p->to)
240 		free(p->to);
241 	next = p->next;
242 	p->next = NULL;
243 	free(p);
244 	return (next);
245 }
246 
247 /*
248  * Scan a hash table hit for a matching hash entry.
249  * This function expects a lock in place when called.
250  */
251 static cache_hash_t *
getldap_scan_hash(cache_type_t type,char * from,cache_hash_t * idx)252 getldap_scan_hash(cache_type_t type, char *from, cache_hash_t *idx)
253 {
254 	while (idx) {
255 		if (idx->type == type &&
256 		    strcasecmp(from, idx->from) == 0) {
257 			return (idx);
258 		}
259 		idx = idx->next;
260 	}
261 	return ((cache_hash_t *)NULL);
262 }
263 
264 /*
265  * Format and return the cache data statistics
266  */
267 static int
getldap_get_cacheData_stat(int max,int current,char ** output)268 getldap_get_cacheData_stat(int max, int current, char **output)
269 {
270 #define	C_HEADER0	"Cache data information: "
271 #define	C_HEADER1	"  Maximum cache entries:   "
272 #define	C_HEADER2	"  Number of cache entries: "
273 	int		hdr0_len = strlen(gettext(C_HEADER0));
274 	int		hdr1_len = strlen(gettext(C_HEADER1));
275 	int		hdr2_len = strlen(gettext(C_HEADER2));
276 	int		len;
277 
278 	if (current_admin.debug_level >= DBG_ALL) {
279 		logit("getldap_get_cacheData_stat()...\n");
280 	}
281 
282 	*output = NULL;
283 
284 	len = hdr0_len + hdr1_len + hdr2_len +
285 	    3 * strlen(DOORLINESEP) + 21;
286 	*output = malloc(len);
287 	if (*output == NULL)
288 		return (-1);
289 
290 	(void) snprintf(*output, len, "%s%s%s%10d%s%s%10d%s",
291 	    gettext(C_HEADER0), DOORLINESEP,
292 	    gettext(C_HEADER1), max, DOORLINESEP,
293 	    gettext(C_HEADER2), current, DOORLINESEP);
294 
295 	return (NS_LDAP_SUCCESS);
296 }
297 
298 static int
getldap_cache_op(cache_op_t op,cache_type_t type,char * from,char ** to)299 getldap_cache_op(cache_op_t op, cache_type_t type,
300     char *from, char **to)
301 {
302 #define	CACHE_HASH_MAX		257
303 #define	CACHE_HASH_MAX_ENTRY	256
304 	static cache_hash_t	*hashTbl[CACHE_HASH_MAX];
305 	cache_hash_t		*next, *idx, *newp;
306 	unsigned long		hash;
307 	static rwlock_t		cache_lock = DEFAULTRWLOCK;
308 	int			i;
309 	static int		entry_num = 0;
310 
311 	if (current_admin.debug_level >= DBG_ALL) {
312 		logit("getldap_cache_op()...\n");
313 	}
314 	switch (op) {
315 	case CACHE_OP_CREATE:
316 		if (current_admin.debug_level >= DBG_ALL) {
317 			logit("operation is CACHE_OP_CREATE...\n");
318 		}
319 		(void) rw_wrlock(&cache_lock);
320 
321 		for (i = 0; i < CACHE_HASH_MAX; i++) {
322 			hashTbl[i] = NULL;
323 		}
324 		entry_num = 0;
325 
326 		(void) rw_unlock(&cache_lock);
327 		break;
328 
329 	case CACHE_OP_DELETE:
330 		if (current_admin.debug_level >= DBG_ALL) {
331 			logit("operation is CACHE_OP_DELETE...\n");
332 		}
333 		(void) rw_wrlock(&cache_lock);
334 
335 		for (i = 0; i < CACHE_HASH_MAX; i++) {
336 			next = hashTbl[i];
337 			while (next != NULL) {
338 				next = getldap_free_hash(next);
339 			}
340 			hashTbl[i] = NULL;
341 		}
342 		entry_num = 0;
343 
344 		(void) rw_unlock(&cache_lock);
345 		break;
346 
347 	case CACHE_OP_ADD:
348 		if (current_admin.debug_level >= DBG_ALL) {
349 			logit("operation is CACHE_OP_ADD...\n");
350 		}
351 		if (from == NULL || to == NULL || *to == NULL)
352 			return (-1);
353 		hash = getldap_hash(from) % CACHE_HASH_MAX;
354 		(void) rw_wrlock(&cache_lock);
355 		idx = hashTbl[hash];
356 		/*
357 		 * replace old "to" value with new one
358 		 * if an entry with same "from"
359 		 * already exists
360 		 */
361 		if (idx) {
362 			newp = getldap_scan_hash(type, from, idx);
363 			if (newp) {
364 				free(newp->to);
365 				newp->to = strdup(*to);
366 				(void) rw_unlock(&cache_lock);
367 				return (NS_LDAP_SUCCESS);
368 			}
369 		}
370 
371 		if (entry_num > CACHE_HASH_MAX_ENTRY) {
372 			(void) rw_unlock(&cache_lock);
373 			return (-1);
374 		}
375 
376 		newp = (cache_hash_t *)malloc(sizeof (cache_hash_t));
377 		if (newp == NULL) {
378 			(void) rw_unlock(&cache_lock);
379 			return (NS_LDAP_MEMORY);
380 		}
381 		newp->type = type;
382 		newp->from = strdup(from);
383 		newp->to = strdup(*to);
384 		newp->next = idx;
385 		hashTbl[hash] = newp;
386 		entry_num++;
387 		(void) rw_unlock(&cache_lock);
388 		break;
389 
390 	case CACHE_OP_FIND:
391 		if (current_admin.debug_level >= DBG_ALL) {
392 			logit("operation is CACHE_OP_FIND...\n");
393 		}
394 		if (from == NULL || to == NULL)
395 			return (-1);
396 		*to = NULL;
397 		hash = getldap_hash(from) % CACHE_HASH_MAX;
398 		(void) rw_rdlock(&cache_lock);
399 		idx = hashTbl[hash];
400 		idx = getldap_scan_hash(type, from, idx);
401 		if (idx)
402 			*to = strdup(idx->to);
403 		(void) rw_unlock(&cache_lock);
404 		if (idx == NULL)
405 			return (-1);
406 		break;
407 
408 	case CACHE_OP_GETSTAT:
409 		if (current_admin.debug_level >= DBG_ALL) {
410 			logit("operation is CACHE_OP_GETSTAT...\n");
411 		}
412 		if (to == NULL)
413 			return (-1);
414 
415 		return (getldap_get_cacheData_stat(CACHE_HASH_MAX_ENTRY,
416 		    entry_num, to));
417 		break;
418 
419 	default:
420 		logit("getldap_cache_op(): "
421 		    "invalid operation code (%d).\n", op);
422 		return (-1);
423 		break;
424 	}
425 	return (NS_LDAP_SUCCESS);
426 }
427 /*
428  * Function: sync_current_with_update_copy
429  *
430  * This function syncs up the 2 sinfo copies in info.
431  *
432  * The 2 copies are identical most of time.
433  * The update copy(sinfo[1]) could be different when
434  * getldap_serverInfo_refresh thread is refreshing the server list
435  * and calls getldap_get_rootDSE to update info.  getldap_get_rootDSE
436  * calls sync_current_with_update_copy to sync up 2 copies before thr_exit.
437  * The calling sequence is
438  *  getldap_serverInfo_refresh->
439  *  getldap_get_serverInfo_op(INFO_OP_CREATE,...)->
440  *  getldap_set_serverInfo->
441  *  getldap_get_rootDSE
442  *
443  * The original server_info_t has one copy of server info. When libsldap
444  * makes door call GETLDAPSERVER to get the server info and getldap_get_rootDSE
445  * is updating the server info, it would hit a unprotected window in
446  * getldap_rootDSE. The door call  will not get server info and libsldap
447  * fails at making ldap connection.
448  *
449  * The new server_info_t provides GETLDAPSERVER thread with a current
450  * copy(sinfo[0]). getldap_get_rootDSE only works on the update copy(sinfo[1])
451  * and syncs up 2 copies before thr_exit. This will close the window in
452  * getldap_get_rootDSE.
453  *
454  */
455 static void
sync_current_with_update_copy(server_info_t * info)456 sync_current_with_update_copy(server_info_t *info)
457 {
458 	if (current_admin.debug_level >= DBG_ALL) {
459 		logit("sync_current_with_update_copy()...\n");
460 	}
461 
462 	(void) mutex_lock(&info->mutex[1]);
463 	(void) mutex_lock(&info->mutex[0]);
464 
465 	if (info->sinfo[1].server_status == INFO_SERVER_UP &&
466 	    info->sinfo[0].server_status != INFO_SERVER_UP)
467 		info->sinfo[1].change = NS_SERVER_UP;
468 	else if (info->sinfo[1].server_status != INFO_SERVER_UP &&
469 	    info->sinfo[0].server_status == INFO_SERVER_UP)
470 		info->sinfo[1].change = NS_SERVER_DOWN;
471 	else
472 		info->sinfo[1].change = 0;
473 
474 
475 	/* free memory in current copy first */
476 	if (info->sinfo[0].addr)
477 		free(info->sinfo[0].addr);
478 	info->sinfo[0].addr = NULL;
479 
480 	if (info->sinfo[0].hostname)
481 		free(info->sinfo[0].hostname);
482 	info->sinfo[0].hostname = NULL;
483 
484 	if (info->sinfo[0].rootDSE_data)
485 		free(info->sinfo[0].rootDSE_data);
486 	info->sinfo[0].rootDSE_data = NULL;
487 
488 	if (info->sinfo[0].errormsg)
489 		free(info->sinfo[0].errormsg);
490 	info->sinfo[0].errormsg = NULL;
491 
492 	/*
493 	 * make current and update copy identical
494 	 */
495 	info->sinfo[0] = info->sinfo[1];
496 
497 	/*
498 	 * getldap_get_server_stat() reads the update copy sinfo[1]
499 	 * so it can't be freed or nullified yet at this point.
500 	 *
501 	 * The sinfo[0] and sinfo[1] have identical string pointers.
502 	 * strdup the strings to avoid the double free problem.
503 	 * The strings of sinfo[1] are freed in
504 	 * getldap_get_rootDSE() and the strings of sinfo[0]
505 	 * are freed earlier in this function. If the pointers are the
506 	 * same, they will be freed twice.
507 	 */
508 	if (info->sinfo[1].addr)
509 		info->sinfo[0].addr = strdup(info->sinfo[1].addr);
510 	if (info->sinfo[1].hostname)
511 		info->sinfo[0].hostname = strdup(info->sinfo[1].hostname);
512 	if (info->sinfo[1].rootDSE_data)
513 		info->sinfo[0].rootDSE_data =
514 		    strdup(info->sinfo[1].rootDSE_data);
515 	if (info->sinfo[1].errormsg)
516 		info->sinfo[0].errormsg = strdup(info->sinfo[1].errormsg);
517 
518 	(void) mutex_unlock(&info->mutex[0]);
519 	(void) mutex_unlock(&info->mutex[1]);
520 
521 }
522 
523 static void *
getldap_get_rootDSE(void * arg)524 getldap_get_rootDSE(void *arg)
525 {
526 	server_info_t	*serverInfo = (server_info_t *)arg;
527 	char		*rootDSE;
528 	int		exitrc = NS_LDAP_SUCCESS;
529 	pid_t		ppid;
530 	int		server_found = 0;
531 	char		errmsg[MAXERROR];
532 	ns_ldap_return_code	rc;
533 	ns_ldap_error_t *error = NULL;
534 
535 	if (current_admin.debug_level >= DBG_ALL) {
536 		logit("getldap_get_rootDSE()....\n");
537 	}
538 
539 	/* initialize the server info element */
540 	(void) mutex_lock(&serverInfo->mutex[1]);
541 	serverInfo->sinfo[1].type	= INFO_RW_UNKNOWN;
542 	serverInfo->sinfo[1].info_status =
543 	    INFO_STATUS_UNKNOWN;
544 	/*
545 	 * When the sever list is refreshed over and over,
546 	 * this function is called each time it is refreshed.
547 	 * The previous server status of the update copy(sinfo[1])
548 	 * is the status of the current copy
549 	 */
550 	(void) mutex_lock(&serverInfo->mutex[0]);
551 	serverInfo->sinfo[1].prev_server_status =
552 	    serverInfo->sinfo[0].server_status;
553 	(void) mutex_unlock(&serverInfo->mutex[0]);
554 
555 	serverInfo->sinfo[1].server_status =
556 	    INFO_SERVER_UNKNOWN;
557 	if (serverInfo->sinfo[1].rootDSE_data)
558 		free(serverInfo->sinfo[1].rootDSE_data);
559 	serverInfo->sinfo[1].rootDSE_data	= NULL;
560 	if (serverInfo->sinfo[1].errormsg)
561 		free(serverInfo->sinfo[1].errormsg);
562 	serverInfo->sinfo[1].errormsg		= NULL;
563 	(void) mutex_unlock(&serverInfo->mutex[1]);
564 
565 	(void) mutex_lock(&serverInfo->mutex[1]);
566 	serverInfo->sinfo[1].server_status = INFO_SERVER_CONNECTING;
567 	(void) mutex_unlock(&serverInfo->mutex[1]);
568 
569 	/*
570 	 * WARNING: anon_fallback == 1 (last argument) means that when
571 	 * __ns_ldap_getRootDSE is unable to bind using the configured
572 	 * credentials, it will try to fall back to using anonymous, non-SSL
573 	 * mode of operation.
574 	 *
575 	 * This is for backward compatibility reasons - we might have machines
576 	 * in the field with broken configuration (invalid credentials) and we
577 	 * don't want them to be disturbed.
578 	 */
579 	if (rc = __ns_ldap_getRootDSE(serverInfo->sinfo[1].addr,
580 	    &rootDSE,
581 	    &error,
582 	    SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
583 		(void) mutex_lock(&serverInfo->mutex[1]);
584 		serverInfo->sinfo[1].server_status = INFO_SERVER_ERROR;
585 		serverInfo->sinfo[1].info_status = INFO_STATUS_ERROR;
586 		if (error && error->message) {
587 			serverInfo->sinfo[1].errormsg = strdup(error->message);
588 		} else {
589 			(void) snprintf(errmsg, sizeof (errmsg), "%s %s "
590 			    "(rc = %d)", gettext("Can not get the root DSE from"
591 			    " server"), serverInfo->sinfo[1].addr, rc);
592 			serverInfo->sinfo[1].errormsg = strdup(errmsg);
593 		}
594 
595 		if (error != NULL) {
596 			(void) __ns_ldap_freeError(&error);
597 		}
598 
599 		if (current_admin.debug_level >= DBG_ALL) {
600 			logit("getldap_get_rootDSE: %s.\n",
601 			    serverInfo->sinfo[1].errormsg);
602 		}
603 		(void) mutex_unlock(&serverInfo->mutex[1]);
604 		/*
605 		 * sync sinfo copies in the serverInfo.
606 		 * protected by mutex
607 		 */
608 		sync_current_with_update_copy(serverInfo);
609 		thr_exit((void *) -1);
610 	}
611 
612 	(void) mutex_lock(&serverInfo->mutex[1]);
613 
614 	/* assume writeable, i.e., can do modify */
615 	serverInfo->sinfo[1].type		= INFO_RW_WRITEABLE;
616 	serverInfo->sinfo[1].server_status	= INFO_SERVER_UP;
617 	serverInfo->sinfo[1].info_status	= INFO_STATUS_NEW;
618 	/* remove the last DOORLINESEP */
619 	*(rootDSE+strlen(rootDSE)-1) = '\0';
620 	serverInfo->sinfo[1].rootDSE_data = rootDSE;
621 
622 	server_found = 1;
623 
624 	(void) mutex_unlock(&serverInfo->mutex[1]);
625 
626 	/*
627 	 * sync sinfo copies in the serverInfo.
628 	 * protected by mutex
629 	 */
630 	sync_current_with_update_copy(serverInfo);
631 	/*
632 	 * signal that the ldap_cachemgr parent process
633 	 * should exit now, if it is still waiting
634 	 */
635 	(void) mutex_lock(&sig_mutex);
636 	if (signal_done == FALSE && server_found) {
637 		ppid = getppid();
638 		(void) kill(ppid, SIGUSR1);
639 		if (current_admin.debug_level >= DBG_ALL) {
640 			logit("getldap_get_rootDSE(): "
641 			    "SIGUSR1 signal sent to "
642 			    "parent process(%ld).\n", ppid);
643 		}
644 		signal_done = TRUE;
645 	}
646 	(void) mutex_unlock(&sig_mutex);
647 
648 	thr_exit((void *) exitrc);
649 
650 	return ((void *) NULL);
651 }
652 
653 static int
getldap_init_serverInfo(server_info_t ** head)654 getldap_init_serverInfo(server_info_t **head)
655 {
656 	char		**servers = NULL;
657 	int		rc = 0, i, exitrc = NS_LDAP_SUCCESS;
658 	ns_ldap_error_t *errorp = NULL;
659 	server_info_t	*info, *tail = NULL;
660 
661 	*head = NULL;
662 	if (current_admin.debug_level >= DBG_ALL) {
663 		logit("getldap_init_serverInfo()...\n");
664 	}
665 	rc = __s_api_getServers(&servers, &errorp);
666 
667 	if (rc != NS_LDAP_SUCCESS) {
668 		logit("getldap_init_serverInfo: "
669 		    "__s_api_getServers failed.\n");
670 		if (errorp)
671 			__ns_ldap_freeError(&errorp);
672 		return (-1);
673 	}
674 	for (i = 0; servers[i] != NULL; i++) {
675 		info = (server_info_t *)calloc(1, sizeof (server_info_t));
676 		if (info == NULL) {
677 			logit("getldap_init_serverInfo: "
678 			    "not enough memory.\n");
679 			exitrc = NS_LDAP_MEMORY;
680 			break;
681 		}
682 		if (i == 0) {
683 			*head = info;
684 			tail  = info;
685 		} else {
686 			tail->next = info;
687 			tail  = info;
688 		}
689 
690 		info->sinfo[0].addr		= strdup(servers[i]);
691 		if (info->sinfo[0].addr == NULL) {
692 			logit("getldap_init_serverInfo: "
693 			    "not enough memory.\n");
694 			exitrc = NS_LDAP_MEMORY;
695 			break;
696 		}
697 		info->sinfo[1].addr		= strdup(servers[i]);
698 		if (info->sinfo[1].addr == NULL) {
699 			logit("getldap_init_serverInfo: "
700 			    "not enough memory.\n");
701 			exitrc = NS_LDAP_MEMORY;
702 			break;
703 		}
704 
705 		info->sinfo[0].type		= INFO_RW_UNKNOWN;
706 		info->sinfo[1].type		= INFO_RW_UNKNOWN;
707 		info->sinfo[0].info_status	= INFO_STATUS_UNKNOWN;
708 		info->sinfo[1].info_status	= INFO_STATUS_UNKNOWN;
709 		info->sinfo[0].server_status	= INFO_SERVER_UNKNOWN;
710 		info->sinfo[1].server_status	= INFO_SERVER_UNKNOWN;
711 
712 		/*
713 		 * Assume at startup or after the configuration
714 		 * profile is refreshed, all servers are good.
715 		 */
716 		info->sinfo[0].prev_server_status =
717 		    INFO_SERVER_UP;
718 		info->sinfo[1].prev_server_status =
719 		    INFO_SERVER_UP;
720 		info->sinfo[0].hostname		= NULL;
721 		info->sinfo[1].hostname		= NULL;
722 		info->sinfo[0].rootDSE_data	= NULL;
723 		info->sinfo[1].rootDSE_data	= NULL;
724 		info->sinfo[0].errormsg		= NULL;
725 		info->sinfo[1].errormsg		= NULL;
726 		info->next			= NULL;
727 	}
728 	__s_api_free2dArray(servers);
729 	if (exitrc != NS_LDAP_SUCCESS) {
730 		if (head && *head) {
731 			(void) getldap_destroy_serverInfo(*head);
732 			*head = NULL;
733 		}
734 	}
735 	return (exitrc);
736 }
737 
738 static int
getldap_destroy_serverInfo(server_info_t * head)739 getldap_destroy_serverInfo(server_info_t *head)
740 {
741 	server_info_t	*info, *next;
742 
743 	if (current_admin.debug_level >= DBG_ALL) {
744 		logit("getldap_destroy_serverInfo()...\n");
745 	}
746 
747 	if (head == NULL) {
748 		logit("getldap_destroy_serverInfo: "
749 		    "invalid serverInfo list.\n");
750 		return (-1);
751 	}
752 
753 	for (info = head; info; info = next) {
754 		if (info->sinfo[0].addr)
755 			free(info->sinfo[0].addr);
756 		if (info->sinfo[1].addr)
757 			free(info->sinfo[1].addr);
758 		if (info->sinfo[0].hostname)
759 			free(info->sinfo[0].hostname);
760 		if (info->sinfo[1].hostname)
761 			free(info->sinfo[1].hostname);
762 		if (info->sinfo[0].rootDSE_data)
763 			free(info->sinfo[0].rootDSE_data);
764 		if (info->sinfo[1].rootDSE_data)
765 			free(info->sinfo[1].rootDSE_data);
766 		if (info->sinfo[0].errormsg)
767 			free(info->sinfo[0].errormsg);
768 		if (info->sinfo[1].errormsg)
769 			free(info->sinfo[1].errormsg);
770 		next = info->next;
771 		free(info);
772 	}
773 	return (NS_LDAP_SUCCESS);
774 }
775 
776 static int
getldap_set_serverInfo(server_info_t * head,int reset_bindtime,info_op_t op)777 getldap_set_serverInfo(server_info_t *head, int reset_bindtime, info_op_t op)
778 {
779 	server_info_t	*info;
780 	int		atleast1 = 0;
781 	thread_t	*tid;
782 	int		num_threads = 0, i, j;
783 	void		*status;
784 	void		**paramVal = NULL;
785 	ns_ldap_error_t	*error = NULL;
786 
787 	if (current_admin.debug_level >= DBG_ALL) {
788 		logit("getldap_set_serverInfo()...\n");
789 	}
790 
791 	if (head == NULL) {
792 		logit("getldap_set_serverInfo: "
793 		    "invalid serverInfo list.\n");
794 		return (-1);
795 	}
796 
797 	/* Get the bind timeout value */
798 	if (reset_bindtime == 1) {
799 		tcptimeout = NS_DEFAULT_BIND_TIMEOUT * 1000;
800 		(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P,
801 		    &paramVal, &error);
802 		if (paramVal != NULL && *paramVal != NULL) {
803 			/* convert to milliseconds */
804 			tcptimeout = **((int **)paramVal);
805 			tcptimeout *= 1000;
806 			(void) __ns_ldap_freeParam(&paramVal);
807 		}
808 		if (error)
809 			(void) __ns_ldap_freeError(&error);
810 	}
811 
812 	for (info = head; info; info = info->next)
813 		num_threads++;
814 
815 	if (num_threads == 0) {
816 		logit("getldap_set_serverInfo: "
817 		    "empty serverInfo list.\n");
818 		return (-1);
819 	}
820 
821 	tid = (thread_t *) calloc(1, sizeof (thread_t) * num_threads);
822 	if (tid == NULL) {
823 		logit("getldap_set_serverInfo: "
824 		    "No memory to create thread ID list.\n");
825 		return (-1);
826 	}
827 
828 	for (info = head, i = 0; info; info = info->next, i++) {
829 		if (thr_create(NULL, 0,
830 		    (void *(*)(void*))getldap_get_rootDSE,
831 		    (void *)info, 0, &tid[i])) {
832 			logit("getldap_set_serverInfo: "
833 			    "can not create thread %d.\n", i + 1);
834 			for (j = 0; j < i; j++)
835 				(void) thr_join(tid[j], NULL, NULL);
836 			free(tid);
837 			return (-1);
838 		}
839 	}
840 
841 	for (i = 0; i < num_threads; i++) {
842 		if (thr_join(tid[i], NULL, &status) == 0) {
843 			if ((int)status == NS_LDAP_SUCCESS)
844 				atleast1 = 1;
845 		}
846 	}
847 
848 	free(tid);
849 
850 	if (op == INFO_OP_REFRESH)
851 		test_server_change(head);
852 	if (atleast1) {
853 		return (NS_LDAP_SUCCESS);
854 	} else
855 		return (-1);
856 }
857 
858 /*
859  * getldap_get_serverInfo processes the GETLDAPSERVER door request passed
860  * to this function from getldap_serverInfo_op().
861  * input:
862  *   a buffer containing an empty string (e.g., input[0]='\0';) or a string
863  *   as the "input" in printf(input, "%s%s%s%s", req, addrtype, DOORLINESEP,
864  *   addr);
865  *   where addr is the address of a server and
866  *   req is one of the following:
867  *   NS_CACHE_NEW:    send a new server address, addr is ignored.
868  *   NS_CACHE_NORESP: send the next one, remove addr from list.
869  *   NS_CACHE_NEXT:   send the next one, keep addr on list.
870  *   NS_CACHE_WRITE:  send a non-replica server, if possible, if not, same
871  *                    as NS_CACHE_NEXT.
872  *   addrtype:
873  *   NS_CACHE_ADDR_IP: return server address as is, this is default.
874  *   NS_CACHE_ADDR_HOSTNAME: return both server address and its FQDN format,
875  *			only self credential case requires such format.
876  * output:
877  *   a buffer containing server info in the following format:
878  *   serveraddress DOORLINESEP [ serveraddress FQDN DOORLINESEP ]
879  *   [ attr=value [DOORLINESEP attr=value ]...]
880  *   For example: ( here | used as DOORLINESEP for visual purposes)
881  *   1) simple bind and sasl/DIGEST-MD5 bind :
882  *   1.2.3.4|supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL|
883  *   supportedSASLmechanisms=GSSAPI
884  *   2) sasl/GSSAPI bind (self credential):
885  *   1.2.3.4|foo.sun.com|supportedControl=1.1.1.1|
886  *   supportedSASLmechanisms=EXTERNAL|supportedSASLmechanisms=GSSAPI
887  *   NOTE: caller should free this buffer when done using it
888  */
889 static int
getldap_get_serverInfo(server_info_t * head,char * input,char ** output,int * svr_removed)890 getldap_get_serverInfo(server_info_t *head, char *input,
891     char **output, int *svr_removed)
892 {
893 	server_info_t	*info	= NULL;
894 	server_info_t	*server	= NULL;
895 	char		*addr	= NULL;
896 	char		*req	= NULL;
897 	char		req_new[] = NS_CACHE_NEW;
898 	char		addr_type[] = NS_CACHE_ADDR_IP;
899 	int		matched = FALSE, len = 0, rc = 0;
900 	char		*ret_addr = NULL, *ret_addrFQDN = NULL;
901 	char		*new_addr = NULL;
902 	pid_t		pid;
903 
904 	if (current_admin.debug_level >= DBG_ALL) {
905 		logit("getldap_get_serverInfo()...\n");
906 	}
907 
908 	if (input == NULL || output == NULL) {
909 		logit("getldap_get_serverInfo: "
910 		    "No input or output buffer.\n");
911 		return (-1);
912 	}
913 
914 	*output = NULL;
915 	*svr_removed = FALSE;
916 
917 	if (head == NULL) {
918 		logit("getldap_get_serverInfo: "
919 		    "invalid serverInfo list.\n");
920 		return (-1);
921 	}
922 	/*
923 	 * parse the input string to get req and addr,
924 	 * if input is empty, i.e., input[0] == '\0',
925 	 * treat it as an NS_CACHE_NEW request
926 	 */
927 	req = req_new;
928 	if (input[0] != '\0') {
929 		req = input;
930 		/* Save addr type flag */
931 		addr_type[0] = input[1];
932 		input[strlen(NS_CACHE_NEW)] = '\0';
933 		/* skip acion type flag, addr type flag and DOORLINESEP */
934 		addr = input + strlen(DOORLINESEP) + strlen(NS_CACHE_NEW)
935 		    + strlen(NS_CACHE_ADDR_IP);
936 	}
937 	/*
938 	 * if NS_CACHE_NEW,
939 	 * or the server info is new,
940 	 * starts from the
941 	 * beginning of the list
942 	 */
943 	if ((strcmp(req, NS_CACHE_NEW) == 0) ||
944 	    (head->sinfo[0].info_status == INFO_STATUS_NEW))
945 		matched = TRUE;
946 	for (info = head; info; info = info->next) {
947 		/*
948 		 * make sure the server info stays the same
949 		 * while the data is being processed
950 		 */
951 
952 		/*
953 		 * This function is called to get server info list
954 		 * and pass it back to door call clients.
955 		 * Access the current copy (sinfo[0]) to get such
956 		 * information
957 		 */
958 		(void) mutex_lock(&info->mutex[0]);
959 
960 		if (matched == FALSE &&
961 		    strcmp(info->sinfo[0].addr, addr) == 0) {
962 			matched = TRUE;
963 			if (strcmp(req, NS_CACHE_NORESP) == 0) {
964 				if (chg_is_called_from_nscd_or_peruser_nscd(
965 				    "REMOVE SERVER", &pid) == 0) {
966 					(void) mutex_unlock(&info->mutex[0]);
967 					if (current_admin.debug_level >=
968 					    DBG_ALL)
969 						logit("Only nscd can remove "
970 						    "servers. pid %ld", pid);
971 					continue;
972 				}
973 
974 				/*
975 				 * if the information is new,
976 				 * give this server one more chance
977 				 */
978 				if (info->sinfo[0].info_status ==
979 				    INFO_STATUS_NEW &&
980 				    info->sinfo[0].server_status  ==
981 				    INFO_SERVER_UP) {
982 					server = info;
983 					break;
984 				} else {
985 					/*
986 					 * it is recommended that
987 					 * before removing the
988 					 * server from the list,
989 					 * the server should be
990 					 * contacted one more time
991 					 * to make sure that it is
992 					 * really unavailable.
993 					 * For now, just trust the client
994 					 * (i.e., the sldap library)
995 					 * that it knows what it is
996 					 * doing and would not try
997 					 * to mess up the server
998 					 * list.
999 					 */
1000 					/*
1001 					 * Make a copy of addr to contact
1002 					 * it later. It's not doing it here
1003 					 * to avoid long wait and possible
1004 					 * recursion to contact an LDAP server.
1005 					 */
1006 					new_addr = strdup(info->sinfo[0].addr);
1007 					if (new_addr)
1008 						remove_server(new_addr);
1009 					*svr_removed = TRUE;
1010 					(void) mutex_unlock(&info->mutex[0]);
1011 					break;
1012 				}
1013 			} else {
1014 				/*
1015 				 * req == NS_CACHE_NEXT or NS_CACHE_WRITE
1016 				 */
1017 				(void) mutex_unlock(&info->mutex[0]);
1018 				continue;
1019 			}
1020 		}
1021 
1022 		if (matched) {
1023 			if (strcmp(req, NS_CACHE_WRITE) == 0) {
1024 				if (info->sinfo[0].type ==
1025 				    INFO_RW_WRITEABLE &&
1026 				    info->sinfo[0].server_status  ==
1027 				    INFO_SERVER_UP) {
1028 					server = info;
1029 					break;
1030 				}
1031 			} else if (info->sinfo[0].server_status ==
1032 			    INFO_SERVER_UP) {
1033 				server = info;
1034 				break;
1035 			}
1036 		}
1037 
1038 		(void) mutex_unlock(&info->mutex[0]);
1039 	}
1040 
1041 	if (server) {
1042 		if (strcmp(addr_type, NS_CACHE_ADDR_HOSTNAME) == 0) {
1043 			/*
1044 			 * In SASL/GSSAPI case, a hostname is required for
1045 			 * Kerberos's service principal.
1046 			 * e.g.
1047 			 * ldap/foo.sun.com@SUN.COM
1048 			 */
1049 			if (server->sinfo[0].hostname == NULL) {
1050 				rc = __s_api_ip2hostname(server->sinfo[0].addr,
1051 				    &server->sinfo[0].hostname);
1052 				if (rc != NS_LDAP_SUCCESS) {
1053 					(void) mutex_unlock(&info->mutex[0]);
1054 					return (rc);
1055 				}
1056 				if (current_admin.debug_level >= DBG_ALL) {
1057 					logit("getldap_get_serverInfo: "
1058 					    "%s is converted to %s\n",
1059 					    server->sinfo[0].addr,
1060 					    server->sinfo[0].hostname);
1061 				}
1062 			}
1063 			ret_addr = server->sinfo[0].addr;
1064 			ret_addrFQDN = server->sinfo[0].hostname;
1065 
1066 		} else
1067 			ret_addr = server->sinfo[0].addr;
1068 
1069 
1070 		len = strlen(ret_addr) +
1071 		    strlen(server->sinfo[0].rootDSE_data) +
1072 		    strlen(DOORLINESEP) + 1;
1073 		if (ret_addrFQDN != NULL)
1074 			len += strlen(ret_addrFQDN) + strlen(DOORLINESEP);
1075 		*output = (char *)malloc(len);
1076 		if (*output == NULL) {
1077 			(void) mutex_unlock(&info->mutex[0]);
1078 			return (NS_LDAP_MEMORY);
1079 		}
1080 		if (ret_addrFQDN == NULL)
1081 			(void) snprintf(*output, len, "%s%s%s",
1082 			    ret_addr, DOORLINESEP,
1083 			    server->sinfo[0].rootDSE_data);
1084 		else
1085 			(void) snprintf(*output, len, "%s%s%s%s%s",
1086 			    ret_addr, DOORLINESEP,
1087 			    ret_addrFQDN, DOORLINESEP,
1088 			    server->sinfo[0].rootDSE_data);
1089 		server->sinfo[0].info_status = INFO_STATUS_OLD;
1090 		(void) mutex_unlock(&info->mutex[0]);
1091 		return (NS_LDAP_SUCCESS);
1092 	}
1093 	else
1094 		return (-99);
1095 }
1096 
1097 /*
1098  * Format previous and next refresh time
1099  */
1100 static int
getldap_format_refresh_time(char ** output,time_t * prev,time_t * next)1101 getldap_format_refresh_time(char **output, time_t *prev, time_t *next)
1102 {
1103 #define	TIME_FORMAT	"%Y/%m/%d %H:%M:%S"
1104 #define	TIME_HEADER1	"  Previous refresh time: "
1105 #define	TIME_HEADER2	"  Next refresh time:     "
1106 	int		hdr1_len = strlen(gettext(TIME_HEADER1));
1107 	int		hdr2_len = strlen(gettext(TIME_HEADER2));
1108 	struct tm	tm;
1109 	char		nbuf[256];
1110 	char		pbuf[256];
1111 	int		len;
1112 
1113 	if (current_admin.debug_level >= DBG_ALL) {
1114 		logit("getldap_format_refresh_time()...\n");
1115 	}
1116 
1117 	*output = NULL;
1118 
1119 	/* format the time of previous refresh  */
1120 	if (*prev != 0) {
1121 		(void) localtime_r(prev, &tm);
1122 		(void) strftime(pbuf, sizeof (pbuf) - 1, TIME_FORMAT, &tm);
1123 	} else {
1124 		(void) strcpy(pbuf, gettext("NOT DONE"));
1125 	}
1126 
1127 	/* format the time of next refresh  */
1128 	if (*next != 0) {
1129 		(void) localtime_r(next, &tm);
1130 		(void) strftime(nbuf, sizeof (nbuf) - 1, TIME_FORMAT, &tm);
1131 	} else {
1132 		(void) strcpy(nbuf, gettext("NOT SET"));
1133 	}
1134 
1135 	len = hdr1_len + hdr2_len + strlen(nbuf) +
1136 	    strlen(pbuf) + 2 * strlen(DOORLINESEP) + 1;
1137 
1138 	*output = malloc(len);
1139 	if (*output == NULL)
1140 		return (-1);
1141 
1142 	(void) snprintf(*output, len, "%s%s%s%s%s%s",
1143 	    gettext(TIME_HEADER1), pbuf, DOORLINESEP,
1144 	    gettext(TIME_HEADER2), nbuf, DOORLINESEP);
1145 
1146 	return (NS_LDAP_SUCCESS);
1147 }
1148 
1149 /*
1150  * getldap_get_server_stat processes the GETSTAT request passed
1151  * to this function from getldap_serverInfo_op().
1152  * output:
1153  *   a buffer containing info for all the servers.
1154  *   For each server, the data is in the following format:
1155  *   server: server address or name, status: unknown|up|down|removed DOORLINESEP
1156  *   for example: ( here | used as DOORLINESEP for visual purposes)
1157  *   server: 1.2.3.4, status: down|server: 2.2.2.2, status: up|
1158  *   NOTE: caller should free this buffer when done using it
1159  */
1160 static int
getldap_get_server_stat(server_info_t * head,char ** output,time_t * prev,time_t * next)1161 getldap_get_server_stat(server_info_t *head, char **output,
1162     time_t *prev, time_t *next)
1163 {
1164 #define	S_HEADER	"Server information: "
1165 #define	S_FORMAT	"  server: %s, status: %s%s"
1166 #define	S_ERROR		"    error message: %s%s"
1167 	server_info_t	*info = NULL;
1168 	int	header_len = strlen(gettext(S_HEADER));
1169 	int	format_len = strlen(gettext(S_FORMAT));
1170 	int	error_len = strlen(gettext(S_ERROR));
1171 	int	len = header_len + strlen(DOORLINESEP);
1172 	int	len1 = 0;
1173 	char	*status, *output1 = NULL, *tmpptr;
1174 
1175 	*output = NULL;
1176 
1177 	if (current_admin.debug_level >= DBG_ALL) {
1178 		logit("getldap_get_server_stat()...\n");
1179 	}
1180 
1181 	if (head == NULL) {
1182 		logit("getldap_get_server_stat: "
1183 		    "invalid serverInfo list.\n");
1184 		return (-1);
1185 	}
1186 
1187 	/* format previous and next refresh time */
1188 	(void) getldap_format_refresh_time(&output1, prev, next);
1189 	if (output1 == NULL)
1190 		return (-1);
1191 	len += strlen(output1);
1192 	len1 = len + strlen(DOORLINESEP) + 1;
1193 
1194 	*output = (char *)calloc(1, len1);
1195 	if (*output == NULL) {
1196 		free(output1);
1197 		return (-1);
1198 	}
1199 
1200 	/* insert header string and refresh time info */
1201 	(void) snprintf(*output, len1, "%s%s%s",
1202 	    gettext(S_HEADER), DOORLINESEP, output1);
1203 
1204 	for (info = head; info; info = info->next) {
1205 
1206 		/*
1207 		 * make sure the server info stays the same
1208 		 * while the data is being processed
1209 		 */
1210 		(void) mutex_lock(&info->mutex[1]);
1211 
1212 		/*
1213 		 * When the updating process is under way(getldap_get_rootDSE)
1214 		 * the update copy(sinfo[1] is the latest copy.
1215 		 * When the updating process
1216 		 * is done, the current copy (sinfo[0]) has the latest status,
1217 		 * which is still identical to the update copy.
1218 		 * So update copy has the latest status.
1219 		 * Use the update copy(sinfo[1]) to show status
1220 		 * (ldap_cachemgr -g).
1221 		 *
1222 		 */
1223 
1224 		switch (info->sinfo[1].server_status) {
1225 		case INFO_SERVER_UNKNOWN:
1226 			status = gettext("UNKNOWN");
1227 			break;
1228 		case INFO_SERVER_CONNECTING:
1229 			status = gettext("CONNECTING");
1230 			break;
1231 		case INFO_SERVER_UP:
1232 			status = gettext("UP");
1233 			break;
1234 		case INFO_SERVER_ERROR:
1235 			status = gettext("ERROR");
1236 			break;
1237 		case INFO_SERVER_REMOVED:
1238 			status = gettext("REMOVED");
1239 			break;
1240 		}
1241 
1242 		len += format_len + strlen(status) +
1243 		    strlen(info->sinfo[1].addr) +
1244 		    strlen(DOORLINESEP);
1245 		if (info->sinfo[1].errormsg != NULL)
1246 			len += error_len +
1247 			    strlen(info->sinfo[1].errormsg) +
1248 			    strlen(DOORLINESEP);
1249 
1250 		tmpptr = (char *)realloc(*output, len);
1251 		if (tmpptr == NULL) {
1252 			free(output1);
1253 			free(*output);
1254 			*output = NULL;
1255 			(void) mutex_unlock(&info->mutex[1]);
1256 			return (-1);
1257 		} else
1258 			*output = tmpptr;
1259 
1260 		/* insert server IP addr or name and status */
1261 		len1 = len - strlen(*output);
1262 		(void) snprintf(*output + strlen(*output), len1,
1263 		    gettext(S_FORMAT), info->sinfo[1].addr,
1264 		    status, DOORLINESEP);
1265 		/* insert error message if any */
1266 		len1 = len - strlen(*output);
1267 		if (info->sinfo[1].errormsg != NULL)
1268 			(void) snprintf(*output + strlen(*output), len1,
1269 			    gettext(S_ERROR),
1270 			    info->sinfo[1].errormsg,
1271 			    DOORLINESEP);
1272 
1273 		(void) mutex_unlock(&info->mutex[1]);
1274 
1275 	}
1276 
1277 	free(output1);
1278 	return (NS_LDAP_SUCCESS);
1279 }
1280 
1281 /*
1282  * Format and return the refresh time statistics
1283  */
1284 static int
getldap_get_refresh_stat(char ** output)1285 getldap_get_refresh_stat(char **output)
1286 {
1287 #define	R_HEADER0	"Configuration refresh information: "
1288 #define	R_HEADER1	"  Configured to NO REFRESH."
1289 	int		hdr0_len = strlen(gettext(R_HEADER0));
1290 	int		hdr1_len = strlen(gettext(R_HEADER1));
1291 	int		cache_ttl = -1, len = 0;
1292 	time_t		expire = 0;
1293 	void		**paramVal = NULL;
1294 	ns_ldap_error_t	*errorp = NULL;
1295 	char		*output1 = NULL;
1296 
1297 	if (current_admin.debug_level >= DBG_ALL) {
1298 		logit("getldap_get_refresh_stat()...\n");
1299 	}
1300 
1301 	*output = NULL;
1302 
1303 	/* get configured cache TTL */
1304 	if ((__ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1305 	    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1306 	    paramVal != NULL &&
1307 	    (char *)*paramVal != NULL) {
1308 			cache_ttl = atol((char *)*paramVal);
1309 	} else {
1310 		if (errorp)
1311 			__ns_ldap_freeError(&errorp);
1312 	}
1313 	(void) __ns_ldap_freeParam(&paramVal);
1314 
1315 	/* cound not get cache TTL */
1316 	if (cache_ttl == -1)
1317 		return (-1);
1318 
1319 	if (cache_ttl == 0) {
1320 		len = hdr0_len + hdr1_len +
1321 		    2 * strlen(DOORLINESEP) + 1;
1322 		*output = malloc(len);
1323 		if (*output == NULL)
1324 			return (-1);
1325 		(void) snprintf(*output, len, "%s%s%s%s",
1326 		    gettext(R_HEADER0), DOORLINESEP,
1327 		    gettext(R_HEADER1), DOORLINESEP);
1328 	} else {
1329 
1330 		/* get configuration expiration time */
1331 		if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
1332 		    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
1333 		    paramVal != NULL &&
1334 		    (char *)*paramVal != NULL) {
1335 				expire = (time_t)atol((char *)*paramVal);
1336 		} else {
1337 			if (errorp)
1338 				__ns_ldap_freeError(&errorp);
1339 		}
1340 
1341 		(void) __ns_ldap_freeParam(&paramVal);
1342 
1343 		/* cound not get expiration time */
1344 		if (expire == -1)
1345 			return (-1);
1346 
1347 		/* format previous and next refresh time */
1348 		(void) getldap_format_refresh_time(&output1,
1349 		    &prev_refresh_time, &expire);
1350 		if (output1 == NULL)
1351 			return (-1);
1352 
1353 		len = hdr0_len + strlen(output1) +
1354 		    2 * strlen(DOORLINESEP) + 1;
1355 		*output = malloc(len);
1356 		if (*output == NULL) {
1357 			free(output1);
1358 			return (-1);
1359 		}
1360 		(void) snprintf(*output, len, "%s%s%s%s",
1361 		    gettext(R_HEADER0), DOORLINESEP,
1362 		    output1, DOORLINESEP);
1363 		free(output1);
1364 	}
1365 
1366 	return (NS_LDAP_SUCCESS);
1367 }
1368 
1369 static int
getldap_get_cacheTTL()1370 getldap_get_cacheTTL()
1371 {
1372 	void		**paramVal = NULL;
1373 	ns_ldap_error_t	*error;
1374 	int		rc = 0, cachettl;
1375 
1376 
1377 	if (current_admin.debug_level >= DBG_ALL) {
1378 		logit("getldap_get_cacheTTL()....\n");
1379 	}
1380 
1381 	if ((rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P,
1382 	    &paramVal, &error)) != NS_LDAP_SUCCESS) {
1383 		if (error != NULL && error->message != NULL)
1384 			logit("Error: Unable to get configuration "
1385 			    "refresh TTL: %s\n",
1386 			    error->message);
1387 		else {
1388 			char *tmp;
1389 
1390 			__ns_ldap_err2str(rc, &tmp);
1391 			logit("Error: Unable to get configuration "
1392 			    "refresh TTL: %s\n", tmp);
1393 		}
1394 		(void) __ns_ldap_freeParam(&paramVal);
1395 		(void) __ns_ldap_freeError(&error);
1396 		return (-1);
1397 	}
1398 	if (paramVal == NULL || (char *)*paramVal == NULL)
1399 			return (-1);
1400 	cachettl = atol((char *)*paramVal);
1401 	(void) __ns_ldap_freeParam(&paramVal);
1402 	return (cachettl);
1403 }
1404 
1405 
1406 /*
1407  * This function implements the adaptive server list refresh
1408  * algorithm used by ldap_cachemgr. The idea is to have the
1409  * refresh TTL adjust itself between maximum and minimum
1410  * values. If the server list has been walked three times
1411  * in a row without errors, the TTL will be doubled. This will
1412  * be done repeatedly until the maximum value is reached
1413  * or passed. If passed, the maximum value will be used.
1414  * If any time a server is found to be down/bad, either
1415  * after another server list walk or informed by libsldap via
1416  * the GETLDAPSERVER door calls, the TTL will be set to half
1417  * of its value, again repeatedly, but no less than the minimum
1418  * value. Also, at any time, if all the servers on the list
1419  * are found to be down/bad, the TTL will be set to minimum,
1420  * so that a "no-server" refresh loop should be entered to try
1421  * to find a good server as soon as possible. The caller
1422  * could check the no_gd_server flag for this situation.
1423  * The maximum and minimum values are initialized when the input
1424  * refresh_ttl is set to zero, this should occur during
1425  * ldap_cachemgr startup or every time the server list is
1426  * recreated after the configuration profile is refreshed
1427  * from an LDAP server. The maximum is set to the value of
1428  * the NS_LDAP_CACHETTL parameter (configuration profile
1429  * refresh TTL), but if it is zero (never refreshed) or can
1430  * not be retrieved, the maximum is set to the macro
1431  * REFRESHTTL_MAX (12 hours) defined below. The minimum is
1432  * set to REFRESHTTL_MIN, which is the TCP connection timeout
1433  * (tcptimeout) set via the LDAP API ldap_set_option()
1434  * with the new LDAP_X_OPT_CONNECT_TIMEOUT option plus 10 seconds.
1435  * This accounts for the maximum possible timeout value for an
1436  * LDAP TCP connect call.The first refresh TTL, initial value of
1437  * refresh_ttl, will be set to the smaller of the two,
1438  * REFRESHTTL_REGULAR (10 minutes) or (REFRESHTTL_MAX + REFRESHTTL_MIN)/2.
1439  * The idea is to have a low starting value and have the value
1440  * stay low if the network/server is unstable, but eventually
1441  * the value will move up to maximum and stay there if the
1442  * network/server is stable.
1443  */
1444 static int
getldap_set_refresh_ttl(server_info_t * head,int * refresh_ttl,int * no_gd_server)1445 getldap_set_refresh_ttl(server_info_t *head, int *refresh_ttl,
1446     int *no_gd_server)
1447 {
1448 #define	REFRESHTTL_REGULAR	600
1449 #define	REFRESHTTL_MAX		43200
1450 /* tcptimeout is in milliseconds */
1451 #define	REFRESHTTL_MIN		(tcptimeout/1000) + 10
1452 #define	UP_REFRESH_TTL_NUM	2
1453 
1454 	static mutex_t		refresh_mutex;
1455 	static int		refresh_ttl_max = 0;
1456 	static int		refresh_ttl_min = 0;
1457 	static int		num_walked_ok = 0;
1458 	int			num_servers = 0;
1459 	int			num_good_servers = 0;
1460 	int			num_prev_good_servers = 0;
1461 	server_info_t		*info;
1462 
1463 	/* allow one thread at a time */
1464 	(void) mutex_lock(&refresh_mutex);
1465 
1466 	if (current_admin.debug_level >= DBG_ALL) {
1467 		logit("getldap_set_refresh_ttl()...\n");
1468 	}
1469 
1470 	if (!head || !refresh_ttl || !no_gd_server) {
1471 		logit("getldap_set_refresh_ttl: head is "
1472 		    "NULL or refresh_ttl is NULL or "
1473 		    "no_gd_server is NULL");
1474 		(void) mutex_unlock(&refresh_mutex);
1475 		return (-1);
1476 	}
1477 	*no_gd_server = FALSE;
1478 
1479 	/*
1480 	 * init max. min. TTLs if first time through or a fresh one
1481 	 */
1482 	if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1483 		logit("getldap_set_refresh_ttl:(1) refresh ttl is %d "
1484 		    "seconds\n", *refresh_ttl);
1485 	}
1486 	if (*refresh_ttl == 0) {
1487 		num_walked_ok = 0;
1488 		/*
1489 		 * init cache manager server list TTL:
1490 		 *
1491 		 * init the min. TTL to
1492 		 * REFRESHTTL_MIN ( 2*(TCP MSL) + 10 seconds)
1493 		 */
1494 		refresh_ttl_min = REFRESHTTL_MIN;
1495 
1496 		/*
1497 		 * try to set the max. TTL to
1498 		 * configuration refresh TTL (NS_LDAP_CACHETTL),
1499 		 * if error (-1), or never refreshed (0),
1500 		 * set it to REFRESHTTL_MAX (12 hours)
1501 		 */
1502 		refresh_ttl_max = getldap_get_cacheTTL();
1503 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1504 			logit("getldap_set_refresh_ttl:(2) refresh ttl is %d "
1505 			    "seconds\n", *refresh_ttl);
1506 			logit("getldap_set_refresh_ttl:(2) max ttl is %d, "
1507 			    "min ttl is %d seconds\n",
1508 			    refresh_ttl_max, refresh_ttl_min);
1509 		}
1510 		if (refresh_ttl_max <= 0)
1511 			refresh_ttl_max = REFRESHTTL_MAX;
1512 		else if (refresh_ttl_max < refresh_ttl_min)
1513 			refresh_ttl_max = refresh_ttl_min;
1514 
1515 		/*
1516 		 * init the first TTL to the smaller of the two:
1517 		 * REFRESHTTL_REGULAR ( 10 minutes),
1518 		 * (refresh_ttl_max + refresh_ttl_min)/2
1519 		 */
1520 		*refresh_ttl = REFRESHTTL_REGULAR;
1521 		if (*refresh_ttl > (refresh_ttl_max + refresh_ttl_min) / 2)
1522 			*refresh_ttl = (refresh_ttl_max + refresh_ttl_min) / 2;
1523 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1524 			logit("getldap_set_refresh_ttl:(3) refresh ttl is %d "
1525 			    "seconds\n", *refresh_ttl);
1526 			logit("getldap_set_refresh_ttl:(3) max ttl is %d, "
1527 			    "min ttl is %d seconds\n",
1528 			    refresh_ttl_max, refresh_ttl_min);
1529 		}
1530 	}
1531 
1532 	/*
1533 	 * get the servers statistics:
1534 	 * number of servers on list
1535 	 * number of good servers on list
1536 	 * number of pevious good servers on list
1537 	 */
1538 	for (info = head; info; info = info->next) {
1539 		num_servers++;
1540 		(void) mutex_lock(&info->mutex[0]);
1541 		if (info->sinfo[0].server_status  == INFO_SERVER_UP)
1542 			num_good_servers++;
1543 		/*
1544 		 * Server's previous status could be UNKNOWN
1545 		 * only between the very first and second
1546 		 * refresh. Treat that UNKNOWN status as up
1547 		 */
1548 		if (info->sinfo[0].prev_server_status
1549 		    == INFO_SERVER_UP ||
1550 		    info->sinfo[0].prev_server_status
1551 		    == INFO_SERVER_UNKNOWN)
1552 			num_prev_good_servers++;
1553 		(void) mutex_unlock(&info->mutex[0]);
1554 	}
1555 
1556 	/*
1557 	 * if the server list is walked three times in a row
1558 	 * without problems, double the refresh TTL but no more
1559 	 * than the max. refresh TTL
1560 	 */
1561 	if (num_good_servers == num_servers) {
1562 		num_walked_ok++;
1563 		if (num_walked_ok > UP_REFRESH_TTL_NUM)  {
1564 
1565 			*refresh_ttl = *refresh_ttl * 2;
1566 			if (*refresh_ttl > refresh_ttl_max)
1567 				*refresh_ttl = refresh_ttl_max;
1568 
1569 			num_walked_ok = 0;
1570 		}
1571 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1572 			logit("getldap_set_refresh_ttl:(4) refresh ttl is %d "
1573 			    "seconds\n", *refresh_ttl);
1574 		}
1575 	} else if (num_good_servers == 0) {
1576 		/*
1577 		 * if no good server found,
1578 		 * set refresh TTL to miminum
1579 		 */
1580 		*refresh_ttl = refresh_ttl_min;
1581 		*no_gd_server = TRUE;
1582 		num_walked_ok = 0;
1583 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1584 			logit("getldap_set_refresh_ttl:(5) refresh ttl is %d "
1585 			    "seconds\n", *refresh_ttl);
1586 		}
1587 	} else if (num_prev_good_servers > num_good_servers) {
1588 		/*
1589 		 * if more down/bad servers found,
1590 		 * decrease the refresh TTL by half
1591 		 * but no less than the min. refresh TTL
1592 		 */
1593 		*refresh_ttl = *refresh_ttl / 2;
1594 		if (*refresh_ttl < refresh_ttl_min)
1595 			*refresh_ttl = refresh_ttl_min;
1596 		num_walked_ok = 0;
1597 		logit("getldap_set_refresh_ttl:(6) refresh ttl is %d "
1598 		    "seconds\n", *refresh_ttl);
1599 
1600 	}
1601 
1602 	if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1603 		logit("getldap_set_refresh_ttl:(7) refresh ttl is %d seconds\n",
1604 		    *refresh_ttl);
1605 	}
1606 	(void) mutex_unlock(&refresh_mutex);
1607 	return (0);
1608 }
1609 
1610 static int
getldap_serverInfo_op(info_op_t op,char * input,char ** output)1611 getldap_serverInfo_op(info_op_t op, char *input, char **output)
1612 {
1613 
1614 	static rwlock_t		info_lock = DEFAULTRWLOCK;
1615 	static rwlock_t		info_lock_old = DEFAULTRWLOCK;
1616 	static mutex_t		info_mutex;
1617 	static cond_t		info_cond;
1618 	static int		creating = FALSE;
1619 	static int		refresh_ttl = 0;
1620 	static int		sec_to_refresh = 0;
1621 	static int		in_no_server_mode = FALSE;
1622 
1623 	static server_info_t	*serverInfo = NULL;
1624 	static server_info_t	*serverInfo_old = NULL;
1625 	server_info_t		*serverInfo_1;
1626 	int			is_creating;
1627 	int			err, no_server_good = FALSE;
1628 	int			server_removed = FALSE;
1629 	int			fall_thru = FALSE;
1630 	static struct timespec	timeout;
1631 	struct timespec		new_timeout;
1632 	struct timeval		tp;
1633 	static time_t		prev_refresh = 0, next_refresh = 0;
1634 	ns_server_status_t		changed = 0;
1635 
1636 	(void) pthread_setname_np(pthread_self(), "getldap_serverinfo");
1637 
1638 	if (current_admin.debug_level >= DBG_ALL) {
1639 		logit("getldap_serverInfo_op()...\n");
1640 	}
1641 	switch (op) {
1642 	case INFO_OP_CREATE:
1643 		if (current_admin.debug_level >= DBG_ALL) {
1644 			logit("operation is INFO_OP_CREATE...\n");
1645 		}
1646 
1647 		/*
1648 		 * indicate that the server info is being
1649 		 * (re)created, so that the refresh thread
1650 		 * will not refresh the info list right
1651 		 * after the list got (re)created
1652 		 */
1653 		(void) mutex_lock(&info_mutex);
1654 		is_creating = creating;
1655 		creating = TRUE;
1656 		(void) mutex_unlock(&info_mutex);
1657 
1658 		if (is_creating)
1659 			break;
1660 		/*
1661 		 * create an empty info list
1662 		 */
1663 		(void) getldap_init_serverInfo(&serverInfo_1);
1664 		/*
1665 		 * exit if list not created
1666 		 */
1667 		if (serverInfo_1 == NULL) {
1668 			(void) mutex_lock(&info_mutex);
1669 			creating = FALSE;
1670 			(void) mutex_unlock(&info_mutex);
1671 			break;
1672 		}
1673 		/*
1674 		 * make the new server info available:
1675 		 * use writer lock here, so that the switch
1676 		 * is done after all the reader locks have
1677 		 * been released.
1678 		 */
1679 		(void) rw_wrlock(&info_lock);
1680 		serverInfo = serverInfo_1;
1681 		/*
1682 		 * if this is the first time
1683 		 * the server list is being created,
1684 		 * (i.e., serverInfo_old is NULL)
1685 		 * make the old list same as the new
1686 		 * so the GETSERVER code can do its work
1687 		 */
1688 		if (serverInfo_old == NULL)
1689 			serverInfo_old = serverInfo_1;
1690 		(void) rw_unlock(&info_lock);
1691 
1692 		/*
1693 		 * fill the new info list
1694 		 */
1695 		(void) rw_rdlock(&info_lock);
1696 		/* reset bind time (tcptimeout) */
1697 		(void) getldap_set_serverInfo(serverInfo, 1, INFO_OP_CREATE);
1698 
1699 		(void) mutex_lock(&info_mutex);
1700 		/*
1701 		 * set cache manager server list TTL,
1702 		 * set refresh_ttl to zero to indicate a fresh one
1703 		 */
1704 		refresh_ttl = 0;
1705 		(void) getldap_set_refresh_ttl(serverInfo,
1706 		    &refresh_ttl, &no_server_good);
1707 		sec_to_refresh = refresh_ttl;
1708 
1709 		/* statistics: previous refresh time */
1710 		if (gettimeofday(&tp, NULL) == 0)
1711 			prev_refresh = tp.tv_sec;
1712 
1713 		creating = FALSE;
1714 
1715 		/*
1716 		 * if no server found or available,
1717 		 * tell the server info refresh thread
1718 		 * to start the "no-server" refresh loop
1719 		 * otherwise reset the in_no_server_mode flag
1720 		 */
1721 		if (no_server_good) {
1722 			sec_to_refresh = 0;
1723 			in_no_server_mode = TRUE;
1724 		} else
1725 			in_no_server_mode = FALSE;
1726 		/*
1727 		 * awake the sleeping refresh thread
1728 		 */
1729 		(void) cond_signal(&info_cond);
1730 
1731 		(void) mutex_unlock(&info_mutex);
1732 		(void) rw_unlock(&info_lock);
1733 
1734 		/*
1735 		 * delete the old server info
1736 		 */
1737 		(void) rw_wrlock(&info_lock_old);
1738 		if (serverInfo_old != serverInfo)
1739 			(void) getldap_destroy_serverInfo(serverInfo_old);
1740 		/*
1741 		 * serverInfo_old needs to be the same as
1742 		 * serverinfo now.
1743 		 * it will be used by GETSERVER processing.
1744 		 */
1745 		serverInfo_old = serverInfo;
1746 		(void) rw_unlock(&info_lock_old);
1747 		break;
1748 	case INFO_OP_DELETE:
1749 		if (current_admin.debug_level >= DBG_ALL) {
1750 			logit("operation is INFO_OP_DELETE...\n");
1751 		}
1752 		/*
1753 		 * use writer lock here, so that the delete would
1754 		 * not start until all the reader locks have
1755 		 * been released.
1756 		 */
1757 		(void) rw_wrlock(&info_lock);
1758 		if (serverInfo)
1759 			(void) getldap_destroy_serverInfo(serverInfo);
1760 		serverInfo = NULL;
1761 		(void) rw_unlock(&info_lock);
1762 		break;
1763 	case INFO_OP_REFRESH:
1764 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1765 			logit("operation is INFO_OP_REFRESH...\n");
1766 		}
1767 		/*
1768 		 * if server info is currently being
1769 		 * (re)created, do nothing
1770 		 */
1771 		(void) mutex_lock(&info_mutex);
1772 		is_creating = creating;
1773 		(void) mutex_unlock(&info_mutex);
1774 		if (is_creating)
1775 			break;
1776 
1777 		(void) rw_rdlock(&info_lock);
1778 		if (serverInfo) {
1779 			/* do not reset bind time (tcptimeout) */
1780 			(void) getldap_set_serverInfo(serverInfo, 0,
1781 			    INFO_OP_REFRESH);
1782 
1783 			(void) mutex_lock(&info_mutex);
1784 
1785 			/* statistics: previous refresh time */
1786 			if (gettimeofday(&tp, NULL) == 0)
1787 				prev_refresh = tp.tv_sec;
1788 			/*
1789 			 * set cache manager server list TTL
1790 			 */
1791 			(void) getldap_set_refresh_ttl(serverInfo,
1792 			    &refresh_ttl, &no_server_good);
1793 			/*
1794 			 * if no good server found,
1795 			 * tell the server info refresh thread
1796 			 * to start the "no-server" refresh loop
1797 			 * otherwise reset the in_no_server_mode flag
1798 			 */
1799 			if (no_server_good) {
1800 				in_no_server_mode = TRUE;
1801 				sec_to_refresh = 0;
1802 			} else {
1803 				in_no_server_mode = FALSE;
1804 				sec_to_refresh = refresh_ttl;
1805 			}
1806 			if (current_admin.debug_level >=
1807 			    DBG_SERVER_LIST_REFRESH) {
1808 				logit("getldap_serverInfo_op("
1809 				    "INFO_OP_REFRESH):"
1810 				    " seconds refresh: %d second(s)....\n",
1811 				    sec_to_refresh);
1812 			}
1813 			(void) mutex_unlock(&info_mutex);
1814 		}
1815 		(void) rw_unlock(&info_lock);
1816 
1817 		break;
1818 	case INFO_OP_REFRESH_WAIT:
1819 		if (current_admin.debug_level >= DBG_SERVER_LIST_REFRESH) {
1820 			logit("operation is INFO_OP_REFRESH_WAIT...\n");
1821 		}
1822 		(void) cond_init(&info_cond, USYNC_THREAD, NULL);
1823 		(void) mutex_lock(&info_mutex);
1824 		err = 0;
1825 		while (err != ETIME) {
1826 			int sleeptime;
1827 			/*
1828 			 * if need to go into the "no-server" refresh
1829 			 * loop, set timout value to
1830 			 * REFRESH_DELAY_WHEN_NO_SERVER
1831 			 */
1832 			if (sec_to_refresh == 0) {
1833 				sec_to_refresh = refresh_ttl;
1834 				timeout.tv_sec = time(NULL) +
1835 				    REFRESH_DELAY_WHEN_NO_SERVER;
1836 				sleeptime = REFRESH_DELAY_WHEN_NO_SERVER;
1837 				if (current_admin.debug_level >=
1838 				    DBG_SERVER_LIST_REFRESH) {
1839 					logit("getldap_serverInfo_op("
1840 					    "INFO_OP_REFRESH_WAIT):"
1841 					    " entering no-server "
1842 					    "refresh loop...\n");
1843 				}
1844 			} else {
1845 				timeout.tv_sec = time(NULL) + sec_to_refresh;
1846 				sleeptime = sec_to_refresh;
1847 			}
1848 			timeout.tv_nsec = 0;
1849 
1850 			/* statistics: next refresh time */
1851 			next_refresh = timeout.tv_sec;
1852 
1853 			if (current_admin.debug_level >=
1854 			    DBG_SERVER_LIST_REFRESH) {
1855 				logit("getldap_serverInfo_op("
1856 				    "INFO_OP_REFRESH_WAIT):"
1857 				    " about to sleep for %d second(s)...\n",
1858 				    sleeptime);
1859 			}
1860 			err = cond_timedwait(&info_cond,
1861 			    &info_mutex, &timeout);
1862 		}
1863 		(void) cond_destroy(&info_cond);
1864 		(void) mutex_unlock(&info_mutex);
1865 		break;
1866 	case INFO_OP_GETSERVER:
1867 		if (current_admin.debug_level >= DBG_ALL) {
1868 			logit("operation is INFO_OP_GETSERVER...\n");
1869 		}
1870 		*output = NULL;
1871 		/*
1872 		 * GETSERVER processing always use
1873 		 * serverInfo_old to retrieve server infomation.
1874 		 * serverInfo_old is equal to serverInfo
1875 		 * most of the time, except when a new
1876 		 * server list is being created.
1877 		 * This is why the check for is_creating
1878 		 * is needed below.
1879 		 */
1880 		(void) rw_rdlock(&info_lock_old);
1881 
1882 		if (serverInfo_old == NULL) {
1883 			(void) rw_unlock(&info_lock_old);
1884 			break;
1885 		} else
1886 			(void) getldap_get_serverInfo(serverInfo_old,
1887 			    input, output, &server_removed);
1888 		(void) rw_unlock(&info_lock_old);
1889 
1890 		/*
1891 		 * Return here and let remove server thread do its job in
1892 		 * another thread. It executes INFO_OP_REMOVESERVER code later.
1893 		 */
1894 		if (server_removed)
1895 			break;
1896 
1897 		fall_thru = TRUE;
1898 
1899 		/* FALL THROUGH */
1900 
1901 	case INFO_OP_REMOVESERVER:
1902 		/*
1903 		 * INFO_OP_GETSERVER and INFO_OP_REMOVESERVER share the
1904 		 * following code except (!fall thru) part.
1905 		 */
1906 
1907 		/*
1908 		 * if server info is currently being
1909 		 * (re)created, do nothing
1910 		 */
1911 
1912 		(void) mutex_lock(&info_mutex);
1913 		is_creating = creating;
1914 		(void) mutex_unlock(&info_mutex);
1915 		if (is_creating)
1916 			break;
1917 
1918 		if (!fall_thru) {
1919 			if (current_admin.debug_level >= DBG_ALL)
1920 				logit("operation is INFO_OP_REMOVESERVER...\n");
1921 			(void) rw_rdlock(&info_lock_old);
1922 			changed = set_server_status(input, serverInfo_old);
1923 			(void) rw_unlock(&info_lock_old);
1924 			if (changed)
1925 				create_buf_and_notify(input, changed);
1926 			else
1927 				break;
1928 		}
1929 
1930 		/*
1931 		 * set cache manager server list TTL if necessary
1932 		 */
1933 		if (*output == NULL || changed) {
1934 			(void) rw_rdlock(&info_lock);
1935 			(void) mutex_lock(&info_mutex);
1936 
1937 			(void) getldap_set_refresh_ttl(serverInfo,
1938 			    &refresh_ttl, &no_server_good);
1939 
1940 			/*
1941 			 * if no good server found, need to go into
1942 			 * the "no-server" refresh loop
1943 			 * to find a server as soon as possible
1944 			 * otherwise reset the in_no_server_mode flag
1945 			 */
1946 			if (no_server_good) {
1947 				/*
1948 				 * if already in no-server mode,
1949 				 * don't brother
1950 				 */
1951 				if (in_no_server_mode == FALSE) {
1952 					sec_to_refresh = 0;
1953 					in_no_server_mode = TRUE;
1954 					(void) cond_signal(&info_cond);
1955 				}
1956 				(void) mutex_unlock(&info_mutex);
1957 				(void) rw_unlock(&info_lock);
1958 				break;
1959 			} else {
1960 				in_no_server_mode = FALSE;
1961 				sec_to_refresh = refresh_ttl;
1962 			}
1963 			/*
1964 			 * if the refresh thread will be timed out
1965 			 * longer than refresh_ttl seconds,
1966 			 * wake it up to make it wait on the new
1967 			 * time out value
1968 			 */
1969 			new_timeout.tv_sec = time(NULL) + refresh_ttl;
1970 			if (new_timeout.tv_sec < timeout.tv_sec)
1971 				(void) cond_signal(&info_cond);
1972 
1973 			(void) mutex_unlock(&info_mutex);
1974 			(void) rw_unlock(&info_lock);
1975 		}
1976 		break;
1977 	case INFO_OP_GETSTAT:
1978 		if (current_admin.debug_level >= DBG_ALL) {
1979 			logit("operation is INFO_OP_GETSTAT...\n");
1980 		}
1981 		*output = NULL;
1982 		(void) rw_rdlock(&info_lock);
1983 		if (serverInfo) {
1984 			(void) getldap_get_server_stat(serverInfo,
1985 			    output, &prev_refresh, &next_refresh);
1986 		}
1987 		(void) rw_unlock(&info_lock);
1988 		break;
1989 	default:
1990 		logit("getldap_serverInfo_op(): "
1991 		    "invalid operation code (%d).\n", op);
1992 		return (-1);
1993 		break;
1994 	}
1995 	return (NS_LDAP_SUCCESS);
1996 }
1997 
1998 void
getldap_serverInfo_refresh()1999 getldap_serverInfo_refresh()
2000 {
2001 	int always = 1;
2002 
2003 	if (current_admin.debug_level >= DBG_ALL) {
2004 		logit("getldap_serverInfo_refresh()...\n");
2005 	}
2006 
2007 	/* create the server info list */
2008 	(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
2009 
2010 	while (always) {
2011 		/*
2012 		 * the operation INFO_OP_REFRESH_WAIT
2013 		 * causes this thread to wait until
2014 		 * it is time to do refresh,
2015 		 * see getldap_serverInfo_op() for details
2016 		 */
2017 		(void) getldap_serverInfo_op(INFO_OP_REFRESH_WAIT, NULL, NULL);
2018 		(void) getldap_serverInfo_op(INFO_OP_REFRESH, NULL, NULL);
2019 	}
2020 }
2021 
2022 void
getldap_getserver(LineBuf * config_info,ldap_call_t * in)2023 getldap_getserver(LineBuf *config_info, ldap_call_t *in)
2024 {
2025 	char		req[] = "0";
2026 
2027 	if (current_admin.debug_level >= DBG_ALL) {
2028 		logit("getldap_getserver()...\n");
2029 	}
2030 
2031 	config_info->len = 0;
2032 
2033 	/* make sure the request is valid */
2034 	req[0] = (in->ldap_u.servername)[0];
2035 	if ((req[0] != '\0') &&
2036 	    (strcmp(req, NS_CACHE_NEW) != 0) &&
2037 	    (strcmp(req, NS_CACHE_NORESP)  != 0) &&
2038 	    (strcmp(req, NS_CACHE_NEXT)    != 0) &&
2039 	    (strcmp(req, NS_CACHE_WRITE)   != 0)) {
2040 		return;
2041 	}
2042 
2043 	(void) getldap_serverInfo_op(INFO_OP_GETSERVER,
2044 	    in->ldap_u.domainname, &config_info->str);
2045 
2046 	if (config_info->str == NULL)
2047 		return;
2048 
2049 	config_info->len = strlen(config_info->str) + 1;
2050 
2051 	if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2052 		/* Log server IP */
2053 		char	*ptr,
2054 		    separator;
2055 		ptr = strstr(config_info->str, DOORLINESEP);
2056 		if (ptr) {
2057 			separator = *ptr;
2058 			*ptr = '\0';
2059 			logit("getldap_getserver: got server %s\n",
2060 			    config_info->str);
2061 			*ptr = separator;
2062 		} else
2063 			logit("getldap_getserver: Missing %s."
2064 			    " Internal error\n", DOORLINESEP);
2065 	}
2066 }
2067 
2068 void
getldap_get_cacheData(LineBuf * config_info,ldap_call_t * in)2069 getldap_get_cacheData(LineBuf *config_info, ldap_call_t *in)
2070 {
2071 	char	*instr = NULL;
2072 	int	datatype = CACHE_MAP_UNKNOWN;
2073 
2074 	if (current_admin.debug_level >= DBG_ALL) {
2075 		logit("getldap_get_cacheData()...\n");
2076 	}
2077 
2078 	config_info->len = 0;
2079 	config_info->str = NULL;
2080 
2081 	/* make sure the request is valid */
2082 	if (strncmp(in->ldap_u.servername,
2083 	    NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2084 		datatype = CACHE_MAP_DN2DOMAIN;
2085 
2086 	if (datatype == CACHE_MAP_UNKNOWN)
2087 		return;
2088 
2089 	instr = strstr(in->ldap_u.servername, DOORLINESEP);
2090 	if (instr == NULL)
2091 		return;
2092 	instr += strlen(DOORLINESEP);
2093 	if (*instr == '\0')
2094 		return;
2095 
2096 	(void) getldap_cache_op(CACHE_OP_FIND, datatype,
2097 	    instr, &config_info->str);
2098 
2099 	if (config_info->str != NULL) {
2100 		config_info->len = strlen(config_info->str) + 1;
2101 	}
2102 }
2103 
2104 int
getldap_set_cacheData(ldap_call_t * in)2105 getldap_set_cacheData(ldap_call_t *in)
2106 {
2107 	char	*instr1 = NULL;
2108 	char	*instr2 = NULL;
2109 	int	datatype = CACHE_MAP_UNKNOWN;
2110 	int	rc = 0;
2111 
2112 	if (current_admin.debug_level >= DBG_ALL) {
2113 		logit("getldap_set_cacheData()...\n");
2114 	}
2115 
2116 	/* make sure the request is valid */
2117 	if (strncmp(in->ldap_u.servername,
2118 	    NS_CACHE_DN2DOMAIN, strlen(NS_CACHE_DN2DOMAIN)) == 0)
2119 		datatype = CACHE_MAP_DN2DOMAIN;
2120 
2121 	if (datatype == CACHE_MAP_UNKNOWN)
2122 		return (-1);
2123 
2124 	instr1 = strstr(in->ldap_u.servername, DOORLINESEP);
2125 	if (instr1 == NULL)
2126 		return (-1);
2127 	*instr1 = '\0';
2128 	instr1 += strlen(DOORLINESEP);
2129 	if (*instr1 == '\0')
2130 		return (-1);
2131 	instr2 = strstr(instr1, DOORLINESEP);
2132 	if (instr2 == NULL)
2133 		return (-1);
2134 	*instr2 = '\0';
2135 	instr2 += strlen(DOORLINESEP);
2136 	if (*instr2 == '\0')
2137 		return (-1);
2138 
2139 	rc = getldap_cache_op(CACHE_OP_ADD, datatype,
2140 	    instr1, &instr2);
2141 	if (rc != NS_LDAP_SUCCESS)
2142 		return (-1);
2143 
2144 	return (0);
2145 }
2146 
2147 void
getldap_get_cacheStat(LineBuf * stat_info)2148 getldap_get_cacheStat(LineBuf *stat_info)
2149 {
2150 	char	*foutstr = NULL;
2151 	char	*soutstr = NULL;
2152 	char	*coutstr = NULL;
2153 	int	infoSize;
2154 
2155 	if (current_admin.debug_level >= DBG_ALL) {
2156 		logit("getldap_get_cacheStat()...\n");
2157 	}
2158 
2159 	stat_info->str = NULL;
2160 	stat_info->len = 0;
2161 
2162 	/* get refersh statisitcs */
2163 	(void) getldap_get_refresh_stat(&foutstr);
2164 	if (foutstr == NULL)
2165 		return;
2166 
2167 	/* get server statisitcs */
2168 	(void) getldap_serverInfo_op(INFO_OP_GETSTAT, NULL, &soutstr);
2169 	if (soutstr == NULL) {
2170 		free(foutstr);
2171 		return;
2172 	}
2173 	/* get cache data statisitcs */
2174 	(void) getldap_cache_op(CACHE_OP_GETSTAT, 0, NULL, &coutstr);
2175 	if (coutstr == NULL) {
2176 		free(foutstr);
2177 		free(soutstr);
2178 		return;
2179 	}
2180 
2181 	infoSize = strlen(foutstr) + strlen(soutstr) + strlen(coutstr) + 3;
2182 	stat_info->str = calloc(infoSize, sizeof (char));
2183 	if (stat_info->str != NULL) {
2184 		(void) strncpy(stat_info->str,
2185 		    foutstr,
2186 		    strlen(foutstr) + 1);
2187 		(void) strncat(stat_info->str,
2188 		    soutstr,
2189 		    strlen(soutstr) + 1);
2190 		(void) strncat(stat_info->str,
2191 		    coutstr,
2192 		    strlen(coutstr) + 1);
2193 		stat_info->len = infoSize;
2194 	}
2195 
2196 	free(foutstr);
2197 	free(soutstr);
2198 	free(coutstr);
2199 }
2200 
2201 static int
checkupdate(int sighup)2202 checkupdate(int sighup)
2203 {
2204 	int	value;
2205 
2206 	(void) rw_wrlock(&ldap_lock);
2207 	value = sighup;
2208 	(void) rw_unlock(&ldap_lock);
2209 
2210 	return (value == TRUE);
2211 }
2212 
2213 
2214 static int
update_from_profile(int * change_status)2215 update_from_profile(int *change_status)
2216 {
2217 	ns_ldap_result_t *result = NULL;
2218 	char		searchfilter[BUFSIZ];
2219 	ns_ldap_error_t	*error;
2220 	int		rc;
2221 	void		**paramVal = NULL;
2222 	ns_config_t	*ptr = NULL;
2223 	char		*profile = NULL;
2224 	char		errstr[MAXERROR];
2225 
2226 	if (current_admin.debug_level >= DBG_ALL) {
2227 		logit("update_from_profile....\n");
2228 	}
2229 	do {
2230 		(void) rw_wrlock(&ldap_lock);
2231 		sighup_update = FALSE;
2232 		(void) rw_unlock(&ldap_lock);
2233 
2234 		if ((rc = __ns_ldap_getParam(NS_LDAP_PROFILE_P,
2235 		    &paramVal, &error)) != NS_LDAP_SUCCESS) {
2236 			if (error != NULL && error->message != NULL)
2237 				logit("Error: Unable to  profile name: %s\n",
2238 				    error->message);
2239 			else {
2240 				char *tmp;
2241 
2242 				__ns_ldap_err2str(rc, &tmp);
2243 				logit("Error: Unable to  profile name: %s\n",
2244 				    tmp);
2245 			}
2246 			(void) __ns_ldap_freeParam(&paramVal);
2247 			(void) __ns_ldap_freeError(&error);
2248 			return (-1);
2249 		}
2250 
2251 		if (paramVal && *paramVal)
2252 			profile = strdup((char *)*paramVal);
2253 		(void) __ns_ldap_freeParam(&paramVal);
2254 
2255 		if (profile == NULL) {
2256 			return (-1);
2257 		}
2258 
2259 		(void) snprintf(searchfilter, BUFSIZ, _PROFILE_FILTER,
2260 		    _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
2261 
2262 		if ((rc = __ns_ldap_list(_PROFILE_CONTAINER,
2263 		    (const char *)searchfilter, NULL,
2264 		    NULL, NULL, 0,
2265 		    &result, &error, NULL, NULL)) != NS_LDAP_SUCCESS) {
2266 
2267 			/*
2268 			 * Is profile name the DEFAULTCONFIGNAME?
2269 			 * syslog Warning, otherwise syslog error.
2270 			 */
2271 			if (strcmp(profile, DEFAULTCONFIGNAME) == 0) {
2272 				syslog(LOG_WARNING,
2273 				    "Ignoring attempt to refresh nonexistent "
2274 				    "default profile: %s.\n",
2275 				    profile);
2276 				logit("Ignoring attempt to refresh nonexistent "
2277 				    "default profile: %s.\n",
2278 				    profile);
2279 			} else if ((error != NULL) &&
2280 			    (error->message != NULL)) {
2281 				syslog(LOG_ERR,
2282 				    "Error: Unable to refresh profile:%s:"
2283 				    " %s\n", profile, error->message);
2284 				logit("Error: Unable to refresh profile:"
2285 				    "%s:%s\n", profile, error->message);
2286 			} else {
2287 				syslog(LOG_ERR, "Error: Unable to refresh "
2288 				    "from profile:%s. (error=%d)\n",
2289 				    profile, rc);
2290 				logit("Error: Unable to refresh from profile "
2291 				    "%s (error=%d)\n", profile, rc);
2292 			}
2293 
2294 			(void) __ns_ldap_freeError(&error);
2295 			(void) __ns_ldap_freeResult(&result);
2296 			free(profile);
2297 			return (-1);
2298 		}
2299 		free(profile);
2300 
2301 
2302 	} while (checkupdate(sighup_update) == TRUE);
2303 
2304 	(void) rw_wrlock(&ldap_lock);
2305 
2306 	ptr = __ns_ldap_make_config(result);
2307 	(void) __ns_ldap_freeResult(&result);
2308 
2309 	if (ptr == NULL) {
2310 		logit("Error: __ns_ldap_make_config failed.\n");
2311 		(void) rw_unlock(&ldap_lock);
2312 		return (-1);
2313 	}
2314 
2315 	/*
2316 	 * cross check the config parameters
2317 	 */
2318 	if (__s_api_crosscheck(ptr, errstr, B_TRUE) == NS_SUCCESS) {
2319 		/*
2320 		 * reset the local profile TTL
2321 		 */
2322 		if (ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc)
2323 			current_admin.ldap_stat.ldap_ttl =
2324 			    atol(ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc);
2325 
2326 		if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2327 			logit("update_from_profile: reset profile TTL to %d"
2328 			    "  seconds\n",
2329 			    current_admin.ldap_stat.ldap_ttl);
2330 			logit("update_from_profile: expire time %ld "
2331 			    "seconds\n",
2332 			    ptr->paramList[NS_LDAP_EXP_P].ns_tm);
2333 		}
2334 
2335 		/* set ptr as current_config if the config is changed */
2336 		chg_test_config_change(ptr, change_status);
2337 		rc = 0;
2338 	} else {
2339 		__s_api_destroy_config(ptr);
2340 		logit("Error: downloaded profile failed to pass "
2341 		    "crosscheck (%s).\n", errstr);
2342 		syslog(LOG_ERR, "ldap_cachemgr: %s", errstr);
2343 		rc = -1;
2344 	}
2345 	(void) rw_unlock(&ldap_lock);
2346 
2347 	return (rc);
2348 }
2349 
2350 int
getldap_init()2351 getldap_init()
2352 {
2353 	ns_ldap_error_t	*error;
2354 	struct timeval	tp;
2355 	ldap_get_chg_cookie_t	cookie;
2356 
2357 	if (current_admin.debug_level >= DBG_ALL) {
2358 		logit("getldap_init()...\n");
2359 	}
2360 
2361 	(void) __ns_ldap_setServer(TRUE);
2362 
2363 	(void) rw_wrlock(&ldap_lock);
2364 	if ((error = __ns_ldap_LoadConfiguration()) != NULL) {
2365 		logit("Error: Unable to read '%s': %s\n",
2366 		    NSCONFIGFILE, error->message);
2367 		(void) fprintf(stderr,
2368 		    gettext("\nError: Unable to read '%s': %s\n"),
2369 		    NSCONFIGFILE, error->message);
2370 		__ns_ldap_freeError(&error);
2371 		(void) rw_unlock(&ldap_lock);
2372 		return (-1);
2373 	}
2374 	(void) rw_unlock(&ldap_lock);
2375 
2376 	if (gettimeofday(&tp, NULL) == 0) {
2377 		/* statistics: previous refresh time */
2378 		prev_refresh_time = tp.tv_sec;
2379 	}
2380 
2381 	/* initialize the data cache */
2382 	(void) getldap_cache_op(CACHE_OP_CREATE,
2383 	    0, NULL, NULL);
2384 
2385 	cookie.mgr_pid = getpid();
2386 	cookie.seq_num = 0;
2387 	chg_config_cookie_set(&cookie);
2388 	return (0);
2389 }
2390 
2391 static void
perform_update(void)2392 perform_update(void)
2393 {
2394 	ns_ldap_error_t	*error = NULL;
2395 	struct timeval	tp;
2396 	char		buf[20];
2397 	int		rc, rc1;
2398 	int		changed = 0;
2399 	void		**paramVal = NULL;
2400 	ns_ldap_self_gssapi_config_t	config;
2401 
2402 	if (current_admin.debug_level >= DBG_ALL) {
2403 		logit("perform_update()...\n");
2404 	}
2405 
2406 	(void) __ns_ldap_setServer(TRUE);
2407 
2408 	if (gettimeofday(&tp, NULL) != 0)
2409 		return;
2410 
2411 	rc = __ns_ldap_getParam(NS_LDAP_CACHETTL_P, &paramVal, &error);
2412 
2413 	if (rc == NS_LDAP_SUCCESS && paramVal != NULL) {
2414 		current_admin.ldap_stat.ldap_ttl = atol((char *)*paramVal);
2415 	}
2416 
2417 	if (error != NULL)
2418 		(void) __ns_ldap_freeError(&error);
2419 
2420 	if (paramVal != NULL)
2421 		(void) __ns_ldap_freeParam(&paramVal);
2422 
2423 	if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
2424 		logit("perform_update: current profile TTL is %d seconds\n",
2425 		    current_admin.ldap_stat.ldap_ttl);
2426 	}
2427 
2428 	if (current_admin.ldap_stat.ldap_ttl > 0) {
2429 		/*
2430 		 * set the profile TTL parameter, just
2431 		 * in case that the downloading of
2432 		 * the profile from server would fail
2433 		 */
2434 
2435 		/*
2436 		 * NS_LDAP_EXP_P is a no op for __ns_ldap_setParam
2437 		 * It depends on NS_LDAP_CACHETTL_P to set it's value
2438 		 * Set NS_LDAP_CACHETTL_P here so NS_LDAP_EXP_P value
2439 		 * can be set.
2440 		 * NS_LDAP_CACHETTL_P value can be reset after the profile is
2441 		 * downloaded from the server, so is NS_LDAP_EXP_P.
2442 		 */
2443 		buf[19] = '\0'; /* null terminated the buffer */
2444 		if (__ns_ldap_setParam(NS_LDAP_CACHETTL_P,
2445 		    lltostr((long long)current_admin.ldap_stat.ldap_ttl,
2446 		    &buf[19]),
2447 		    &error) != NS_LDAP_SUCCESS) {
2448 			logit("Error: __ns_ldap_setParam failed, status: %d "
2449 			    "message: %s\n", error->status, error->message);
2450 			(void)  __ns_ldap_freeError(&error);
2451 			return;
2452 		}
2453 
2454 		(void) rw_wrlock(&ldap_lock);
2455 		sighup_update = FALSE;
2456 		(void) rw_unlock(&ldap_lock);
2457 
2458 		do {
2459 			rc = update_from_profile(&changed);
2460 			if (rc != 0) {
2461 				logit("Error: Unable to update from profile\n");
2462 			}
2463 		} while (checkupdate(sighup_update) == TRUE);
2464 	} else {
2465 		rc = 0;
2466 	}
2467 
2468 	/*
2469 	 * recreate the server info list
2470 	 */
2471 	if (rc == 0) {
2472 		(void) getldap_serverInfo_op(INFO_OP_CREATE, NULL, NULL);
2473 
2474 		/* flush the data cache */
2475 		(void) getldap_cache_op(CACHE_OP_DELETE,
2476 		    0, NULL, NULL);
2477 
2478 		/* statistics: previous refresh time */
2479 		prev_refresh_time = tp.tv_sec;
2480 	}
2481 	rc1 = __ns_ldap_self_gssapi_config(&config);
2482 	if (rc1 == NS_LDAP_SUCCESS) {
2483 		if (config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) {
2484 			rc1 = __ns_ldap_check_all_preq(0, 0, 0, config, &error);
2485 			(void)  __ns_ldap_freeError(&error);
2486 			if (rc1 != NS_LDAP_SUCCESS) {
2487 				logit("Error: Check on self credential "
2488 				    "prerquesites failed: %d\n",
2489 				    rc1);
2490 				exit(rc1);
2491 			}
2492 		}
2493 	} else {
2494 		logit("Error: Failed to get self credential configuration %d\n",
2495 		    rc1);
2496 			exit(rc1);
2497 	}
2498 
2499 	if (!changed)
2500 		return;
2501 
2502 	(void) rw_rdlock(&ldap_lock);
2503 	if (((error = __ns_ldap_DumpConfiguration(NSCONFIGREFRESH)) != NULL) ||
2504 	    ((error = __ns_ldap_DumpConfiguration(NSCREDREFRESH)) != NULL)) {
2505 		logit("Error: __ns_ldap_DumpConfiguration failed, "
2506 		    "status: %d message: %s\n", error->status, error->message);
2507 		__ns_ldap_freeError(&error);
2508 		(void) rw_unlock(&ldap_lock);
2509 		return;
2510 	}
2511 	if (rename(NSCONFIGREFRESH, NSCONFIGFILE) != 0) {
2512 		logit("Error: unlink failed - errno: %s\n", strerror(errno));
2513 		syslog(LOG_ERR, "Unable to refresh profile, LDAP configuration"
2514 		    "files not written");
2515 		(void) rw_unlock(&ldap_lock);
2516 		return;
2517 	}
2518 	if (rename(NSCREDREFRESH, NSCREDFILE) != 0) {
2519 		/*
2520 		 * We probably have inconsistent configuration at this point.
2521 		 * If we were to create a backup file and rename it here, that
2522 		 * operation might also fail. Consequently there is no safe way
2523 		 * to roll back.
2524 		 */
2525 		logit("Error: unlink failed - errno: %s\n", strerror(errno));
2526 		syslog(LOG_ERR, "Unable to refresh profile consistently, "
2527 		    "LDAP configuration files inconsistent");
2528 		(void) rw_unlock(&ldap_lock);
2529 		return;
2530 	}
2531 
2532 	(void) rw_unlock(&ldap_lock);
2533 }
2534 
2535 void
getldap_refresh()2536 getldap_refresh()
2537 {
2538 	struct timespec	timeout;
2539 	int		sleeptime;
2540 	struct timeval	tp;
2541 	long		expire = 0;
2542 	void		**paramVal = NULL;
2543 	ns_ldap_error_t	*errorp;
2544 	int		always = 1, err;
2545 	int		first_time = 1;
2546 	int		sig_done = 0;
2547 	int		dbg_level;
2548 
2549 	(void) pthread_setname_np(pthread_self(), "getldap_refresh");
2550 
2551 	if (current_admin.debug_level >= DBG_ALL) {
2552 		logit("getldap_refresh()...\n");
2553 	}
2554 
2555 	/*
2556 	 * wait for an available server
2557 	 */
2558 	while (sig_done == 0) {
2559 		(void) mutex_lock(&sig_mutex);
2560 		sig_done = signal_done;
2561 		(void) mutex_unlock(&sig_mutex);
2562 	}
2563 
2564 	(void) __ns_ldap_setServer(TRUE);
2565 	while (always) {
2566 		dbg_level = current_admin.debug_level;
2567 		(void) rw_rdlock(&ldap_lock);
2568 		sleeptime = current_admin.ldap_stat.ldap_ttl;
2569 		if (dbg_level >= DBG_PROFILE_REFRESH) {
2570 			logit("getldap_refresh: current profile TTL is %d "
2571 			"seconds\n", current_admin.ldap_stat.ldap_ttl);
2572 		}
2573 		if (gettimeofday(&tp, NULL) == 0) {
2574 			if ((__ns_ldap_getParam(NS_LDAP_EXP_P,
2575 			    &paramVal, &errorp) == NS_LDAP_SUCCESS) &&
2576 			    paramVal != NULL &&
2577 			    (char *)*paramVal != NULL) {
2578 				errno = 0;
2579 				expire = atol((char *)*paramVal);
2580 				(void) __ns_ldap_freeParam(&paramVal);
2581 				if (errno == 0) {
2582 					if (expire == 0) {
2583 						first_time = 0;
2584 						(void) rw_unlock(&ldap_lock);
2585 						(void) cond_init(&cond,
2586 						    USYNC_THREAD, NULL);
2587 						(void) mutex_lock(&sighuplock);
2588 						timeout.tv_sec =
2589 						    CACHESLEEPTIME;
2590 						timeout.tv_nsec = 0;
2591 						if (dbg_level >=
2592 						    DBG_PROFILE_REFRESH) {
2593 							logit("getldap_refresh:"
2594 							    "(1)about to sleep"
2595 							    " for %d seconds\n",
2596 							    CACHESLEEPTIME);
2597 						}
2598 						err = cond_reltimedwait(&cond,
2599 						    &sighuplock, &timeout);
2600 						(void) cond_destroy(&cond);
2601 						(void) mutex_unlock(
2602 						    &sighuplock);
2603 						/*
2604 						 * if woke up by
2605 						 * getldap_revalidate(),
2606 						 * do update right away
2607 						 */
2608 						if (err == ETIME)
2609 							continue;
2610 						else {
2611 							/*
2612 							 * if load
2613 							 * configuration failed
2614 							 * don't do update
2615 							 */
2616 							if (load_config())
2617 								perform_update
2618 								    ();
2619 							continue;
2620 						}
2621 					}
2622 					sleeptime = expire - tp.tv_sec;
2623 					if (dbg_level >= DBG_PROFILE_REFRESH) {
2624 						logit("getldap_refresh: expire "
2625 						    "time = %ld\n", expire);
2626 					}
2627 
2628 				}
2629 			}
2630 		}
2631 
2632 		(void) rw_unlock(&ldap_lock);
2633 
2634 		/*
2635 		 * if this is the first time downloading
2636 		 * the profile or expire time already passed,
2637 		 * do not wait, do update
2638 		 */
2639 		if (first_time == 0 && sleeptime > 0) {
2640 			if (dbg_level >= DBG_PROFILE_REFRESH) {
2641 				logit("getldap_refresh: (2)about to sleep "
2642 				"for %d seconds\n", sleeptime);
2643 			}
2644 			(void) cond_init(&cond, USYNC_THREAD, NULL);
2645 			(void) mutex_lock(&sighuplock);
2646 			timeout.tv_sec = sleeptime;
2647 			timeout.tv_nsec = 0;
2648 			err = cond_reltimedwait(&cond,
2649 			    &sighuplock, &timeout);
2650 			(void) cond_destroy(&cond);
2651 			(void) mutex_unlock(&sighuplock);
2652 		}
2653 		/*
2654 		 * if load concfiguration failed
2655 		 * don't do update
2656 		 */
2657 		if (load_config())
2658 			perform_update();
2659 		first_time = 0;
2660 	}
2661 }
2662 
2663 void
getldap_revalidate(int signal __unused)2664 getldap_revalidate(int signal __unused)
2665 {
2666 	if (current_admin.debug_level >= DBG_ALL) {
2667 		logit("getldap_revalidate()...\n");
2668 	}
2669 	/* block signal SIGHUP */
2670 	(void) sighold(SIGHUP);
2671 
2672 	/* now awake the sleeping refresh thread */
2673 	(void) cond_signal(&cond);
2674 
2675 	/* release signal SIGHUP */
2676 	(void) sigrelse(SIGHUP);
2677 
2678 }
2679 
2680 void
getldap_admincred(LineBuf * config_info,ldap_call_t * in)2681 getldap_admincred(LineBuf *config_info, ldap_call_t *in)
2682 {
2683 	ns_ldap_error_t	*error;
2684 	ldap_config_out_t *cout;
2685 	ucred_t *uc = NULL;
2686 
2687 	if (current_admin.debug_level >= DBG_ALL) {
2688 		logit("getldap_admincred()...\n");
2689 	}
2690 	/* check privileges */
2691 	if (is_root_or_all_privs("GETADMINCRED", &uc) == 0) {
2692 		logit("admin credential requested by a non-root and no ALL "
2693 		    "privilege user not allowed");
2694 		config_info->str = NULL;
2695 		config_info->len = 0;
2696 	} else {
2697 		(void) rw_rdlock(&ldap_lock);
2698 		if ((error = __ns_ldap_LoadDoorInfo(config_info,
2699 		    in->ldap_u.domainname, NULL, 1)) != NULL) {
2700 			if (error != NULL && error->message != NULL)
2701 				logit("Error: ldap_lookup: %s\n",
2702 				    error->message);
2703 			(void) __ns_ldap_freeError(&error);
2704 
2705 			config_info->str = NULL;
2706 			config_info->len = 0;
2707 		}
2708 		/* set change cookie */
2709 		cout = (ldap_config_out_t *)config_info->str;
2710 		if (cout)
2711 			cout->cookie = chg_config_cookie_get();
2712 		(void) rw_unlock(&ldap_lock);
2713 	}
2714 }
2715 
2716 void
getldap_lookup(LineBuf * config_info,ldap_call_t * in)2717 getldap_lookup(LineBuf *config_info, ldap_call_t *in)
2718 {
2719 	ns_ldap_error_t	*error;
2720 	ldap_config_out_t *cout;
2721 
2722 	if (current_admin.debug_level >= DBG_ALL) {
2723 		logit("getldap_lookup()...\n");
2724 	}
2725 	(void) rw_rdlock(&ldap_lock);
2726 	if ((error = __ns_ldap_LoadDoorInfo(config_info,
2727 	    in->ldap_u.domainname, NULL, 0)) != NULL) {
2728 		if (error != NULL && error->message != NULL)
2729 			logit("Error: ldap_lookup: %s\n", error->message);
2730 		(void) __ns_ldap_freeError(&error);
2731 
2732 		config_info->str = NULL;
2733 		config_info->len = 0;
2734 	}
2735 	/* set change cookie */
2736 	cout = (ldap_config_out_t *)config_info->str;
2737 	if (cout)
2738 		cout->cookie = chg_config_cookie_get();
2739 	(void) rw_unlock(&ldap_lock);
2740 }
2741 /*
2742  * It creates the header and data stream to be door returned and notify
2743  * chg_get_statusChange() threads.
2744  * This is called after all getldap_get_rootDSE() threads are joined.
2745  */
2746 void
test_server_change(server_info_t * head)2747 test_server_change(server_info_t *head)
2748 {
2749 	server_info_t *info;
2750 	int	len = 0, num = 0, ds_len = 0, new_len = 0, tlen = 0;
2751 	char	*tmp_buf = NULL, *ptr = NULL, *status = NULL;
2752 	ldap_get_change_out_t *cout;
2753 
2754 	ds_len = strlen(DOORLINESEP);
2755 
2756 	for (info = head; info; info = info->next) {
2757 		(void) mutex_lock(&info->mutex[0]);
2758 		if (info->sinfo[0].change != 0) {
2759 			/* "9.9.9.9|NS_SERVER_CHANGE_UP|" */
2760 			len += 2 * ds_len + strlen(info->sinfo[0].addr) +
2761 			    strlen(NS_SERVER_CHANGE_UP);
2762 			num++;
2763 		}
2764 		(void) mutex_unlock(&info->mutex[0]);
2765 	}
2766 
2767 	if (len == 0)
2768 		return;
2769 
2770 	len++; /* '\0' */
2771 
2772 	tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2773 	if ((tmp_buf = malloc(tlen)) == NULL)
2774 		return;
2775 
2776 	cout = (ldap_get_change_out_t *)tmp_buf;
2777 	cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2778 	/* cout->cookie is set by chg_notify_statusChange */
2779 	cout->server_count = num;
2780 	cout->data_size = len;
2781 
2782 	/* Create IP|UP or DOWN|IP|UP or DOWN| ... */
2783 	ptr = cout->data;
2784 	new_len = len;
2785 	for (info = head; info; info = info->next) {
2786 		(void) mutex_lock(&info->mutex[0]);
2787 		if (info->sinfo[0].change == 0) {
2788 			(void) mutex_unlock(&info->mutex[0]);
2789 			continue;
2790 		}
2791 
2792 		if (info->sinfo[0].change == NS_SERVER_UP)
2793 			status = NS_SERVER_CHANGE_UP;
2794 		else if (info->sinfo[0].change == NS_SERVER_DOWN)
2795 			status = NS_SERVER_CHANGE_DOWN;
2796 		else {
2797 			syslog(LOG_WARNING, gettext("Bad change value %d"),
2798 			    info->sinfo[0].change);
2799 			(void) mutex_unlock(&info->mutex[0]);
2800 			free(tmp_buf);
2801 			return;
2802 		}
2803 
2804 		if ((snprintf(ptr, new_len, "%s%s%s%s",
2805 		    info->sinfo[0].addr, DOORLINESEP,
2806 		    status, DOORLINESEP)) >= new_len) {
2807 			(void) mutex_unlock(&info->mutex[0]);
2808 			break;
2809 		}
2810 		new_len -= strlen(ptr);
2811 		ptr += strlen(ptr);
2812 
2813 		(void) mutex_unlock(&info->mutex[0]);
2814 	}
2815 	(void) chg_notify_statusChange(tmp_buf);
2816 }
2817 /*
2818  * It creates the header and data stream to be door returned and notify
2819  * chg_get_statusChange() threads.
2820  * This is called in removing server case.
2821  */
2822 static void
create_buf_and_notify(char * input,ns_server_status_t st)2823 create_buf_and_notify(char *input, ns_server_status_t st)
2824 {
2825 	rm_svr_t *rms = (rm_svr_t *)input;
2826 	char	*tmp_buf, *ptr, *status;
2827 	int	len, tlen;
2828 	ldap_get_change_out_t *cout;
2829 
2830 	/* IP|UP or DOWN| */
2831 	len = 2 * strlen(DOORLINESEP) + strlen(rms->addr) +
2832 	    strlen(NS_SERVER_CHANGE_UP) + 1;
2833 
2834 	tlen = sizeof (ldap_get_change_out_t) - sizeof (int) + len;
2835 
2836 	if ((tmp_buf = malloc(tlen)) == NULL)
2837 		return;
2838 
2839 	cout = (ldap_get_change_out_t *)tmp_buf;
2840 	cout->type = NS_STATUS_CHANGE_TYPE_SERVER;
2841 	/* cout->cookie is set by chg_notify_statusChange */
2842 	cout->server_count = 1;
2843 	cout->data_size = len;
2844 
2845 	/* Create IP|DOWN| */
2846 	ptr = cout->data;
2847 	if (st == NS_SERVER_UP)
2848 		status = NS_SERVER_CHANGE_UP;
2849 	else if (st == NS_SERVER_DOWN)
2850 		status = NS_SERVER_CHANGE_DOWN;
2851 
2852 	(void) snprintf(ptr, len, "%s%s%s%s",
2853 	    rms->addr, DOORLINESEP, status, DOORLINESEP);
2854 
2855 	(void) chg_notify_statusChange(tmp_buf);
2856 
2857 }
2858 
2859 /*
2860  * Return: 0 server is down, 1 server is up
2861  */
2862 static int
contact_server(char * addr)2863 contact_server(char *addr)
2864 {
2865 	char		*rootDSE = NULL;
2866 	ns_ldap_error_t	*error = NULL;
2867 	int		rc;
2868 
2869 	if (__ns_ldap_getRootDSE(addr, &rootDSE, &error,
2870 	    SA_ALLOW_FALLBACK) != NS_LDAP_SUCCESS) {
2871 		if (current_admin.debug_level >= DBG_ALL)
2872 			logit("get rootDSE %s failed. %s", addr,
2873 			    error->message ? error->message : "");
2874 		rc = 0;
2875 	} else
2876 		rc = 1;
2877 
2878 	if (rootDSE)
2879 		free(rootDSE);
2880 	if (error)
2881 		(void) __ns_ldap_freeError(&error);
2882 
2883 	return (rc);
2884 }
2885 
2886 /*
2887  * The thread is spawned to do contact_server() so it won't be blocking
2888  * getldap_serverInfo_op(INFO_OP_GETSERVER, ...) case.
2889  * After contact_server() is done, it calls
2890  * getldap_serverInfo_op(INFO_OP_REMOVESERVER, ...) to return to the remaining
2891  * program flow. It's meant to maintain the original program flow yet be
2892  * non-blocking when it's contacting server.
2893  */
2894 static void *
remove_server_thread(void * arg)2895 remove_server_thread(void *arg)
2896 {
2897 	char *addr = (char *)arg, *out = NULL;
2898 	int up;
2899 	rm_svr_t rms;
2900 
2901 	(void) pthread_setname_np(pthread_self(), "remove_server");
2902 
2903 	up = contact_server(addr);
2904 
2905 	rms.addr = addr;
2906 	rms.up = up;
2907 
2908 	(void) getldap_serverInfo_op(INFO_OP_REMOVESERVER, (char *)&rms, &out);
2909 
2910 	free(addr);
2911 
2912 	thr_exit(NULL);
2913 	return (NULL);
2914 }
2915 /*
2916  * addr is allocated and is freed by remove_server_thread
2917  * It starts a thread to contact server and remove server to avoid long wait
2918  * or recursion.
2919  */
2920 static void
remove_server(char * addr)2921 remove_server(char *addr)
2922 {
2923 	if (thr_create(NULL, 0, remove_server_thread,
2924 	    (void *)addr, THR_BOUND|THR_DETACHED, NULL) != 0) {
2925 		free(addr);
2926 		syslog(LOG_ERR, "thr_create failed for remove_server_thread");
2927 	}
2928 }
2929 /*
2930  * Compare the server_status and mark it up or down accordingly.
2931  * This is called in removing server case.
2932  */
2933 static ns_server_status_t
set_server_status(char * input,server_info_t * head)2934 set_server_status(char *input, server_info_t *head)
2935 {
2936 	rm_svr_t *rms = (rm_svr_t *)input;
2937 	ns_server_status_t changed = 0;
2938 	server_info_t *info;
2939 
2940 	for (info = head; info != NULL; info = info->next) {
2941 		(void) mutex_lock(&info->mutex[0]);
2942 		if (strcmp(info->sinfo[0].addr, rms->addr) == 0) {
2943 			if (info->sinfo[0].server_status == INFO_SERVER_UP &&
2944 			    rms->up == FALSE) {
2945 				info->sinfo[0].prev_server_status =
2946 				    info->sinfo[0].server_status;
2947 				info->sinfo[0].server_status =
2948 				    INFO_SERVER_ERROR;
2949 				info->sinfo[0].change = NS_SERVER_DOWN;
2950 				changed = NS_SERVER_DOWN;
2951 
2952 			} else if (info->sinfo[0].server_status ==
2953 			    INFO_SERVER_ERROR && rms->up == TRUE) {
2954 				/*
2955 				 * It should be INFO_SERVER_UP, but check here
2956 				 */
2957 				info->sinfo[0].prev_server_status =
2958 				    info->sinfo[0].server_status;
2959 				info->sinfo[0].server_status =
2960 				    INFO_SERVER_UP;
2961 				info->sinfo[0].change = NS_SERVER_UP;
2962 				changed = NS_SERVER_UP;
2963 			}
2964 			(void) mutex_unlock(&info->mutex[0]);
2965 			break;
2966 		}
2967 		(void) mutex_unlock(&info->mutex[0]);
2968 	}
2969 	if (changed) {
2970 		/* ldap_cachemgr -g option looks up [1] */
2971 		(void) mutex_lock(&info->mutex[1]);
2972 		info->sinfo[1].prev_server_status =
2973 		    info->sinfo[1].server_status;
2974 		if (changed == NS_SERVER_DOWN)
2975 			info->sinfo[1].server_status = INFO_SERVER_ERROR;
2976 		else if (changed == NS_SERVER_UP)
2977 			info->sinfo[1].server_status = INFO_SERVER_UP;
2978 		(void) mutex_unlock(&info->mutex[1]);
2979 	}
2980 	return (changed);
2981 }
2982