OpenSolaris_b135/lib/libsldap/common/ns_confmgr.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* libsldap - cachemgr side configuration components */

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <libintl.h>
#include <string.h>
#include <ctype.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <locale.h>
#include <errno.h>
#include <sys/time.h>

#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"

#define	ALWAYS		1


/*
 * **************************************************************
 * Configuration File Routines
 * **************************************************************
 */


/* Size of the errstr buffer needs to be MAXERROR */
static int
read_line(FILE *fp, char *buffer, int buflen, char *errstr)
{
	int	linelen;
	char	c;

	*errstr = '\0';

	for (linelen = 0; linelen < buflen; ) {
		c = getc(fp);
		if (c == EOF)
			break;
		switch (c) {
		case '\n':
			if (linelen > 0 && buffer[linelen - 1] == '\\') {
				/* Continuation line found */
				--linelen;
			} else {
				/* end of line found */
				buffer[linelen] = '\0';
				return (linelen);
			}
			break;
		default:
			buffer[linelen++] = c;
		}
	}

	if (linelen >= buflen) {
		(void) snprintf(errstr, MAXERROR,
		    gettext("Buffer overflow, line too long."));
		return (-2);
	} else if (linelen > 0 && buffer[linelen - 1] == '\\') {
		(void) snprintf(errstr, MAXERROR,
		    gettext("Unterminated continuation line."));
		return (-2);
	} else {
		/* end of file */
		buffer[linelen] = '\0';
	}
	return (linelen > 0 ? linelen : -1);
}


static ns_parse_status
read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error)
{
	ParamIndexType	i = 0;
	char		errstr[MAXERROR];
	char		buffer[BUFSIZE], *name, *value;
	int		emptyfile, lineno;
	FILE		*fp;
	int		ret;
	int		linelen;
	char		*file;
	int		first = 1;


	if (cred_file) {
		file = NSCREDFILE;
	} else {
		file = NSCONFIGFILE;
	}
	fp = fopen(file, "rF");
	if (fp == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("Unable to open filename '%s' "
		    "for reading (errno=%d)."), file, errno);
		MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errstr), NULL);
		return (NS_NOTFOUND);
	}

	emptyfile = 1;
	lineno = 0;
	for (; ; ) {
		if ((linelen = read_line(fp, buffer, sizeof (buffer),
		    errstr)) < 0)
			/* End of file */
			break;
		lineno++;
		if (linelen == 0)
			continue;
		/* get rid of comment lines */
		if (buffer[0] == '#')
			continue;
		emptyfile = 0;
		name = NULL;
		value = NULL;
		__s_api_split_key_value(buffer, &name, &value);
		if (name == NULL || value == NULL) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("Missing Name or Value on line %d."),
			    lineno);
			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
			    strdup(errstr), NULL);
			(void) fclose(fp);
			return (NS_PARSE_ERR);
		}
		if (__s_api_get_versiontype(ptr, name, &i) != 0) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("Illegal profile type on line %d."),
			    lineno);
			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
			    strdup(errstr), NULL);
			(void) fclose(fp);
			return (NS_PARSE_ERR);
		}
		if (!first && i == NS_LDAP_FILE_VERSION_P) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("Illegal NS_LDAP_FILE_VERSION "
			    "on line %d."), lineno);
			MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
			    strdup(errstr), NULL);
			(void) fclose(fp);
			return (NS_PARSE_ERR);
		}
		first = 0;
		switch (__s_api_get_configtype(i)) {
		case SERVERCONFIG:
		case CLIENTCONFIG:
			if (cred_file == 0) {
				ret = __ns_ldap_setParamValue(ptr, i, value,
				    error);
				if (ret != NS_SUCCESS) {
					(void) fclose(fp);
					return (ret);
				}
			} else if (i != NS_LDAP_FILE_VERSION_P) {
				(void) snprintf(errstr, sizeof (errstr),
				    gettext("Illegal entry in '%s' on "
				    "line %d"), file, lineno);
				MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
				    strdup(errstr), NULL);
				(void) fclose(fp);
				return (NS_PARSE_ERR);
			}
			break;
		case CREDCONFIG:
			if (i == NS_LDAP_FILE_VERSION_P)
				break;
			if (cred_file) {
				ret = __ns_ldap_setParamValue(ptr, i, value,
				    error);
				if (ret != NS_SUCCESS) {
					(void) fclose(fp);
					return (ret);
				}
			} else {
				(void) snprintf(errstr, sizeof (errstr),
				    gettext("Illegal entry in '%s' on "
				    "line %d"), file, lineno);
				MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
				    strdup(errstr), NULL);
				(void) fclose(fp);
				return (NS_PARSE_ERR);
			}
		}
	}
	(void) fclose(fp);
	if (!cred_file && emptyfile) {
		/* Error in read_line */
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("Empty config file: '%s'"), file);
		MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
		    NULL);
		return (NS_PARSE_ERR);
	}
	if (linelen == -2) {
		/* Error in read_line */
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("Line too long in '%s'"), file);
		MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
		    NULL);
		return (NS_PARSE_ERR);
	}
	return (NS_SUCCESS);
}


