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