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