/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
 *
 *	Openvision retains the copyright to derivative works of
 *	this source code.  Do *NOT* create a derivative of this
 *	source code before consulting with your legal department.
 *	Do *NOT* integrate *ANY* of this source code into another
 *	product before consulting with your legal department.
 *
 *	For further information, read the top-level Openvision
 *	copyright which is contained in the top-level MIT Kerberos
 *	copyright.
 *
 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
 *
 */


/*
 * lib/kadm/alt_prof.c
 *
 * Copyright 1995 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 *
 */

/*
 * alt_prof.c - Implement alternate profile file handling.
 */
#include <k5-int.h>
#include <kadm5/admin.h>
#include <adm_proto.h>
#include <stdio.h>
#include <ctype.h>
#include <os-proto.h>
#include <kdb/kdb_log.h>

krb5_error_code kadm5_free_config_params();

#define	DEFAULT_ENCTYPE_LIST \
	"aes256-cts-hmac-sha1-96:normal " \
	"aes128-cts-hmac-sha1-96:normal " \
	"des3-cbc-hmac-sha1-kd:normal " \
	"arcfour-hmac-md5:normal " \
	"arcfour-hmac-md5-exp:normal " \
	"des-cbc-md5:normal " \
	"des-cbc-crc:normal"

/*
 * krb5_aprof_init()	- Initialize alternate profile context.
 *
 * Parameters:
 *	fname		- default file name of the profile.
 *	envname		- environment variable name which can override fname.
 *	acontextp	- Pointer to opaque context for alternate profile.
 *
 * Returns:
 *	error codes from profile_init()
 */
krb5_error_code
krb5_aprof_init(fname, envname, acontextp)
char		*fname;
char		*envname;
krb5_pointer	*acontextp;
{
	krb5_error_code	kret;
	const char		*namelist[2];
	profile_t		profile;

	namelist[1] = (char *)NULL;
	profile = (profile_t)NULL;
	if (envname) {
		if ((namelist[0] = getenv(envname))) {
			kret = profile_init(namelist, &profile);
			if (kret)
				return (kret);
			*acontextp = (krb5_pointer) profile;
			return (0);
		}
	}
	profile = (profile_t)NULL;
	if (fname) {
		kret = profile_init_path(fname, &profile);
		if (kret == ENOENT) {
			profile = 0;
		} else if (kret)
			return (kret);
		*acontextp = (krb5_pointer) profile;
		return (0);
	}
	return (0);
}

/*
 * krb5_aprof_getvals()	- Get values from alternate profile.
 *
 * Parameters:
 *	acontext	- opaque context for alternate profile.
 *	hierarchy	- hierarchy of value to retrieve.
 *	retdata		- Returned data values.
 *
 * Returns:
 * 	error codes from profile_get_values()
 */
krb5_error_code
krb5_aprof_getvals(acontext, hierarchy, retdata)
krb5_pointer	acontext;
const char		**hierarchy;
char		***retdata;
{
	return (profile_get_values((profile_t)acontext,
				hierarchy,
				retdata));
}

/*
 * krb5_aprof_get_deltat()	- Get a delta time value from the alternate
 *				  profile.
 *
 * Parameters:
 *	acontext		- opaque context for alternate profile.
 *	hierarchy		- hierarchy of value to retrieve.
 *	uselast			- if true, use last value, otherwise use
 *				  first value found.
 *	deltatp			- returned delta time value.
 *
 * Returns:
 * 	error codes from profile_get_values()
 *	error codes from krb5_string_to_deltat()
 */
krb5_error_code
krb5_aprof_get_deltat(acontext, hierarchy, uselast, deltatp)
krb5_pointer	acontext;
const char		**hierarchy;
krb5_boolean	uselast;
krb5_deltat		*deltatp;
{
	krb5_error_code	kret;
	char		**values;
	char		*valp;
	int			index;

	if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
		index = 0;
		if (uselast) {
			for (index = 0; values[index]; index++);
			index--;
		}
		valp = values[index];
		kret = krb5_string_to_deltat(valp, deltatp);

		/* Free the string storage */
		for (index = 0; values[index]; index++)
			krb5_xfree(values[index]);
		krb5_xfree(values);
	}
	return (kret);
}

/*
 * krb5_aprof_get_string()	- Get a string value from the alternate
 *				  profile.
 *
 * Parameters:
 *	acontext		- opaque context for alternate profile.
 *	hierarchy		- hierarchy of value to retrieve.
 *	uselast			- if true, use last value, otherwise use
 *				  first value found.
 *	stringp			- returned string value.
 *
 * Returns:
 * 	error codes from profile_get_values()
 */
