1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /* libsldap - cachemgr side configuration components */
29 
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <stdlib.h>
33 #include <libintl.h>
34 #include <string.h>
35 #include <ctype.h>
36 
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <syslog.h>
41 #include <locale.h>
42 #include <errno.h>
43 #include <sys/time.h>
44 
45 #include "ns_sldap.h"
46 #include "ns_internal.h"
47 #include "ns_cache_door.h"
48 
49 #define	ALWAYS		1
50 
51 
52 /*
53  * **************************************************************
54  * Configuration File Routines
55  * **************************************************************
56  */
57 
58 
59 /* Size of the errstr buffer needs to be MAXERROR */
60 static int
61 read_line(FILE *fp, char *buffer, int buflen, char *errstr)
62 {
63 	int	linelen;
64 	char	c;
65 
66 	*errstr = '\0';
67 
68 	for (linelen = 0; linelen < buflen; ) {
69 		c = getc(fp);
70 		if (c == EOF)
71 			break;
72 		switch (c) {
73 		case '\n':
74 			if (linelen > 0 && buffer[linelen - 1] == '\\') {
75 				/* Continuation line found */
76 				--linelen;
77 			} else {
78 				/* end of line found */
79 				buffer[linelen] = '\0';
80 				return (linelen);
81 			}
82 			break;
83 		default:
84 			buffer[linelen++] = c;
85 		}
86 	}
87 
88 	if (linelen >= buflen) {
89 		(void) snprintf(errstr, MAXERROR,
90 		    gettext("Buffer overflow, line too long."));
91 		return (-2);
92 	} else if (linelen > 0 && buffer[linelen - 1] == '\\') {
93 		(void) snprintf(errstr, MAXERROR,
94 		    gettext("Unterminated continuation line."));
95 		return (-2);
96 	} else {
97 		/* end of file */
98 		buffer[linelen] = '\0';
99 	}
100 	return (linelen > 0 ? linelen : -1);
101 }
102 
103 
104 static ns_parse_status
105 read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error)
106 {
107 	ParamIndexType	i = 0;
108 	char		errstr[MAXERROR];
109 	char		buffer[BUFSIZE], *name, *value;
110 	int		emptyfile, lineno;
111 	FILE		*fp;
112 	int		ret;
113 	int		linelen;
114 	char		*file;
115 	int		first = 1;
116 
117 
118 	if (cred_file) {
119 		file = NSCREDFILE;
120 	} else {
121 		file = NSCONFIGFILE;
122 	}
123 	fp = fopen(file, "rF");
124 	if (fp == NULL) {
125 		(void) snprintf(errstr, sizeof (errstr),
126 		    gettext("Unable to open filename '%s' "
127 		    "for reading (errno=%d)."), file, errno);
128 		MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errstr), NULL);
129 		return (NS_NOTFOUND);
130 	}
131 
132 	emptyfile = 1;
133 	lineno = 0;
134 	for (; ; ) {
135 		if ((linelen = read_line(fp, buffer, sizeof (buffer),
136 		    errstr)) < 0)
137 			/* End of file */
138 			break;
139 		lineno++;
140 		if (linelen == 0)
141 			continue;
142 		/* get rid of comment lines */
143 		if (buffer[0] == '#')
144 			continue;
145 		emptyfile = 0;
146 		name = NULL;
147 		value = NULL;
148 		__s_api_split_key_value(buffer, &name, &value);
149 		if (name == NULL || value == NULL) {
150 			(void) snprintf(errstr, sizeof (errstr),
151 			    gettext("Missing Name or Value on line %d."),
152 			    lineno);
153 			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
154 			    strdup(errstr), NULL);
155 			(void) fclose(fp);
156 			return (NS_PARSE_ERR);
157 		}
158 		if (__s_api_get_versiontype(ptr, name, &i) != 0) {
159 			(void) snprintf(errstr, sizeof (errstr),
160 			    gettext("Illegal profile type on line %d."),
161 			    lineno);
162 			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
163 			    strdup(errstr), NULL);
164 			(void) fclose(fp);
165 			return (NS_PARSE_ERR);
166 		}
167 		if (!first && i == NS_LDAP_FILE_VERSION_P) {
168 			(void) snprintf(errstr, sizeof (errstr),
169 			    gettext("Illegal NS_LDAP_FILE_VERSION "
170 			    "on line %d."), lineno);
171 			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
172 			    strdup(errstr), NULL);
173 			(void) fclose(fp);
174 			return (NS_PARSE_ERR);
175 		}
176 		first = 0;
177 		switch (__s_api_get_configtype(i)) {
178 		case SERVERCONFIG:
179 		case CLIENTCONFIG:
180 			if (cred_file == 0) {
181 				ret = __ns_ldap_setParamValue(ptr, i, value,
182 				    error);
183 				if (ret != NS_SUCCESS) {
184 					(void) fclose(fp);
185 					return (ret);
186 				}
187 			} else if (i != NS_LDAP_FILE_VERSION_P) {
188 				(void) snprintf(errstr, sizeof (errstr),
189 				    gettext("Illegal entry in '%s' on "
190 				    "line %d"), file, lineno);
191 				MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
192 				    strdup(errstr), NULL);
193 				(void) fclose(fp);
194 				return (NS_PARSE_ERR);
195 			}
196 			break;
197 		case CREDCONFIG:
198 			if (i == NS_LDAP_FILE_VERSION_P)
199 				break;
200 			if (cred_file) {
201 				ret = __ns_ldap_setParamValue(ptr, i, value,
202 				    error);
203 				if (ret != NS_SUCCESS) {
204 					(void) fclose(fp);
205 					return (ret);
206 				}
207 			} else {
208 				(void) snprintf(errstr, sizeof (errstr),
209 				    gettext("Illegal entry in '%s' on "
210 				    "line %d"), file, lineno);
211 				MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
212 				    strdup(errstr), NULL);
213 				(void) fclose(fp);
214 				return (NS_PARSE_ERR);
215 			}
216 		}
217 	}
218 	(void) fclose(fp);
219 	if (!cred_file && emptyfile) {
220 		/* Error in read_line */
221 		(void) snprintf(errstr, sizeof (errstr),
222 		    gettext("Empty config file: '%s'"), file);
223 		MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
224 		    NULL);
225 		return (NS_PARSE_ERR);
226 	}
227 	if (linelen == -2) {
228 		/* Error in read_line */
229 		(void) snprintf(errstr, sizeof (errstr),
230 		    gettext("Line too long in '%s'"), file);
231 		MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
232 		    NULL);
233 		return (NS_PARSE_ERR);
234 	}
235 	return (NS_SUCCESS);
236 }
237 
238 
239 static
240 ns_ldap_return_code
241 set_attr(ns_config_t *config_struct,
242 		char *attr_name,
243 		char *attr_val,
244 		ns_ldap_error_t **errorp)
245 {
246 	ParamIndexType	idx;
247 	char		errmsg[MAXERROR];
248 
249 	if (errorp == NULL) {
250 		return (NS_LDAP_INVALID_PARAM);
251 	}
252 
253 	*errorp = NULL;
254 
255 	/*
256 	 * This double call is made due to the presence of
257 	 * two sets of LDAP config. attribute names.
258 	 * An LDAP configuration can be obtained either from a server
259 	 * or from SMF. The former sends a DUA with attributes' names
260 	 * styled like "preferredServerList". But local configurations
261 	 * will have names inherited from the /var/ldap/ldap* files such as
262 	 * "NS_LDAP_SERVER_PREF".
263 	 * So, the standalone bits are able to process both sets of
264 	 * attributes' names.
265 	 */
266 	if (__s_api_get_profiletype(attr_name, &idx) < 0 &&
267 	    __s_api_get_versiontype(config_struct, attr_name, &idx) < 0) {
268 		(void) snprintf(errmsg, sizeof (errmsg),
269 		    gettext("Illegal DUAProfile property: <%s>."), attr_name);
270 		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
271 		return (NS_LDAP_CONFIG);
272 	}
273 
274 	return (__ns_ldap_setParamValue(config_struct, idx, attr_val, errorp));
275 }
276 
277 
278 /*
279  * This function creates a configuration which will be used
280  * for all LDAP requests in the Standalone mode.
281  *
282  * INPUT:
283  *     config - a buffer returned by __ns_ldap_getConnectionInfo()'s
284  *              dua_profile parameter.
285  *
286  */
287 ns_config_t *
288 __s_api_create_config_door_str(char *config, ns_ldap_error_t **errorp)
289 {
290 	char		*attr, *attrName, *attrVal, *rest;
291 	ns_config_t	*configStruct = NULL;
292 	char		errmsg[MAXERROR];
293 
294 	if (config == NULL || errorp == NULL)
295 		return (NULL);
296 
297 	if ((configStruct = __s_api_create_config()) == NULL) {
298 		return (NULL);
299 	}
300 
301 	*errorp = NULL;
302 
303 	attr = strtok_r(config, DOORLINESEP, &rest);
304 	if (!attr) {
305 		__s_api_destroy_config(configStruct);
306 		(void) snprintf(errmsg, sizeof (errmsg),
307 		    gettext("DUAProfile received from the server"
308 		    " has bad format"));
309 		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
310 		return (NULL);
311 	}
312 
313 	do {
314 		__s_api_split_key_value(attr, &attrName, &attrVal);
315 
316 		if (attrName == NULL || attrVal == NULL) {
317 			__s_api_destroy_config(configStruct);
318 			(void) snprintf(errmsg, sizeof (errmsg),
319 			    gettext("Attribute %s is not valid"), attr);
320 			MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG,
321 			    strdup(errmsg), NULL);
322 			return (NULL);
323 		}
324 
325 		/* Get the version of the profile. */
326 		if (strcasecmp(attrName, "objectclass") == 0) {
327 			if (strcasecmp(attrVal, _PROFILE2_OBJECTCLASS) == 0) {
328 				if (__ns_ldap_setParamValue(configStruct,
329 				    NS_LDAP_FILE_VERSION_P,
330 				    NS_LDAP_VERSION_2,
331 				    errorp) != NS_LDAP_SUCCESS) {
332 					__s_api_destroy_config(configStruct);
333 					return (NULL);
334 				}
335 			}
336 			continue;
337 		}
338 
339 		if (set_attr(configStruct, attrName, attrVal, errorp) !=
340 		    NS_LDAP_SUCCESS) {
341 			__s_api_destroy_config(configStruct);
342 			return (NULL);
343 		}
344 	} while (attr = strtok_r(NULL, DOORLINESEP, &rest));
345 
346 	if (__s_api_crosscheck(configStruct, errmsg, B_FALSE) != NS_SUCCESS) {
347 		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
348 		__s_api_destroy_config(configStruct);
349 		return (NULL);
350 	}
351 
352 	return (configStruct);
353 }
354 
355 
356 /*
357  * Cache Manager side of configuration file loading
358  */
359 
360 ns_ldap_error_t *
361 __ns_ldap_LoadConfiguration()
362 {
363 	ns_ldap_error_t	*error = NULL;
364 	ns_config_t	*ptr = NULL;
365 	char		errstr[MAXERROR];
366 	ns_parse_status	ret;
367 
368 
369 	ptr = __s_api_create_config();
370 	if (ptr == NULL) {
371 		(void) snprintf(errstr, sizeof (errstr),
372 		    gettext("__ns_ldap_LoadConfiguration: Out of memory."));
373 		MKERROR(LOG_ERR, error, NS_CONFIG_NOTLOADED,
374 		    strdup(errstr), NULL);
375 		return (error);
376 	}
377 
378 	/* Load in Configuration file */
379 	ret = read_file(ptr, 0, &error);
380 	if (ret != NS_SUCCESS) {
381 		__s_api_destroy_config(ptr);
382 		return (error);
383 	}
384 
385 	/* Load in Credential file */
386 	ret = read_file(ptr, 1, &error);
387 	if (ret != NS_SUCCESS) {
388 		__s_api_destroy_config(ptr);
389 		return (error);
390 	}
391 
392 	if (__s_api_crosscheck(ptr, errstr, B_TRUE) != NS_SUCCESS) {
393 		__s_api_destroy_config(ptr);
394 		MKERROR(LOG_ERR, error, NS_CONFIG_SYNTAX, strdup(errstr), NULL);
395 		return (error);
396 	}
397 
398 	__s_api_init_config(ptr);
399 	return (NULL);
400 }
401 
402 
403 static int
404 _print2buf(LineBuf *line, char *toprint, int addsep)
405 {
406 	int	newsz = 0;
407 	int	newmax = 0;
408 	char	*str;
409 
410 	if (line == NULL)
411 		return (-1);
412 
413 	newsz = strlen(toprint) + line->len + 1;
414 	if (addsep) {
415 		newsz += strlen(DOORLINESEP);
416 	}
417 	if (line->alloc == 0 || newsz > line->alloc) {
418 		/* Round up to next buffer and add 1 */
419 		newmax = (((newsz+(BUFSIZ-1))/BUFSIZ)+1) * BUFSIZ;
420 		if (line->alloc == 0)
421 			line->str = (char *)calloc(newmax, 1);
422 		else {
423 			/*
424 			 * if realloc() returns NULL,
425 			 * the original buffer is untouched.
426 			 * It needs to be freed.
427 			 */
428 			str = (char *)realloc(line->str, newmax);
429 			if (str == NULL) {
430 				free(line->str);
431 				line->str = NULL;
432 			}
433 			else
434 				line->str = str;
435 		}
436 		line->alloc = newmax;
437 		if (line->str == NULL) {
438 			line->alloc = 0;
439 			line->len = 0;
440 			return (-1);
441 		}
442 	}
443 	/* now add new 'toprint' data to buffer */
444 	(void) strlcat(line->str, toprint, line->alloc);
445 	if (addsep) {
446 		(void) strlcat(line->str, DOORLINESEP, line->alloc);
447 	}
448 	line->len = newsz;
449 	return (0);
450 }
451 
452 
453 /*
454  * __ns_ldap_LoadDoorInfo is a routine used by the ldapcachemgr
455  * to create a configuration buffer to transmit back to a client
456  * domainname is transmitted to ldapcachemgr and ldapcachemgr uses
457  * it to select a configuration to transmit back.  Otherwise it
458  * is essentially unused in sldap.
459  */
460 
461 ns_ldap_error_t *
462 __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname, ns_config_t *new)
463 {
464 	ns_config_t	*ptr;
465 	char		errstr[MAXERROR];
466 	ns_ldap_error_t	*errorp;
467 	char		string[BUFSIZE];
468 	char		*str;
469 	ParamIndexType	i = 0;
470 	int		len;
471 	ldap_config_out_t *cout;
472 
473 	/*
474 	 * If new is NULL, it outputs the flatten data of current default
475 	 * config, if it's non-NULL, it outputs the flatten data of a temporary
476 	 * config. It's used to compare the new config data with the current
477 	 * default config data.
478 	 */
479 	if (new == NULL)
480 		ptr = __s_api_get_default_config();
481 	else
482 		ptr = new;
483 	if (ptr == NULL) {
484 		(void) snprintf(errstr, sizeof (errstr),
485 		    gettext("No configuration information available for %s."),
486 		    domainname == NULL ? "<no domain specified>" : domainname);
487 		MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
488 		    strdup(errstr), NULL);
489 		return (errorp);
490 	}
491 	(void) memset((char *)configinfo, 0, sizeof (LineBuf));
492 	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
493 		str = __s_api_strValue(ptr, string, sizeof (string), i,
494 		    NS_DOOR_FMT);
495 		if (str == NULL)
496 			continue;
497 		if (_print2buf(configinfo, str, 1) != 0) {
498 			(void) snprintf(errstr, sizeof (errstr),
499 			    gettext("_print2buf: Out of memory."));
500 			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
501 			    strdup(errstr), NULL);
502 			__s_api_release_config(ptr);
503 			if (str != (char *)&string[0]) {
504 				free(str);
505 				str = NULL;
506 			}
507 			return (errorp);
508 		}
509 		if (str != (char *)&string[0]) {
510 			free(str);
511 			str = NULL;
512 		}
513 	}
514 	if (new == NULL)
515 		__s_api_release_config(ptr);
516 
517 	/*
518 	 * The new interface of the configuration between ldap_cachemgr
519 	 * & libsldap contains a header structure ldap_config_out_t.
520 	 * The flatten configuration data configinfo->str is cloned
521 	 * to cout->config_str, configinfo->len is saved in
522 	 * cout->data_size and cout->cookie is set later after this function
523 	 * is returned in ldap_cachemgr.
524 	 * configinfo->str & configinfo->len are reused to save info of
525 	 * header + data.
526 	 * The format:
527 	 * [cookie|data_size|config_str .............]
528 	 */
529 
530 	if (configinfo->str) {
531 		len = sizeof (ldap_config_out_t) - sizeof (int) +
532 		    configinfo->len;
533 		if ((cout = calloc(1, len)) == NULL) {
534 			free(configinfo->str);
535 			configinfo->str = NULL;
536 			configinfo->len = 0;
537 			(void) snprintf(errstr, sizeof (errstr),
538 			    gettext("calloc: Out of memory."));
539 			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
540 			    strdup(errstr), NULL);
541 			return (errorp);
542 		}
543 		/*
544 		 * cout->cookie is set by the caller,
545 		 * which is in ldap_cachemgr.
546 		 */
547 		cout->data_size = configinfo->len;
548 		(void) memcpy(cout->config_str, configinfo->str,
549 		    configinfo->len);
550 		free(configinfo->str);
551 		configinfo->str = (char *)cout;
552 		configinfo->len = len;
553 	}
554 	return (NULL);
555 }
556 
557 
558 ns_ldap_error_t *
559 __ns_ldap_DumpLdif(char *filename)
560 {
561 	ns_config_t	*ptr;
562 	char		errstr[MAXERROR];
563 	ns_ldap_error_t	*errorp;
564 	char		string[BUFSIZE];
565 	char		*str;
566 	FILE		*fp;
567 	ParamIndexType	i = 0;
568 	char		*profile, *container, *base;
569 
570 	ptr = __s_api_get_default_config();
571 	if (ptr == NULL) {
572 		(void) snprintf(errstr, sizeof (errstr),
573 		    gettext("No configuration information available."));
574 		MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
575 		    NULL);
576 		return (errorp);
577 	}
578 
579 	if (filename == NULL) {
580 		fp = stdout;
581 	} else {
582 		fp = fopen(filename, "wF");
583 		if (fp == NULL) {
584 			(void) snprintf(errstr, sizeof (errstr),
585 			    gettext("Unable to open filename %s for ldif "
586 			    "dump (errno=%d)."), filename, errno);
587 			MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE,
588 			    strdup(errstr), NULL);
589 			__s_api_release_config(ptr);
590 			return (errorp);
591 		}
592 		(void) fchmod(fileno(fp), 0444);
593 	}
594 
595 	if (ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_ptype != CHARPTR ||
596 	    ptr->paramList[NS_LDAP_PROFILE_P].ns_ptype != CHARPTR) {
597 		(void) snprintf(errstr, sizeof (errstr),
598 		    gettext("Required BaseDN and/or Profile name "
599 		    "ldif fields not present"));
600 		MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE, strdup(errstr),
601 		    NULL);
602 		__s_api_release_config(ptr);
603 		return (errorp);
604 	}
605 
606 	profile = ptr->paramList[NS_LDAP_PROFILE_P].ns_pc;
607 	base = ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_pc;
608 	container = _PROFILE_CONTAINER;
609 
610 	/*
611 	 * Construct DN, but since this is the profile, there is no need
612 	 * to worry about mapping.  The profile itself can not be mapped
613 	 */
614 	(void) fprintf(fp, "dn: cn=%s,ou=%s,%s\n", profile, container, base);
615 
616 	/* dump objectclass names */
617 	if (ptr->version == NS_LDAP_V1) {
618 		(void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n",
619 		    _PROFILE1_OBJECTCLASS);
620 	} else {
621 		(void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n",
622 		    _PROFILE2_OBJECTCLASS);
623 	}
624 
625 	/* For each parameter - construct value */
626 	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
627 		str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_LDIF_FMT);
628 		if (str == NULL)
629 			continue;
630 		/*
631 		 * don't dump binddn, bind password, or cert path as they
632 		 * are not part of version 2 profiles
633 		 */
634 		if ((i != NS_LDAP_BINDDN_P) && (i != NS_LDAP_BINDPASSWD_P) &&
635 		    (i != NS_LDAP_HOST_CERTPATH_P))
636 			(void) fprintf(fp, "%s\n", str);
637 		if (str != (char *)&string[0]) {
638 			free(str);
639 			str = NULL;
640 		}
641 	}
642 
643 	if (filename != NULL)
644 		(void) fclose(fp);
645 
646 	__s_api_release_config(ptr);
647 	return (NULL);
648 }
649 
650 /*
651  * This routine can process the configuration  and/or
652  * the credential files at the same time.
653  * files is char *[3] = { "config", "cred", NULL };
654  */
655 
656 static
657 ns_ldap_error_t *
658 __ns_ldap_DumpConfigFiles(char **files)
659 {
660 	char		*filename;
661 	int		fi;
662 	int		docred;
663 	ns_config_t	*ptr;
664 	char		string[BUFSIZE];
665 	char		*str;
666 	char		errstr[MAXERROR];
667 	ParamIndexType	i = 0;
668 	FILE		*fp;
669 	int		rc;
670 	ns_ldap_error_t	*errorp = NULL;
671 	struct stat	buf;
672 	int		cfgtype;
673 	boolean_t	file_export_error = B_FALSE;
674 
675 	ptr = __s_api_get_default_config();
676 	if (ptr == NULL) {
677 		(void) snprintf(errstr, sizeof (errstr),
678 		    gettext("No configuration information available."));
679 		MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
680 		    NULL);
681 		return (errorp);
682 	}
683 
684 	for (fi = 0; fi < 2; fi++) {
685 		docred = 0;
686 		filename = files[fi];
687 		if (filename == NULL)
688 			continue;
689 		if (fi == 1)
690 			docred++;
691 		rc = stat(filename, &buf);
692 		fp = fopen(filename, "wF");
693 		if (fp == NULL) {
694 			(void) snprintf(errstr, sizeof (errstr),
695 			    gettext("Unable to open filename %s"
696 			    " for configuration dump (%s)."),
697 			    filename, strerror(errno));
698 			MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE,
699 			    strdup(errstr), NULL);
700 			__s_api_release_config(ptr);
701 			return (errorp);
702 		}
703 		if (rc == 0) {
704 			if (fchmod(fileno(fp), buf.st_mode) != 0) {
705 				(void) snprintf(errstr, sizeof (errstr),
706 				    gettext("Unable to set permissions for file"
707 				    " %s for configuration dump (%s)."),
708 				    filename, strerror(errno));
709 				(void) fclose(fp);
710 				file_export_error = B_TRUE;
711 				break;
712 			}
713 		} else {
714 			if (fchmod(fileno(fp), 0400) != 0) {
715 				(void) snprintf(errstr, sizeof (errstr),
716 				    gettext("Unable to set permissions for file"
717 				    " %s for configuration dump (%s)."),
718 				    filename, strerror(errno));
719 				(void) fclose(fp);
720 				file_export_error = B_TRUE;
721 				break;
722 			}
723 		}
724 		if (fprintf(fp, "#\n# %s\n#\n", DONOTEDIT) < 0) {
725 			(void) snprintf(errstr, sizeof (errstr), gettext(
726 			    "Writing to file %s for configuration dump failed "
727 			    "(%s)."), filename, strerror(errno));
728 			file_export_error = B_TRUE;
729 		}
730 
731 		/* assume VERSION is set and it outputs first */
732 
733 		/* For each parameter - construct value */
734 		for (i = 0; !file_export_error && (i <= NS_LDAP_MAX_PIT_P);
735 		    i++) {
736 			cfgtype = __s_api_get_configtype(i);
737 			if ((docred == 0 && cfgtype == CREDCONFIG) ||
738 			    (docred == 1 && cfgtype != CREDCONFIG))
739 				continue;
740 
741 			str = __s_api_strValue(ptr, string, BUFSIZ, i,
742 			    NS_FILE_FMT);
743 			if (str == NULL)
744 				continue;
745 			if (fprintf(fp, "%s\n", str) < 0) {
746 				(void) snprintf(errstr, sizeof (errstr),
747 				    gettext("Writing to file %s for"
748 				    "configuration dump failed (%s)."),
749 				    filename, strerror(errno));
750 				file_export_error = B_TRUE;
751 			}
752 
753 			if (str != (char *)&string[0]) {
754 				free(str);
755 				str = NULL;
756 			}
757 		}
758 		if (fclose(fp) != 0) {
759 			/* Break if error already hit */
760 			if (file_export_error)
761 				break;
762 
763 			(void) snprintf(errstr, sizeof (errstr), gettext(
764 			    "Writing to file %s for configuration dump failed "
765 			    "during file close (%s)."), filename,
766 			    strerror(errno));
767 			file_export_error = B_TRUE;
768 			break;
769 		}
770 
771 	}
772 
773 	if (file_export_error) {
774 		MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE,
775 		    strdup(errstr), NULL);
776 		(void) unlink(filename);
777 	}
778 
779 	__s_api_release_config(ptr);
780 	return (errorp);
781 }
782 
783 ns_ldap_error_t *
784 __ns_ldap_DumpConfiguration(char *file)
785 {
786 	ns_ldap_error_t	*ret;
787 	char		*files[3];
788 
789 	files[0] = NULL;
790 	files[1] = NULL;
791 	files[2] = NULL;
792 	if (strcmp(file, NSCONFIGFILE) == 0) {
793 		files[0] = file;
794 	} else if (strcmp(file, NSCONFIGREFRESH) == 0) {
795 		files[0] = file;
796 	} else if (strcmp(file, NSCREDFILE) == 0) {
797 		files[1] = file;
798 	} else if (strcmp(file, NSCREDREFRESH) == 0) {
799 		files[1] = file;
800 	}
801 	ret = __ns_ldap_DumpConfigFiles(files);
802 	return (ret);
803 }
804 
805 /*
806  * **************************************************************
807  * Misc Routines
808  * **************************************************************
809  */
810 
811 ns_config_t *
812 __ns_ldap_make_config(ns_ldap_result_t *result)
813 {
814 	int		l, m;
815 	char		val[BUFSIZ];
816 	char    	*attrname;
817 	ns_ldap_entry_t	*entry;
818 	ns_ldap_attr_t	*attr;
819 	char		**attrval;
820 	ParamIndexType	index;
821 	ns_config_t	*ptr;
822 	ns_ldap_error_t	*error = NULL;
823 	int		firsttime;
824 	int		prof_ver;
825 	ns_config_t	*curr_ptr = NULL;
826 	char		errstr[MAXERROR];
827 	ns_ldap_error_t	*errorp;
828 
829 	if (result == NULL)
830 		return (NULL);
831 
832 	if (result->entries_count > 1) {
833 		(void) snprintf(errstr, MAXERROR,
834 		    gettext("Configuration Error: More than one profile "
835 		    "found"));
836 		MKERROR(LOG_ERR, errorp, NS_PARSE_ERR, strdup(errstr), NULL);
837 		(void) __ns_ldap_freeError(&errorp);
838 		return (NULL);
839 	}
840 
841 	ptr = __s_api_create_config();
842 	if (ptr == NULL)
843 		return (NULL);
844 
845 	curr_ptr = __s_api_get_default_config();
846 	if (curr_ptr == NULL) {
847 		__s_api_destroy_config(ptr);
848 		return (NULL);
849 	}
850 
851 	/* Check to see if the profile is version 1 or version 2 */
852 	prof_ver = 1;
853 	entry = result->entry;
854 	for (l = 0; l < entry->attr_count; l++) {
855 		attr = entry->attr_pair[l];
856 
857 		attrname = attr->attrname;
858 		if (attrname == NULL)
859 			continue;
860 		if (strcasecmp(attrname, "objectclass") == 0) {
861 			for (m = 0; m < attr->value_count; m++) {
862 				if (strcasecmp(_PROFILE2_OBJECTCLASS,
863 				    attr->attrvalue[m]) == 0) {
864 					prof_ver = 2;
865 					break;
866 				}
867 			}
868 		}
869 	}
870 	/* update the configuration to accept v1 or v2 attributes */
871 	if (prof_ver == 1) {
872 		(void) strcpy(val, NS_LDAP_VERSION_1);
873 		(void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P,
874 		    val, &error);
875 	} else {
876 		(void) strcpy(val, NS_LDAP_VERSION_2);
877 		(void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P,
878 		    val, &error);
879 	}
880 
881 	for (l = 0; l < entry->attr_count; l++) {
882 		attr = entry->attr_pair[l];
883 
884 		attrname = attr->attrname;
885 		if (attrname == NULL)
886 			continue;
887 		if (__s_api_get_profiletype(attrname, &index) != 0)
888 			continue;
889 
890 		attrval = attr->attrvalue;
891 		switch (index) {
892 		case NS_LDAP_SEARCH_DN_P:
893 		case NS_LDAP_SERVICE_SEARCH_DESC_P:
894 		case NS_LDAP_ATTRIBUTEMAP_P:
895 		case NS_LDAP_OBJECTCLASSMAP_P:
896 		case NS_LDAP_SERVICE_CRED_LEVEL_P:
897 		case NS_LDAP_SERVICE_AUTH_METHOD_P:
898 			/* Multiple Value - insert 1 at a time */
899 			for (m = 0; m < attr->value_count; m++) {
900 				(void) __ns_ldap_setParamValue(ptr, index,
901 				    attrval[m], &error);
902 			}
903 			break;
904 		default:
905 			firsttime = 1;
906 			/* Single or Multiple Value */
907 			val[0] = '\0';
908 			for (m = 0; m < attr->value_count; m++) {
909 				if (firsttime == 1) {
910 					firsttime = 0;
911 					(void) strlcpy(val, attrval[m],
912 					    sizeof (val));
913 				} else {
914 					(void) strlcat(val, " ", sizeof (val));
915 					(void) strlcat(val, attrval[m],
916 					    sizeof (val));
917 				}
918 			}
919 			(void) __ns_ldap_setParamValue(ptr, index, val, &error);
920 
921 			break;
922 		}
923 	}
924 	if (ptr->version != NS_LDAP_V1) {
925 		if (curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_ptype == CHARPTR) {
926 			(void) __ns_ldap_setParamValue(ptr, NS_LDAP_BINDDN_P,
927 			    curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_pc,
928 			    &error);
929 		}
930 		if (curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_ptype ==
931 		    CHARPTR) {
932 			(void) __ns_ldap_setParamValue(ptr,
933 			    NS_LDAP_BINDPASSWD_P,
934 			    curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_pc,
935 			    &error);
936 		}
937 		if (curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_ptype ==
938 		    CHARPTR) {
939 			(void) __ns_ldap_setParamValue(ptr,
940 			    NS_LDAP_HOST_CERTPATH_P,
941 			    curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_pc,
942 			    &error);
943 		}
944 	}
945 	__s_api_release_config(curr_ptr);
946 	return (ptr);
947 }
948 
949 /*
950  * Download a profile into our internal structure.  The calling application
951  * needs to DumpConfig() to save the information to NSCONFIGFILE and NSCREDFILE
952  * if desired.
953  */
954 int
955 __ns_ldap_download(const char *profile, char *addr, char *baseDN,
956 	ns_ldap_error_t **errorp)
957 {
958 	char filter[BUFSIZ];
959 	int rc;
960 	ns_ldap_result_t *result = NULL;
961 	ns_config_t	*ptr = NULL;
962 	ns_config_t	*new_ptr = NULL;
963 	char		errstr[MAXERROR];
964 
965 	*errorp = NULL;
966 	if (baseDN == NULL)
967 		return (NS_LDAP_INVALID_PARAM);
968 
969 	ptr = __s_api_get_default_config();
970 	if (ptr == NULL) {
971 		(void) snprintf(errstr, sizeof (errstr),
972 		    gettext("No configuration information available."));
973 		MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
974 		    NULL);
975 		return (NS_LDAP_CONFIG);
976 	}
977 
978 	rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SEARCH_BASEDN_P, baseDN,
979 	    errorp);
980 	if (rc != NS_LDAP_SUCCESS) {
981 		__s_api_release_config(ptr);
982 		return (rc);
983 	}
984 
985 	rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SERVERS_P, addr, errorp);
986 	__s_api_release_config(ptr);
987 	if (rc != NS_LDAP_SUCCESS)
988 		return (rc);
989 
990 	(void) snprintf(filter, sizeof (filter), _PROFILE_FILTER,
991 	    _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
992 	rc = __ns_ldap_list(_PROFILE_CONTAINER, (const char *)filter,
993 	    NULL, NULL, NULL, 0, &result, errorp, NULL, NULL);
994 
995 	if (rc != NS_LDAP_SUCCESS)
996 		return (rc);
997 
998 	new_ptr = __ns_ldap_make_config(result);
999 	(void) __ns_ldap_freeResult(&result);
1000 
1001 	if (new_ptr == NULL)
1002 		return (NS_LDAP_OP_FAILED);
1003 
1004 	rc = __s_api_crosscheck(new_ptr, errstr, B_FALSE);
1005 	if (rc != NS_LDAP_SUCCESS) {
1006 		__s_api_destroy_config(new_ptr);
1007 		MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
1008 		    NULL);
1009 		return (NS_LDAP_CONFIG);
1010 	}
1011 
1012 	__s_api_init_config(new_ptr);
1013 	return (rc);
1014 }
1015 
1016 /*
1017  * **************************************************************
1018  * Configuration Printing Routines
1019  * **************************************************************
1020  */
1021 
1022 /*
1023  * Yes the use of stdio is okay here because all we are doing is sending
1024  * output to stdout.  This would not be necessary if we could get to the
1025  * configuration pointer outside this file.
1026  */
1027 ns_ldap_error_t *
1028 __ns_ldap_print_config(int verbose)
1029 {
1030 	ns_config_t	*ptr;
1031 	char		errstr[MAXERROR];
1032 	ns_ldap_error_t *errorp;
1033 	char		string[BUFSIZE];
1034 	char		*str;
1035 	int		i;
1036 
1037 	ptr = __s_api_get_default_config();
1038 	if (ptr == NULL) {
1039 		errorp = __ns_ldap_LoadConfiguration();
1040 		if (errorp != NULL)
1041 			return (errorp);
1042 		ptr = __s_api_get_default_config();
1043 		if (ptr == NULL) {
1044 			(void) snprintf(errstr, sizeof (errstr),
1045 			    gettext("No configuration information."));
1046 			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
1047 			    strdup(errstr), NULL);
1048 			return (errorp);
1049 		}
1050 	}
1051 
1052 	if (verbose && (ptr->domainName != NULL)) {
1053 		(void) fputs("ptr->domainName ", stdout);
1054 		(void) fputs(ptr->domainName, stdout);
1055 		(void) putchar('\n');
1056 	}
1057 	/* For each parameter - construct value */
1058 	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
1059 			/*
1060 			 * Version 1 skipped this entry because:
1061 			 *
1062 			 * don't print default cache TTL for now since
1063 			 * we don't store it in the ldap_client_file.
1064 			 */
1065 		if ((i == NS_LDAP_CACHETTL_P) && (ptr->version == NS_LDAP_V1))
1066 			continue;
1067 
1068 		str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_FILE_FMT);
1069 		if (str == NULL)
1070 			continue;
1071 		if (verbose)
1072 			(void) putchar('\t');
1073 		(void) fprintf(stdout, "%s\n", str);
1074 		if (str != (char *)&string[0]) {
1075 			free(str);
1076 			str = NULL;
1077 		}
1078 	}
1079 
1080 	__s_api_release_config(ptr);
1081 	return (NULL);
1082 }
1083