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