krb5_error_code
krb5_aprof_get_string(acontext, hierarchy, uselast, stringp)
krb5_pointer	acontext;
const char		**hierarchy;
krb5_boolean	uselast;
char		**stringp;
{
	krb5_error_code	kret;
	char		**values;
	int			index, i;

	if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
		index = 0;
		if (uselast) {
			for (index = 0; values[index]; index++);
			index--;
		}

		*stringp = values[index];

		/* Free the string storage */
		for (i = 0; values[i]; i++)
			if (i != index)
				krb5_xfree(values[i]);
		krb5_xfree(values);
	}
	return (kret);
}

/*
 * krb5_aprof_get_int32()	- Get a 32-bit integer value from the alternate
 *				  profile.
 *
 * Parameters:
 *	acontext		- opaque context for alternate profile.
 *	hierarchy		- hierarchy of value to retrieve.
 *	uselast			- if true, use last value, otherwise use
 *				  first value found.
 *	intp			- returned 32-bit integer value.
 *
 * Returns:
 * 	error codes from profile_get_values()
 *	EINVAL			- value is not an integer
 */
krb5_error_code
krb5_aprof_get_int32(acontext, hierarchy, uselast, intp)
krb5_pointer	acontext;
const char		**hierarchy;
krb5_boolean	uselast;
krb5_int32		*intp;
{
	krb5_error_code	kret;
	char		**values;
	int			index;

	if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
		index = 0;
		if (uselast) {
			for (index = 0; values[index]; index++);
			index--;
		}

		if (sscanf(values[index], "%d", intp) != 1)
			kret = EINVAL;

		/* Free the string storage */
		for (index = 0; values[index]; index++)
			krb5_xfree(values[index]);
		krb5_xfree(values);
	}
	return (kret);
}

/*
 * krb5_aprof_finish()	- Finish alternate profile context.
 *
 * Parameter:
 *	acontext	- opaque context for alternate profile.
 *
 * Returns:
 *	0 on success, something else on failure.
 */
krb5_error_code
krb5_aprof_finish(acontext)
krb5_pointer	acontext;
{
	profile_release(acontext);
	return (0);
}

/*
 * Function: kadm5_get_config_params
 *
 * Purpose: Merge configuration parameters provided by the caller with
 * values specified in configuration files and with default values.
 *
 * Arguments:
 *
 *	context(r) krb5_context to use
 *	profile(r) profile file to use
 *	envname(r) envname that contains a profile name to
 *			override profile
 *	params_in(r) params structure containing user-supplied
 *			values, or NULL
 *	params_out(w) params structure to be filled in
 *
 * Effects:
 *
 * The fields and mask of params_out are filled in with values
 * obtained from params_in, the specified profile, and default
 * values.  Only and all fields specified in params_out->mask are
 * set.  The context of params_out must be freed with
 * kadm5_free_config_params.
 *
 * params_in and params_out may be the same pointer.  However, all pointers
 * in params_in for which the mask is set will be re-assigned to newly copied
 * versions, overwriting the old pointer value.
 */
krb5_error_code kadm5_get_config_params(context, kdcprofile, kdcenv,
					params_in, params_out)
