ldap_realm.c   [plain text]


/*
 * lib/kdb/kdb_ldap/ldap_realm.c
 *
 * Copyright (c) 2004-2005, Novell, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *   * The copyright holder's name is not used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

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

#include "ldap_main.h"
#include "ldap_realm.h"
#include "ldap_principal.h"
#include "ldap_pwd_policy.h"
#include "ldap_err.h"

#define END_OF_LIST -1
char  *realm_attributes[] = {"krbSearchScope","krbSubTrees", "krbPrincContainerRef", 
			     "krbMaxTicketLife", "krbMaxRenewableAge",
			     "krbTicketFlags", "krbUpEnabled",
			     "krbTicketPolicyReference",
			     "krbLdapServers",
			     "krbKdcServers",  "krbAdmServers",
			     "krbPwdServers", NULL};


char  *policy_attributes[] = { "krbMaxTicketLife",
			       "krbMaxRenewableAge",
			       "krbTicketFlags",
			       NULL };



char  *policyclass[] =     { "krbTicketPolicy", NULL };
char  *kdcclass[] =        { "krbKdcService", NULL };
char  *adminclass[] =      { "krbAdmService", NULL };
char  *pwdclass[] =        { "krbPwdService", NULL };
char  *subtreeclass[] =    { "Organization", "OrganizationalUnit", "Domain", "krbContainer",
                             "krbRealmContainer", "Country", "Locality", NULL };


char  *krbContainerRefclass[] = { "krbContainerRefAux", NULL};

/*
 * list realms from eDirectory
 */

/*
 * Function to remove all special characters from a string (rfc2254).
 * Use whenever exact matching is to be done ...
 */
char *ldap_filter_correct (char *in)
{
    size_t i, count;
    char *out, *ptr;
    size_t len = strlen(in);

    for (i = 0, count = 0; i < len; i++)
	switch (in[i]) {
	case '*':
	case '(':
	case ')':
	case '\\':
	case '\0':
	    count ++;
	}

    out = (char *)malloc((len + (count * 2) + 1) * sizeof (char));
    assert (out != NULL);
    memset(out, 0, len + (count * 2) + 1);

    for (i = 0, ptr = out; i < len; i++)
	switch (in[i]) {
	case '*':
	    ptr[0] = '\\';
	    ptr[1] = '2';
	    ptr[2] = 'a';
	    ptr += 3;
	    break;
	case '(':
	    ptr[0] = '\\';
	    ptr[1] = '2';
	    ptr[2] = '8';
	    ptr += 3;
	    break;
	case ')':
	    ptr[0] = '\\';
	    ptr[1] = '2';
	    ptr[2] = '9';
	    ptr += 3;
	    break;
	case '\\':
	    ptr[0] = '\\';
	    ptr[1] = '5';
	    ptr[2] = 'c';
	    ptr += 3;
	    break;
	case '\0':
	    ptr[0] = '\\';
	    ptr[1] = '0';
	    ptr[2] = '0';
	    ptr += 3;
	    break;
	default:
	    ptr[0] = in[i];
	    ptr += 1;
	    break;
	}

    /* ptr[count - 1] = '\0'; */

    return out;
}

static int principal_in_realm_2(krb5_principal principal, char *realm) {
    /* Cross realm trust ... */
    if (principal->length == 2 &&
	principal->data[0].length == sizeof ("krbtgt") &&
	strncasecmp (principal->data[0].data, "krbtgt", sizeof ("krbtgt")) &&
	principal->data[1].length == strlen (realm) &&
	strncasecmp (principal->data[1].data, realm, strlen (realm)))
	return 0;

    if (strlen(realm) != principal->realm.length)
	return 1;

    if (strncasecmp(realm, principal->realm.data, principal->realm.length) != 0)
	return 1;

    return 0;
}

/*
 * Lists the realms in the Directory.
 */

krb5_error_code
krb5_ldap_list_realm(context, realms)
    krb5_context	        context;
    char                        ***realms;
{
    char                        **values = NULL;
    unsigned int                i = 0;
    int                		count = 0;
    krb5_error_code             st = 0, tempst = 0;
    LDAP                        *ld = NULL;
    LDAPMessage                 *result = NULL, *ent = NULL;
    kdb5_dal_handle             *dal_handle = NULL;
    krb5_ldap_context           *ldap_context = NULL;
    krb5_ldap_server_handle     *ldap_server_handle = NULL;

    SETUP_CONTEXT ();

    /* get the kerberos container DN information */
    if (ldap_context->krbcontainer == NULL) {
	if ((st = krb5_ldap_read_krbcontainer_params(context,
						     &(ldap_context->krbcontainer))) != 0)
	    goto cleanup;
    }

    /* get ldap handle */
    GET_HANDLE ();

    {
	char *cn[] = {"cn", NULL};
	LDAP_SEARCH(ldap_context->krbcontainer->DN,
		    LDAP_SCOPE_ONELEVEL,
		    "(objectclass=krbRealmContainer)",
		    cn);
    }

    *realms = NULL;

    count = ldap_count_entries (ld, result);
    if (count == -1) {
	ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &st);
	st = set_ldap_error (context, st, OP_SEARCH);
	goto cleanup;
    }

    *realms = calloc((unsigned int) count+1, sizeof (char *));
    CHECK_NULL(*realms);

    for (ent = ldap_first_entry(ld, result), count = 0; ent != NULL;
	 ent = ldap_next_entry(ld, ent)) {

	if ((values = ldap_get_values (ld, ent, "cn")) != NULL) {

	    (*realms)[count] = strdup(values[0]);
	    CHECK_NULL((*realms)[count]);
	    count += 1;

	    ldap_value_free(values);
	}
    } /* for (ent= ... */
    ldap_msgfree(result);

cleanup:

    /* some error, free up all the memory */
    if (st != 0) {
	if (*realms) {
	    for (i=0; (*realms)[i] != NULL; ++i) {
		free ((*realms)[i]);
	    }
	    free (*realms);
	    *realms = NULL;
	}
    }

    /* If there are no elements, still return a NULL terminated array */

    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return st;
}

/*
 * Delete the realm along with the principals belonging to the realm in the Directory.
 */

