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