krb5_context		context;
char			*kdcprofile;
char			*kdcenv;
kadm5_config_params	*params_in, *params_out;
{
	char		*filename;
	char		*envname;
	char		*lrealm;
	krb5_pointer	aprofile = 0;
	const char	*hierarchy[4];
	char		*svalue;
	krb5_int32		ivalue;
	kadm5_config_params params, empty_params;

	krb5_error_code	kret = 0;
	krb5_error_code dnsret = 1;

#ifdef KRB5_DNS_LOOKUP
	char dns_host[MAX_DNS_NAMELEN];
	unsigned short dns_portno;
	krb5_data dns_realm;
#endif /* KRB5_DNS_LOOKUP */

	memset((char *)&params, 0, sizeof (params));
	memset((char *)&empty_params, 0, sizeof (empty_params));

	if (params_in == NULL) params_in = &empty_params;

	if (params_in->mask & KADM5_CONFIG_REALM) {
		lrealm = params.realm = strdup(params_in->realm);
		if (params.realm)
			params.mask |= KADM5_CONFIG_REALM;
	} else {
		kret = krb5_get_default_realm(context, &lrealm);
		if (kret)
			goto cleanup;
		params.realm = lrealm;
		params.mask |= KADM5_CONFIG_REALM;
	}
	if (params_in->mask & KADM5_CONFIG_PROFILE) {
		filename = params.profile = strdup(params_in->profile);
		if (params.profile)
			params.mask |= KADM5_CONFIG_PROFILE;
		envname = NULL;
	} else {
		/*
		 * XXX These defaults should to work on both client and
		 * server.  kadm5_get_config_params can be implemented as a
		 * wrapper function in each library that provides correct
		 * defaults for NULL values.
		 */
		filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE;
		envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV;
		if (context->profile_secure == TRUE) envname = 0;
	}

	kret = krb5_aprof_init(filename, envname, &aprofile);
	if (kret)
		goto cleanup;

	/* Initialize realm parameters */
	hierarchy[0] = "realms";
	hierarchy[1] = lrealm;
	hierarchy[3] = (char *)NULL;

#ifdef KRB5_DNS_LOOKUP
	/*
	 * Initialize realm info for (possible) DNS lookups.
	 */
	dns_realm.data = strdup(lrealm);
	dns_realm.length = strlen(lrealm);
	dns_realm.magic = 0;
#endif /* KRB5_DNS_LOOKUP */

	/* Get the value for the admin server */
	hierarchy[2] = "admin_server";
	if (params_in->mask & KADM5_CONFIG_ADMIN_SERVER) {
		params.admin_server = strdup(params_in->admin_server);
		if (params.admin_server)
			params.mask |= KADM5_CONFIG_ADMIN_SERVER;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.admin_server = svalue;
		params.mask |= KADM5_CONFIG_ADMIN_SERVER;
	}
#ifdef KRB5_DNS_LOOKUP
	else if (strcmp(envname, "KRB5_CONFIG") == 0) {
		/*
		 * Solaris Kerberos: only do DNS lookup for admin_server if this
		 * is a krb5.conf type of config file.  Note, the filename may
		 * not be /etc/krb5/krb5.conf so we assume that the KRB5_CONFIG
		 * envname string will consistently indicate the type of config
		 * file.
		 */
		dnsret = krb5_get_servername(context, &dns_realm,
					"_kerberos-adm", "_udp",
					dns_host, &dns_portno);
		if (dnsret == 0) {
			params.admin_server = strdup(dns_host);
			if (params.admin_server)
				params.mask |= KADM5_CONFIG_ADMIN_SERVER;
			params.kadmind_port = dns_portno;
			params.mask |= KADM5_CONFIG_KADMIND_PORT;
		}
	}
#endif /* KRB5_DNS_LOOKUP */

	if ((params.mask & KADM5_CONFIG_ADMIN_SERVER) && dnsret) {
		char *p;
		if (p = strchr(params.admin_server, ':')) {
			params.kadmind_port = atoi(p+1);
			params.mask |= KADM5_CONFIG_KADMIND_PORT;
			*p = '\0';
		}
	}

	/* Get the value for the database */
	hierarchy[2] = "database_name";
	if (params_in->mask & KADM5_CONFIG_DBNAME) {
		params.dbname = strdup(params_in->dbname);
		if (params.dbname)
			params.mask |= KADM5_CONFIG_DBNAME;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.dbname = svalue;
		params.mask |= KADM5_CONFIG_DBNAME;
	} else {
		params.dbname = strdup(DEFAULT_KDB_FILE);
		if (params.dbname)
			params.mask |= KADM5_CONFIG_DBNAME;
	}

	/*
	 * admin database name and lockfile are now always derived from dbname
	 */
	if (params.mask & KADM5_CONFIG_DBNAME) {
		params.admin_dbname = (char *)malloc(strlen(params.dbname)
						    + 7);
		if (params.admin_dbname) {
			sprintf(params.admin_dbname, "%s.kadm5",
				params.dbname);
			params.mask |= KADM5_CONFIG_ADBNAME;
		}
	}

	if (params.mask & KADM5_CONFIG_ADBNAME) {
		params.admin_lockfile =
			(char *)malloc(strlen(params.admin_dbname)+ 6);
		if (params.admin_lockfile) {
			sprintf(params.admin_lockfile, "%s.lock",
				params.admin_dbname);
			params.mask |= KADM5_CONFIG_ADB_LOCKFILE;
		}
	}

	/* Get the value for the admin(policy) database lock file */
	hierarchy[2] = "admin_keytab";
	if (params_in->mask & KADM5_CONFIG_ADMIN_KEYTAB) {
		params.admin_keytab = strdup(params_in->admin_keytab);
		if (params.admin_keytab)
			params.mask |= KADM5_CONFIG_ADMIN_KEYTAB;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.mask |= KADM5_CONFIG_ADMIN_KEYTAB;
		params.admin_keytab = svalue;
	} else if (params.admin_keytab = (char *)getenv("KRB5_KTNAME")) {
		params.admin_keytab = strdup(params.admin_keytab);
		if (params.admin_keytab)
			params.mask |= KADM5_CONFIG_ADMIN_KEYTAB;
	} else {
		params.admin_keytab = strdup(DEFAULT_KADM5_KEYTAB);
		if (params.admin_keytab)
			params.mask |= KADM5_CONFIG_ADMIN_KEYTAB;
	}

	/* Get the name of the acl file */
	hierarchy[2] = "acl_file";
	if (params_in->mask & KADM5_CONFIG_ACL_FILE) {
		params.acl_file = strdup(params_in->acl_file);
		if (params.acl_file)
			params.mask |= KADM5_CONFIG_ACL_FILE;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.mask |= KADM5_CONFIG_ACL_FILE;
		params.acl_file = svalue;
	} else {
		params.acl_file = strdup(DEFAULT_KADM5_ACL_FILE);
		if (params.acl_file)
			params.mask |= KADM5_CONFIG_ACL_FILE;
	}

	/* Get the name of the dict file */
	hierarchy[2] = "dict_file";
	if (params_in->mask & KADM5_CONFIG_DICT_FILE) {
		params.dict_file = strdup(params_in->dict_file);
		if (params.dict_file)
			params.mask |= KADM5_CONFIG_DICT_FILE;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.mask |= KADM5_CONFIG_DICT_FILE;
		params.dict_file = svalue;
	}

	/* Get the value for the kadmind port */
	if (! (params.mask & KADM5_CONFIG_KADMIND_PORT)) {
		hierarchy[2] = "kadmind_port";
		if (params_in->mask & KADM5_CONFIG_KADMIND_PORT) {
			params.mask |= KADM5_CONFIG_KADMIND_PORT;
			params.kadmind_port = params_in->kadmind_port;
		} else if (aprofile &&
			!krb5_aprof_get_int32(aprofile, hierarchy, TRUE,
					    &ivalue)) {
			params.kadmind_port = ivalue;
			params.mask |= KADM5_CONFIG_KADMIND_PORT;
		} else {
			params.kadmind_port = DEFAULT_KADM5_PORT;
			params.mask |= KADM5_CONFIG_KADMIND_PORT;
		}
	}

	/* Get the value for the master key name */
	hierarchy[2] = "master_key_name";
	if (params_in->mask & KADM5_CONFIG_MKEY_NAME) {
		params.mkey_name = strdup(params_in->mkey_name);
		if (params.mkey_name)
			params.mask |= KADM5_CONFIG_MKEY_NAME;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.mask |= KADM5_CONFIG_MKEY_NAME;
		params.mkey_name = svalue;
	}

	/* Get the value for the master key type */
	hierarchy[2] = "master_key_type";
	if (params_in->mask & KADM5_CONFIG_ENCTYPE) {
		params.mask |= KADM5_CONFIG_ENCTYPE;
		params.enctype = params_in->enctype;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		if (!krb5_string_to_enctype(svalue, &params.enctype)) {
			params.mask |= KADM5_CONFIG_ENCTYPE;
			krb5_xfree(svalue);
		}
	} else {
		params.mask |= KADM5_CONFIG_ENCTYPE;
		params.enctype = DEFAULT_KDC_ENCTYPE;
	}

	/* Get the value for mkey_from_kbd */
	if (params_in->mask & KADM5_CONFIG_MKEY_FROM_KBD) {
		params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
		params.mkey_from_kbd = params_in->mkey_from_kbd;
	}

	/* Get the value for the stashfile */
	hierarchy[2] = "key_stash_file";
	if (params_in->mask & KADM5_CONFIG_STASH_FILE) {
		params.stash_file = strdup(params_in->stash_file);
		if (params.stash_file)
			params.mask |= KADM5_CONFIG_STASH_FILE;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		params.mask |= KADM5_CONFIG_STASH_FILE;
		params.stash_file = svalue;
	}

	/*
	 * Get the value for maximum ticket lifetime.
	 * See SEAM documentation or the Bug ID 4184504
	 * We have changed the logic so that the entries are
	 * created in the database with the maximum duration
	 * for life and renew life KRB5_INT32_MAX
	 * However this wil get negotiated down when
	 * as or tgs request is processed by KDC.
	 */
	hierarchy[2] = "max_life";
	if (params_in->mask & KADM5_CONFIG_MAX_LIFE) {
		params.mask |= KADM5_CONFIG_MAX_LIFE;
		params.max_life = params_in->max_life;
	} else {
		params.mask |= KADM5_CONFIG_MAX_LIFE;
		params.max_life = KRB5_INT32_MAX;
	}

	/* Get the value for maximum renewable ticket lifetime. */
	hierarchy[2] = "max_renewable_life";
	if (params_in->mask & KADM5_CONFIG_MAX_RLIFE) {
		params.mask |= KADM5_CONFIG_MAX_RLIFE;
		params.max_rlife = params_in->max_rlife;
	} else {
		params.mask |= KADM5_CONFIG_MAX_RLIFE;
		params.max_rlife =  KRB5_INT32_MAX;
	}

	/* Get the value for the default principal expiration */
	hierarchy[2] = "default_principal_expiration";
	if (params_in->mask & KADM5_CONFIG_EXPIRATION) {
		params.mask |= KADM5_CONFIG_EXPIRATION;
		params.expiration = params_in->expiration;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		if (!krb5_string_to_timestamp(svalue, &params.expiration)) {
			params.mask |= KADM5_CONFIG_EXPIRATION;
			krb5_xfree(svalue);
		}
	} else {
		params.mask |= KADM5_CONFIG_EXPIRATION;
		params.expiration = 0;
	}

	/* Get the value for the default principal flags */
	hierarchy[2] = "default_principal_flags";
	if (params_in->mask & KADM5_CONFIG_FLAGS) {
		params.mask |= KADM5_CONFIG_FLAGS;
		params.flags = params_in->flags;
	} else if (aprofile &&
		!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		char *sp, *ep, *tp;

		sp = svalue;
		params.flags = 0;
		while (sp) {
			if ((ep = strchr(sp, (int)',')) ||
			    (ep = strchr(sp, (int)' ')) ||
			    (ep = strchr(sp, (int)'\t'))) {
				/* Fill in trailing whitespace of sp */
				tp = ep - 1;
				while (isspace(*tp) && (tp < sp)) {
					*tp = '\0';
					tp--;
				}
				*ep = '\0';
				ep++;
				/* Skip over trailing whitespace of ep */
				while (isspace(*ep) && (*ep)) ep++;
			}
			/* Convert this flag */
			if (krb5_string_to_flags(sp,
						"+",
						"-",
						&params.flags))
				break;
			sp = ep;
		}
		if (!sp)
			params.mask |= KADM5_CONFIG_FLAGS;
		krb5_xfree(svalue);
	} else {
		params.mask |= KADM5_CONFIG_FLAGS;
		params.flags = KRB5_KDB_DEF_FLAGS;
	}

	/* Get the value for the supported enctype/salttype matrix */
	hierarchy[2] = "supported_enctypes";
	if (params_in->mask & KADM5_CONFIG_ENCTYPES) {
		params.mask |= KADM5_CONFIG_ENCTYPES;
		if (params_in->num_keysalts > 0) {
		    params.keysalts = malloc(params_in->num_keysalts *
			    sizeof (*params.keysalts));
		    if (params.keysalts == NULL) {
			kret = ENOMEM;
			goto cleanup;
		    }
		    (void) memcpy(params.keysalts, params_in->keysalts,
			    (params_in->num_keysalts *
			    sizeof (*params.keysalts)));
		    params.num_keysalts = params_in->num_keysalts;
		}
	} else {
		svalue = NULL;
		if (aprofile)
			krb5_aprof_get_string(aprofile, hierarchy,
					    TRUE, &svalue);
		if (svalue == NULL)
			svalue = strdup(DEFAULT_ENCTYPE_LIST);

		params.keysalts = NULL;
		params.num_keysalts = 0;
		krb5_string_to_keysalts(svalue,
					", \t", /* Tuple separators	*/
					":.-",	/* Key/salt separators	*/
					0,	/* No duplicates	*/
					&params.keysalts,
					&params.num_keysalts);
		if (params.num_keysalts)
			params.mask |= KADM5_CONFIG_ENCTYPES;

		if (svalue)
			krb5_xfree(svalue);
	}

	hierarchy[2] = "kpasswd_server";
	if (params_in->mask & KADM5_CONFIG_KPASSWD_SERVER) {
		params.mask |= KADM5_CONFIG_KPASSWD_SERVER;
		params.kpasswd_server = strdup(params_in->kpasswd_server);
	} else {
		svalue = NULL;

		if (aprofile)
			krb5_aprof_get_string(aprofile, hierarchy,
					    TRUE, &svalue);
		if (svalue == NULL) {
#ifdef KRB5_DNS_LOOKUP
			if (strcmp(envname, "KRB5_CONFIG") == 0) {
				/*
				 * Solaris Kerberos: only do DNS lookup for
				 * kpasswd_server if this is a krb5.conf type of
				 * config file.  Note, the filename may not be
				 * /etc/krb5/krb5.conf so we assume that the
				 * KRB5_CONFIG envname string will consistently
				 * indicate the type of config file.
				 */
				dnsret = krb5_get_servername(context,
				    &dns_realm, "_kpasswd", "_udp",
				    dns_host, &dns_portno);

				if (dnsret == 0) {
					params.kpasswd_server =
					    strdup(dns_host);
					if (params.kpasswd_server) {
						params.mask |=
						    KADM5_CONFIG_KPASSWD_SERVER;
					}
					params.kpasswd_port = dns_portno;
					params.mask |=
					    KADM5_CONFIG_KPASSWD_PORT;
				}
			}
#endif /* KRB5_DNS_LOOKUP */

			/*
			 * If a unique 'kpasswd_server' is not specified,
			 * use the normal 'admin_server'.
			 */
			if ((params.mask & KADM5_CONFIG_ADMIN_SERVER) &&
				    dnsret) {
				params.kpasswd_server =
					strdup(params.admin_server);
				params.mask |= KADM5_CONFIG_KPASSWD_SERVER;
			}
		} else {
			char *p;
			params.kpasswd_server = svalue;
			params.mask |= KADM5_CONFIG_KPASSWD_SERVER;

			if ((p = strchr(params.kpasswd_server, ':'))) {
				params.kpasswd_port = atoi(p+1);
				params.mask |= KADM5_CONFIG_KPASSWD_PORT;
				*p = '\0';
			}
		}
	}

	hierarchy[2] = "kpasswd_protocol";

	/* default to current RPCSEC_GSS protocol */
	params.kpasswd_protocol = KRB5_CHGPWD_RPCSEC;
	params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;

	if (params_in->mask & KADM5_CONFIG_KPASSWD_PROTOCOL) {
		params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;
		params.kpasswd_protocol = params_in->kpasswd_protocol;
	} else {
		svalue = NULL;

		if (aprofile)
			krb5_aprof_get_string(aprofile, hierarchy,
					    TRUE, &svalue);
		if (svalue != NULL) {
			if (strcasecmp(svalue, "RPCSEC_GSS") == 0) {
				params.kpasswd_protocol = KRB5_CHGPWD_RPCSEC;
				params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;
			} else if (strcasecmp(svalue, "SET_CHANGE") == 0) {
				params.kpasswd_protocol =
					KRB5_CHGPWD_CHANGEPW_V2;
				params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;
			}
		}
		if (svalue)
			krb5_xfree(svalue);
	}

	/*
	 * If the kpasswd_port is not yet defined, define it now.
	 */
	if (! (params.mask & KADM5_CONFIG_KPASSWD_PORT)) {
		if (params_in->mask & KADM5_CONFIG_KPASSWD_PORT)
			params.kpasswd_port = params_in->kpasswd_port;
		/*
		 * If kpasswd_port is not explicitly defined,
		 * determine the port to use based on the protocol.
		 * The alternative protocol uses a different port
		 * than the standard admind port.
		 */
		else if (params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC) {
			params.kpasswd_port = DEFAULT_KADM5_PORT;
		} else {
			/*
			 * When using the Horowitz/IETF protocol for
			 * password changing, the default port is 464
			 * (officially recognized by IANA).
			 */
			params.kpasswd_port = DEFAULT_KPASSWD_PORT;
		}
		params.mask |= KADM5_CONFIG_KPASSWD_PORT;
	}

	hierarchy[2] = "sunw_dbprop_enable";

	params.iprop_enabled = FALSE;
	params.mask |= KADM5_CONFIG_IPROP_ENABLED;

	if (params_in->mask & KADM5_CONFIG_IPROP_ENABLED) {
		params.mask |= KADM5_CONFIG_IPROP_ENABLED;
		params.iprop_enabled = params_in->iprop_enabled;
	} else {
		if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy,
		    TRUE, &svalue)) {
			if (strncasecmp(svalue, "Y", 1) == 0)
				params.iprop_enabled = TRUE;
			if (strncasecmp(svalue, "true", 4) == 0)
				params.iprop_enabled = TRUE;
			params.mask |= KADM5_CONFIG_IPROP_ENABLED;
			krb5_xfree(svalue);
		}
	}

	hierarchy[2] = "sunw_dbprop_master_ulogsize";

	params.iprop_ulogsize = DEF_ULOGENTRIES;
	params.mask |= KADM5_CONFIG_ULOG_SIZE;

	if (params_in->mask & KADM5_CONFIG_ULOG_SIZE) {
		params.mask |= KADM5_CONFIG_ULOG_SIZE;
		params.iprop_ulogsize = params_in->iprop_ulogsize;
	} else {
		if (aprofile && !krb5_aprof_get_int32(aprofile, hierarchy,
		    TRUE, &ivalue)) {
			if (ivalue > MAX_ULOGENTRIES)
				params.iprop_ulogsize = MAX_ULOGENTRIES;
			else if (ivalue <= 0)
				params.iprop_ulogsize = DEF_ULOGENTRIES;
			else
				params.iprop_ulogsize = ivalue;
			params.mask |= KADM5_CONFIG_ULOG_SIZE;
		}
	}

	hierarchy[2] = "sunw_dbprop_slave_poll";

	params.iprop_polltime = "2m";
	params.mask |= KADM5_CONFIG_POLL_TIME;

	if (params_in->mask & KADM5_CONFIG_POLL_TIME) {
		params.iprop_polltime = strdup(params_in->iprop_polltime);
		if (params.iprop_polltime)
			params.mask |= KADM5_CONFIG_POLL_TIME;
	} else {
		if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy,
		    TRUE, &svalue)) {
			params.iprop_polltime = strdup(svalue);
			params.mask |= KADM5_CONFIG_POLL_TIME;
			krb5_xfree(svalue);
		}
	}

	*params_out = params;