static
ns_ldap_return_code
set_attr(ns_config_t *config_struct,
		char *attr_name,
		char *attr_val,
		ns_ldap_error_t **errorp)
{
	ParamIndexType	idx;
	char		errmsg[MAXERROR];

	if (errorp == NULL) {
		return (NS_LDAP_INVALID_PARAM);
	}

	*errorp = NULL;

	/*
	 * This double call is made due to the presence of
	 * two sets of LDAP config. attribute names.
	 * An LDAP configuration can be obtained either from a server
	 * or from SMF. The former sends a DUA with attributes' names
	 * styled like "preferredServerList". But local configurations
	 * will have names inherited from the /var/ldap/ldap* files such as
	 * "NS_LDAP_SERVER_PREF".
	 * So, the standalone bits are able to process both sets of
	 * attributes' names.
	 */
	if (__s_api_get_profiletype(attr_name, &idx) < 0 &&
	    __s_api_get_versiontype(config_struct, attr_name, &idx) < 0) {
		(void) snprintf(errmsg, sizeof (errmsg),
		    gettext("Illegal DUAProfile property: <%s>."), attr_name);
		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
		return (NS_LDAP_CONFIG);
	}

	return (__ns_ldap_setParamValue(config_struct, idx, attr_val, errorp));
}


/*
 * This function creates a configuration which will be used
 * for all LDAP requests in the Standalone mode.
 *
 * INPUT:
 *     config - a buffer returned by __ns_ldap_getConnectionInfo()'s
 *              dua_profile parameter.
 *
 */
ns_config_t *
__s_api_create_config_door_str(char *config, ns_ldap_error_t **errorp)
{
	char		*attr, *attrName, *attrVal, *rest;
	ns_config_t	*configStruct = NULL;
	char		errmsg[MAXERROR];

	if (config == NULL || errorp == NULL)
		return (NULL);

	if ((configStruct = __s_api_create_config()) == NULL) {
		return (NULL);
	}

	*errorp = NULL;

	attr = strtok_r(config, DOORLINESEP, &rest);
	if (!attr) {
		__s_api_destroy_config(configStruct);
		(void) snprintf(errmsg, sizeof (errmsg),
		    gettext("DUAProfile received from the server"
		    " has bad format"));
		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
		return (NULL);
	}

	do {
		__s_api_split_key_value(attr, &attrName, &attrVal);

		if (attrName == NULL || attrVal == NULL) {
			__s_api_destroy_config(configStruct);
			(void) snprintf(errmsg, sizeof (errmsg),
			    gettext("Attribute %s is not valid"), attr);
			MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG,
			    strdup(errmsg), NULL);
			return (NULL);
		}

		/* Get the version of the profile. */
		if (strcasecmp(attrName, "objectclass") == 0) {
			if (strcasecmp(attrVal, _PROFILE2_OBJECTCLASS) == 0) {
				if (__ns_ldap_setParamValue(configStruct,
				    NS_LDAP_FILE_VERSION_P,
				    NS_LDAP_VERSION_2,
				    errorp) != NS_LDAP_SUCCESS) {
					__s_api_destroy_config(configStruct);
					return (NULL);
				}
			} else if (strcasecmp(attrVal,
			    _PROFILE1_OBJECTCLASS) == 0) {
				if (__ns_ldap_setParamValue(configStruct,
				    NS_LDAP_FILE_VERSION_P,
				    NS_LDAP_VERSION_1,
				    errorp) != NS_LDAP_SUCCESS) {
					__s_api_destroy_config(configStruct);
					return (NULL);
				}
			}
			continue;
		}

		if (set_attr(configStruct, attrName, attrVal, errorp) !=
		    NS_LDAP_SUCCESS) {
			__s_api_destroy_config(configStruct);
			return (NULL);
		}
	} while (attr = strtok_r(NULL, DOORLINESEP, &rest));

	if (__s_api_crosscheck(configStruct, errmsg, B_FALSE) != NS_SUCCESS) {
		MKERROR(LOG_ERR, *errorp, NS_LDAP_CONFIG, strdup(errmsg), NULL);
		__s_api_destroy_config(configStruct);
		return (NULL);
	}

	return (configStruct);
}


