xref: /illumos-gate/usr/src/lib/libsldap/common/ns_common.c (revision 4db555a5389470c6f15aa8b50a38ca5d533d0641)
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 
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <stdlib.h>
29 #include <libintl.h>
30 #include <ctype.h>
31 
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <sys/socket.h>
39 #include <sys/sockio.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <net/if.h>
43 #include <netdir.h>
44 #include <lber.h>
45 #include <ldap.h>
46 
47 #include "ns_sldap.h"
48 #include "ns_internal.h"
49 #include "ns_cache_door.h"
50 
51 #define	UDP	"/dev/udp"
52 #define	MAXIFS	32
53 
54 struct ifinfo {
55 	struct in_addr addr, netmask;
56 };
57 
58 static ns_service_map ns_def_map[] = {
59 	{ "passwd",	"ou=people,",		NULL },
60 	{ "shadow",	"ou=people,",		"passwd" },
61 	{ "user_attr",	"ou=people,",		"passwd" },
62 	{ "audit_user",	"ou=people,",		"passwd" },
63 	{ "group",	"ou=group,",		NULL },
64 	{ "rpc",	"ou=rpc,",		NULL },
65 	{ "project",	"ou=projects,",		NULL },
66 	{ "protocols",	"ou=protocols,",	NULL },
67 	{ "networks",	"ou=networks,",		NULL },
68 	{ "netmasks",	"ou=networks,",		"networks" },
69 	{ "netgroup",	"ou=netgroup,",		NULL },
70 	{ "aliases",	"ou=aliases,",		NULL },
71 	{ "Hosts",	"ou=Hosts,",		NULL },
72 	{ "ipnodes",	"ou=Hosts,",		"hosts" },
73 	{ "Services",	"ou=Services,",		NULL },
74 	{ "bootparams",	"ou=ethers,",		"ethers" },
75 	{ "ethers",	"ou=ethers,",		NULL },
76 	{ "auth_attr",	"ou=SolarisAuthAttr,",	NULL },
77 	{ "prof_attr",	"ou=SolarisProfAttr,",	NULL },
78 	{ "exec_attr",	"ou=SolarisProfAttr,",	"prof_attr" },
79 	{ "profile",	"ou=profile,",		NULL },
80 	{ "printers",	"ou=printers,",		NULL },
81 	{ "automount",	"",			NULL },
82 	{ "tnrhtp",	"ou=ipTnet,",		NULL },
83 	{ "tnrhdb",	"ou=ipTnet,",		"tnrhtp" },
84 	{ NULL, NULL, NULL }
85 };
86 
87 
88 static char ** parseDN(const char *val, const char *service);
89 static char ** sortServerNet(char **srvlist);
90 static char ** sortServerPref(char **srvlist, char **preflist,
91 		boolean_t flag, int version, int *error);
92 
93 /*
94  * FUNCTION:	s_api_printResult
95  *	Given a ns_ldap_result structure print it.
96  */
97 int
98 __s_api_printResult(ns_ldap_result_t *result)
99 {
100 
101 	ns_ldap_entry_t	*curEntry;
102 	int		i, j, k = 0;
103 
104 #ifdef DEBUG
105 	(void) fprintf(stderr, "__s_api_printResult START\n");
106 #endif
107 	(void) printf("--------------------------------------\n");
108 	if (result == NULL) {
109 		(void) printf("No result\n");
110 		return (0);
111 	}
112 	(void) printf("entries_count %d\n", result->entries_count);
113 	curEntry = result->entry;
114 	for (i = 0; i < result->entries_count; i++) {
115 
116 		(void) printf("entry %d has attr_count = %d \n", i,
117 		    curEntry->attr_count);
118 		for (j = 0; j < curEntry->attr_count; j++) {
119 			(void) printf("entry %d has attr_pair[%d] = %s \n",
120 			    i, j, curEntry->attr_pair[j]->attrname);
121 			for (k = 0; k < 20 &&
122 			    curEntry->attr_pair[j]->attrvalue[k]; k++)
123 				(void) printf("entry %d has attr_pair[%d]->"
124 				    "attrvalue[%d] = %s \n", i, j, k,
125 				    curEntry->attr_pair[j]->attrvalue[k]);
126 		}
127 		(void) printf("\n--------------------------------------\n");
128 		curEntry = curEntry->next;
129 	}
130 	return (1);
131 }
132 
133 /*
134  * FUNCTION:	__s_api_getSearchScope
135  *
136  *	Retrieve the search scope for ldap search from the config module.
137  *
138  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_CONFIG
139  * INPUT:		NONE
140  * OUTPUT:		searchScope, errorp
141  */
142 int
143 __s_api_getSearchScope(
144 	int *searchScope,
145 	ns_ldap_error_t **errorp)
146 {
147 
148 	char		errmsg[MAXERROR];
149 	void		**paramVal = NULL;
150 	int		rc = 0;
151 	int		scope = 0;
152 
153 #ifdef DEBUG
154 	(void) fprintf(stderr, "__s_api_getSearchScope START\n");
155 #endif
156 	if (*searchScope == 0) {
157 		if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_SCOPE_P,
158 		    &paramVal, errorp)) != NS_LDAP_SUCCESS) {
159 			return (rc);
160 		}
161 		if (paramVal && *paramVal)
162 			scope = * (int *)(*paramVal);
163 		else
164 			scope = NS_LDAP_SCOPE_ONELEVEL;
165 		(void) __ns_ldap_freeParam(&paramVal);
166 	} else {
167 		scope = *searchScope;
168 	}
169 
170 	switch (scope) {
171 
172 		case	NS_LDAP_SCOPE_ONELEVEL:
173 			*searchScope = LDAP_SCOPE_ONELEVEL;
174 			break;
175 		case	NS_LDAP_SCOPE_BASE:
176 			*searchScope = LDAP_SCOPE_BASE;
177 			break;
178 		case	NS_LDAP_SCOPE_SUBTREE:
179 			*searchScope = LDAP_SCOPE_SUBTREE;
180 			break;
181 		default:
182 			(void) snprintf(errmsg, sizeof (errmsg),
183 			    gettext("Invalid search scope!"));
184 			MKERROR(LOG_ERR, *errorp, NS_CONFIG_FILE,
185 			    strdup(errmsg), NS_LDAP_CONFIG);
186 			return (NS_LDAP_CONFIG);
187 	}
188 
189 	return (NS_LDAP_SUCCESS);
190 }
191 
192 /*
193  * FUNCTION:	__ns_ldap_dupAuth
194  *
195  *	Duplicates an authentication structure.
196  *
197  * RETURN VALUES:	copy of authp or NULL on error
198  * INPUT:		authp
199  */
200 ns_cred_t *
201 __ns_ldap_dupAuth(const ns_cred_t *authp)
202 {
203 	ns_cred_t *ap;
204 
205 #ifdef DEBUG
206 	(void) fprintf(stderr, "__ns_ldap_dupAuth START\n");
207 #endif
208 	if (authp == NULL)
209 		return (NULL);
210 
211 	ap = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
212 	if (ap == NULL)
213 		return (NULL);
214 
215 	if (authp->hostcertpath) {
216 		ap->hostcertpath = strdup(authp->hostcertpath);
217 		if (ap->hostcertpath == NULL) {
218 			free(ap);
219 			return (NULL);
220 		}
221 	}
222 	if (authp->cred.unix_cred.userID) {
223 		ap->cred.unix_cred.userID =
224 		    strdup(authp->cred.unix_cred.userID);
225 		if (ap->cred.unix_cred.userID == NULL) {
226 			(void) __ns_ldap_freeCred(&ap);
227 			return (NULL);
228 		}
229 	}
230 	if (authp->cred.unix_cred.passwd) {
231 		ap->cred.unix_cred.passwd =
232 		    strdup(authp->cred.unix_cred.passwd);
233 		if (ap->cred.unix_cred.passwd == NULL) {
234 			(void) __ns_ldap_freeCred(&ap);
235 			return (NULL);
236 		}
237 	}
238 	if (authp->cred.cert_cred.nickname) {
239 		ap->cred.cert_cred.nickname =
240 		    strdup(authp->cred.cert_cred.nickname);
241 		if (ap->cred.cert_cred.nickname == NULL) {
242 			(void) __ns_ldap_freeCred(&ap);
243 			return (NULL);
244 		}
245 	}
246 	ap->auth.type = authp->auth.type;
247 	ap->auth.tlstype = authp->auth.tlstype;
248 	ap->auth.saslmech = authp->auth.saslmech;
249 	ap->auth.saslopt = authp->auth.saslopt;
250 	return (ap);
251 }
252 
253 /*
254  * FUNCTION:	__ns_ldap_freeUnixCred
255  *
256  *	Frees all the memory associated with a UnixCred_t structure.
257  *
258  * RETURN VALUES:	NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS
259  * INPUT:		UnixCred
260  */
261 int
262 __ns_ldap_freeUnixCred(UnixCred_t ** credp)
263 {
264 	UnixCred_t *ap;
265 
266 #ifdef DEBUG
267 	(void) fprintf(stderr, "__ns_ldap_freeUnixCred START\n");
268 #endif
269 	if (credp == NULL || *credp == NULL)
270 		return (NS_LDAP_INVALID_PARAM);
271 
272 	ap = *credp;
273 	if (ap->userID) {
274 		(void) memset(ap->userID, 0, strlen(ap->userID));
275 		free(ap->userID);
276 	}
277 
278 	if (ap->passwd) {
279 		(void) memset(ap->passwd, 0, strlen(ap->passwd));
280 		free(ap->passwd);
281 	}
282 
283 	free(ap);
284 	*credp = NULL;
285 	return (NS_LDAP_SUCCESS);
286 }
287 
288 /*
289  * FUNCTION:	__ns_ldap_freeCred
290  *
291  *	Frees all the memory associated with a ns_cred_t structure.
292  *
293  * RETURN VALUES:	NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS
294  * INPUT:		ns_cred_t
295  */
296 int
297 __ns_ldap_freeCred(ns_cred_t ** credp)
298 {
299 	ns_cred_t *ap;
300 
301 #ifdef DEBUG
302 	(void) fprintf(stderr, "__ns_ldap_freeCred START\n");
303 #endif
304 	if (credp == NULL || *credp == NULL)
305 		return (NS_LDAP_INVALID_PARAM);
306 
307 	ap = *credp;
308 	if (ap->hostcertpath) {
309 		(void) memset(ap->hostcertpath, 0,
310 		    strlen(ap->hostcertpath));
311 		free(ap->hostcertpath);
312 	}
313 
314 	if (ap->cred.unix_cred.userID) {
315 		(void) memset(ap->cred.unix_cred.userID, 0,
316 		    strlen(ap->cred.unix_cred.userID));
317 		free(ap->cred.unix_cred.userID);
318 	}
319 
320 	if (ap->cred.unix_cred.passwd) {
321 		(void) memset(ap->cred.unix_cred.passwd, 0,
322 		    strlen(ap->cred.unix_cred.passwd));
323 		free(ap->cred.unix_cred.passwd);
324 	}
325 
326 	if (ap->cred.cert_cred.nickname) {
327 		(void) memset(ap->cred.cert_cred.nickname, 0,
328 		    strlen(ap->cred.cert_cred.nickname));
329 		free(ap->cred.cert_cred.nickname);
330 	}
331 
332 	free(ap);
333 	*credp = NULL;
334 	return (NS_LDAP_SUCCESS);
335 }
336 
337 /*
338  * FUNCTION:	__s_api_is_auth_matched
339  *
340  *	Compare an authentication structure.
341  *
342  * RETURN VALUES:	B_TRUE if matched, B_FALSE otherwise.
343  * INPUT:		auth1, auth2
344  */
345 boolean_t
346 __s_api_is_auth_matched(const ns_cred_t *auth1,
347     const ns_cred_t *auth2)
348 {
349 	if ((auth1->auth.type != auth2->auth.type) ||
350 	    (auth1->auth.tlstype != auth2->auth.tlstype) ||
351 	    (auth1->auth.saslmech != auth2->auth.saslmech) ||
352 	    (auth1->auth.saslopt != auth2->auth.saslopt))
353 		return (B_FALSE);
354 
355 	if ((((auth1->auth.type == NS_LDAP_AUTH_SASL) &&
356 	    ((auth1->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
357 	    (auth1->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
358 	    (auth1->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
359 	    ((auth1->cred.unix_cred.userID == NULL) ||
360 	    (auth1->cred.unix_cred.passwd == NULL) ||
361 	    ((strcasecmp(auth1->cred.unix_cred.userID,
362 	    auth2->cred.unix_cred.userID) != 0)) ||
363 	    ((strcmp(auth1->cred.unix_cred.passwd,
364 	    auth2->cred.unix_cred.passwd) != 0))))
365 		return (B_FALSE);
366 
367 	return (B_TRUE);
368 }
369 
370 /*
371  * FUNCTION:	__s_api_getDNs
372  *
373  *	Retrieves the default base dn for the given
374  *	service.
375  *
376  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG
377  * INPUT:		service
378  * OUTPUT:		DN, error
379  */
380 typedef int (*pf)(const char *, char **, ns_ldap_error_t **);
381 int
382 __s_api_getDNs(
383 	char *** DN,
384 	const char *service,
385 	ns_ldap_error_t ** error)
386 {
387 
388 	void	**paramVal = NULL;
389 	char	**dns = NULL;
390 	int	rc = 0;
391 	int	i, len;
392 	pf	prepend_auto2dn = __s_api_prepend_automountmapname_to_dn;
393 
394 #ifdef DEBUG
395 	(void) fprintf(stderr, "__s_api_getDNs START\n");
396 #endif
397 	if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
398 	    &paramVal, error)) != NS_LDAP_SUCCESS) {
399 		return (rc);
400 	}
401 	if (!paramVal) {
402 		char errmsg[MAXERROR];
403 
404 		(void) snprintf(errmsg, sizeof (errmsg),
405 		    gettext("BaseDN not defined"));
406 		MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errmsg),
407 		    NS_LDAP_CONFIG);
408 		return (NS_LDAP_CONFIG);
409 	}
410 
411 	dns = (char **)calloc(2, sizeof (char *));
412 	if (dns == NULL) {
413 		(void) __ns_ldap_freeParam(&paramVal);
414 		return (NS_LDAP_MEMORY);
415 	}
416 
417 	if (service == NULL) {
418 		dns[0] = strdup((char *)*paramVal);
419 		if (dns[0] == NULL) {
420 			(void) __ns_ldap_freeParam(&paramVal);
421 			free(dns);
422 			return (NS_LDAP_MEMORY);
423 		}
424 	} else {
425 		for (i = 0; ns_def_map[i].service != NULL; i++) {
426 			if (strcasecmp(service,
427 			    ns_def_map[i].service) == 0) {
428 
429 				len = strlen((char *)*paramVal) +
430 				    strlen(ns_def_map[i].rdn) + 1;
431 				dns[0] = (char *)
432 				    calloc(len, sizeof (char));
433 				if (dns[0] == NULL) {
434 					(void) __ns_ldap_freeParam(
435 					    &paramVal);
436 					free(dns);
437 					return (NS_LDAP_MEMORY);
438 				}
439 				(void) strcpy(dns[0],
440 				    ns_def_map[i].rdn);
441 				(void) strcat(dns[0],
442 				    (char *)*paramVal);
443 				break;
444 			}
445 		}
446 		if (ns_def_map[i].service == NULL) {
447 			char *p = (char *)*paramVal;
448 			char *buffer = NULL;
449 			int  buflen = 0;
450 
451 			if (strchr(service, '=') == NULL) {
452 			    /* automount entries */
453 				if (strncasecmp(service, "auto_", 5) == 0) {
454 					buffer = strdup(p);
455 					if (!buffer) {
456 						free(dns);
457 						(void) __ns_ldap_freeParam(
458 						    &paramVal);
459 						return (NS_LDAP_MEMORY);
460 					}
461 					/* shorten name to avoid cstyle error */
462 					rc = prepend_auto2dn(
463 					    service, &buffer, error);
464 					if (rc != NS_LDAP_SUCCESS) {
465 						free(dns);
466 						free(buffer);
467 						(void) __ns_ldap_freeParam(
468 						    &paramVal);
469 						return (rc);
470 					}
471 				} else {
472 				/* strlen("nisMapName")+"="+","+'\0' = 13 */
473 					buflen = strlen(service) + strlen(p) +
474 					    13;
475 					buffer = (char *)malloc(buflen);
476 					if (buffer == NULL) {
477 						free(dns);
478 						(void) __ns_ldap_freeParam(
479 						    &paramVal);
480 						return (NS_LDAP_MEMORY);
481 					}
482 					(void) snprintf(buffer, buflen,
483 					    "nisMapName=%s,%s", service, p);
484 				}
485 			} else {
486 				buflen = strlen(service) + strlen(p) + 2;
487 				buffer = (char *)malloc(buflen);
488 				if (buffer == NULL) {
489 					free(dns);
490 					(void) __ns_ldap_freeParam(&paramVal);
491 					return (NS_LDAP_MEMORY);
492 				}
493 				(void) snprintf(buffer, buflen,
494 				    "%s,%s", service, p);
495 			}
496 			dns[0] = buffer;
497 		}
498 	}
499 
500 	(void) __ns_ldap_freeParam(&paramVal);
501 	*DN = dns;
502 	return (NS_LDAP_SUCCESS);
503 }
504 /*
505  * FUNCTION:	__s_api_get_search_DNs_v1
506  *
507  *	Retrieves the list of search DNS from the V1 profile for the given
508  *	service.
509  *
510  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG
511  * INPUT:		service
512  * OUTPUT:		DN, error
513  */
514 int
515 __s_api_get_search_DNs_v1(
516 	char *** DN,
517 	const char *service,
518 	ns_ldap_error_t ** error)
519 {
520 
521 	void	**paramVal = NULL;
522 	void	**temptr = NULL;
523 	char	**dns = NULL;
524 	int	rc = 0;
525 
526 	if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_DN_P,
527 	    &paramVal, error)) != NS_LDAP_SUCCESS) {
528 		return (rc);
529 	}
530 
531 	if (service && paramVal) {
532 		for (temptr = paramVal; *temptr != NULL; temptr++) {
533 			dns = parseDN((const char *)(*temptr),
534 			    (const char *)service);
535 			if (dns != NULL)
536 				break;
537 		}
538 	}
539 
540 	(void) __ns_ldap_freeParam(&paramVal);
541 	*DN = dns;
542 	return (NS_LDAP_SUCCESS);
543 
544 }
545 /*
546  * FUNCTION:	parseDN
547  *
548  *	Parse a special formated list(val) into an array of char *.
549  *
550  * RETURN VALUE:	A char * pointer to the new list of dns.
551  * INPUT:		val, service
552  */
553 static char **
554 parseDN(
555 	const char *val,
556 	const char *service)
557 {
558 
559 	size_t		len = 0;
560 	size_t		slen = 0;
561 	char		**retVal = NULL;
562 	const char	*temptr;
563 	char		*temptr2;
564 	const char	*valend;
565 	int 		valNo = 0;
566 	int		valSize = 0;
567 	int		i;
568 	char		*SSD_service = NULL;
569 
570 #ifdef DEBUG
571 	(void) fprintf(stderr, "parseDN START\n");
572 #endif
573 	if (val == NULL || *val == '\0')
574 		return (NULL);
575 	if (service == NULL || *service == '\0')
576 		return (NULL);
577 
578 	len = strlen(val);
579 	slen = strlen(service);
580 	if (strncasecmp(val, service, slen) != 0) {
581 		/*
582 		 * This routine is only called
583 		 * to process V1 profile and
584 		 * for V1 profile, map service
585 		 * to the corresponding SSD_service
586 		 * which is associated with a
587 		 * real container in the LDAP directory
588 		 * tree, e.g., map "shadow" to
589 		 * "password". See function
590 		 * __s_api_get_SSD_from_SSDtoUse_service
591 		 * for similar service to SSD_service
592 		 * mapping handling for V2 profile.
593 		 */
594 		for (i = 0; ns_def_map[i].service != NULL; i++) {
595 			if (ns_def_map[i].SSDtoUse_service &&
596 			    strcasecmp(service,
597 			    ns_def_map[i].service) == 0) {
598 				SSD_service =
599 				    ns_def_map[i].SSDtoUse_service;
600 				break;
601 			}
602 		}
603 
604 		if (SSD_service == NULL)
605 			return (NULL);
606 
607 		slen = strlen(SSD_service);
608 		if (strncasecmp(val, SSD_service, slen) != 0)
609 			return (NULL);
610 	}
611 
612 	temptr = val + slen;
613 	while (*temptr == SPACETOK || *temptr == TABTOK)
614 		temptr++;
615 	if (*temptr != COLONTOK)
616 		return (NULL);
617 
618 	while (*temptr) {
619 		temptr2 = strchr(temptr, OPARATOK);
620 		if (temptr2 == NULL)
621 			break;
622 		temptr2++;
623 		temptr2 = strchr(temptr2, CPARATOK);
624 		if (temptr2 == NULL)
625 			break;
626 		valNo++;
627 		temptr = temptr2+1;
628 	}
629 
630 	retVal = (char **)calloc(valNo +1, sizeof (char *));
631 	if (retVal == NULL)
632 		return (NULL);
633 
634 	temptr = val;
635 	valend = val+len;
636 
637 	for (i = 0; (i < valNo) && (temptr < valend); i++) {
638 		temptr = strchr(temptr, OPARATOK);
639 		if (temptr == NULL) {
640 			__s_api_free2dArray(retVal);
641 			return (NULL);
642 		}
643 		temptr++;
644 		temptr2 = strchr(temptr, CPARATOK);
645 		if (temptr2 == NULL) {
646 			__s_api_free2dArray(retVal);
647 			return (NULL);
648 		}
649 		valSize = temptr2 - temptr;
650 
651 		retVal[i] = (char *)calloc(valSize + 1, sizeof (char));
652 		if (retVal[i] == NULL) {
653 			__s_api_free2dArray(retVal);
654 			return (NULL);
655 		}
656 		(void) strncpy(retVal[i], temptr, valSize);
657 		retVal[i][valSize] = '\0';
658 		temptr = temptr2 + 1;
659 	}
660 
661 	return (retVal);
662 }
663 
664 
665 /*
666  * __s_api_get_local_interfaces
667  *
668  * Returns a pointer to an array of addresses and netmasks of all interfaces
669  * configured on the system.
670  *
671  * NOTE: This function is very IPv4 centric.
672  */
673 static struct ifinfo *
674 __s_api_get_local_interfaces()
675 {
676 	struct ifconf		ifc;
677 	struct ifreq		ifreq, *ifr;
678 	struct ifinfo		*localinfo;
679 	struct in_addr		netmask;
680 	struct sockaddr_in	*sin;
681 	void			*buf = NULL;
682 	int			fd = 0;
683 	int			numifs = 0;
684 	int			i, n = 0;
685 
686 	if ((fd = open(UDP, O_RDONLY)) < 0)
687 		return ((struct ifinfo *)NULL);
688 
689 	if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0) {
690 		numifs = MAXIFS;
691 	}
692 
693 	buf = malloc(numifs * sizeof (struct ifreq));
694 	if (buf == NULL) {
695 		(void) close(fd);
696 		return ((struct ifinfo *)NULL);
697 	}
698 	ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
699 	ifc.ifc_buf = buf;
700 	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
701 		(void) close(fd);
702 		free(buf);
703 		buf = NULL;
704 		return ((struct ifinfo *)NULL);
705 	}
706 	ifr = (struct ifreq *)buf;
707 	numifs = ifc.ifc_len/(int)sizeof (struct ifreq);
708 	localinfo = (struct ifinfo *)malloc((numifs + 1) *
709 	    sizeof (struct ifinfo));
710 	if (localinfo == NULL) {
711 		(void) close(fd);
712 		free(buf);
713 		buf = NULL;
714 		return ((struct ifinfo *)NULL);
715 	}
716 
717 	for (i = 0, n = numifs; n > 0; n--, ifr++) {
718 		uint_t ifrflags;
719 
720 		ifreq = *ifr;
721 		if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0)
722 			continue;
723 
724 		ifrflags = ifreq.ifr_flags;
725 		if (((ifrflags & IFF_UP) == 0) ||
726 		    (ifr->ifr_addr.sa_family != AF_INET))
727 			continue;
728 
729 		if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifreq) < 0)
730 			continue;
731 		netmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
732 
733 		if (ioctl(fd, SIOCGIFADDR, (char *)&ifreq) < 0)
734 			continue;
735 
736 		sin = (struct sockaddr_in *)&ifreq.ifr_addr;
737 
738 		localinfo[i].addr = sin->sin_addr;
739 		localinfo[i].netmask = netmask;
740 		i++;
741 	}
742 	localinfo[i].addr.s_addr = 0;
743 
744 	free(buf);
745 	buf = NULL;
746 	(void) close(fd);
747 	return (localinfo);
748 }
749 
750 
751 /*
752  * __s_api_samenet(char *, struct ifinfo *)
753  *
754  * Returns 1 if address is on the same subnet of the array of addresses
755  * passed in.
756  *
757  * NOTE: This function is only valid for IPv4 addresses.
758  */
759 static int
760 __s_api_IPv4sameNet(char *addr, struct ifinfo *ifs)
761 {
762 	int		answer = 0;
763 
764 	if (addr && ifs) {
765 		char		*addr_raw;
766 		unsigned long	iaddr;
767 		int		i;
768 
769 		if ((addr_raw = strdup(addr)) != NULL) {
770 			char	*s;
771 
772 			/* Remove port number. */
773 			if ((s = strchr(addr_raw, ':')) != NULL)
774 				*s = '\0';
775 
776 			iaddr = inet_addr(addr_raw);
777 
778 			/* Loop through interface list to find match. */
779 			for (i = 0; ifs[i].addr.s_addr != 0; i++) {
780 				if ((iaddr & ifs[i].netmask.s_addr) ==
781 				    (ifs[i].addr.s_addr &
782 				    ifs[i].netmask.s_addr))
783 					answer++;
784 			}
785 			free(addr_raw);
786 		}
787 	}
788 
789 	return (answer);
790 }
791 
792 /*
793  * FUNCTION:	__s_api_getServers
794  *
795  *	Retrieve a list of ldap servers from the config module.
796  *
797  * RETURN VALUE:	NS_LDAP_SUCCESS, NS_LDAP_CONFIG, NS_LDAP_MEMORY
798  * INPUT:		NONE
799  * OUTPUT:		servers, error
800  */
801 int
802 __s_api_getServers(
803 		char *** servers,
804 		ns_ldap_error_t ** error)
805 {
806 	void	**paramVal = NULL;
807 	char	errmsg[MAXERROR];
808 	char	**sortServers = NULL;
809 	char	**netservers = NULL;
810 	int	rc = 0, err = NS_LDAP_CONFIG, version = 1;
811 	const 	char	*str, *str1;
812 
813 #ifdef DEBUG
814 	(void) fprintf(stderr, "__s_api_getServers START\n");
815 #endif
816 	*servers = NULL;
817 	/* get profile version number */
818 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
819 	    &paramVal, error)) != NS_LDAP_SUCCESS)
820 		return (rc);
821 
822 	if (paramVal == NULL || *paramVal == NULL) {
823 		(void) snprintf(errmsg, sizeof (errmsg),
824 		    gettext("No file version"));
825 		MKERROR(LOG_INFO, *error, NS_CONFIG_FILE, strdup(errmsg),
826 		    NS_LDAP_CONFIG);
827 		return (NS_LDAP_CONFIG);
828 	}
829 
830 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
831 		version = 1;
832 	else if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_2) == 0)
833 		version = 2;
834 
835 	(void) __ns_ldap_freeParam(&paramVal);
836 	paramVal = NULL;
837 
838 	if ((rc = __ns_ldap_getParam(NS_LDAP_SERVERS_P,
839 	    &paramVal, error)) != NS_LDAP_SUCCESS)
840 		return (rc);
841 
842 	/*
843 	 * For version 2, default server list could be
844 	 * empty.
845 	 */
846 	if ((paramVal == NULL || (char *)*paramVal == NULL) &&
847 	    version == 1) {
848 		str = NULL_OR_STR(__s_api_get_configname(NS_LDAP_SERVERS_P));
849 		(void) snprintf(errmsg, sizeof (errmsg),
850 		    gettext("Unable to retrieve the '%s' list"), str);
851 		MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE, strdup(errmsg),
852 		    NS_LDAP_CONFIG);
853 		return (NS_LDAP_CONFIG);
854 	}
855 
856 	/*
857 	 * Get server address(es) and go through them.
858 	 */
859 	*servers = (char **)paramVal;
860 	paramVal = NULL;
861 
862 	/* Sort servers based on network. */
863 	if (*servers) {
864 		netservers = sortServerNet(*servers);
865 		if (netservers) {
866 			free(*servers);
867 			*servers = netservers;
868 		} else {
869 			return (NS_LDAP_MEMORY);
870 		}
871 	}
872 
873 	/* Get preferred server list and sort servers based on that. */
874 	if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
875 	    &paramVal, error)) != NS_LDAP_SUCCESS) {
876 		if (*servers)
877 			__s_api_free2dArray(*servers);
878 		*servers = NULL;
879 		return (rc);
880 	}
881 
882 	if (paramVal != NULL) {
883 		char **prefServers;
884 		void **val = NULL;
885 
886 		if ((rc =  __ns_ldap_getParam(NS_LDAP_PREF_ONLY_P,
887 		    &val, error)) != NS_LDAP_SUCCESS) {
888 				if (*servers)
889 					__s_api_free2dArray(*servers);
890 				*servers = NULL;
891 			(void) __ns_ldap_freeParam(&paramVal);
892 			return (rc);
893 		}
894 
895 		prefServers = (char **)paramVal;
896 		paramVal = NULL;
897 		if (prefServers) {
898 			if (val != NULL && (*val) != NULL &&
899 			    *(int *)val[0] == 1)
900 				sortServers = sortServerPref(*servers,
901 				    prefServers, B_FALSE, version,
902 				    &err);
903 			else
904 				sortServers = sortServerPref(*servers,
905 				    prefServers, B_TRUE, version,
906 				    &err);
907 			if (sortServers) {
908 				if (*servers)
909 					free(*servers);
910 				*servers = NULL;
911 				free(prefServers);
912 				prefServers = NULL;
913 				*servers = sortServers;
914 			} else {
915 				if (*servers)
916 					__s_api_free2dArray(*servers);
917 				*servers = NULL;
918 				__s_api_free2dArray(prefServers);
919 				prefServers = NULL;
920 			}
921 		}
922 		(void) __ns_ldap_freeParam(&val);
923 	}
924 	(void) __ns_ldap_freeParam(&paramVal);
925 
926 	if (*servers == NULL) {
927 		if (err == NS_LDAP_CONFIG) {
928 		str = NULL_OR_STR(__s_api_get_configname(
929 		    NS_LDAP_SERVERS_P));
930 		str1 = NULL_OR_STR(__s_api_get_configname(
931 		    NS_LDAP_SERVER_PREF_P));
932 			(void) snprintf(errmsg, sizeof (errmsg),
933 			    gettext("Unable to generate a new server list "
934 			    "based on '%s' and/or '%s'"), str, str1);
935 			MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE,
936 			    strdup(errmsg), err);
937 			return (err);
938 		}
939 		return (NS_LDAP_MEMORY);
940 	}
941 
942 	return (NS_LDAP_SUCCESS);
943 
944 }
945 
946 /*
947  * FUNCTION:	sortServerNet
948  *	Sort the serverlist based on the distance from client as long
949  *	as the list only contains IPv4 addresses.  Otherwise do nothing.
950  */
951 static char **
952 sortServerNet(char **srvlist)
953 {
954 	int		count = 0;
955 	int		all = 0;
956 	int		ipv4only = 1;
957 	struct ifinfo	*ifs = __s_api_get_local_interfaces();
958 	char		**tsrvs;
959 	char		**psrvs, **retsrvs;
960 
961 	/* Sanity check. */
962 	if (srvlist == NULL || srvlist[0] == NULL)
963 		return (NULL);
964 
965 	/* Count the number of servers to sort. */
966 	for (count = 0; srvlist[count] != NULL; count++) {
967 		if (!__s_api_isipv4(srvlist[count]))
968 			ipv4only = 0;
969 	}
970 	count++;
971 
972 	/* Make room for the returned list of servers. */
973 	retsrvs = (char **)calloc(count, sizeof (char *));
974 	if (retsrvs == NULL) {
975 		free(ifs);
976 		ifs = NULL;
977 		return (NULL);
978 	}
979 
980 	retsrvs[count - 1] = NULL;
981 
982 	/* Make a temporary list of servers. */
983 	psrvs = (char **)calloc(count, sizeof (char *));
984 	if (psrvs == NULL) {
985 		free(ifs);
986 		ifs = NULL;
987 		free(retsrvs);
988 		retsrvs = NULL;
989 		return (NULL);
990 	}
991 
992 	/* Filter servers on the same subnet */
993 	tsrvs = srvlist;
994 	while (*tsrvs) {
995 		if (ipv4only && __s_api_IPv4sameNet(*tsrvs, ifs)) {
996 			psrvs[all] = *tsrvs;
997 			retsrvs[all++] = *(tsrvs);
998 		}
999 		tsrvs++;
1000 	}
1001 
1002 	/* Filter remaining servers. */
1003 	tsrvs = srvlist;
1004 	while (*tsrvs) {
1005 		char	**ttsrvs = psrvs;
1006 
1007 		while (*ttsrvs) {
1008 			if (strcmp(*tsrvs, *ttsrvs) == 0)
1009 				break;
1010 			ttsrvs++;
1011 		}
1012 
1013 		if (*ttsrvs == NULL)
1014 			retsrvs[all++] = *(tsrvs);
1015 		tsrvs++;
1016 	}
1017 
1018 	free(ifs);
1019 	ifs = NULL;
1020 	free(psrvs);
1021 	psrvs = NULL;
1022 
1023 	return (retsrvs);
1024 }
1025 
1026 /*
1027  * FUNCTION:	sortServerPref
1028  *	Sort the serverlist based on the preferred server list.
1029  *
1030  * The sorting algorithm works as follows:
1031  *
1032  * If version 1, if flag is TRUE, find all the servers in both preflist
1033  * and srvlist, then append other servers in srvlist to this list
1034  * and return the list.
1035  * If flag is FALSE, just return srvlist.
1036  * srvlist can not be empty.
1037  *
1038  * If version 2, append all the servers in srvlist
1039  * but not in preflist to preflist, and return the merged list.
1040  * If srvlist is empty, just return preflist.
1041  * If preflist is empty, just return srvlist.
1042  */
1043 static char **
1044 sortServerPref(char **srvlist, char **preflist,
1045 		boolean_t flag, int version, int *error)
1046 {
1047 	int		i, scount = 0, pcount = 0;
1048 	int		all = 0, dup = 0;
1049 	char		**tsrvs;
1050 	char		**retsrvs;
1051 	char		**dupsrvs;
1052 
1053 	/* Count the number of servers to sort. */
1054 	if (srvlist && srvlist[0])
1055 		for (i = 0; srvlist[i] != NULL; i++)
1056 			scount++;
1057 
1058 	/* Sanity check. */
1059 	if (scount == 0 && version == 1) {
1060 		*error = NS_LDAP_CONFIG;
1061 		return (NULL);
1062 	}
1063 
1064 	/* Count the number of preferred servers */
1065 	if (preflist && preflist[0])
1066 		for (i = 0; preflist[i] != NULL; i++)
1067 			pcount++;
1068 
1069 	/* Sanity check. */
1070 	if (scount == 0 && pcount == 0) {
1071 		*error = NS_LDAP_CONFIG;
1072 		return (NULL);
1073 	}
1074 
1075 	/* Make room for the returned list of servers */
1076 	retsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *));
1077 	if (retsrvs == NULL) {
1078 		*error = NS_LDAP_MEMORY;
1079 		return (NULL);
1080 	}
1081 
1082 	/*
1083 	 * if the preferred server list is empty,
1084 	 * just return a copy of the server list
1085 	 */
1086 	if (pcount == 0) {
1087 		tsrvs = srvlist;
1088 		while (*tsrvs)
1089 			retsrvs[all++] = *(tsrvs++);
1090 		return (retsrvs);
1091 	}
1092 	all = 0;
1093 
1094 	/*
1095 	 * if the server list is empty,
1096 	 * just return a copy of the preferred server list
1097 	 */
1098 	if (scount == 0) {
1099 		tsrvs = preflist;
1100 		while (*tsrvs)
1101 			retsrvs[all++] = *(tsrvs++);
1102 		return (retsrvs);
1103 	}
1104 	all = 0;
1105 
1106 	/* Make room for the servers whose memory needs to be freed */
1107 	dupsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *));
1108 	if (dupsrvs == NULL) {
1109 		free(retsrvs);
1110 		*error = NS_LDAP_MEMORY;
1111 		return (NULL);
1112 	}
1113 
1114 	/*
1115 	 * If version 1,
1116 	 * throw out preferred servers not on server list.
1117 	 * If version 2, make a copy of the preferred server list.
1118 	 */
1119 	if (version == 1) {
1120 		tsrvs = preflist;
1121 		while (*tsrvs) {
1122 			char	**ttsrvs = srvlist;
1123 
1124 			while (*ttsrvs) {
1125 				if (strcmp(*tsrvs, *(ttsrvs)) == 0)
1126 					break;
1127 				ttsrvs++;
1128 			}
1129 			if (*ttsrvs != NULL)
1130 				retsrvs[all++] = *tsrvs;
1131 			else
1132 				dupsrvs[dup++] = *tsrvs;
1133 			tsrvs++;
1134 		}
1135 	} else {
1136 		tsrvs = preflist;
1137 		while (*tsrvs)
1138 			retsrvs[all++] = *(tsrvs++);
1139 	}
1140 	/*
1141 	 * If version 1,
1142 	 * if PREF_ONLY is false, we append the non-preferred servers
1143 	 * to bottom of list.
1144 	 * For version 2, always append.
1145 	 */
1146 	if (flag == B_TRUE || version != 1) {
1147 
1148 		tsrvs = srvlist;
1149 		while (*tsrvs) {
1150 			char	**ttsrvs = preflist;
1151 
1152 			while (*ttsrvs) {
1153 				if (strcmp(*tsrvs, *ttsrvs) == 0) {
1154 					break;
1155 				}
1156 				ttsrvs++;
1157 			}
1158 			if (*ttsrvs == NULL)
1159 				retsrvs[all++] = *tsrvs;
1160 			else
1161 				dupsrvs[dup++] = *tsrvs;
1162 			tsrvs++;
1163 		}
1164 	}
1165 
1166 	/* free memory for duplicate servers */
1167 	if (dup) {
1168 		for (tsrvs = dupsrvs; *tsrvs; tsrvs++)
1169 			free(*tsrvs);
1170 	}
1171 	free(dupsrvs);
1172 
1173 	return (retsrvs);
1174 }
1175 
1176 /*
1177  * FUNCTION:	__s_api_removeBadServers
1178  *	Contacts the ldap cache manager for marking the
1179  *	problem servers as down, so that the server is
1180  *	not contacted until the TTL expires.
1181  */
1182 void
1183 __s_api_removeBadServers(char ** Servers)
1184 {
1185 
1186 	char	**host;
1187 
1188 	if (Servers == NULL)
1189 		return;
1190 
1191 	for (host = Servers; *host != NULL; host++) {
1192 		if (__s_api_removeServer(*host) < 0) {
1193 			/*
1194 			 * Couldn't remove server from
1195 			 * server list. Log a warning.
1196 			 */
1197 			syslog(LOG_WARNING, "libsldap: could "
1198 			    "not remove %s from servers list", *host);
1199 		}
1200 	}
1201 }
1202 
1203 /*
1204  * FUNCTION:	__s_api_free2dArray
1205  */
1206 void
1207 __s_api_free2dArray(char ** inarray)
1208 {
1209 
1210 	char	**temptr;
1211 
1212 	if (inarray == NULL)
1213 		return;
1214 
1215 	for (temptr = inarray; *temptr != NULL; temptr++) {
1216 		free(*temptr);
1217 	}
1218 	free(inarray);
1219 }
1220 
1221 /*
1222  * FUNCTION:	__s_api_cp2dArray
1223  */
1224 char **
1225 __s_api_cp2dArray(char **inarray)
1226 {
1227 	char	**newarray;
1228 	char	 **ttarray, *ret;
1229 	int	count;
1230 
1231 	if (inarray == NULL)
1232 		return (NULL);
1233 
1234 	for (count = 0; inarray[count] != NULL; count++)
1235 		;
1236 
1237 	newarray = (char **)calloc(count + 1, sizeof (char *));
1238 	if (newarray == NULL)
1239 		return (NULL);
1240 
1241 	ttarray = newarray;
1242 	for (; *inarray; inarray++) {
1243 		*(ttarray++) = ret = strdup(*inarray);
1244 		if (ret == NULL) {
1245 			__s_api_free2dArray(newarray);
1246 			return (NULL);
1247 		}
1248 	}
1249 	return (newarray);
1250 }
1251 
1252 /*
1253  * FUNCTION:	__s_api_isCtrlSupported
1254  *	Determines if the passed control is supported by the LDAP sever.
1255  * RETURNS:	NS_LDAP_SUCCESS if yes, NS_LDAP_OP_FAIL if not.
1256  */
1257 int
1258 __s_api_isCtrlSupported(Connection *con, char *ctrlString)
1259 {
1260 	char		**ctrl;
1261 	int		len;
1262 
1263 	len = strlen(ctrlString);
1264 	for (ctrl = con->controls; ctrl && *ctrl; ctrl++) {
1265 		if (strncasecmp(*ctrl, ctrlString, len) == 0)
1266 			return (NS_LDAP_SUCCESS);
1267 	}
1268 	return (NS_LDAP_OP_FAILED);
1269 }
1270 
1271 /*
1272  * FUNCTION:	__s_api_toFollowReferrals
1273  *	Determines if need to follow referral for an SLDAP API.
1274  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_INVALID_PARAM, or
1275  *			other rc from __ns_ldap_getParam()
1276  * INPUT:		flags
1277  * OUTPUT:		toFollow, errorp
1278  */
1279 int
1280 __s_api_toFollowReferrals(const int flags,
1281 	int *toFollow,
1282 	ns_ldap_error_t **errorp)
1283 {
1284 	void		**paramVal = NULL;
1285 	int		rc = 0;
1286 	int		iflags = 0;
1287 
1288 #ifdef DEBUG
1289 	(void) fprintf(stderr, "__s_api_toFollowReferrals START\n");
1290 #endif
1291 
1292 	/* Either NS_LDAP_NOREF or NS_LDAP_FOLLOWREF not both */
1293 	if ((flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) ==
1294 	    (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) {
1295 		return (NS_LDAP_INVALID_PARAM);
1296 	}
1297 
1298 	/*
1299 	 * if the NS_LDAP_NOREF or NS_LDAP_FOLLOWREF is set
1300 	 * this will take precendence over the values specified
1301 	 * in the configuration file
1302 	 */
1303 	if (flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) {
1304 			iflags = flags;
1305 	} else {
1306 		rc = __ns_ldap_getParam(NS_LDAP_SEARCH_REF_P,
1307 		    &paramVal, errorp);
1308 		if (rc != NS_LDAP_SUCCESS)
1309 			return (rc);
1310 		if (paramVal == NULL || *paramVal == NULL) {
1311 			(void) __ns_ldap_freeParam(&paramVal);
1312 			if (*errorp)
1313 				(void) __ns_ldap_freeError(errorp);
1314 			*toFollow = TRUE;
1315 			return (NS_LDAP_SUCCESS);
1316 		}
1317 		iflags = (* (int *)(*paramVal));
1318 		(void) __ns_ldap_freeParam(&paramVal);
1319 	}
1320 
1321 	if (iflags & NS_LDAP_NOREF)
1322 		*toFollow = FALSE;
1323 	else
1324 		*toFollow = TRUE;
1325 
1326 	return (NS_LDAP_SUCCESS);
1327 }
1328 
1329 /*
1330  * FUNCTION:	__s_api_addRefInfo
1331  *	Insert a referral info into a referral info list.
1332  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_OP_FAILED
1333  * INPUT:		LDAP URL, pointer to the referral info list,
1334  *                      search baseDN, search scope, search filter,
1335  *                      previous connection
1336  */
1337 int
1338 __s_api_addRefInfo(ns_referral_info_t **head, char *url,
1339 			char *baseDN, int *scope,
1340 			char *filter, LDAP *ld)
1341 {
1342 	char			errmsg[MAXERROR], *tmp;
1343 	ns_referral_info_t	*ref, *tmpref;
1344 	LDAPURLDesc		*ludp = NULL;
1345 	int			hostlen;
1346 	char *ld_defhost = NULL;
1347 
1348 #ifdef DEBUG
1349 	(void) fprintf(stderr, "__s_api_addRefInfo START\n");
1350 #endif
1351 
1352 	/* sanity check */
1353 	if (head == NULL)
1354 		return (NS_LDAP_OP_FAILED);
1355 
1356 	/*
1357 	 * log error and return NS_LDAP_SUCCESS
1358 	 * if one of the following:
1359 	 * 1. non-LDAP URL
1360 	 * 2. LDAP URL which can not be parsed
1361 	 */
1362 	if (!ldap_is_ldap_url(url) ||
1363 	    ldap_url_parse_nodn(url, &ludp) != 0) {
1364 		(void) snprintf(errmsg, MAXERROR, "%s: %s",
1365 		    gettext("Invalid or non-LDAP URL when"
1366 		    " processing referrals URL"),
1367 		    url);
1368 		syslog(LOG_ERR, "libsldap: %s", errmsg);
1369 		if (ludp)
1370 				ldap_free_urldesc(ludp);
1371 		return (NS_LDAP_SUCCESS);
1372 	}
1373 
1374 	ref = (ns_referral_info_t *)calloc(1,
1375 	    sizeof (ns_referral_info_t));
1376 	if (ref == NULL) {
1377 		ldap_free_urldesc(ludp);
1378 		return (NS_LDAP_MEMORY);
1379 	}
1380 
1381 	/*
1382 	 * we do have a valid URL and we were able to parse it
1383 	 * however, we still need to find out what hostport to
1384 	 * use if none were provided in the LDAP URL
1385 	 * (e.g., ldap:///...)
1386 	 */
1387 	if ((ludp->lud_port == 0) && (ludp->lud_host == NULL)) {
1388 		if (ld == NULL) {
1389 			(void) snprintf(errmsg, MAXERROR, "%s: %s",
1390 			    gettext("no LDAP handle when"
1391 			    " processing referrals URL"),
1392 			    url);
1393 			syslog(LOG_WARNING, "libsldap: %s", errmsg);
1394 			ldap_free_urldesc(ludp);
1395 			free(ref);
1396 			return (NS_LDAP_SUCCESS);
1397 		} else {
1398 			(void) ldap_get_option(ld, LDAP_OPT_HOST_NAME,
1399 			    &ld_defhost);
1400 			if (ld_defhost == NULL) {
1401 				(void) snprintf(errmsg, MAXERROR, "%s: %s",
1402 				    gettext("not able to retrieve default "
1403 				    "host when processing "
1404 				    "referrals URL"),
1405 				    url);
1406 				syslog(LOG_WARNING, "libsldap: %s", errmsg);
1407 				ldap_free_urldesc(ludp);
1408 				free(ref);
1409 				return (NS_LDAP_SUCCESS);
1410 			} else {
1411 				ref->refHost = strdup(ld_defhost);
1412 				if (ref->refHost == NULL) {
1413 					ldap_free_urldesc(ludp);
1414 					free(ref);
1415 					return (NS_LDAP_MEMORY);
1416 				}
1417 			}
1418 		}
1419 	} else {
1420 		/*
1421 		 * add 4 here:
1422 		 * 1 for the last '\0'.
1423 		 * 1 for host and prot separator ":"
1424 		 * and "[" & "]" for possible ipV6 addressing
1425 		 */
1426 		hostlen = strlen(ludp->lud_host) +
1427 		    sizeof (MAXPORTNUMBER_STR) + 4;
1428 		ref->refHost = (char *)malloc(hostlen);
1429 		if (ref->refHost == NULL) {
1430 			ldap_free_urldesc(ludp);
1431 			free(ref);
1432 			return (NS_LDAP_MEMORY);
1433 		}
1434 
1435 		if (ludp->lud_port != 0) {
1436 			/*
1437 			 * serverAddr = host:port
1438 			 * or
1439 			 * if host is an IPV6 address
1440 			 * [host]:port
1441 			 */
1442 			tmp = strstr(url, ludp->lud_host);
1443 			if (tmp && (tmp > url) && *(tmp - 1) == '[') {
1444 				(void) snprintf(ref->refHost, hostlen,
1445 				    "[%s]:%d",
1446 				    ludp->lud_host,
1447 				    ludp->lud_port);
1448 			} else {
1449 				(void) snprintf(ref->refHost, hostlen,
1450 				    "%s:%d",
1451 				    ludp->lud_host,
1452 				    ludp->lud_port);
1453 			}
1454 		} else {
1455 			/* serverAddr = host */
1456 			(void) snprintf(ref->refHost, hostlen, "%s",
1457 			    ludp->lud_host);
1458 		}
1459 	}
1460 
1461 	if (ludp->lud_dn) {
1462 		ref->refDN = strdup(ludp->lud_dn);
1463 		if (ref->refDN == NULL) {
1464 			ldap_free_urldesc(ludp);
1465 			free(ref->refHost);
1466 			free(ref);
1467 			return (NS_LDAP_MEMORY);
1468 		}
1469 	} else {
1470 		if (baseDN) {
1471 			ref->refDN = strdup(baseDN);
1472 			if (ref->refDN == NULL) {
1473 				ldap_free_urldesc(ludp);
1474 				free(ref->refHost);
1475 				free(ref);
1476 				return (NS_LDAP_MEMORY);
1477 			}
1478 		}
1479 	}
1480 
1481 	if (filter)
1482 		ref->refFilter = strdup(filter);
1483 	else if (ludp->lud_filter)
1484 		ref->refFilter = strdup(ludp->lud_filter);
1485 	else
1486 		ref->refFilter = strdup("");
1487 
1488 	if (ref->refFilter == NULL) {
1489 		ldap_free_urldesc(ludp);
1490 		free(ref->refHost);
1491 		if (ref->refDN)
1492 			free(ref->refDN);
1493 		free(ref);
1494 		return (NS_LDAP_MEMORY);
1495 	}
1496 
1497 	/*
1498 	 * If the scope is specified in the URL use it.
1499 	 * Note if the scope is missing in the URL, ldap_url_parse_nodn()
1500 	 * returns the scope BASE. We need to check that the scope of BASE
1501 	 * is actually present in the URL.
1502 	 * If the scope is missing in the URL then use the passed-in
1503 	 * scope.
1504 	 * If there is no passed-in scope, then use the scope SUBTREE.
1505 	 */
1506 	if (ludp->lud_dn && ludp->lud_scope != LDAP_SCOPE_BASE)
1507 		ref->refScope = ludp->lud_scope;
1508 	else if (ludp->lud_dn && strstr(url, "?base"))
1509 		ref->refScope = LDAP_SCOPE_BASE;
1510 	else if (scope)
1511 		ref->refScope = *scope;
1512 	else
1513 		ref->refScope = LDAP_SCOPE_SUBTREE;
1514 
1515 	ref->next = NULL;
1516 
1517 	ldap_free_urldesc(ludp);
1518 
1519 	/* insert the referral info */
1520 	if (*head) {
1521 		for (tmpref = *head; tmpref->next; tmpref = tmpref->next)
1522 			;
1523 		tmpref->next = ref;
1524 	} else
1525 		*head = ref;
1526 
1527 	return (NS_LDAP_SUCCESS);
1528 }
1529 
1530 /*
1531  * FUNCTION:	__s_api_deleteRefInfo
1532  *	Delete a referral info list.
1533  * INPUT:		pointer to the referral info list
1534  */
1535 void
1536 __s_api_deleteRefInfo(ns_referral_info_t *head)
1537 {
1538 	ns_referral_info_t	*ref, *tmp;
1539 
1540 #ifdef DEBUG
1541 	(void) fprintf(stderr, "__s_api_deleteRefInfo START\n");
1542 #endif
1543 
1544 	for (ref = head; ref; ) {
1545 		if (ref->refHost)
1546 			free(ref->refHost);
1547 		if (ref->refDN)
1548 			free(ref->refDN);
1549 		if (ref->refFilter)
1550 			free(ref->refFilter);
1551 		tmp = ref->next;
1552 		free(ref);
1553 		ref = tmp;
1554 	}
1555 
1556 }
1557 
1558 /*
1559  * FUNCTION:	__s_api_get_SSD_from_SSDtoUse_service
1560  *
1561  *	Retrieves the Service Search Descriptors which should be used for
1562  *	the given service. For example, return all the "passwd" SSDs for
1563  *	service "shadow" if no SSD is defined for service "shadow" and
1564  *	no filter component is defined in all the "passwd" SSDs. This idea
1565  *	of sharing the SSDs defined for some other service is to reduce the
1566  *	configuration complexity. For a service, which does not have its own
1567  *	entries in the LDAP directory, SSD for it is useless, and should not
1568  *	be set. But since this service must share the container with at least
1569  *	one other service which does have it own entries, the SSD for
1570  *	this other service will be shared by this service.
1571  *	This other service is called the SSD-to-use service.
1572  *	The static data structure, ns_def_map[], in this file
1573  *	defines the SSD-to-use service for all the services supported.
1574  *
1575  * RETURN VALUES:	NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_INVALID_PARAM
1576  * INPUT:		service
1577  * OUTPUT:		*SSDlist, *errorp if error
1578  */
1579 int
1580 __s_api_get_SSD_from_SSDtoUse_service(const char *service,
1581 		ns_ldap_search_desc_t ***SSDlist,
1582 		ns_ldap_error_t **errorp)
1583 {
1584 	int 			i, rc;
1585 	int 			found = FALSE;
1586 	int 			filter_found = FALSE;
1587 	char			*SSD_service = NULL;
1588 	char			errmsg[MAXERROR];
1589 	ns_ldap_search_desc_t	**sdlist;
1590 	int			auto_service = FALSE;
1591 
1592 #ifdef DEBUG
1593 	(void) fprintf(stderr,
1594 	    "__s_api_get_SSD_from_SSDtoUse_service START\n");
1595 #endif
1596 
1597 	if (SSDlist == NULL || errorp == NULL)
1598 		return (NS_LDAP_INVALID_PARAM);
1599 
1600 	*SSDlist = NULL;
1601 	*errorp = NULL;
1602 
1603 	if (service == NULL)
1604 		return (NS_LDAP_SUCCESS);
1605 
1606 	if (strncasecmp(service, "auto_", 5) == 0)
1607 		auto_service = TRUE;
1608 
1609 	/*
1610 	 * First try to return the configured SSDs for the input server
1611 	 */
1612 	rc = __ns_ldap_getSearchDescriptors(service, SSDlist, errorp);
1613 	if (rc != NS_LDAP_SUCCESS)
1614 		return (rc);
1615 	else {
1616 		if (*SSDlist != NULL)
1617 			return (NS_LDAP_SUCCESS);
1618 	}
1619 
1620 	/*
1621 	 * If service == auto_* and SSD is not found,
1622 	 * then try automount to see if there is an SSD
1623 	 * for automount.
1624 	 */
1625 
1626 	if (auto_service) {
1627 		rc = __ns_ldap_getSearchDescriptors(
1628 		    "automount", SSDlist, errorp);
1629 		if (rc != NS_LDAP_SUCCESS)
1630 			return (rc);
1631 		else {
1632 			if (*SSDlist != NULL) {
1633 				/*
1634 				 * If SSDlist is found,
1635 				 * prepend automountMapName to the basedn
1636 				 * in the SSDlist
1637 				 *
1638 				 */
1639 				rc = __s_api_prepend_automountmapname(
1640 				    service,
1641 				    SSDlist,
1642 				    errorp);
1643 
1644 				if (rc != NS_LDAP_SUCCESS) {
1645 					(void) __ns_ldap_freeSearchDescriptors(
1646 					    SSDlist);
1647 					*SSDlist = NULL;
1648 				}
1649 
1650 				return (rc);
1651 			}
1652 		}
1653 	}
1654 
1655 	/*
1656 	 * Find the SSDtoUse service.
1657 	 * If none found, flag "found" remains FALSE.
1658 	 */
1659 	for (i = 0; ns_def_map[i].service != NULL; i++) {
1660 		if (ns_def_map[i].SSDtoUse_service &&
1661 		    strcasecmp(service,
1662 		    ns_def_map[i].service) == 0) {
1663 			found = TRUE;
1664 			SSD_service = ns_def_map[i].SSDtoUse_service;
1665 			break;
1666 		}
1667 	}
1668 
1669 	if (!found)
1670 		return (NS_LDAP_SUCCESS);
1671 
1672 	/*
1673 	 * return the SSDs for SSD_service only if no optional filter
1674 	 * component is defined in the SSDs
1675 	 */
1676 	rc = __ns_ldap_getSearchDescriptors(SSD_service,
1677 	    SSDlist, errorp);
1678 	if (rc != NS_LDAP_SUCCESS) {
1679 		return (rc);
1680 	} else {
1681 		if (*SSDlist == NULL)
1682 			return (NS_LDAP_SUCCESS);
1683 
1684 		/* check to see if filter defined in SSD */
1685 		for (sdlist = *SSDlist; *sdlist; sdlist++) {
1686 			if ((*sdlist)->filter &&
1687 			    strlen((*sdlist)->filter) > 0) {
1688 				filter_found = TRUE;
1689 				break;
1690 			}
1691 		}
1692 		if (filter_found) {
1693 			(void) __ns_ldap_freeSearchDescriptors(SSDlist);
1694 			*SSDlist = NULL;
1695 			(void) snprintf(errmsg, sizeof (errmsg),
1696 			    gettext("Service search descriptor for "
1697 			    "service '%s' contains filter, "
1698 			    "which can not be used for "
1699 			    "service '%s'."),
1700 			    SSD_service, service);
1701 			MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE,
1702 			    strdup(errmsg), NS_LDAP_CONFIG);
1703 			return (NS_LDAP_CONFIG);
1704 		}
1705 
1706 	}
1707 	return (NS_LDAP_SUCCESS);
1708 }
1709 
1710 
1711 /*
1712  * verify addr is an IPv4 address with the optional [:portno]
1713  * RFC2373 & RFC2732 & RFC2396
1714  */
1715 int
1716 __s_api_isipv4(char *addr)
1717 {
1718 	int i, seg, digit, port;
1719 
1720 	if (!addr)
1721 		return (0);
1722 
1723 	digit = seg = port = 0;
1724 
1725 	for (i = 0; i < strlen(addr); i++) {
1726 		if (isdigit(addr[i])) {
1727 			digit++;
1728 			continue;
1729 		}
1730 		if (addr[i] == '.') {
1731 			if (digit > 3 || digit == 0)
1732 				return (0);
1733 			digit = 0;
1734 			seg++;
1735 			continue;
1736 		}
1737 		if (addr[i] == ':') {
1738 			if (digit > 3)
1739 				return (0);
1740 			port++;
1741 			digit = 0;
1742 			seg++;
1743 			continue;
1744 		}
1745 		return (0);
1746 	}
1747 
1748 	if ((seg == 3 && port == 0 && digit > 0 && digit < 4) ||
1749 	    (seg == 4 && port == 1 && digit > 0))
1750 		return (1);
1751 
1752 	return (0);
1753 }
1754 
1755 
1756 /*
1757  * verify addr is an IPv6 address with the optional [IPv6]:portno
1758  * RFC2373 & RFC2732 & RFC2396
1759  */
1760 int
1761 __s_api_isipv6(char *addr)
1762 {
1763 	int i, col, digit, port, dc, tc;
1764 	char *laddr, *c1, *s;
1765 
1766 	if (!addr)
1767 		return (0);
1768 
1769 	s = addr;
1770 	laddr = NULL;
1771 	digit = col = port = 0;
1772 	if (addr[0] == '[') {
1773 		laddr = strdup(addr);
1774 		if (!laddr)
1775 			return (0);
1776 		c1 = strchr(laddr, ']');
1777 		/* only 1 ']' should be in an addr */
1778 		if (!c1 || (strchr(c1+1, ']')))
1779 			goto bad;
1780 		switch (c1[1]) {
1781 			case ':':
1782 				port++;
1783 				for (i = 2; i < strlen(c1); i++) {
1784 					if (!isdigit(c1[i]))
1785 						goto bad;
1786 					digit++;
1787 				}
1788 				if (!digit)
1789 					goto bad;
1790 				c1[0] = '\0';
1791 				break;
1792 			case '\0':
1793 				c1[0] = '\0';
1794 				break;
1795 			default:
1796 				goto bad;
1797 		}
1798 		s = &laddr[1];
1799 	}
1800 
1801 	digit = dc = tc = 0;
1802 	for (i = 0; i < strlen(s); i++) {
1803 		if (isxdigit(s[i])) {
1804 			if (digit == 0)
1805 				dc = i;
1806 			digit++;
1807 			col = 0;
1808 			continue;
1809 		}
1810 		if (s[i] == ':') {
1811 			tc++;
1812 			if ((col > 1) || (i && !col && !digit))
1813 				goto bad;
1814 			digit = 0;
1815 			col++;
1816 			continue;
1817 		}
1818 		if (s[i] == '.') {
1819 			if (__s_api_isipv4(&s[dc]) && tc)
1820 				goto good;
1821 			else
1822 				goto bad;
1823 		}
1824 		goto bad;
1825 	}
1826 
1827 good:
1828 	free(laddr);
1829 	return (1);
1830 bad:
1831 	free(laddr);
1832 	return (0);
1833 }
1834 
1835 
1836 /*
1837  * verify addr is a valid hostname with the optional [:portno]
1838  * RFC2373 & RFC2732 & RFC2396
1839  */
1840 int
1841 __s_api_ishost(char *addr)
1842 {
1843 	int i, seg, alpha, digit, port;
1844 
1845 	if (!addr)
1846 		return (0);
1847 
1848 	alpha = digit = seg = port = 0;
1849 
1850 	/* must start with alpha character */
1851 	if (!isalpha(addr[0]))
1852 		return (0);
1853 
1854 	for (i = 0; i < strlen(addr); i++) {
1855 		if (isalpha(addr[i]) || (i && addr[i] == '-')) {
1856 			alpha++;
1857 			continue;
1858 		}
1859 		if (isdigit(addr[i])) {
1860 			digit++;
1861 			continue;
1862 		}
1863 		if (addr[i] == '.') {
1864 			if (!alpha && !digit)
1865 				return (0);
1866 			alpha = digit = 0;
1867 			seg++;
1868 			continue;
1869 		}
1870 		if (addr[i] == ':') {
1871 			if (!alpha && !digit)
1872 				return (0);
1873 			alpha = digit = 0;
1874 			port++;
1875 			seg++;
1876 			continue;
1877 		}
1878 		return (0);
1879 	}
1880 
1881 	if ((port == 0 && (seg || alpha || digit)) ||
1882 	    (port == 1 && alpha == 0 && digit))
1883 		return (1);
1884 
1885 	return (0);
1886 }
1887 
1888 
1889 /*
1890  * Prepend automountMapName=auto_xxx to the basedn
1891  * in the SSDlist
1892  */
1893 
1894 int __s_api_prepend_automountmapname(
1895 	const char *service,
1896 	ns_ldap_search_desc_t ***SSDlist,
1897 	ns_ldap_error_t **errorp)
1898 {
1899 	int			i, rc;
1900 	ns_ldap_search_desc_t	** ssdlist = NULL;
1901 
1902 	if (service == NULL || SSDlist == NULL || *SSDlist == NULL)
1903 		return (NS_LDAP_INVALID_PARAM);
1904 
1905 	ssdlist = *SSDlist;
1906 
1907 	for (i = 0; ssdlist[i] != NULL; i++) {
1908 		rc = __s_api_prepend_automountmapname_to_dn(
1909 		    service, &ssdlist[i]->basedn, errorp);
1910 
1911 		if (rc != NS_LDAP_SUCCESS)
1912 			return (rc);
1913 	}
1914 
1915 	return (NS_LDAP_SUCCESS);
1916 }
1917 
1918 
1919 /*
1920  * Prepend automountMapName=auto_xxx to the DN
1921  * Construct a string of
1922  * "automountMapName=auto_xxx,dn"
1923  *
1924  * If automountMapName is mapped to some other attribute,
1925  * then use the mapping in the setup.
1926  *
1927  * If a version 1 profile is in use, use nisMapName for
1928  * backward compatibility (i.e. "nisMapName=auto_xxx,dn").
1929  */
1930 
1931 int
1932 __s_api_prepend_automountmapname_to_dn(
1933 	const char *service,
1934 	char **dn,
1935 	ns_ldap_error_t **errorp)
1936 {
1937 	int rc, len_s = 0, len_d = 0, len = 0;
1938 	char *buffer = NULL;
1939 	char *default_automountmapname = "automountMapName";
1940 	char *automountmapname = NULL;
1941 	char **mappedattrs = NULL;
1942 	char errstr[MAXERROR];
1943 	void **paramVal = NULL;
1944 
1945 	if (service == NULL || dn == NULL || *dn == NULL)
1946 		return (NS_LDAP_INVALID_PARAM);
1947 
1948 	rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, &paramVal, errorp);
1949 	if (rc != NS_LDAP_SUCCESS || !paramVal || !*paramVal) {
1950 		if (paramVal)
1951 			(void) __ns_ldap_freeParam(&paramVal);
1952 		return (rc);
1953 	}
1954 	if (strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0) {
1955 		automountmapname = strdup("nisMapName");
1956 		(void) __ns_ldap_freeParam(&paramVal);
1957 		if (automountmapname == NULL) {
1958 			return (NS_LDAP_MEMORY);
1959 		}
1960 	} else {
1961 		(void) __ns_ldap_freeParam(&paramVal);
1962 
1963 		/* Find mapped attribute name of auto_xxx first */
1964 		mappedattrs = __ns_ldap_getMappedAttributes(
1965 		    service, default_automountmapname);
1966 		/*
1967 		 * if mapped attribute name of auto_xxx is not found,
1968 		 * find the mapped attribute name of automount
1969 		 */
1970 
1971 		if (mappedattrs == NULL)
1972 			mappedattrs = __ns_ldap_getMappedAttributes(
1973 			"automount", default_automountmapname);
1974 
1975 		/*
1976 		 * if mapped attr is not found, use the default automountmapname
1977 		 */
1978 
1979 		if (mappedattrs == NULL) {
1980 			automountmapname = strdup(default_automountmapname);
1981 			if (automountmapname == NULL)
1982 				return (NS_LDAP_MEMORY);
1983 		} else {
1984 			if (mappedattrs[0] != NULL) {
1985 				/*
1986 				 * Copy it from the mapped attr list
1987 				 * Assume it's 1 to 1 mapping
1988 				 * 1 to n does not make sense
1989 				 */
1990 				automountmapname = strdup(mappedattrs[0]);
1991 				__s_api_free2dArray(mappedattrs);
1992 				if (automountmapname == NULL) {
1993 					return (NS_LDAP_MEMORY);
1994 				}
1995 			} else {
1996 
1997 				/*
1998 				 * automountmapname is mapped to an empty string
1999 				 */
2000 
2001 				__s_api_free2dArray(mappedattrs);
2002 
2003 				(void) sprintf(errstr,
2004 				    gettext(
2005 				    "Attribute automountMapName is "
2006 				    "mapped to an empty string.\n"));
2007 
2008 				MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
2009 				    strdup(errstr), NS_LDAP_MEMORY);
2010 
2011 				return (NS_LDAP_CONFIG);
2012 			}
2013 		}
2014 	}
2015 
2016 	len_s = strlen(service);
2017 	len_d  = strlen(*dn);
2018 	/* automountMapName + "=" + service + "," + dn + '\0' */
2019 	len = strlen(automountmapname) + 1 + len_s + 1 + len_d + 1;
2020 	buffer = (char *)malloc(len);
2021 	if (buffer == NULL) {
2022 		free(automountmapname);
2023 		return (NS_LDAP_MEMORY);
2024 	}
2025 
2026 	(void) snprintf(buffer, len, "%s=%s,%s",
2027 	    automountmapname, service, *dn);
2028 
2029 	buffer[len-1] = '\0';
2030 
2031 	free(automountmapname);
2032 
2033 	/* free the original dn */
2034 	(void) free(*dn);
2035 
2036 	*dn = buffer;
2037 
2038 	return (NS_LDAP_SUCCESS);
2039 }
2040 
2041 /*
2042  * Map the LDAP error code and error message from LDAP server
2043  * to a password status used for password aging/management.
2044  */
2045 ns_ldap_passwd_status_t
2046 __s_api_set_passwd_status(int errnum, char *errmsg)
2047 {
2048 	syslog(LOG_DEBUG, "libsldap: got LDAP errnum %d & message: %s ", errnum,
2049 	    (errmsg != NULL) ? errmsg : "error msg not available");
2050 	if (errmsg) {
2051 		if (errnum ==
2052 		    LDAP_INVALID_CREDENTIALS) {
2053 			/*
2054 			 * case 1 (Bind):
2055 			 * password expired
2056 			 */
2057 			if (strstr(errmsg,
2058 			    NS_PWDERR_EXPIRED))
2059 				return (NS_PASSWD_EXPIRED);
2060 		}
2061 
2062 		if (errnum ==
2063 		    LDAP_UNWILLING_TO_PERFORM) {
2064 			/*
2065 			 * case 1.1 (Bind):
2066 			 * password expired
2067 			 */
2068 			if (strstr(errmsg,
2069 			    NS_PWDERR_EXPIRED))
2070 				return (NS_PASSWD_EXPIRED);
2071 
2072 			/*
2073 			 * case 2 (Bind):
2074 			 * Account inactivated
2075 			 */
2076 			if (strstr(errmsg,
2077 			    NS_PWDERR_ACCT_INACTIVATED))
2078 				return (NS_PASSWD_EXPIRED);
2079 
2080 
2081 			/*
2082 			 * case 3 (Modify passwd):
2083 			 * the user is not allow to change
2084 			 * password; only admin can change it
2085 			 */
2086 			if (strstr(errmsg,
2087 			    NS_PWDERR_CHANGE_NOT_ALLOW))
2088 				return (NS_PASSWD_CHANGE_NOT_ALLOWED);
2089 		}
2090 
2091 		if (errnum ==
2092 		    LDAP_CONSTRAINT_VIOLATION) {
2093 			/*
2094 			 * case 4 (Bind):
2095 			 * the user account is locked due to
2096 			 * too many login failures.
2097 			 */
2098 			if (strstr(errmsg,
2099 			    NS_PWDERR_MAXTRIES))
2100 				return (NS_PASSWD_RETRY_EXCEEDED);
2101 			/*
2102 			 * case 5 (Modify passwd):
2103 			 * syntax error: the new password
2104 			 * has length less than defined
2105 			 * minimum
2106 			 * Not true anymore with strong password
2107 			 * policies on LDAP server: errmsg that
2108 			 * contain NS_PWDERR_INVALID_SYNTAX may
2109 			 * have different meanings.
2110 			 * To keep compatibility with older password
2111 			 * policy, check if errmsg is strictly equal
2112 			 * to NS_PWDERR_INVALID_SYNTAX and if yes only,
2113 			 * return NS_PASSWD_TOO_SHORT.
2114 			 */
2115 			if (strcmp(errmsg,
2116 			    NS_PWDERR_INVALID_SYNTAX) == 0)
2117 				return (NS_PASSWD_TOO_SHORT);
2118 			if (strstr(errmsg,
2119 			    NS_PWDERR_INVALID_SYNTAX))
2120 				return (NS_PASSWD_INVALID_SYNTAX);
2121 			/*
2122 			 * case 6 (Modify passwd):
2123 			 * trivial password: same value as
2124 			 * that of attribute cn, sn, or uid ...
2125 			 */
2126 			if (strstr(errmsg,
2127 			    NS_PWDERR_TRIVIAL_PASSWD))
2128 				return (NS_PASSWD_INVALID_SYNTAX);
2129 			/*
2130 			 * case 7 (Modify passwd):
2131 			 * re-use one of the old passwords
2132 			 * in history list
2133 			 */
2134 			if (strstr(errmsg,
2135 			    NS_PWDERR_IN_HISTORY))
2136 				return (NS_PASSWD_IN_HISTORY);
2137 			/*
2138 			 * case 8 (Modify passwd):
2139 			 * password not allowed to be
2140 			 * changed yet; within minimum
2141 			 * age
2142 			 */
2143 			if (strstr(errmsg,
2144 			    NS_PWDERR_WITHIN_MIN_AGE))
2145 				return (NS_PASSWD_WITHIN_MIN_AGE);
2146 		}
2147 
2148 	}
2149 
2150 	return (NS_PASSWD_GOOD);
2151 }
2152 
2153 /*
2154  * Determine if the input OID list contains
2155  * one of the password control OIDs, which are:
2156  * LDAP_CONTROL_PWEXPIRED: 2.16.840.1.113730.3.4.4
2157  * LDAP_CONTROL_PWEXPIRING: 2.16.840.1.113730.3.4.5.
2158  * If yes, return 1, if no, 0.
2159  */
2160 int
2161 __s_api_contain_passwd_control_oid(char **oids)
2162 {
2163 	char **oid;
2164 
2165 	if (oids == NULL)
2166 		return (0);
2167 
2168 	for (oid = oids; *oid; oid++) {
2169 		if (strcmp(*oid, LDAP_CONTROL_PWEXPIRED) == 0 ||
2170 		    strcmp(*oid, LDAP_CONTROL_PWEXPIRING) == 0) {
2171 			return (1);
2172 		}
2173 	}
2174 
2175 	return (0);
2176 }
2177 
2178 /*
2179  * Determine if the input OID list contains LDAP V3 password less
2180  * account management control OID, which is:
2181  * NS_LDAP_ACCOUNT_USABLE_CONTROL:1.3.6.1.4.1.42.2.27.9.5.8
2182  * If yes, return 1, if no, 0.
2183  */
2184 int
2185 __s_api_contain_account_usable_control_oid(char **oids)
2186 {
2187 	char **oid;
2188 
2189 	if (oids == NULL)
2190 		return (0);
2191 
2192 	for (oid = oids; *oid; oid++) {
2193 		if (strcmp(*oid, NS_LDAP_ACCOUNT_USABLE_CONTROL) == 0) {
2194 			return (1);
2195 		}
2196 	}
2197 
2198 	return (0);
2199 }
2200 
2201 /*
2202  * For some databases in name switch, the name and aliases are saved
2203  * as "cn". When the "cn" valuse are retrieved, there is no distinction
2204  * which is  the name and which is(are) aliase(s).
2205  * This function is to parse RDN and find the value of the "cn" and
2206  * then find the matching value in "cn" attribute.
2207  * Also see RFC 2307 section 5.6.
2208  *
2209  * Input -
2210  *  entry:	An LDAP entry
2211  *  attrptr:	A attribute which value appears in RDN
2212  *		This should be "cn" for the name switch for now.
2213  *  case_ignore:    0 Case sensitive comparison on the attribute value
2214  *		    1 Case insensitive comparison
2215  *
2216  * Return -
2217  *		The value of an attrbute which is used as canonical name
2218  *		This is read only and the caller should not try to free it.
2219  *		If it's a NULL, it could be either an RDN parsing error
2220  *		or RDN value does not match any existing "cn" values.
2221  *		e.g.
2222  *		dn: cn=xx+ipserviceprotocol=udp,......
2223  *		cn: aa
2224  *		cn: bb
2225  *
2226  * Note:
2227  *  Although the name switch/ldap's  rdn is in "cn=xx" or "cn=xx+..."
2228  * format, this function makes no such assumption. If the DN
2229  * is saved as "dn: yy=...+sn=my_canocical_name, ..", then it can still work.
2230  * The comments use "cn" as an example only.
2231  *
2232  */
2233 typedef int (*cmpfunc)(const char *, const char *);
2234 
2235 char *
2236 __s_api_get_canonical_name(ns_ldap_entry_t *entry, ns_ldap_attr_t *attrptr,
2237 			int case_ignore) {
2238 	uint_t			i;
2239 	char			*token, *lasts, *value = NULL;
2240 	char			**rdn = NULL, **attrs = NULL, **values = NULL;
2241 	char			*rdn_attr_value = NULL;
2242 	cmpfunc			cmp;
2243 
2244 	if (entry == NULL || attrptr == NULL)
2245 		return (NULL);
2246 
2247 	/* "values" is read-only */
2248 	if ((values = __ns_ldap_getAttr(entry, "dn")) == NULL ||
2249 	    values[0] == NULL)
2250 		return (NULL);
2251 
2252 	if ((rdn = ldap_explode_dn(values[0], 0)) == NULL ||
2253 	    rdn[0] == NULL)
2254 		return (NULL);
2255 
2256 	if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) {
2257 		ldap_value_free(rdn);
2258 		return (NULL);
2259 	}
2260 	/* Assume the rdn is normalized */
2261 	for (i = 0; attrs[i] != NULL; i++) {
2262 		/* parse attribute name and value, get attribute name first */
2263 		if ((token = strtok_r(attrs[i], "=", &lasts)) == NULL) {
2264 			goto cleanup;
2265 		}
2266 		if (strcasecmp(token, attrptr->attrname) == 0) {
2267 			/* get value */
2268 			rdn_attr_value = lasts;
2269 			break;
2270 		}
2271 	}
2272 	if (rdn_attr_value) {
2273 		if (case_ignore)
2274 			cmp = strcasecmp;
2275 		else
2276 			cmp = strcmp;
2277 		/*
2278 		 * After parsing RDN and find the matching attribute in RDN,
2279 		 * match rdn value with values in "cn".
2280 		 */
2281 		for (i = 0; i < attrptr->value_count; i++) {
2282 			if (attrptr->attrvalue[i] &&
2283 			    (*cmp)(rdn_attr_value,
2284 			    attrptr->attrvalue[i]) == 0) {
2285 				/* RDN "cn" value matches the "cn" value */
2286 				value = attrptr->attrvalue[i];
2287 				break;
2288 			}
2289 		}
2290 	}
2291 cleanup:
2292 	ldap_value_free(rdn);
2293 	ldap_value_free(attrs);
2294 
2295 	return (value);
2296 }
2297 
2298 /*
2299  * This function requests a server to be removed from
2300  * the cache manager maintained server list. This is
2301  * done via the door functionality.
2302  * Returns 0 if OK, else a negative value.
2303  */
2304 
2305 int
2306 __s_api_removeServer(const char *server)
2307 {
2308 	union {
2309 		ldap_data_t	s_d;
2310 		char		s_b[DOORBUFFERSIZE];
2311 	} space;
2312 
2313 	ns_server_info_t		r, *ret = &r;
2314 	const char		*ireq;
2315 	ldap_data_t		*sptr;
2316 	int			ndata;
2317 	int			adata;
2318 	int			len;
2319 	int			rc;
2320 	ns_ldap_error_t		*error = NULL;
2321 
2322 	if (server == NULL)
2323 		return (-1);
2324 
2325 	ireq = NS_CACHE_NORESP;
2326 
2327 	if (__s_api_isStandalone()) {
2328 		/*
2329 		 * Remove 'server' from the standalone server list.
2330 		 * __s_api_findRootDSE() is the standalone version
2331 		 * of getldap_get_serverInfo() used in ldap_cachemgr.
2332 		 * Request NS_CACHE_NORESP indicates 'server' should
2333 		 * be removed.
2334 		 */
2335 		if (__s_api_findRootDSE(ireq,
2336 		    server,
2337 		    NS_CACHE_ADDR_IP,
2338 		    NULL,
2339 		    &error) != NS_LDAP_SUCCESS) {
2340 			syslog(LOG_WARNING,
2341 			    "libsldap (\"standalone\" mode): "
2342 			    " Unable to remove %s - %s",
2343 			    server,
2344 			    error != NULL && error->message != NULL ?
2345 			    error->message : " no error info");
2346 			if (error != NULL) {
2347 				(void) __ns_ldap_freeError(&error);
2348 			}
2349 
2350 			return (NS_CACHE_NOSERVER);
2351 		}
2352 
2353 		return (0);
2354 	}
2355 
2356 	(void) memset(ret, 0, sizeof (ns_server_info_t));
2357 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
2358 
2359 	adata = (sizeof (ldap_call_t) + strlen(ireq) +
2360 	    strlen(NS_CACHE_ADDR_IP) + 1);
2361 	adata += strlen(DOORLINESEP) + 1;
2362 	adata += strlen(server) + 1;
2363 
2364 	ndata = sizeof (space);
2365 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
2366 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
2367 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
2368 		return (-1);
2369 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
2370 	    NS_CACHE_ADDR_IP, len) >= len)
2371 		return (-1);
2372 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, DOORLINESEP, len) >=
2373 	    len)
2374 		return (-1);
2375 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, len) >= len)
2376 		return (-1);
2377 	sptr = &space.s_d;
2378 
2379 	/* try to remove the server via the door interface */
2380 	rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
2381 
2382 	/* clean up the door call */
2383 	if (sptr != &space.s_d) {
2384 		(void) munmap((char *)sptr, ndata);
2385 	}
2386 
2387 	return (rc);
2388 }
2389 
2390 void
2391 __s_api_free_server_info(ns_server_info_t *sinfo) {
2392 	if (sinfo->server) {
2393 		free(sinfo->server);
2394 		sinfo->server = NULL;
2395 	}
2396 	if (sinfo->serverFQDN) {
2397 		free(sinfo->serverFQDN);
2398 		sinfo->serverFQDN = NULL;
2399 	}
2400 	__s_api_free2dArray(sinfo->saslMechanisms);
2401 	sinfo->saslMechanisms = NULL;
2402 	__s_api_free2dArray(sinfo->controls);
2403 	sinfo->controls = NULL;
2404 }
2405 
2406 /*
2407  * Create an ns_ldap_error structure, set status to 'rc',
2408  * and copy in the error message 'msg'.
2409  */
2410 ns_ldap_error_t *
2411 __s_api_make_error(int rc, char *msg) {
2412 	ns_ldap_error_t *ep;
2413 
2414 	ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep));
2415 	if (ep == NULL)
2416 		return (NULL);
2417 
2418 	ep->status = rc;
2419 	if (msg != NULL)
2420 		ep->message =  strdup(msg); /* OK if ep->message is NULL */
2421 
2422 	return (ep);
2423 }
2424 
2425 /*
2426  * Make a copy of the input ns_ldap_error.
2427  */
2428 ns_ldap_error_t *
2429 __s_api_copy_error(ns_ldap_error_t *errorp) {
2430 	ns_ldap_error_t *ep;
2431 	char		*msg;
2432 
2433 	if (errorp == NULL)
2434 		return (NULL);
2435 
2436 	ep = (ns_ldap_error_t *)malloc(sizeof (*ep));
2437 	if (ep != NULL) {
2438 		*ep = *errorp;
2439 		if (ep->message != NULL) {
2440 			msg = strdup(ep->message);
2441 			if (msg == NULL) {
2442 				free(ep);
2443 				ep = NULL;
2444 			} else
2445 				ep->message = msg;
2446 		}
2447 	}
2448 	return (ep);
2449 }
2450