cleanup:
	if (aprofile)
		krb5_aprof_finish(aprofile);
	if (kret) {
		(void) kadm5_free_config_params(context, &params);
		params_out->mask = 0;
	}
#ifdef KRB5_DNS_LOOKUP
	if (dns_realm.data)
		free(dns_realm.data);
#endif /* KRB5_DNS_LOOKUP */

	return (kret);
}
/*
 * kadm5_free_config_params()	- Free data allocated by above.
 */
/*ARGSUSED*/
krb5_error_code
kadm5_free_config_params(context, params)
krb5_context	context;
kadm5_config_params	*params;
{
	if (params) {
		if (params->profile) {
			krb5_xfree(params->profile);
			params->profile = NULL;
		}
		if (params->dbname) {
			krb5_xfree(params->dbname);
			params->dbname = NULL;
		}
		if (params->mkey_name) {
			krb5_xfree(params->mkey_name);
			params->mkey_name = NULL;
		}
		if (params->stash_file) {
			krb5_xfree(params->stash_file);
			params->stash_file = NULL;
		}
		if (params->keysalts) {
			krb5_xfree(params->keysalts);
			params->keysalts = NULL;
			params->num_keysalts = 0;
		}
		if (params->admin_keytab) {
			free(params->admin_keytab);
			params->admin_keytab = NULL;
		}
		if (params->dict_file) {
			free(params->dict_file);
			params->dict_file = NULL;
		}
		if (params->acl_file) {
			free(params->acl_file);
			params->acl_file = NULL;
		}
		if (params->realm) {
			free(params->realm);
			params->realm = NULL;
		}
		if (params->admin_dbname) {
			free(params->admin_dbname);
			params->admin_dbname = NULL;
		}
		if (params->admin_lockfile) {
			free(params->admin_lockfile);
			params->admin_lockfile = NULL;
		}
		if (params->admin_server) {
			free(params->admin_server);
			params->admin_server = NULL;
		}
		if (params->kpasswd_server) {
			free(params->kpasswd_server);
			params->kpasswd_server = NULL;
		}
	}
	return (0);
}