/*
 * Cache Manager side of configuration file loading
 */

ns_ldap_error_t *
__ns_ldap_LoadConfiguration()
{
	ns_ldap_error_t	*error = NULL;
	ns_config_t	*ptr = NULL;
	char		errstr[MAXERROR];
	ns_parse_status	ret;


	ptr = __s_api_create_config();
	if (ptr == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("__ns_ldap_LoadConfiguration: Out of memory."));
		MKERROR(LOG_ERR, error, NS_CONFIG_NOTLOADED,
		    strdup(errstr), NULL);
		return (error);
	}

	/* Load in Configuration file */
	ret = read_file(ptr, 0, &error);
	if (ret != NS_SUCCESS) {
		__s_api_destroy_config(ptr);
		return (error);
	}

	/* Load in Credential file */
	ret = read_file(ptr, 1, &error);
	if (ret != NS_SUCCESS) {
		__s_api_destroy_config(ptr);
		return (error);
	}

	if (__s_api_crosscheck(ptr, errstr, B_TRUE) != NS_SUCCESS) {
		__s_api_destroy_config(ptr);
		MKERROR(LOG_ERR, error, NS_CONFIG_SYNTAX, strdup(errstr), NULL);
		return (error);
	}

	__s_api_init_config(ptr);
	return (NULL);
}


int
__print2buf(LineBuf *line, const char *toprint, char *sep)
{
	int	newsz = 0;
	int	newmax = 0;
	char	*str;

	if (line == NULL)
		return (-1);

	newsz = strlen(toprint) + line->len + 1;
	if (sep != NULL) {
		newsz += strlen(sep);
	}
	if (line->alloc == 0 || newsz > line->alloc) {
		/* Round up to next buffer and add 1 */
		newmax = (((newsz+(BUFSIZ-1))/BUFSIZ)+1) * BUFSIZ;
		if (line->alloc == 0)
			line->str = (char *)calloc(newmax, 1);
		else {
			/*
			 * if realloc() returns NULL,
			 * the original buffer is untouched.
			 * It needs to be freed.
			 */
			str = (char *)realloc(line->str, newmax);
			if (str == NULL) {
				free(line->str);
				line->str = NULL;
			}
			else
				line->str = str;
		}
		line->alloc = newmax;
		if (line->str == NULL) {
			line->alloc = 0;
			line->len = 0;
			return (-1);
		}
	}
	/* now add new 'toprint' data to buffer */
	(void) strlcat(line->str, toprint, line->alloc);
	if (sep != NULL) {
		(void) strlcat(line->str, sep, line->alloc);
	}
	line->len = newsz;
	return (0);
}


/*
 * __ns_ldap_LoadDoorInfo is a routine used by the ldapcachemgr
 * to create a configuration buffer to transmit back to a client
 * domainname is transmitted to ldapcachemgr and ldapcachemgr uses
 * it to select a configuration to transmit back.  Otherwise it
 * is essentially unused in sldap.
 * If cred_only is not 0, then only the credentials for shadow
 * update are taken care of.
 */