static void
delete_password_policy (krb5_pointer ptr, osa_policy_ent_t pol)
{
    krb5_ldap_delete_password_policy ((krb5_context)ptr, pol->name);
}

krb5_error_code
krb5_ldap_delete_realm (context, lrealm)
    krb5_context                context;
    char                        *lrealm;
{
    LDAP                        *ld = NULL;
    krb5_error_code             st = 0, tempst=0;
    char                        **values=NULL, **subtrees=NULL, **policy=NULL;
    LDAPMessage                 **result_arr=NULL, *result = NULL, *ent = NULL;
    krb5_principal              principal;
    int                         l=0, ntree=0, i=0, j=0, mask=0;
    kdb5_dal_handle             *dal_handle = NULL;
    krb5_ldap_context           *ldap_context = NULL;
    krb5_ldap_server_handle     *ldap_server_handle = NULL;
    krb5_ldap_realm_params      *rparam=NULL;

    SETUP_CONTEXT ();

    if (lrealm == NULL) {
	st = EINVAL;
	krb5_set_error_message (context, st, "Realm information not available");
	goto cleanup;
    }

    if ((st=krb5_ldap_read_realm_params(context, lrealm, &rparam, &mask)) != 0)
	goto cleanup;

    /* get ldap handle */
    GET_HANDLE ();

    /* delete all the principals belonging to the realm in the tree */
    {
	char *attr[] = {"krbprincipalname", NULL}, *realm=NULL, filter[256];
	krb5_ldap_context lcontext;

	realm = ldap_filter_correct (lrealm);
	assert (sizeof (filter) >= sizeof ("(krbprincipalname=)") +
		strlen (realm) + 2 /* "*@" */ + 1);

	sprintf (filter, "(krbprincipalname=*@%s)", realm);
	free (realm);

	/* LDAP_SEARCH(NULL, LDAP_SCOPE_SUBTREE, filter, attr); */
	memset(&lcontext, 0, sizeof(krb5_ldap_context));
	lcontext.lrparams = rparam;
	if ((st=krb5_get_subtree_info(&lcontext, &subtrees, &ntree)) != 0)
	    goto cleanup;

        result_arr = (LDAPMessage **)  calloc((unsigned int)ntree+1,
					      sizeof(LDAPMessage *));
        if (result_arr == NULL) {
            st = ENOMEM;
            goto cleanup;
        }

	for (l=0; l < ntree; ++l) {
	    LDAP_SEARCH(subtrees[l], rparam->search_scope, filter, attr);
	    result_arr[l] = result;
	}
    }

    /* NOTE: Here all the principals should be cached and the ldap handle should be freed,
     * as a DAL-LDAP interface is called right down here. Caching might be constrained by
     * availability of the memory. The caching is not done, however there would be limit
     * on the minimum number of handles for a server and it is 2. As the DAL-LDAP is not
     * thread-safe this should suffice.
     */
    for (j=0; (result=result_arr[j]) != NULL; ++j) {
	for (ent = ldap_first_entry (ld, result); ent != NULL;
	     ent = ldap_next_entry (ld, ent)) {
	    if ((values = ldap_get_values(ld, ent, "krbPrincipalName")) != NULL) {
		for (i = 0; values[i] != NULL; ++i) {
		    krb5_parse_name(context, values[i], &principal);
		    if (principal_in_realm_2(principal, lrealm) == 0) {
			int nent = 0;
			if ((st=krb5_ldap_delete_principal(context, principal,
							   &nent)) != LDAP_SUCCESS)
			    goto cleanup;
		    }
		    krb5_free_principal(context, principal);
		}
		ldap_value_free(values);
	    }
	}
	ldap_msgfree(result);
    }

    /* Delete all password policies */
    krb5_ldap_iterate_password_policy (context, "*", delete_password_policy, context);

    /* Delete all ticket policies */
    {
	if ((st = krb5_ldap_list_policy (context, ldap_context->lrparams->realmdn, &policy)) != 0) {
	    prepend_err_str (context, "Error reading ticket policy: ", st, st);
	    goto cleanup;
	}

	for (i = 0; policy [i] != NULL; i++)
	    krb5_ldap_delete_policy(context, policy[i]);
    }

    /* Delete the realm object */
    if ((st=ldap_delete_ext_s(ld, ldap_context->lrparams->realmdn, NULL, NULL)) != LDAP_SUCCESS) {
	int ost = st;
	st = translate_ldap_error (st, OP_DEL);
	krb5_set_error_message (context, st, "Realm Delete FAILED: %s",
				ldap_err2string(ost));
    }

cleanup:
    if (subtrees) {
	for (l=0; l < ntree; ++l) {
	if (subtrees[l])
	    free (subtrees[l]);
        }
	free (subtrees);
    }

    if (policy != NULL) {
	for (i = 0; policy[i] != NULL; i++)
	    free (policy[i]);
	free (policy);
    }

    krb5_ldap_free_realm_params(rparam);
    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return st;
}


/*
 * Modify the realm attributes in the Directory.
 */