/*
 * This is the old krb5_realm_read_params, which I mutated into
 * kadm5_get_config_params but which old code(kdb5_* and krb5kdc)
 * still uses.
 */

/*
 * krb5_read_realm_params()	- Read per-realm parameters from KDC
 *				  alternate profile.
 */
krb5_error_code
krb5_read_realm_params(kcontext, realm, kdcprofile, kdcenv, rparamp)
krb5_context	kcontext;
char		*realm;
char		*kdcprofile;
char		*kdcenv;
krb5_realm_params	**rparamp;
{
	char		*filename;
	char		*envname;
	char		*lrealm;
	krb5_pointer	aprofile = 0;
	krb5_realm_params	*rparams;
	const char		*hierarchy[4];
	char		*svalue;
	krb5_int32		ivalue;
	krb5_deltat		dtvalue;

	krb5_error_code	kret;

	filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE;
	envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV;

	if (kcontext->profile_secure == TRUE) envname = 0;

	rparams = (krb5_realm_params *) NULL;
	if (realm)
		lrealm = strdup(realm);
	else {
		kret = krb5_get_default_realm(kcontext, &lrealm);
		if (kret)
			goto cleanup;
	}

	kret = krb5_aprof_init(filename, envname, &aprofile);
	if (kret)
		goto cleanup;

	rparams = (krb5_realm_params *) malloc(sizeof (krb5_realm_params));
	if (rparams == 0) {
		kret = ENOMEM;
		goto cleanup;
	}

	/* Initialize realm parameters */
	memset((char *)rparams, 0, sizeof (krb5_realm_params));

	/* Get the value for the database */
	hierarchy[0] = "realms";
	hierarchy[1] = lrealm;
	hierarchy[2] = "database_name";
	hierarchy[3] = (char *)NULL;
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
		rparams->realm_dbname = svalue;

	/* Get the value for the KDC port list */
	hierarchy[2] = "kdc_ports";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
		rparams->realm_kdc_ports = svalue;
	hierarchy[2] = "kdc_tcp_ports";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
	    rparams->realm_kdc_tcp_ports = svalue;

	/* Get the name of the acl file */
	hierarchy[2] = "acl_file";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
		rparams->realm_acl_file = svalue;

	/* Get the value for the kadmind port */
	hierarchy[2] = "kadmind_port";
	if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) {
		rparams->realm_kadmind_port = ivalue;
		rparams->realm_kadmind_port_valid = 1;
	}

	/* Get the value for the master key name */
	hierarchy[2] = "master_key_name";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
		rparams->realm_mkey_name = svalue;

	/* Get the value for the master key type */
	hierarchy[2] = "master_key_type";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		if (!krb5_string_to_enctype(svalue, &rparams->realm_enctype))
			rparams->realm_enctype_valid = 1;
		krb5_xfree(svalue);
	}

	/* Get the value for the stashfile */
	hierarchy[2] = "key_stash_file";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
		rparams->realm_stash_file = svalue;

	/* Get the value for maximum ticket lifetime. */
	hierarchy[2] = "max_life";
	if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) {
		rparams->realm_max_life = dtvalue;
		rparams->realm_max_life_valid = 1;
	}

	/* Get the value for maximum renewable ticket lifetime. */
	hierarchy[2] = "max_renewable_life";
	if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) {
		rparams->realm_max_rlife = dtvalue;
		rparams->realm_max_rlife_valid = 1;
	}

	/* Get the value for the default principal expiration */
	hierarchy[2] = "default_principal_expiration";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		if (!krb5_string_to_timestamp(svalue,
					    &rparams->realm_expiration))
			rparams->realm_expiration_valid = 1;
		krb5_xfree(svalue);
	}

	/* Get the value for the default principal flags */
	hierarchy[2] = "default_principal_flags";
	if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
		char *sp, *ep, *tp;

		sp = svalue;
		rparams->realm_flags = 0;
		while (sp) {
			if ((ep = strchr(sp, (int)',')) ||
			    (ep = strchr(sp, (int)' ')) ||
			    (ep = strchr(sp, (int)'\t'))) {
				/* Fill in trailing whitespace of sp */
				tp = ep - 1;
				while (isspace(*tp) && (tp < sp)) {
					*tp = '\0';
					tp--;
				}
				*ep = '\0';
				ep++;
				/* Skip over trailing whitespace of ep */
				while (isspace(*ep) && (*ep)) ep++;
			}
				/* Convert this flag */
			if (krb5_string_to_flags(sp,
						"+",
						"-",
						&rparams->realm_flags))
				break;
			sp = ep;
		}
		if (!sp)
			rparams->realm_flags_valid = 1;
		krb5_xfree(svalue);
	}

	/* Get the value for the supported enctype/salttype matrix */
	/*
	 * SUNWresync121
	 * Solaris kerberos: updated this code to support default values for
	 * the supported_enctypes.
	 */
	hierarchy[2] = "supported_enctypes";
	svalue = NULL;
	krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue);

	/*
	 * Set the default value if supported_enctypes was not explicitly
	 * set in the kdc.conf.
	 */
	if (svalue == NULL) {
	    svalue = strdup(DEFAULT_ENCTYPE_LIST);
	}
	if (svalue != NULL) {
	    krb5_string_to_keysalts(svalue,
				    ", \t",	/* Tuple separators	*/
				    ":.-",	/* Key/salt separators	*/
				    0,	/* No duplicates	*/
				    &rparams->realm_keysalts,
				    &rparams->realm_num_keysalts);
	    krb5_xfree(svalue);
	    svalue = NULL;
	}

cleanup:
	if (aprofile)
		krb5_aprof_finish(aprofile);
	if (lrealm)
		free(lrealm);
	if (kret) {
		if (rparams)
			krb5_free_realm_params(kcontext, rparams);
		rparams = 0;
	}
	*rparamp = rparams;
	return (kret);
}

/*
 * krb5_free_realm_params()	- Free data allocated by above.
 */
/*ARGSUSED*/
krb5_error_code
krb5_free_realm_params(kcontext, rparams)
krb5_context	kcontext;
krb5_realm_params	*rparams;
{
	if (rparams) {
		if (rparams->realm_profile)
			krb5_xfree(rparams->realm_profile);
		if (rparams->realm_dbname)
			krb5_xfree(rparams->realm_dbname);
		if (rparams->realm_mkey_name)
			krb5_xfree(rparams->realm_mkey_name);
		if (rparams->realm_stash_file)
			krb5_xfree(rparams->realm_stash_file);
		if (rparams->realm_keysalts)
			krb5_xfree(rparams->realm_keysalts);
		if (rparams->realm_kdc_ports)
			krb5_xfree(rparams->realm_kdc_ports);
		krb5_xfree(rparams);
	}
	return (0);
}