ns_ldap_error_t *
__ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname,
			ns_config_t *new, int cred_only)
{
	ns_config_t	*ptr;
	char		errstr[MAXERROR];
	ns_ldap_error_t	*errorp;
	char		*str;
	ParamIndexType	i = 0;
	int		len;
	ldap_config_out_t *cout;

	/*
	 * If new is NULL, it outputs the flatten data of current default
	 * config, if it's non-NULL, it outputs the flatten data of a temporary
	 * config. It's used to compare the new config data with the current
	 * default config data.
	 */
	if (new == NULL)
		ptr = __s_api_get_default_config();
	else
		ptr = new;
	if (ptr == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("No configuration information available for %s."),
		    domainname == NULL ? "<no domain specified>" : domainname);
		MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
		    strdup(errstr), NULL);
		return (errorp);
	}
	(void) memset((char *)configinfo, 0, sizeof (LineBuf));
	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
		if (cred_only) {
			/* only exposed credential for shadow update */
			if (i != NS_LDAP_ADMIN_BINDDN_P &&
			    i != NS_LDAP_ADMIN_BINDPASSWD_P)
				continue;
		} else {
			/* credential for shadow update is not to be exposed */
			if (i == NS_LDAP_ADMIN_BINDDN_P ||
			    i == NS_LDAP_ADMIN_BINDPASSWD_P)
				continue;
		}
		str = __s_api_strValue(ptr, i, NS_DOOR_FMT);
		if (str == NULL)
			continue;
		if (__print2buf(configinfo, str, DOORLINESEP)) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("__print2buf: Out of memory."));
			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
			    strdup(errstr), NULL);
			__s_api_release_config(ptr);
			free(str);
			return (errorp);
		}
		free(str);
	}
	if (new == NULL)
		__s_api_release_config(ptr);

	/*
	 * The new interface of the configuration between ldap_cachemgr
	 * & libsldap contains a header structure ldap_config_out_t.
	 * The flatten configuration data configinfo->str is cloned
	 * to cout->config_str, configinfo->len is saved in
	 * cout->data_size and cout->cookie is set later after this function
	 * is returned in ldap_cachemgr.
	 * configinfo->str & configinfo->len are reused to save info of
	 * header + data.
	 * The format:
	 * [cookie|data_size|config_str .............]
	 */

	if (configinfo->str) {
		len = sizeof (ldap_config_out_t) - sizeof (int) +
		    configinfo->len;
		if ((cout = calloc(1, len)) == NULL) {
			free(configinfo->str);
			configinfo->str = NULL;
			configinfo->len = 0;
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("calloc: Out of memory."));
			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
			    strdup(errstr), NULL);
			return (errorp);
		}
		/*
		 * cout->cookie is set by the caller,
		 * which is in ldap_cachemgr.
		 */
		cout->data_size = configinfo->len;
		(void) memcpy(cout->config_str, configinfo->str,
		    configinfo->len);
		free(configinfo->str);
		configinfo->str = (char *)cout;
		configinfo->len = len;
	}
	return (NULL);
}