krb5_error_code
krb5_ldap_modify_realm(context, rparams, mask)
    krb5_context             context;
    krb5_ldap_realm_params   *rparams;
    int                      mask;
{
    LDAP                  *ld=NULL;
    krb5_error_code       st=0;
    char                  **strval=NULL, *strvalprc[5]={NULL};
#ifdef HAVE_EDIRECTORY
    char                  **values=NULL;
    char                  **oldkdcservers=NULL, **oldadminservers=NULL, **oldpasswdservers=NULL;
    LDAPMessage           *result=NULL, *ent=NULL;
    int                   count=0;
    char errbuf[1024];
#endif
    LDAPMod               **mods = NULL;
#ifdef HAVE_EDIRECTORY
    int                   i=0;
#endif
    int                   oldmask=0, objectmask=0,k=0;
    kdb5_dal_handle       *dal_handle=NULL;
    krb5_ldap_context     *ldap_context=NULL;
    krb5_ldap_server_handle *ldap_server_handle=NULL;

    if (mask == 0)
	return 0;

    if (rparams == NULL) {
	st = EINVAL;
	return st;
    }

    SETUP_CONTEXT ();

    /* Check validity of arguments */
    if (ldap_context->krbcontainer == NULL ||
	rparams->tl_data == NULL ||
	rparams->tl_data->tl_data_contents == NULL ||
	((mask & LDAP_REALM_SUBTREE) && rparams->subtree == NULL) ||
	((mask & LDAP_REALM_CONTREF) && rparams->containerref == NULL) ||
#ifdef HAVE_EDIRECTORY
	((mask & LDAP_REALM_KDCSERVERS) && rparams->kdcservers == NULL) ||
	((mask & LDAP_REALM_ADMINSERVERS) && rparams->adminservers == NULL) ||
	((mask & LDAP_REALM_PASSWDSERVERS) && rparams->passwdservers == NULL) ||
#endif
	0) {
	st = EINVAL;
	goto cleanup;
    }

    /* get ldap handle */
    GET_HANDLE ();

    /* get the oldmask obtained from the krb5_ldap_read_realm_params */
    {
	void *voidptr=NULL;

	if ((st=decode_tl_data(rparams->tl_data, KDB_TL_MASK, &voidptr)) == 0) {
	    oldmask = *((int *) voidptr);
	    free (voidptr);
	} else {
	    st = EINVAL;
	    krb5_set_error_message (context, st, "tl_data not available");
	    return st;
	}
    }


    /* SUBTREE ATTRIBUTE */
    if (mask & LDAP_REALM_SUBTREE) {
        if ( rparams->subtree!=NULL)  {
            /*replace the subtrees with the present if the subtrees are present*/
            for(k=0;k<rparams->subtreecount && rparams->subtree[k]!=NULL;k++) {
                    if (strlen(rparams->subtree[k]) != 0) {
                        st = checkattributevalue(ld, rparams->subtree[k], "Objectclass", subtreeclass,
                                &objectmask);
                        CHECK_CLASS_VALIDITY(st, objectmask, "subtree value: ");
                    }
            }
	    strval = rparams->subtree;
	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbsubtrees", LDAP_MOD_REPLACE,
					    strval)) != 0) {
	       goto cleanup;
	    }
        }
    }

    /* CONTAINERREF ATTRIBUTE */
    if (mask & LDAP_REALM_CONTREF) {
        if (strlen(rparams->containerref) != 0 ) {
            st = checkattributevalue(ld, rparams->containerref, "Objectclass", subtreeclass,
                     &objectmask);
            CHECK_CLASS_VALIDITY(st, objectmask, "container reference value: ");
            strvalprc[0] = rparams->containerref;
            strvalprc[1] = NULL;
            if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbPrincContainerRef", LDAP_MOD_REPLACE,
                            strvalprc)) != 0)
                goto cleanup;
        }
    }

    /* SEARCHSCOPE ATTRIBUTE */
    if (mask & LDAP_REALM_SEARCHSCOPE) {
	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbsearchscope", LDAP_MOD_REPLACE,
					  (rparams->search_scope == LDAP_SCOPE_ONELEVEL
					   || rparams->search_scope == LDAP_SCOPE_SUBTREE) ?
					  rparams->search_scope : LDAP_SCOPE_SUBTREE)) != 0)
	    goto cleanup;
    }

    if (mask & LDAP_REALM_MAXRENEWLIFE) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbMaxRenewableAge", LDAP_MOD_REPLACE,
					  rparams->max_renewable_life)) != 0)
	    goto cleanup;
    }

    /* krbMaxTicketLife ATTRIBUTE */

    if (mask & LDAP_REALM_MAXTICKETLIFE) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbMaxTicketLife", LDAP_MOD_REPLACE,
					  rparams->max_life)) != 0)
	    goto cleanup;
    }

    /* krbTicketFlags ATTRIBUTE */

    if (mask & LDAP_REALM_KRBTICKETFLAGS) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbTicketFlags", LDAP_MOD_REPLACE,
					  rparams->tktflags)) != 0)
	    goto cleanup;
    }


#ifdef HAVE_EDIRECTORY

    /* KDCSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_KDCSERVERS) {
	/* validate the server list */
	for (i=0; rparams->kdcservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->kdcservers[i], "objectClass", kdcclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "kdc service object value: ");
	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbkdcservers", LDAP_MOD_REPLACE,
					  rparams->kdcservers)) != 0)
	    goto cleanup;
    }

    /* ADMINSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_ADMINSERVERS) {
	/* validate the server list */
	for (i=0; rparams->adminservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->adminservers[i], "objectClass", adminclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "admin service object value: ");
	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbadmservers", LDAP_MOD_REPLACE,
					  rparams->adminservers)) != 0)
	    goto cleanup;
    }

    /* PASSWDSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_PASSWDSERVERS) {
	/* validate the server list */
	for (i=0; rparams->passwdservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->passwdservers[i], "objectClass", pwdclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "password service object value: ");
	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdservers", LDAP_MOD_REPLACE,
					  rparams->passwdservers)) != 0)
	    goto cleanup;
    }

    /*
     * Read the old values of the krbkdcservers, krbadmservers and
     * krbpwdservers.  This information is later used to decided the
     * deletions/additions to the list.
     */
    if (mask & LDAP_REALM_KDCSERVERS || mask & LDAP_REALM_ADMINSERVERS ||
	mask & LDAP_REALM_PASSWDSERVERS) {
	char *servers[] = {"krbKdcServers", "krbAdmServers", "krbPwdServers", NULL};

	if ((st= ldap_search_ext_s(ld,
				   rparams->realmdn,
				   LDAP_SCOPE_BASE,
				   0,
				   servers,
				   0,
				   NULL,
				   NULL,
				   NULL,
				   0,
				   &result)) != LDAP_SUCCESS) {
	    st = set_ldap_error (context, st, OP_SEARCH);
	    goto cleanup;
	}

	ent = ldap_first_entry(ld, result);
	if (ent) {
	    if ((values=ldap_get_values(ld, ent, "krbKdcServers")) != NULL) {
		count = ldap_count_values(values);
		if ((st=copy_arrays(values, &oldkdcservers, count)) != 0)
		    goto cleanup;
		ldap_value_free(values);
	    }

	    if ((values=ldap_get_values(ld, ent, "krbAdmServers")) != NULL) {
		count = ldap_count_values(values);
		if ((st=copy_arrays(values, &oldadminservers, count)) != 0)
		    goto cleanup;
		ldap_value_free(values);
	    }

	    if ((values=ldap_get_values(ld, ent, "krbPwdServers")) != NULL) {
		count = ldap_count_values(values);
		if ((st=copy_arrays(values, &oldpasswdservers, count)) != 0)
		    goto cleanup;
		ldap_value_free(values);
	    }
	}
	ldap_msgfree(result);
    }
