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