ns_ldap_error_t *
__ns_ldap_DumpLdif(char *filename)
{
	ns_config_t	*ptr;
	char		errstr[MAXERROR];
	ns_ldap_error_t	*errorp;
	char		*str;
	FILE		*fp;
	ParamIndexType	i = 0;
	char		*profile, *container, *base;

	ptr = __s_api_get_default_config();
	if (ptr == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("No configuration information available."));
		MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
		    NULL);
		return (errorp);
	}

	if (filename == NULL) {
		fp = stdout;
	} else {
		fp = fopen(filename, "wF");
		if (fp == NULL) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("Unable to open filename %s for ldif "
			    "dump (errno=%d)."), filename, errno);
			MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE,
			    strdup(errstr), NULL);
			__s_api_release_config(ptr);
			return (errorp);
		}
		(void) fchmod(fileno(fp), 0444);
	}

	if (ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_ptype != CHARPTR ||
	    ptr->paramList[NS_LDAP_PROFILE_P].ns_ptype != CHARPTR) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("Required BaseDN and/or Profile name "
		    "ldif fields not present"));
		MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE, strdup(errstr),
		    NULL);
		__s_api_release_config(ptr);
		return (errorp);
	}

	profile = ptr->paramList[NS_LDAP_PROFILE_P].ns_pc;
	base = ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_pc;
	container = _PROFILE_CONTAINER;

	/*
	 * Construct DN, but since this is the profile, there is no need
	 * to worry about mapping.  The profile itself can not be mapped
	 */
	(void) fprintf(fp, "dn: cn=%s,ou=%s,%s\n", profile, container, base);

	/* dump objectclass names */
	if (ptr->version == NS_LDAP_V1) {
		(void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n",
		    _PROFILE1_OBJECTCLASS);
	} else {
		(void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n",
		    _PROFILE2_OBJECTCLASS);
	}

	/* For each parameter - construct value */
	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
		str = __s_api_strValue(ptr, i, NS_LDIF_FMT);
		if (str == NULL)
			continue;
		/*
		 * don't dump binddn, bind password, admin binddn, admin
		 * bind password, enableShadowUpdate flag, or cert path
		 * as they are not part of version 2 profiles
		 */
		if ((i != NS_LDAP_BINDDN_P) &&
		    (i != NS_LDAP_BINDPASSWD_P) &&
		    (i != NS_LDAP_ADMIN_BINDDN_P) &&
		    (i != NS_LDAP_ADMIN_BINDPASSWD_P) &&
		    (i != NS_LDAP_ENABLE_SHADOW_UPDATE_P) &&
		    (i != NS_LDAP_HOST_CERTPATH_P))
			(void) fprintf(fp, "%s\n", str);
		free(str);
	}

	if (filename != NULL)
		(void) fclose(fp);

	__s_api_release_config(ptr);
	return (NULL);
}

/*
 * This routine can process the configuration  and/or
 * the credential files at the same time.
 * files is char *[3] = { "config", "cred", NULL };
 */

static
ns_ldap_error_t *
__ns_ldap_DumpConfigFiles(char **files)
{
	char		*filename;
	int		fi;
	int		docred;
	ns_config_t	*ptr;
	char		*str;
	char		errstr[MAXERROR];
	ParamIndexType	i = 0;
	FILE		*fp;
	int		rc;
	ns_ldap_error_t	*errorp = NULL;
	struct stat	buf;
	int		cfgtype;
	boolean_t	file_export_error = B_FALSE;

	ptr = __s_api_get_default_config();
	if (ptr == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("No configuration information available."));
		MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
		    NULL);
		return (errorp);
	}

	for (fi = 0; fi < 2; fi++) {
		docred = 0;
		filename = files[fi];
		if (filename == NULL)
			continue;
		if (fi == 1)
			docred++;
		rc = stat(filename, &buf);
		fp = fopen(filename, "wF");
		if (fp == NULL) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("Unable to open filename %s"
			    " for configuration dump (%s)."),
			    filename, strerror(errno));
			MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE,
			    strdup(errstr), NULL);
			__s_api_release_config(ptr);
			return (errorp);
		}
		if (rc == 0) {
			if (fchmod(fileno(fp), buf.st_mode) != 0) {
				(void) snprintf(errstr, sizeof (errstr),
				    gettext("Unable to set permissions for file"
				    " %s for configuration dump (%s)."),
				    filename, strerror(errno));
				(void) fclose(fp);
				file_export_error = B_TRUE;
				break;
			}
		} else {
			if (fchmod(fileno(fp), 0400) != 0) {
				(void) snprintf(errstr, sizeof (errstr),
				    gettext("Unable to set permissions for file"
				    " %s for configuration dump (%s)."),
				    filename, strerror(errno));
				(void) fclose(fp);
				file_export_error = B_TRUE;
				break;
			}
		}
		if (fprintf(fp, "#\n# %s\n#\n", DONOTEDIT) < 0) {
			(void) snprintf(errstr, sizeof (errstr), gettext(
			    "Writing to file %s for configuration dump failed "
			    "(%s)."), filename, strerror(errno));
			file_export_error = B_TRUE;
		}

		/* assume VERSION is set and it outputs first */

		/* For each parameter - construct value */
		for (i = 0; !file_export_error && (i <= NS_LDAP_MAX_PIT_P);
		    i++) {
			cfgtype = __s_api_get_configtype(i);
			if ((docred == 0 && cfgtype == CREDCONFIG) ||
			    (docred == 1 && cfgtype != CREDCONFIG))
				continue;

			str = __s_api_strValue(ptr, i, NS_FILE_FMT);
			if (str == NULL)
				continue;
			if (fprintf(fp, "%s\n", str) < 0) {
				(void) snprintf(errstr, sizeof (errstr),
				    gettext("Writing to file %s for"
				    "configuration dump failed (%s)."),
				    filename, strerror(errno));
				file_export_error = B_TRUE;
			}

			free(str);
		}
		if (fclose(fp) != 0) {
			/* Break if error already hit */
			if (file_export_error)
				break;

			(void) snprintf(errstr, sizeof (errstr), gettext(
			    "Writing to file %s for configuration dump failed "
			    "during file close (%s)."), filename,
			    strerror(errno));
			file_export_error = B_TRUE;
			break;
		}

	}

	if (file_export_error) {
		MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE,
		    strdup(errstr), NULL);
		(void) unlink(filename);
	}

	__s_api_release_config(ptr);
	return (errorp);
}