#endif

    /* Realm modify opearation */
    if (mods != NULL) {
        if ((st=ldap_modify_ext_s(ld, rparams->realmdn, mods, NULL, NULL)) != LDAP_SUCCESS) {
	    st = set_ldap_error (context, st, OP_MOD);
	    goto cleanup;
        }
    }

#ifdef HAVE_EDIRECTORY
    /* krbRealmReferences attribute is updated here, depending on the additions/deletions
     * to the 4 servers' list.
     */
    if (mask & LDAP_REALM_KDCSERVERS) {
	char **newkdcservers=NULL;

	count = ldap_count_values(rparams->kdcservers);
	if ((st=copy_arrays(rparams->kdcservers, &newkdcservers, count)) != 0)
	    goto cleanup;

	/* find the deletions and additions to the server list */
	if (oldkdcservers && newkdcservers)
	    disjoint_members(oldkdcservers, newkdcservers);

	/* delete the krbRealmReferences attribute from the servers that are dis-associated. */
	if (oldkdcservers)
	    for (i=0; oldkdcservers[i]; ++i)
		if ((st=deleteAttribute(ld, oldkdcservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf (errbuf, "Error removing 'krbRealmReferences' from %s: ",
			     oldkdcservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}

	/* add the krbRealmReferences attribute from the servers that are associated. */
	if (newkdcservers)
	    for (i=0; newkdcservers[i]; ++i)
		if ((st=updateAttribute(ld, newkdcservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf (errbuf, "Error adding 'krbRealmReferences' to %s: ",
			     newkdcservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}

	if (newkdcservers)
	    ldap_value_free(newkdcservers);
    }

    if (mask & LDAP_REALM_ADMINSERVERS) {
	char **newadminservers=NULL;

	count = ldap_count_values(rparams->adminservers);
	if ((st=copy_arrays(rparams->adminservers, &newadminservers, count)) != 0)
	    goto cleanup;

	/* find the deletions and additions to the server list */
	if (oldadminservers && newadminservers)
	    disjoint_members(oldadminservers, newadminservers);

	/* delete the krbRealmReferences attribute from the servers that are dis-associated. */
	if (oldadminservers)
	    for (i=0; oldadminservers[i]; ++i)
		if ((st=deleteAttribute(ld, oldadminservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf(errbuf, "Error removing 'krbRealmReferences' from "
			    "%s: ", oldadminservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}

	/* add the krbRealmReferences attribute from the servers that are associated. */
	if (newadminservers)
	    for (i=0; newadminservers[i]; ++i)
		if ((st=updateAttribute(ld, newadminservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf(errbuf, "Error adding 'krbRealmReferences' to %s: ",
			    newadminservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}
	if (newadminservers)
	    ldap_value_free(newadminservers);
    }

    if (mask & LDAP_REALM_PASSWDSERVERS) {
	char **newpasswdservers=NULL;

	count = ldap_count_values(rparams->passwdservers);
	if ((st=copy_arrays(rparams->passwdservers, &newpasswdservers, count)) != 0)
	    goto cleanup;

	/* find the deletions and additions to the server list */
	if (oldpasswdservers && newpasswdservers)
	    disjoint_members(oldpasswdservers, newpasswdservers);

	/* delete the krbRealmReferences attribute from the servers that are dis-associated. */
	if (oldpasswdservers)
	    for (i=0; oldpasswdservers[i]; ++i)
		if ((st=deleteAttribute(ld, oldpasswdservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf(errbuf, "Error removing 'krbRealmReferences' from "
			    "%s: ", oldpasswdservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}

	/* add the krbRealmReferences attribute from the servers that are associated. */
	if (newpasswdservers)
	    for (i=0; newpasswdservers[i]; ++i)
		if ((st=updateAttribute(ld, newpasswdservers[i], "krbRealmReferences",
					rparams->realmdn)) != 0) {
		    sprintf(errbuf, "Error adding 'krbRealmReferences' to %s: ",
			    newpasswdservers[i]);
		    prepend_err_str (context, errbuf, st, st);
		    goto cleanup;
		}
	if (newpasswdservers)
	    ldap_value_free(newpasswdservers);
    }
#endif

cleanup:

#ifdef HAVE_EDIRECTORY
    if (oldkdcservers) {
	for (i=0; oldkdcservers[i]; ++i)
	    free(oldkdcservers[i]);
	free(oldkdcservers);
    }

    if (oldadminservers) {
	for (i=0; oldadminservers[i]; ++i)
	    free(oldadminservers[i]);
	free(oldadminservers);
    }

    if (oldpasswdservers) {
	for (i=0; oldpasswdservers[i]; ++i)
	    free(oldpasswdservers[i]);
	free(oldpasswdservers);
    }
#endif

    ldap_mods_free(mods, 1);
    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return st;
}



/*
 * Create the Kerberos container in the Directory
 */

krb5_error_code
krb5_ldap_create_krbcontainer(context, krbcontparams)
    krb5_context                          context;
    const krb5_ldap_krbcontainer_params   *krbcontparams;
{
    LDAP                        *ld=NULL;
    char                        *strval[2]={NULL}, *kerberoscontdn=NULL, **rdns=NULL;
    int                         pmask=0;
    LDAPMod                     **mods = NULL;
    krb5_error_code             st=0;
    kdb5_dal_handle             *dal_handle=NULL;
    krb5_ldap_context           *ldap_context=NULL;
    krb5_ldap_server_handle     *ldap_server_handle=NULL;
#ifdef HAVE_EDIRECTORY
    int                         crmask=0;
#endif

    SETUP_CONTEXT ();

    /* get ldap handle */
    GET_HANDLE ();

    if (krbcontparams != NULL && krbcontparams->DN != NULL) {
	kerberoscontdn = krbcontparams->DN;
    } else {
	/* If the user has not given, use the default cn=Kerberos,cn=Security */
#ifdef HAVE_EDIRECTORY
	kerberoscontdn = KERBEROS_CONTAINER;
#else
	st = EINVAL;
	krb5_set_error_message (context, st, "Kerberos Container information is missing");
	goto cleanup;
#endif
    }

    strval[0] = "krbContainer";
    strval[1] = NULL;
    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
	goto cleanup;

    rdns = ldap_explode_dn(kerberoscontdn, 1);
    if (rdns == NULL) {
	st = EINVAL;
	krb5_set_error_message(context, st, "Invalid Kerberos container DN");
	goto cleanup;
    }

    strval[0] = rdns[0];
    strval[1] = NULL;
    if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
	goto cleanup;

    /* check if the policy reference value exists and is of krbticketpolicyreference object class */
    if (krbcontparams && krbcontparams->policyreference) {
	st = checkattributevalue(ld, krbcontparams->policyreference, "objectclass", policyclass,
				 &pmask);
	CHECK_CLASS_VALIDITY(st, pmask, "ticket policy object value: ");

	strval[0] = krbcontparams->policyreference;
	strval[1] = NULL;
	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_ADD,
					  strval)) != 0)
	    goto cleanup;
    }

    /* create the kerberos container */
    if ((st = ldap_add_ext_s(ld, kerberoscontdn, mods, NULL, NULL)) != LDAP_SUCCESS) {
	int ost = st;
	st = translate_ldap_error (st, OP_ADD);
	krb5_set_error_message (context, st, "Kerberos Container create FAILED: %s", ldap_err2string(ost));
	goto cleanup;
    }

#ifdef HAVE_EDIRECTORY

    /* free the mods array */
    ldap_mods_free(mods, 1);
    mods=NULL;

    /* check whether the security container is bound to krbcontainerrefaux object class */
    if ((st=checkattributevalue(ld, SECURITY_CONTAINER, "objectClass",
				krbContainerRefclass, &crmask)) != 0) {
	prepend_err_str (context, "Security Container read FAILED: ", st, st);
	/* delete Kerberos Container, status ignored intentionally */
	ldap_delete_ext_s(ld, kerberoscontdn, NULL, NULL);
	goto cleanup;
    }

    if (crmask == 0) {
	/* Security Container is extended with krbcontainerrefaux object class */
	strval[0] = "krbContainerRefAux";
	if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
	    goto cleanup;
    }

    strval[0] = kerberoscontdn;
    strval[1] = NULL;
    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbcontainerreference", LDAP_MOD_ADD, strval)) != 0)
	goto cleanup;

    /* update the security container with krbContainerReference attribute */
    if ((st=ldap_modify_ext_s(ld, SECURITY_CONTAINER, mods, NULL, NULL)) != LDAP_SUCCESS) {
	int ost = st;
	st = translate_ldap_error (st, OP_MOD);
	krb5_set_error_message (context, st, "Security Container update FAILED: %s", ldap_err2string(ost));
	/* delete Kerberos Container, status ignored intentionally */
	ldap_delete_ext_s(ld, kerberoscontdn, NULL, NULL);
	goto cleanup;
    }
#endif

cleanup:

    if (rdns)
	ldap_value_free (rdns);

    ldap_mods_free(mods, 1);
    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return(st);
}

/*
 * Delete the Kerberos container in the Directory
 */

krb5_error_code
krb5_ldap_delete_krbcontainer(krb5_context context,
    const krb5_ldap_krbcontainer_params *krbcontparams)
{
    LDAP                        *ld=NULL;
    char                        *kerberoscontdn=NULL;
    krb5_error_code             st=0;
    kdb5_dal_handle             *dal_handle=NULL;
    krb5_ldap_context           *ldap_context=NULL;
    krb5_ldap_server_handle     *ldap_server_handle=NULL;

    SETUP_CONTEXT ();

    /* get ldap handle */
    GET_HANDLE ();

    if (krbcontparams != NULL && krbcontparams->DN != NULL) {
	kerberoscontdn = krbcontparams->DN;
    } else {
	/* If the user has not given, use the default cn=Kerberos,cn=Security */
#ifdef HAVE_EDIRECTORY
	kerberoscontdn = KERBEROS_CONTAINER;
#else
	st = EINVAL;
	krb5_set_error_message (context, st, "Kerberos Container information is missing");
	goto cleanup;
#endif
    }

    /* delete the kerberos container */
    if ((st = ldap_delete_ext_s(ld, kerberoscontdn, NULL, NULL)) != LDAP_SUCCESS) {
	int ost = st;
	st = translate_ldap_error (st, OP_ADD);
	krb5_set_error_message (context, st, "Kerberos Container delete FAILED: %s", ldap_err2string(ost));
	goto cleanup;
    }

cleanup:

    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return(st);
}


/*
 * Create Realm in eDirectory. This is used by kdb5_util
 */

krb5_error_code
krb5_ldap_create_realm(context, rparams, mask)
    krb5_context                context;
    krb5_ldap_realm_params      *rparams;
    int                         mask;
{
    LDAP                        *ld=NULL;
    krb5_error_code             st=0;
    char                        *dn=NULL;
    char                        *strval[4]={NULL};
    char		        *contref[2]={NULL}; 
    LDAPMod                     **mods = NULL;
    int                         i=0, objectmask=0, subtreecount=0; 
    kdb5_dal_handle             *dal_handle=NULL;
    krb5_ldap_context           *ldap_context=NULL;
    krb5_ldap_server_handle     *ldap_server_handle=NULL;
#ifdef HAVE_EDIRECTORY
    char errbuf[1024];
#endif
    char                        *realm_name;

    SETUP_CONTEXT ();

    /* Check input validity ... */
    if (ldap_context->krbcontainer == NULL ||
	ldap_context->krbcontainer->DN == NULL ||
	rparams == NULL ||
	rparams->realm_name == NULL ||
	((mask & LDAP_REALM_SUBTREE) && rparams->subtree  == NULL) ||
	((mask & LDAP_REALM_CONTREF) && rparams->containerref == NULL) || 
	((mask & LDAP_REALM_POLICYREFERENCE) && rparams->policyreference == NULL) ||
#ifdef HAVE_EDIRECTORY
	((mask & LDAP_REALM_KDCSERVERS) && rparams->kdcservers == NULL) ||
	((mask & LDAP_REALM_ADMINSERVERS) && rparams->adminservers == NULL) ||
	((mask & LDAP_REALM_PASSWDSERVERS) && rparams->passwdservers == NULL) ||
#endif
	0) {
	st = EINVAL;
	return st;
    }

    if (ldap_context->krbcontainer == NULL) {
	if ((st = krb5_ldap_read_krbcontainer_params(context,
						     &(ldap_context->krbcontainer))) != 0)
	    goto cleanup;
    }

    /* get ldap handle */
    GET_HANDLE ();

    realm_name = rparams->realm_name;

    dn = malloc(strlen("cn=") + strlen(realm_name) + strlen(ldap_context->krbcontainer->DN) + 2);
    CHECK_NULL(dn);
    sprintf(dn, "cn=%s,%s", realm_name, ldap_context->krbcontainer->DN);

    strval[0] = realm_name;
    strval[1] = NULL;
    if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
	goto cleanup;

    strval[0] = "top";
    strval[1] = "krbrealmcontainer";
    strval[2] = "krbticketpolicyaux"; 
    strval[3] = NULL;

    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
	goto cleanup;

    /* SUBTREE ATTRIBUTE */
    if (mask & LDAP_REALM_SUBTREE) {
        if ( rparams->subtree!=NULL)  {
              subtreecount = rparams->subtreecount;
	      for (i=0; rparams->subtree[i]!=NULL && i<subtreecount; i++) {
	          if (strlen(rparams->subtree[i]) != 0) {
                      st = checkattributevalue(ld, rparams->subtree[i], "Objectclass", subtreeclass,
                             &objectmask);
                      CHECK_CLASS_VALIDITY(st, objectmask, "realm object value: ");
		  }
	      }
	      if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbsubtrees", LDAP_MOD_ADD,
                              rparams->subtree)) != 0) {
	         goto cleanup;
	      }
	}
    }

    /* CONTAINER REFERENCE ATTRIBUTE */
    if (mask & LDAP_REALM_CONTREF) {
        if (strlen(rparams->containerref) != 0 ) {
            st = checkattributevalue(ld, rparams->containerref, "Objectclass", subtreeclass,
                             &objectmask);
            CHECK_CLASS_VALIDITY(st, objectmask, "realm object value: ");
            contref[0] = rparams->containerref;
            contref[1] = NULL;
            if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbPrincContainerRef", LDAP_MOD_ADD,
                                              contref)) != 0)
                goto cleanup;
        }
    }

    /* SEARCHSCOPE ATTRIBUTE */
    if (mask & LDAP_REALM_SEARCHSCOPE) {
	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbsearchscope", LDAP_MOD_ADD,
					  (rparams->search_scope == LDAP_SCOPE_ONELEVEL
					   || rparams->search_scope == LDAP_SCOPE_SUBTREE) ?
					  rparams->search_scope : LDAP_SCOPE_SUBTREE)) != 0)
	    goto cleanup;
    }
    if (mask & LDAP_REALM_MAXRENEWLIFE) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbMaxRenewableAge", LDAP_MOD_ADD,
					  rparams->max_renewable_life)) != 0)
	    goto cleanup;
    }

    /* krbMaxTicketLife ATTRIBUTE */

    if (mask & LDAP_REALM_MAXTICKETLIFE) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbMaxTicketLife", LDAP_MOD_ADD,
					  rparams->max_life)) != 0)
	    goto cleanup;
    }

    /* krbTicketFlags ATTRIBUTE */

    if (mask & LDAP_REALM_KRBTICKETFLAGS) {

	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbTicketFlags", LDAP_MOD_ADD,
					  rparams->tktflags)) != 0)
	    goto cleanup;
    }