ns_ldap_error_t *
__ns_ldap_DumpConfiguration(char *file)
{
	ns_ldap_error_t	*ret;
	char		*files[3];

	files[0] = NULL;
	files[1] = NULL;
	files[2] = NULL;
	if (strcmp(file, NSCONFIGFILE) == 0) {
		files[0] = file;
	} else if (strcmp(file, NSCONFIGREFRESH) == 0) {
		files[0] = file;
	} else if (strcmp(file, NSCREDFILE) == 0) {
		files[1] = file;
	} else if (strcmp(file, NSCREDREFRESH) == 0) {
		files[1] = file;
	}
	ret = __ns_ldap_DumpConfigFiles(files);
	return (ret);
}

/*
 * **************************************************************
 * Misc Routines
 * **************************************************************
 */

ns_config_t *
__ns_ldap_make_config(ns_ldap_result_t *result)
{
	int		l, m;
	char		val[BUFSIZE];
	char    	*attrname;
	ns_ldap_entry_t	*entry;
	ns_ldap_attr_t	*attr;
	char		**attrval;
	ParamIndexType	index;
	ns_config_t	*ptr;
	ns_ldap_error_t	*error = NULL;
	int		prof_ver;
	ns_config_t	*curr_ptr = NULL;
	char		errstr[MAXERROR];
	ns_ldap_error_t	*errorp;
	LineBuf		buffer;
	char		*sepstr;

	if (result == NULL)
		return (NULL);

	if (result->entries_count > 1) {
		(void) snprintf(errstr, MAXERROR,
		    gettext("Configuration Error: More than one profile "
		    "found"));
		MKERROR(LOG_ERR, errorp, NS_PARSE_ERR, strdup(errstr), NULL);
		(void) __ns_ldap_freeError(&errorp);
		return (NULL);
	}

	ptr = __s_api_create_config();
	if (ptr == NULL)
		return (NULL);

	curr_ptr = __s_api_get_default_config();
	if (curr_ptr == NULL) {
		__s_api_destroy_config(ptr);
		return (NULL);
	}

	/* Check to see if the profile is version 1 or version 2 */
	prof_ver = 1;
	entry = result->entry;
	for (l = 0; l < entry->attr_count; l++) {
		attr = entry->attr_pair[l];

		attrname = attr->attrname;
		if (attrname == NULL)
			continue;
		if (strcasecmp(attrname, "objectclass") == 0) {
			for (m = 0; m < attr->value_count; m++) {
				if (strcasecmp(_PROFILE2_OBJECTCLASS,
				    attr->attrvalue[m]) == 0) {
					prof_ver = 2;
					break;
				}
			}
		}
	}
	/* update the configuration to accept v1 or v2 attributes */
	if (prof_ver == 1) {
		(void) strcpy(val, NS_LDAP_VERSION_1);
		(void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P,
		    val, &error);
	} else {
		(void) strcpy(val, NS_LDAP_VERSION_2);
		(void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P,
		    val, &error);
	}

	for (l = 0; l < entry->attr_count; l++) {
		attr = entry->attr_pair[l];

		attrname = attr->attrname;
		if (attrname == NULL)
			continue;
		if (__s_api_get_profiletype(attrname, &index) != 0)
			continue;

		attrval = attr->attrvalue;
		switch (index) {
		case NS_LDAP_SEARCH_DN_P:
		case NS_LDAP_SERVICE_SEARCH_DESC_P:
		case NS_LDAP_ATTRIBUTEMAP_P:
		case NS_LDAP_OBJECTCLASSMAP_P:
		case NS_LDAP_SERVICE_CRED_LEVEL_P:
		case NS_LDAP_SERVICE_AUTH_METHOD_P:
			/* Multiple Value - insert 1 at a time */
			for (m = 0; m < attr->value_count; m++) {
				(void) __ns_ldap_setParamValue(ptr, index,
				    attrval[m], &error);
			}
			break;
		default:
			(void) memset((void *)&buffer, 0, sizeof (LineBuf));

			/* Single or Multiple Value */
			for (m = 0; m < attr->value_count; m++) {
				sepstr = NULL;
				if (m != attr->value_count - 1) {
					sepstr = SPACESEP;
				}
				if (__print2buf(&buffer, attrval[m], sepstr))
					goto makeconfigerror;
			}
			(void) __ns_ldap_setParamValue(ptr, index, buffer.str,
			    &error);
			if (buffer.len > 0) {
				free(buffer.str);
				buffer.len = 0;
			}
			break;
		}
	}
	if (ptr->version != NS_LDAP_V1) {
		ParamIndexType i;
		if (curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_ptype == CHARPTR) {
			(void) __ns_ldap_setParamValue(ptr, NS_LDAP_BINDDN_P,
			    curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_pc,
			    &error);
		}
		if (curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_ptype ==
		    CHARPTR) {
			(void) __ns_ldap_setParamValue(ptr,
			    NS_LDAP_BINDPASSWD_P,
			    curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_pc,
			    &error);
		}
		i = NS_LDAP_ENABLE_SHADOW_UPDATE_P;
		if (curr_ptr->paramList[i].ns_ptype == INT) {
			char *valt;
			valt = __s_get_shadowupdate_name(
			    curr_ptr->paramList[i].ns_i);
			(void) __ns_ldap_setParamValue(ptr, i, valt, &error);
		}
		if (curr_ptr->paramList[NS_LDAP_ADMIN_BINDDN_P].ns_ptype ==
		    CHARPTR) {
			(void) __ns_ldap_setParamValue(ptr,
			    NS_LDAP_ADMIN_BINDDN_P,
			    curr_ptr->paramList[NS_LDAP_ADMIN_BINDDN_P].ns_pc,
			    &error);
		}
		if (curr_ptr->paramList[NS_LDAP_ADMIN_BINDPASSWD_P].ns_ptype ==
		    CHARPTR) {
			(void) __ns_ldap_setParamValue(ptr,
			    NS_LDAP_ADMIN_BINDPASSWD_P,
			    curr_ptr->
			    paramList[NS_LDAP_ADMIN_BINDPASSWD_P].ns_pc,
			    &error);
		}
		if (curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_ptype ==
		    CHARPTR) {
			(void) __ns_ldap_setParamValue(ptr,
			    NS_LDAP_HOST_CERTPATH_P,
			    curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_pc,
			    &error);
		}
	}
	__s_api_release_config(curr_ptr);
	return (ptr);

makeconfigerror:
	if (buffer.len > 0)
		free(buffer.str);

	__s_api_debug_pause(LOG_ERR, NS_PARSE_ERR,
	    "__ns_ldap_make_config: Not enough memory");
	return (NULL);
}

/*
 * Download a profile into our internal structure.  The calling application
 * needs to DumpConfig() to save the information to NSCONFIGFILE and NSCREDFILE
 * if desired.
 */
int
__ns_ldap_download(const char *profile, char *addr, char *baseDN,
	ns_ldap_error_t **errorp)
{
	char filter[BUFSIZE];
	int rc;
	ns_ldap_result_t *result = NULL;
	ns_config_t	*ptr = NULL;
	ns_config_t	*new_ptr = NULL;
	char		errstr[MAXERROR];

	*errorp = NULL;
	if (baseDN == NULL)
		return (NS_LDAP_INVALID_PARAM);

	ptr = __s_api_get_default_config();
	if (ptr == NULL) {
		(void) snprintf(errstr, sizeof (errstr),
		    gettext("No configuration information available."));
		MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
		    NULL);
		return (NS_LDAP_CONFIG);
	}

	rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SEARCH_BASEDN_P, baseDN,
	    errorp);
	if (rc != NS_LDAP_SUCCESS) {
		__s_api_release_config(ptr);
		return (rc);
	}

	rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SERVERS_P, addr, errorp);
	__s_api_release_config(ptr);
	if (rc != NS_LDAP_SUCCESS)
		return (rc);

	(void) snprintf(filter, sizeof (filter), _PROFILE_FILTER,
	    _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile);
	rc = __ns_ldap_list(_PROFILE_CONTAINER, (const char *)filter,
	    NULL, NULL, NULL, 0, &result, errorp, NULL, NULL);

	if (rc != NS_LDAP_SUCCESS)
		return (rc);

	new_ptr = __ns_ldap_make_config(result);
	(void) __ns_ldap_freeResult(&result);

	if (new_ptr == NULL)
		return (NS_LDAP_OP_FAILED);

	rc = __s_api_crosscheck(new_ptr, errstr, B_FALSE);
	if (rc != NS_LDAP_SUCCESS) {
		__s_api_destroy_config(new_ptr);
		MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr),
		    NULL);
		return (NS_LDAP_CONFIG);
	}

	__s_api_init_config(new_ptr);
	return (rc);
}