#ifdef HAVE_EDIRECTORY

    /* KDCSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_KDCSERVERS) {
	/* validate the server list */
	for (i=0; rparams->kdcservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->kdcservers[i], "objectClass", kdcclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "kdc service object value: ");

	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbkdcservers", LDAP_MOD_ADD,
					  rparams->kdcservers)) != 0)
	    goto cleanup;
    }

    /* ADMINSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_ADMINSERVERS) {
	/* validate the server list */
	for (i=0; rparams->adminservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->adminservers[i], "objectClass", adminclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "admin service object value: ");

	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbadmservers", LDAP_MOD_ADD,
					  rparams->adminservers)) != 0)
	    goto cleanup;
    }

    /* PASSWDSERVERS ATTRIBUTE */
    if (mask & LDAP_REALM_PASSWDSERVERS) {
	/* validate the server list */
	for (i=0; rparams->passwdservers[i] != NULL; ++i) {
	    st = checkattributevalue(ld, rparams->passwdservers[i], "objectClass", pwdclass,
				     &objectmask);
	    CHECK_CLASS_VALIDITY(st, objectmask, "password service object value: ");

	}

	if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdservers", LDAP_MOD_ADD,
					  rparams->passwdservers)) != 0)
	    goto cleanup;
    }
#endif

    /* realm creation operation */
    if ((st=ldap_add_ext_s(ld, dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
	st = set_ldap_error (context, st, OP_ADD);
	goto cleanup;
    }

#ifdef HAVE_EDIRECTORY
    if (mask & LDAP_REALM_KDCSERVERS)
	for (i=0; rparams->kdcservers[i]; ++i)
	    if ((st=updateAttribute(ld, rparams->kdcservers[i], "krbRealmReferences", dn)) != 0) {
		sprintf(errbuf, "Error adding 'krbRealmReferences' to %s: ",
			rparams->kdcservers[i]);
		prepend_err_str (context, errbuf, st, st);
		/* delete Realm, status ignored intentionally */
		ldap_delete_ext_s(ld, dn, NULL, NULL);
		goto cleanup;
	    }

    if (mask & LDAP_REALM_ADMINSERVERS)
	for (i=0; rparams->adminservers[i]; ++i)
	    if ((st=updateAttribute(ld, rparams->adminservers[i], "krbRealmReferences", dn)) != 0) {
		sprintf(errbuf, "Error adding 'krbRealmReferences' to %s: ",
			rparams->adminservers[i]);
		prepend_err_str (context, errbuf, st, st);
		/* delete Realm, status ignored intentionally */
		ldap_delete_ext_s(ld, dn, NULL, NULL);
		goto cleanup;
	    }

    if (mask & LDAP_REALM_PASSWDSERVERS)
	for (i=0; rparams->passwdservers[i]; ++i)
	    if ((st=updateAttribute(ld, rparams->passwdservers[i], "krbRealmReferences", dn)) != 0) {
		sprintf(errbuf, "Error adding 'krbRealmReferences' to %s: ",
			rparams->passwdservers[i]);
		prepend_err_str (context, errbuf, st, st);
		/* delete Realm, status ignored intentionally */
		ldap_delete_ext_s(ld, dn, NULL, NULL);
		goto cleanup;
	    }
#endif

cleanup:

    if (dn)
	free(dn);

    ldap_mods_free(mods, 1);
    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return st;
}

/*
 * Read the realm container configuration from eDirectory for the specified realm.
 */

krb5_error_code
krb5_ldap_read_realm_params(context, lrealm, rlparamp, mask)
    krb5_context	context;
    char            *lrealm;
    krb5_ldap_realm_params **rlparamp;
    int             *mask;
{
    char                   **values=NULL, *krbcontDN=NULL /*, *curr=NULL */;
#ifdef HAVE_EDIRECTORY
    unsigned int           count=0;
#endif
    krb5_error_code        st=0, tempst=0;
    LDAP                   *ld=NULL;
    LDAPMessage            *result=NULL,*ent=NULL;
    krb5_ldap_realm_params *rlparams=NULL;
    kdb5_dal_handle        *dal_handle=NULL;
    krb5_ldap_context      *ldap_context=NULL;
    krb5_ldap_server_handle *ldap_server_handle=NULL;
    int x=0;

    SETUP_CONTEXT ();

    /* validate the input parameter */
    if (lrealm == NULL ||
	ldap_context->krbcontainer == NULL ||
	ldap_context->krbcontainer->DN == NULL) {
	st = EINVAL;
	goto cleanup;
    }

    /* read kerberos container, if not read already */
    if (ldap_context->krbcontainer == NULL) {
	if ((st = krb5_ldap_read_krbcontainer_params(context,
						     &(ldap_context->krbcontainer))) != 0)
	    goto cleanup;
    }
    /* get ldap handle */
    GET_HANDLE ();

    /* Initialize realm container structure */
    rlparams =(krb5_ldap_realm_params *) malloc(sizeof(krb5_ldap_realm_params));
    CHECK_NULL(rlparams);
    memset((char *) rlparams, 0, sizeof(krb5_ldap_realm_params));

    /* allocate tl_data structure to store MASK information */
    rlparams->tl_data = malloc (sizeof(krb5_tl_data));
    if (rlparams->tl_data == NULL) {
	st = ENOMEM;
	goto cleanup;
    }
    memset((char *) rlparams->tl_data, 0, sizeof(krb5_tl_data));
    rlparams->tl_data->tl_data_type = KDB_TL_USER_INFO;

    /* set the mask parameter to 0 */
    *mask = 0;

    /* set default values */
    rlparams->search_scope = LDAP_SCOPE_SUBTREE;

    krbcontDN = ldap_context->krbcontainer->DN;

    rlparams->realmdn = (char *) malloc(strlen("cn=") + strlen(lrealm) + strlen(krbcontDN) + 2);
    if (rlparams->realmdn == NULL) {
	st = ENOMEM;
	goto cleanup;
    }
    sprintf(rlparams->realmdn, "cn=%s,%s", lrealm, krbcontDN);

    /* populate the realm name in the structure */
    rlparams->realm_name = strdup(lrealm);
    CHECK_NULL(rlparams->realm_name);

    LDAP_SEARCH(rlparams->realmdn, LDAP_SCOPE_BASE, "(objectclass=krbRealmContainer)", realm_attributes);

    if ((st = ldap_count_entries(ld, result)) <= 0) {
        /* This could happen when the DN used to bind and read the realm object
         * does not have sufficient rights to read its attributes
         */
        st = KRB5_KDB_ACCESS_ERROR; /* return some other error ? */
        goto cleanup;
    }

    ent = ldap_first_entry (ld, result);
    if (ent == NULL) {
	ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, (void *) &st);
#if 0
	st = translate_ldap_error(st, OP_SEARCH);
#endif
	goto cleanup;
    }

    /* Read the attributes */
    {
	if ((values=ldap_get_values(ld, ent, "krbSubTrees")) != NULL) {
            rlparams->subtreecount = ldap_count_values(values);
            rlparams->subtree = (char **) malloc(sizeof(char *) * (rlparams->subtreecount + 1));
	    if (rlparams->subtree == NULL) {
		st = ENOMEM;
		goto cleanup;
	    }
            for (x=0; x<rlparams->subtreecount; x++) {
                rlparams->subtree[x] = strdup(values[x]);
	        if (rlparams->subtree[x] == NULL) {
		    st = ENOMEM;
		    goto cleanup;
	        }
            }
            rlparams->subtree[rlparams->subtreecount] = NULL;
	    *mask |= LDAP_REALM_SUBTREE;
	    ldap_value_free(values);
	}

        if((values=ldap_get_values(ld, ent, "krbPrincContainerRef")) != NULL) {
            rlparams->containerref = strdup(values[0]);
            if(rlparams->containerref == NULL) {
                st = ENOMEM;
                goto cleanup;
            }
            *mask |= LDAP_REALM_CONTREF;
            ldap_value_free(values);
        }

	if ((values=ldap_get_values(ld, ent, "krbSearchScope")) != NULL) {
	    rlparams->search_scope=atoi(values[0]);
	    /* searchscope can be ONE-LEVEL or SUBTREE, else default to SUBTREE */
	    if (!(rlparams->search_scope==1 || rlparams->search_scope==2))
		rlparams->search_scope = LDAP_SCOPE_SUBTREE;
	    *mask |= LDAP_REALM_SEARCHSCOPE;
	    ldap_value_free(values);
	}

	if ((values=ldap_get_values(ld, ent, "krbMaxTicketLife")) != NULL) {
	    rlparams->max_life = atoi(values[0]);
	    *mask |= LDAP_REALM_MAXTICKETLIFE;
	    ldap_value_free(values);
	}

	if ((values=ldap_get_values(ld, ent, "krbMaxRenewableAge")) != NULL) {
	    rlparams->max_renewable_life = atoi(values[0]);
	    *mask |= LDAP_REALM_MAXRENEWLIFE;
	    ldap_value_free(values);
	}

	if ((values=ldap_get_values(ld, ent, "krbTicketFlags")) != NULL) {
	    rlparams->tktflags = atoi(values[0]);
	    *mask |= LDAP_REALM_KRBTICKETFLAGS;
	    ldap_value_free(values);
	}

#ifdef HAVE_EDIRECTORY

	if ((values=ldap_get_values(ld, ent, "krbKdcServers")) != NULL) {
	    count = ldap_count_values(values);
	    if ((st=copy_arrays(values, &(rlparams->kdcservers), (int) count)) != 0)
		goto cleanup;
	    *mask |= LDAP_REALM_KDCSERVERS;
	    ldap_value_free(values);
	}

	if ((values=ldap_get_values(ld, ent, "krbAdmServers")) != NULL) {
	    count = ldap_count_values(values);
	    if ((st=copy_arrays(values, &(rlparams->adminservers), (int) count)) != 0)
		goto cleanup;
	    *mask |= LDAP_REALM_ADMINSERVERS;
	    ldap_value_free(values);
	}

	if ((values=ldap_get_values(ld, ent, "krbPwdServers")) != NULL) {
	    count = ldap_count_values(values);
	    if ((st=copy_arrays(values, &(rlparams->passwdservers), (int) count)) != 0)
		goto cleanup;
	    *mask |= LDAP_REALM_PASSWDSERVERS;
	    ldap_value_free(values);
	}
#endif
    }
    ldap_msgfree(result);

    /*
     * If all of maxtktlife, maxrenewlife and ticketflags are not directly
     * available, use the policy dn from the policy reference attribute, if
     * available, to fetch the missing.
     */

    if ((!(*mask & LDAP_REALM_MAXTICKETLIFE && *mask & LDAP_REALM_MAXRENEWLIFE &&
	   *mask & LDAP_REALM_KRBTICKETFLAGS)) && rlparams->policyreference) {

	LDAP_SEARCH_1(rlparams->policyreference, LDAP_SCOPE_BASE, NULL, policy_attributes, IGNORE_STATUS);
	if (st != LDAP_SUCCESS && st != LDAP_NO_SUCH_OBJECT) {
	    int ost = st;
	    st = translate_ldap_error (st, OP_SEARCH);
	    krb5_set_error_message (context, st, "Policy object read failed: %s", ldap_err2string(ost));
	    goto cleanup;
	}
	ent = ldap_first_entry (ld, result);
	if (ent != NULL) {
	    if ((*mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
		if ((values=ldap_get_values(ld, ent, "krbmaxticketlife")) != NULL) {
		    rlparams->max_life = atoi(values[0]);
		    *mask |= LDAP_REALM_MAXTICKETLIFE;
		    ldap_value_free(values);
		}
	    }

	    if ((*mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
		if ((values=ldap_get_values(ld, ent, "krbmaxrenewableage")) != NULL) {
		    rlparams->max_renewable_life = atoi(values[0]);
		    *mask |= LDAP_REALM_MAXRENEWLIFE;
		    ldap_value_free(values);
		}
	    }

	    if ((*mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
		if ((values=ldap_get_values(ld, ent, "krbticketflags")) != NULL) {
		    rlparams->tktflags = atoi(values[0]);
		    *mask |= LDAP_REALM_KRBTICKETFLAGS;
		    ldap_value_free(values);
		}
	    }
	}
	ldap_msgfree(result);
    }

    rlparams->mask = *mask;
    *rlparamp = rlparams;
    st = store_tl_data(rlparams->tl_data, KDB_TL_MASK, mask);

cleanup:

    /* if there is an error, free allocated structures */
    if (st != 0) {
	krb5_ldap_free_realm_params(rlparams);
	*rlparamp=NULL;
    }
    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
    return st;
}


/*
  Free the krb5_ldap_realm_params.
*/
void
krb5_ldap_free_realm_params(rparams)
    krb5_ldap_realm_params *rparams;
{
    int i=0;

    if (rparams) {
	if (rparams->realmdn)
	    free(rparams->realmdn);

	if (rparams->realm_name)
	    krb5_xfree(rparams->realm_name);

	if (rparams->subtree) {
	    for (i=0; i<rparams->subtreecount && rparams->subtree[i] ; i++)
	        krb5_xfree(rparams->subtree[i]);
	    krb5_xfree(rparams->subtree);
        }

	if (rparams->kdcservers) {
	    for (i=0; rparams->kdcservers[i]; ++i)
		krb5_xfree(rparams->kdcservers[i]);
	    krb5_xfree(rparams->kdcservers);
	}

	if (rparams->adminservers) {
	    for (i=0; rparams->adminservers[i]; ++i)
		krb5_xfree(rparams->adminservers[i]);
	    krb5_xfree(rparams->adminservers);
	}

	if (rparams->passwdservers) {
	    for (i=0; rparams->passwdservers[i]; ++i)
		krb5_xfree(rparams->passwdservers[i]);
	    krb5_xfree(rparams->passwdservers);
	}

	if (rparams->tl_data) {
	    if (rparams->tl_data->tl_data_contents)
		krb5_xfree(rparams->tl_data->tl_data_contents);
	    krb5_xfree(rparams->tl_data);
	}

	if (rparams->mkey.contents) {
	    memset(rparams->mkey.contents, 0, rparams->mkey.length);
	    krb5_xfree(rparams->mkey.contents);
	}

	krb5_xfree(rparams);
    }
    return;
}

/* 
 * ******************************************************************************
 * DAL functions
 * ******************************************************************************
 */

krb5_error_code
krb5_ldap_delete_realm_1(krb5_context kcontext, char *conf_section, char **db_args)
{
    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
    return status;
}