/*
 * **************************************************************
 * Configuration Printing Routines
 * **************************************************************
 */

/*
 * Yes the use of stdio is okay here because all we are doing is sending
 * output to stdout.  This would not be necessary if we could get to the
 * configuration pointer outside this file.
 */
ns_ldap_error_t *
__ns_ldap_print_config(int verbose)
{
	ns_config_t	*ptr;
	char		errstr[MAXERROR];
	ns_ldap_error_t *errorp;
	char		*str;
	int		i;

	ptr = __s_api_get_default_config();
	if (ptr == NULL) {
		errorp = __ns_ldap_LoadConfiguration();
		if (errorp != NULL)
			return (errorp);
		ptr = __s_api_get_default_config();
		if (ptr == NULL) {
			(void) snprintf(errstr, sizeof (errstr),
			    gettext("No configuration information."));
			MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED,
			    strdup(errstr), NULL);
			return (errorp);
		}
	}

	if (verbose && (ptr->domainName != NULL)) {
		(void) fputs("ptr->domainName ", stdout);
		(void) fputs(ptr->domainName, stdout);
		(void) putchar('\n');
	}
	/* For each parameter - construct value */
	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
			/*
			 * Version 1 skipped this entry because:
			 *
			 * don't print default cache TTL for now since
			 * we don't store it in the ldap_client_file.
			 */
		if ((i == NS_LDAP_CACHETTL_P) && (ptr->version == NS_LDAP_V1))
			continue;

		/* the credential for shadow update is not to be exposed */
		if (i == NS_LDAP_ADMIN_BINDDN_P ||
		    i == NS_LDAP_ADMIN_BINDPASSWD_P)
			continue;

		str = __s_api_strValue(ptr, i, NS_FILE_FMT);
		if (str == NULL)
			continue;
		if (verbose)
			(void) putchar('\t');
		(void) fprintf(stdout, "%s\n", str);
		free(str);
	}

	__s_api_release_config(ptr);
	return (NULL);
}