root/usr/src/lib/krb5/kdb/kdb5.c
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright 2006 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.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * 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.
 */

/*
 * This code was based on code donated to MIT by Novell for
 * distribution under the MIT license.
 */

/*
 * Include files
 */

#include <stdio.h>
#include <string.h>
#include <k5-int.h>
#include <osconf.h>
#include "kdb5.h"
#include <assert.h>
#include "k5-platform.h"
#include <libintl.h>

/* Currently DB2 policy related errors are exported from DAL.  But
   other databases should set_err function to return string.  */
#include "adb_err.h"

/*
 * Type definitions
 */
#define KRB5_TL_DB_ARGS                 0x7fff

/*
 * internal static variable
 */

static k5_mutex_t db_lock = K5_MUTEX_PARTIAL_INITIALIZER;

#ifdef _KDB5_STATIC_LINK
#undef _KDB5_DYNAMIC_LINK
#else
#undef _KDB5_DYNAMIC_LINK
/* to avoid redefinition problem */
#define _KDB5_DYNAMIC_LINK
#endif

static db_library lib_list;

/*
 * Helper Functions
 */

MAKE_INIT_FUNCTION(kdb_init_lock_list);
MAKE_FINI_FUNCTION(kdb_fini_lock_list);

int
kdb_init_lock_list(void)
{
    return k5_mutex_finish_init(&db_lock);
}

static int
kdb_lock_list()
{
    int err;
    err = CALL_INIT_FUNCTION (kdb_init_lock_list);
    if (err)
        return err;
    return k5_mutex_lock(&db_lock);
}

void
kdb_fini_lock_list(void)
{
    if (INITIALIZER_RAN(kdb_init_lock_list))
        k5_mutex_destroy(&db_lock);
}

static int
kdb_unlock_list()
{
    return k5_mutex_unlock(&db_lock);
}

#define kdb_init_lib_lock(a) 0
#define kdb_destroy_lib_lock(a) (void)0
#define kdb_lock_lib_lock(a, b) 0
#define kdb_unlock_lib_lock(a, b) (void)0

/* Caller must free result*/

static char *
kdb_get_conf_section(krb5_context kcontext)
{
    krb5_error_code status = 0;
    char   *result = NULL;
    char   *value = NULL;

    if (kcontext->default_realm == NULL)
        return NULL;
    /* The profile has to have been initialized.  If the profile was
       not initialized, expect nothing less than a crash.  */
    status = profile_get_string(kcontext->profile,
                                /* realms */
                                KDB_REALM_SECTION,
                                kcontext->default_realm,
                                /* under the realm name, database_module */
                                KDB_MODULE_POINTER,
                                /* default value is the realm name itself */
                                kcontext->default_realm,
                                &value);

    if (status) {
        /* some problem */
        result = strdup(kcontext->default_realm);
        /* let NULL be handled by the caller */
    } else {
        result = strdup(value);
        /* free profile string */
        profile_release_string(value);
    }

    return result;
}

static char *
kdb_get_library_name(krb5_context kcontext)
{
    krb5_error_code status = 0;
    char   *result = NULL;
    char   *value = NULL;
    char   *lib = NULL;

    status = profile_get_string(kcontext->profile,
                                /* realms */
                                KDB_REALM_SECTION,
                                kcontext->default_realm,
                                /* under the realm name, database_module */
                                KDB_MODULE_POINTER,
                                /* default value is the realm name itself */
                                kcontext->default_realm,
                                &value);
    if (status) {
        goto clean_n_exit;
    }

#define DB2_NAME "db2"
    /* we got the module section. Get the library name from the module */
    status = profile_get_string(kcontext->profile, KDB_MODULE_SECTION, value,
                                KDB_LIB_POINTER,
                                /* default to db2 */
                                DB2_NAME,
                                &lib);

    if (status) {
        goto clean_n_exit;
    }

    result = strdup(lib);
  clean_n_exit:
    if (value) {
        /* free profile string */
        profile_release_string(value);
    }

    if (lib) {
        /* free profile string */
        profile_release_string(lib);
    }
    return result;
}

static void
kdb_setup_opt_functions(db_library lib)
{
    if (lib->vftabl.set_master_key == NULL) {
        lib->vftabl.set_master_key = kdb_def_set_mkey;
    }

    if (lib->vftabl.get_master_key == NULL) {
        lib->vftabl.get_master_key = kdb_def_get_mkey;
    }

    if (lib->vftabl.fetch_master_key == NULL) {
        lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey;
    }

    if (lib->vftabl.verify_master_key == NULL) {
        lib->vftabl.verify_master_key = krb5_def_verify_master_key;
    }

    if (lib->vftabl.dbe_search_enctype == NULL) {
        lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
    }

    if (lib->vftabl.db_change_pwd == NULL) {
        lib->vftabl.db_change_pwd = krb5_dbe_def_cpw;
    }

    if (lib->vftabl.store_master_key == NULL) {
        lib->vftabl.store_master_key = krb5_def_store_mkey;
    }

    if (lib->vftabl.promote_db == NULL) {
        lib->vftabl.promote_db = krb5_def_promote_db;
    }
}

static int kdb_db2_pol_err_loaded = 0;
#ifdef _KDB5_STATIC_LINK
#define DEF_SYMBOL(a) extern kdb_vftabl krb5_db_vftabl_ ## a
#define GET_SYMBOL(a) (krb5_db_vftabl_ ## a)
static krb5_error_code
kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
{
    krb5_error_code status;
    void   *vftabl_addr = NULL;
    char    buf[KRB5_MAX_ERR_STR];

    if (!strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0)) {
        initialize_adb_error_table();
        kdb_db2_pol_err_loaded = 1;
    }

    *lib = calloc((size_t) 1, sizeof(**lib));
    if (*lib == NULL) {
        status = ENOMEM;
        goto clean_n_exit;
    }

    status = kdb_init_lib_lock(*lib);
    if (status) {
        goto clean_n_exit;
    }

    strcpy((*lib)->name, lib_name);

#if !defined(KDB5_USE_LIB_KDB_DB2) && !defined(KDB5_USE_LIB_TEST)
#error No database module defined
#endif

#ifdef KDB5_USE_LIB_KDB_DB2
    if (strcmp(lib_name, "kdb_db2") == 0) {
        DEF_SYMBOL(kdb_db2);
        vftabl_addr = (void *) &GET_SYMBOL(kdb_db2);
    } else
#endif
#ifdef KDB5_USE_LIB_TEST
    if (strcmp(lib_name, "test") == 0) {
        DEF_SYMBOL(test);
        vftabl_addr = (void *) &GET_SYMBOL(test);
    } else
#endif
    {
        snprintf(buf, sizeof(buf), gettext("Program not built to support %s database type\n"),
                lib_name);
        status = KRB5_KDB_DBTYPE_NOSUP;
        krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
        goto clean_n_exit;
    }

    memcpy(&(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl));

    kdb_setup_opt_functions(*lib);

    if ((status = (*lib)->vftabl.init_library())) {
        /* ERROR. library not initialized cleanly */
        snprintf(buf, sizeof(buf), gettext("%s library initialization failed, error code %ld\n"),
                lib_name, status);
        status = KRB5_KDB_DBTYPE_INIT;
        krb5_db_set_err(kcontext, krb5_err_have_str, status, buf);
        goto clean_n_exit;
    }

  clean_n_exit:
    if (status) {
        free(*lib), *lib = NULL;
    }
    return status;
}

#else /* KDB5_STATIC_LINK*/

static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH;
#define db_dl_n_locations (sizeof(db_dl_location) / sizeof(db_dl_location[0]))

static krb5_error_code
kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib)
{
    krb5_error_code status = 0;
    int     ndx;
    void  **vftabl_addrs = NULL;
    /* N.B.: If this is "const" but not "static", the Solaris 10
       native compiler has trouble building the library because of
       absolute relocations needed in read-only section ".rodata".
       When it's static, it goes into ".picdata", which is
       read-write.  */
    static const char *const dbpath_names[] = {
        KDB_MODULE_SECTION, "db_module_dir", NULL,
    };
    const char *filebases[2];
    char **profpath = NULL;
    char **path = NULL;

    filebases[0] = lib_name;
    filebases[1] = NULL;

    if (!strcmp(DB2_NAME, lib_name) && (kdb_db2_pol_err_loaded == 0)) {
        initialize_adb_error_table();
        kdb_db2_pol_err_loaded = 1;
    }

    *lib = calloc((size_t) 1, sizeof(**lib));
    if (*lib == NULL) {
        status = ENOMEM;
        goto clean_n_exit;
    }

    status = kdb_init_lib_lock(*lib);
    if (status) {
        goto clean_n_exit;
    }

    strcpy((*lib)->name, lib_name);

    /* Fetch the list of directories specified in the config
       file(s) first.  */
    status = profile_get_values(kcontext->profile, dbpath_names, &profpath);
    if (status != 0 && status != PROF_NO_RELATION)
        goto clean_n_exit;
    ndx = 0;
    if (profpath)
        while (profpath[ndx] != NULL)
            ndx++;

    path = calloc(ndx + db_dl_n_locations, sizeof (char *));
    if (path == NULL) {
        status = errno;
        goto clean_n_exit;
    }
    if (ndx)
        memcpy(path, profpath, ndx * sizeof(profpath[0]));
    memcpy(path + ndx, db_dl_location, db_dl_n_locations * sizeof(char *));
    status = 0;

    if ((status = krb5int_open_plugin_dirs ((const char **) path,
                                            filebases,
                                            &(*lib)->dl_dir_handle, &kcontext->err))) {
        const char *err_str = krb5_get_error_message(kcontext, status);
        status = KRB5_KDB_DBTYPE_NOTFOUND;
        krb5_set_error_message (kcontext, status,
                                gettext("Unable to find requested database type: %s"), err_str);
        krb5_free_error_message (kcontext, err_str);
        goto clean_n_exit;
    }

    if ((status = krb5int_get_plugin_dir_data (&(*lib)->dl_dir_handle, "kdb_function_table",
                                               &vftabl_addrs, &kcontext->err))) {
        const char *err_str = krb5_get_error_message(kcontext, status);
        status = KRB5_KDB_DBTYPE_INIT;
        krb5_set_error_message (kcontext, status,
                                gettext("plugin symbol 'kdb_function_table' lookup failed: %s"), err_str);
        krb5_free_error_message (kcontext, err_str);
        goto clean_n_exit;
    }

    if (vftabl_addrs[0] == NULL) {
        /* No plugins! */
        status = KRB5_KDB_DBTYPE_NOTFOUND;
        krb5_set_error_message (kcontext, status,
                                gettext("Unable to load requested database module '%s': plugin symbol 'kdb_function_table' not found"),
                                lib_name);
        goto clean_n_exit;
    }

    memcpy(&(*lib)->vftabl, vftabl_addrs[0], sizeof(kdb_vftabl));
    kdb_setup_opt_functions(*lib);

    if ((status = (*lib)->vftabl.init_library())) {
        /* ERROR. library not initialized cleanly */
        goto clean_n_exit;
    }

clean_n_exit:
    if (vftabl_addrs != NULL) { krb5int_free_plugin_dir_data (vftabl_addrs); }
    /* Both of these DTRT with NULL.  */
    profile_free_list(profpath);
    free(path);
    if (status) {
        if (*lib) {
            kdb_destroy_lib_lock(*lib);
            if (PLUGIN_DIR_OPEN((&(*lib)->dl_dir_handle))) {
                krb5int_close_plugin_dirs (&(*lib)->dl_dir_handle);
            }
            free(*lib);
            *lib = NULL;
        }
    }
    return status;
}

#endif /* end of _KDB5_STATIC_LINK */

static krb5_error_code
kdb_find_library(krb5_context kcontext, char *lib_name, db_library * lib)
{
    /* lock here so that no two threads try to do the same at the same time */
    krb5_error_code status = 0;
    int     locked = 0;
    db_library curr_elt, prev_elt = NULL;

    if ((status = kdb_lock_list()) != 0) {
        goto clean_n_exit;
    }
    locked = 1;

    curr_elt = lib_list;
    while (curr_elt != NULL) {
        if (strcmp(lib_name, curr_elt->name) == 0) {
            *lib = curr_elt;
            goto clean_n_exit;
        }
        prev_elt = curr_elt;
        curr_elt = curr_elt->next;
    }

    /* module not found. create and add to list */
    status = kdb_load_library(kcontext, lib_name, lib);
    if (status) {
        goto clean_n_exit;
    }

    if (prev_elt) {
        /* prev_elt points to the last element in the list */
        prev_elt->next = *lib;
        (*lib)->prev = prev_elt;
    } else {
        lib_list = *lib;
    }

  clean_n_exit:
    if (*lib) {
        (*lib)->reference_cnt++;
    }

    if (locked) {
        (void)kdb_unlock_list();
    }

    return status;
}

static krb5_error_code
kdb_free_library(db_library lib)
{
    krb5_error_code status = 0;
    int     locked = 0;

    if ((status = kdb_lock_list()) != 0) {
        goto clean_n_exit;
    }
    locked = 1;

    lib->reference_cnt--;

    if (lib->reference_cnt == 0) {
        status = lib->vftabl.fini_library();
        if (status) {
            goto clean_n_exit;
        }

        /* close the library */
        if (PLUGIN_DIR_OPEN((&lib->dl_dir_handle))) {
            krb5int_close_plugin_dirs (&lib->dl_dir_handle);
        }

        kdb_destroy_lib_lock(lib);

        if (lib->prev == NULL) {
            /* first element in the list */
            lib_list = lib->next;
        } else {
            lib->prev->next = lib->next;
        }

        if (lib->next) {
            lib->next->prev = lib->prev;
        }
        free(lib);
    }

  clean_n_exit:
    if (locked) {
        (void)kdb_unlock_list();
    }

    return status;
}

static krb5_error_code
kdb_setup_lib_handle(krb5_context kcontext)
{
    char   *library = NULL;
    krb5_error_code status = 0;
    db_library lib = NULL;
    kdb5_dal_handle *dal_handle = NULL;

    dal_handle = calloc((size_t) 1, sizeof(kdb5_dal_handle));
    if (dal_handle == NULL) {
        status = ENOMEM;
        goto clean_n_exit;
    }

    library = kdb_get_library_name(kcontext);
    if (library == NULL) {
        status = KRB5_KDB_DBTYPE_NOTFOUND;
        goto clean_n_exit;
    }

    status = kdb_find_library(kcontext, library, &lib);
    if (status) {
        goto clean_n_exit;
    }

    dal_handle->lib_handle = lib;
    kcontext->db_context = (void *) dal_handle;

  clean_n_exit:
    free(library);

    if (status) {
        free(dal_handle);
        if (lib) {
            (void)kdb_free_library(lib);
        }
    }

    return status;
}

static krb5_error_code
kdb_free_lib_handle(krb5_context kcontext)
{
    krb5_error_code status = 0;

    status =
        kdb_free_library(((kdb5_dal_handle *) kcontext->db_context)->
                         lib_handle);
    if (status) {
        goto clean_n_exit;
    }

    free(kcontext->db_context);
    kcontext->db_context = NULL;

  clean_n_exit:
    return status;
}

static void
get_errmsg (krb5_context kcontext, krb5_error_code err_code)
{
    kdb5_dal_handle *dal_handle;
    const char *e;
    if (err_code == 0)
        return;
    assert(kcontext != NULL);
    /* Must be called with dal_handle->lib_handle locked!  */
    assert(kcontext->db_context != NULL);
    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    if (dal_handle->lib_handle->vftabl.errcode_2_string == NULL)
        return;
    e = dal_handle->lib_handle->vftabl.errcode_2_string(kcontext, err_code);
    assert (e != NULL);
    krb5_set_error_message(kcontext, err_code, "%s", e);
    if (dal_handle->lib_handle->vftabl.release_errcode_string)
        dal_handle->lib_handle->vftabl.release_errcode_string(kcontext, e);
}

/*
 *      External functions... DAL API
 */
krb5_error_code
krb5_db_open(krb5_context kcontext, char **db_args, int mode)
{
    krb5_error_code status = 0;
    char   *section = NULL;
    kdb5_dal_handle *dal_handle;

    section = kdb_get_conf_section(kcontext);
    if (section == NULL) {
        status = KRB5_KDB_SERVER_INTERNAL_ERR;
        krb5_set_error_message (kcontext, status,
                gettext("unable to determine configuration section for realm %s\n"),
                kcontext->default_realm ? kcontext->default_realm : "[UNSET]");
        goto clean_n_exit;
    }

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        /* Solaris Kerberos */
        kdb_free_lib_handle(kcontext);
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args,
                                                   mode);
    get_errmsg(kcontext, status);

    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

    /* Solaris Kerberos */
    if (status)
        kdb_free_lib_handle(kcontext);

  clean_n_exit:
    if (section)
        free(section);
    return status;
}

krb5_error_code
krb5_db_inited(krb5_context kcontext)
{
    return !(kcontext && kcontext->db_context &&
             ((kdb5_dal_handle *) kcontext->db_context)->db_context);
}

krb5_error_code
krb5_db_create(krb5_context kcontext, char **db_args)
{
    krb5_error_code status = 0;
    char   *section = NULL;
    kdb5_dal_handle *dal_handle;

    section = kdb_get_conf_section(kcontext);
    if (section == NULL) {
        status = KRB5_KDB_SERVER_INTERNAL_ERR;
        krb5_set_error_message (kcontext, status,
                gettext("unable to determine configuration section for realm %s\n"),
                kcontext->default_realm);
        goto clean_n_exit;
    }

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args);
    get_errmsg(kcontext, status);

    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    if (section)
        free(section);
    return status;
}

krb5_error_code
krb5_db_fini(krb5_context kcontext)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        /* module not loaded. So nothing to be done */
        goto clean_n_exit;
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.fini_module(kcontext);
    get_errmsg(kcontext, status);

    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

    if (status) {
        goto clean_n_exit;
    }

    status = kdb_free_lib_handle(kcontext);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_destroy(krb5_context kcontext, char **db_args)
{
    krb5_error_code status = 0;
    char   *section = NULL;
    kdb5_dal_handle *dal_handle;

    section = kdb_get_conf_section(kcontext);
    if (section == NULL) {
        status = KRB5_KDB_SERVER_INTERNAL_ERR;
        krb5_set_error_message (kcontext, status,
                gettext("unable to determine configuration section for realm %s\n"),
                kcontext->default_realm);
        goto clean_n_exit;
    }

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    if (section)
        free(section);
    return status;
}

krb5_error_code
krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_set_option(krb5_context kcontext, int option, void *value)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_lock(krb5_context kcontext, int lock_mode)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    /* acquire an exclusive lock, ensures no other thread uses this context */
    status = kdb_lock_lib_lock(dal_handle->lib_handle, TRUE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode);
    get_errmsg(kcontext, status);

    /* exclusive lock is still held, so no other thread could use this context */
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_unlock(krb5_context kcontext)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    /* normal lock acquired and exclusive lock released */
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_unlock(kcontext);
    get_errmsg(kcontext, status);

    kdb_unlock_lib_lock(dal_handle->lib_handle, TRUE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_get_principal(krb5_context kcontext,
                      krb5_const_principal search_for,
                      krb5_db_entry * entries,
                      int *nentries, krb5_boolean * more)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for,
                                                        entries, nentries,
                                                        more);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_get_principal_nolock(krb5_context kcontext,
                      krb5_const_principal search_for,
                      krb5_db_entry * entries,
                      int *nentries, krb5_boolean * more)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_get_principal_nolock(kcontext,
                                                        search_for,
                                                        entries, nentries,
                                                        more);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_free_principal(kcontext, entry,
                                                         count);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_put_principal(krb5_context kcontext,
                      krb5_db_entry * entries, int *nentries)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;
    char  **db_args = NULL;
    krb5_tl_data *prev, *curr, *next;
    int     db_args_size = 0;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    /* Giving db_args as part of tl data causes, db2 to store the
       tl_data as such.  To prevent this, tl_data is collated and
       passed as a sepearte argument. Currently supports only one
       principal.  but passing it as a seperate argument makes it
       difficult for kadmin remote to pass arguments to server.  */
    prev = NULL, curr = entries->tl_data;
    while (curr) {
        if (curr->tl_data_type == KRB5_TL_DB_ARGS) {
            char  **t;
            /* Since this is expected to be NULL terminated string and
               this could come from any client, do a check before
               passing it to db.  */
            if (((char *) curr->tl_data_contents)[curr->tl_data_length - 1] !=
                '\0') {
                /* not null terminated. Dangerous input */
                status = EINVAL;
                goto clean_n_exit;
            }

            db_args_size++;
            t = realloc(db_args, sizeof(char *) * (db_args_size + 1));  /* 1 for NULL */
            if (t == NULL) {
                status = ENOMEM;
                goto clean_n_exit;
            }

            db_args = t;
            db_args[db_args_size - 1] = (char *) curr->tl_data_contents;
            db_args[db_args_size] = NULL;

            next = curr->tl_data_next;
            if (prev == NULL) {
                /* current node is the first in the linked list. remove it */
                entries->tl_data = curr->tl_data_next;
            } else {
                prev->tl_data_next = curr->tl_data_next;
            }
            entries->n_tl_data--;
            krb5_db_free(kcontext, curr);

            /* previous does not change */
            curr = next;
        } else {
            prev = curr;
            curr = curr->tl_data_next;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
                                                             nentries,
                                                             db_args);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    while (db_args_size) {
        if (db_args[db_args_size - 1])
            krb5_db_free(kcontext, db_args[db_args_size - 1]);

        db_args_size--;
    }

    if (db_args)
        free(db_args);

    return status;
}

krb5_error_code
krb5_db_delete_principal(krb5_context kcontext,
                         krb5_principal search_for, int *nentries)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
                                                           search_for,
                                                           nentries);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_iterate(krb5_context kcontext,
                char *match_entry,
                int (*func) (krb5_pointer, krb5_db_entry *),
                krb5_pointer func_arg,
                /* Solaris Kerberos: adding support for db_args */
                char **db_args)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    /* Solaris Kerberos: adding support for db_args */
    status = dal_handle->lib_handle->vftabl.db_iterate(kcontext,
                                                       match_entry,
                                                       func, func_arg,
                                                       db_args);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_supported_realms(krb5_context kcontext, char **realms)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_free_supported_realms(krb5_context kcontext, char **realms)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext,
                                                                realms);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_set_master_key_ext(krb5_context kcontext,
                           char *pwd, krb5_keyblock * key)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key);
    get_errmsg(kcontext, status);

    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
{
    return krb5_db_set_master_key_ext(context, NULL, key);
}

krb5_error_code
krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    /* Lets use temp key and copy it later to avoid memory problems
       when freed by the caller.  */
    status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_store_master_key(krb5_context kcontext,
                         char *db_arg,
                         krb5_principal mname,
                         krb5_keyblock * key, char *master_pwd)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.store_master_key(kcontext,
                                                             db_arg,
                                                             mname,
                                                             key, master_pwd);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;

krb5_error_code
krb5_db_fetch_mkey(krb5_context context,
                   krb5_principal mname,
                   krb5_enctype etype,
                   krb5_boolean fromkeyboard,
                   krb5_boolean twice,
                   char *db_args, krb5_data * salt, krb5_keyblock * key)
{
    krb5_error_code retval;
    char    password[BUFSIZ];
    krb5_data pwd;
    unsigned int size = sizeof(password);
    int     kvno;
    krb5_keyblock tmp_key;

    memset(&tmp_key, 0, sizeof(tmp_key));

    if (fromkeyboard) {
        krb5_data scratch;

        if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1,
                                         twice ? krb5_mkey_pwd_prompt2 : 0,
                                         password, &size))) {
            goto clean_n_exit;
        }

        pwd.data = password;
        pwd.length = size;
        if (!salt) {
            retval = krb5_principal2salt(context, mname, &scratch);
            if (retval)
                goto clean_n_exit;
        }
        retval =
            krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch,
                                 key);

        if (!salt)
            krb5_xfree(scratch.data);
        memset(password, 0, sizeof(password));  /* erase it */

    } else {
        kdb5_dal_handle *dal_handle;

        if (context->db_context == NULL) {
            retval = kdb_setup_lib_handle(context);
            if (retval) {
                goto clean_n_exit;
            }
        }

        dal_handle = (kdb5_dal_handle *) context->db_context;
        retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
        if (retval) {
            goto clean_n_exit;
        }
#if 0 /************** Begin IFDEF'ed OUT *******************************/
        /* Orig MIT */
        tmp_key.enctype = key->enctype;
#else
        /* Solaris Kerberos: need to use etype */
        tmp_key.enctype = etype;
#endif /**************** END IFDEF'ed OUT *******************************/
        retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
                                                                 mname,
                                                                 &tmp_key,
                                                                 &kvno,
                                                                 db_args);
        get_errmsg(context, retval);
        kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

        if (retval) {
            goto clean_n_exit;
        }

        key->contents = malloc(tmp_key.length);
        if (key->contents == NULL) {
            retval = ENOMEM;
            goto clean_n_exit;
        }

        key->magic = tmp_key.magic;
        key->enctype = tmp_key.enctype;
        key->length = tmp_key.length;
        memcpy(key->contents, tmp_key.contents, tmp_key.length);
    }

  clean_n_exit:
    if (tmp_key.contents) {
        memset(tmp_key.contents, 0, tmp_key.length);
        krb5_db_free(context, tmp_key.contents);
    }
    return retval;
}

krb5_error_code
krb5_db_verify_master_key(krb5_context kcontext,
                          krb5_principal mprinc, krb5_keyblock * mkey)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext,
                                                              mprinc, mkey);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

void   *
krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
{
    krb5_error_code status;
    kdb5_dal_handle *dal_handle;
    void   *new_ptr = NULL;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;

    new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size);

  clean_n_exit:
    return new_ptr;
}

void
krb5_db_free(krb5_context kcontext, void *ptr)
{
    krb5_error_code status;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;

    dal_handle->lib_handle->vftabl.db_free(kcontext, ptr);

  clean_n_exit:
    return;
}

/* has to be modified */

krb5_error_code
krb5_dbe_find_enctype(krb5_context kcontext,
                      krb5_db_entry * dbentp,
                      krb5_int32 ktype,
                      krb5_int32 stype,
                      krb5_int32 kvno, krb5_key_data ** kdatap)
{
    krb5_int32 start = 0;
    return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype,
                                   kvno, kdatap);
}

krb5_error_code
krb5_dbe_search_enctype(krb5_context kcontext,
                        krb5_db_entry * dbentp,
                        krb5_int32 * start,
                        krb5_int32 ktype,
                        krb5_int32 stype,
                        krb5_int32 kvno, krb5_key_data ** kdatap)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext,
                                                               dbentp,
                                                               start,
                                                               ktype,
                                                               stype,
                                                               kvno, kdatap);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

#define REALM_SEP_STRING        "@"

krb5_error_code
krb5_db_setup_mkey_name(krb5_context context,
                        const char *keyname,
                        const char *realm,
                        char **fullname, krb5_principal * principal)
{
    krb5_error_code retval;
    size_t  keylen;
    size_t  rlen = strlen(realm);
    char   *fname;

    if (!keyname)
        keyname = KRB5_KDB_M_NAME;      /* XXX external? */

    keylen = strlen(keyname);

    fname = malloc(keylen + rlen + strlen(REALM_SEP_STRING) + 1);
    if (!fname)
        return ENOMEM;

    strcpy(fname, keyname);
    (void)strcat(fname, REALM_SEP_STRING);
    (void)strcat(fname, realm);

    if ((retval = krb5_parse_name(context, fname, principal)))
        return retval;
    if (fullname)
        *fullname = fname;
    else
        free(fname);
    return 0;
}

krb5_error_code
krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_timestamp *stamp;
{
    krb5_tl_data tl_data;
    krb5_error_code code;
    krb5_int32 tmp;

    tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;

    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
        return (code);

    if (tl_data.tl_data_length != 4) {
        *stamp = 0;
        return (0);
    }

    krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp);

    *stamp = (krb5_timestamp) tmp;

    return (0);
}

/*ARGSUSED*/
krb5_error_code
krb5_dbe_lookup_tl_data(context, entry, ret_tl_data)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_tl_data *ret_tl_data;
{
    krb5_tl_data *tl_data;

    for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
        if (tl_data->tl_data_type == ret_tl_data->tl_data_type) {
            *ret_tl_data = *tl_data;
            return (0);
        }
    }

    /* if the requested record isn't found, return zero bytes.
     * if it ever means something to have a zero-length tl_data,
     * this code and its callers will have to be changed */

    ret_tl_data->tl_data_length = 0;
    ret_tl_data->tl_data_contents = NULL;
    return (0);
}

krb5_error_code
krb5_dbe_create_key_data(context, entry)
    krb5_context context;
    krb5_db_entry *entry;
{
    if ((entry->key_data =
         (krb5_key_data *) krb5_db_alloc(context, entry->key_data,
                                         (sizeof(krb5_key_data) *
                                          (entry->n_key_data + 1)))) == NULL)
        return (ENOMEM);

    memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data));
    entry->n_key_data++;

    return 0;
}

krb5_error_code
krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_timestamp mod_date;
    krb5_const_principal mod_princ;
{
    krb5_tl_data tl_data;

    krb5_error_code retval = 0;
    krb5_octet *nextloc = 0;
    char   *unparse_mod_princ = 0;
    unsigned int unparse_mod_princ_size;

    if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ)))
        return (retval);

    unparse_mod_princ_size = strlen(unparse_mod_princ) + 1;

    if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4))
        == NULL) {
        free(unparse_mod_princ);
        return (ENOMEM);
    }

    tl_data.tl_data_type = KRB5_TL_MOD_PRINC;
    tl_data.tl_data_length = unparse_mod_princ_size + 4;
    tl_data.tl_data_contents = nextloc;

    /* Mod Date */
    krb5_kdb_encode_int32(mod_date, nextloc);

    /* Mod Princ */
    memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size);

    retval = krb5_dbe_update_tl_data(context, entry, &tl_data);

    free(unparse_mod_princ);
    free(nextloc);

    return (retval);
}

krb5_error_code
krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_timestamp *mod_time;
    krb5_principal *mod_princ;
{
    krb5_tl_data tl_data;
    krb5_error_code code;

    tl_data.tl_data_type = KRB5_TL_MOD_PRINC;

    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
        return (code);

    if ((tl_data.tl_data_length < 5) ||
        (tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0'))
        return (KRB5_KDB_TRUNCATED_RECORD);

    /* Mod Date */
    krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time);

    /* Mod Princ */
    if ((code = krb5_parse_name(context,
                                (const char *) (tl_data.tl_data_contents + 4),
                                mod_princ)))
        return (code);

    return (0);
}

krb5_error_code
krb5_dbe_update_last_pwd_change(context, entry, stamp)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_timestamp stamp;
{
    krb5_tl_data tl_data;
    krb5_octet buf[4];          /* this is the encoded size of an int32 */

    tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
    tl_data.tl_data_length = sizeof(buf);
    krb5_kdb_encode_int32((krb5_int32) stamp, buf);
    tl_data.tl_data_contents = buf;

    return (krb5_dbe_update_tl_data(context, entry, &tl_data));
}

krb5_error_code
krb5_dbe_update_tl_data(context, entry, new_tl_data)
    krb5_context context;
    krb5_db_entry *entry;
    krb5_tl_data *new_tl_data;
{
    krb5_tl_data *tl_data = NULL;
    krb5_octet *tmp;

    /* copy the new data first, so we can fail cleanly if malloc()
     * fails */
    if ((tmp =
         (krb5_octet *) krb5_db_alloc(context, NULL,
                                      new_tl_data->tl_data_length)) == NULL)
        return (ENOMEM);

    /* Find an existing entry of the specified type and point at
     * it, or NULL if not found */

    if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) { /* db_args can be multiple */
        for (tl_data = entry->tl_data; tl_data;
             tl_data = tl_data->tl_data_next)
            if (tl_data->tl_data_type == new_tl_data->tl_data_type)
                break;
    }

    /* if necessary, chain a new record in the beginning and point at it */

    if (!tl_data) {
        if ((tl_data =
             (krb5_tl_data *) krb5_db_alloc(context, NULL,
                                            sizeof(krb5_tl_data)))
            == NULL) {
            free(tmp);
            return (ENOMEM);
        }
        memset(tl_data, 0, sizeof(krb5_tl_data));
        tl_data->tl_data_next = entry->tl_data;
        entry->tl_data = tl_data;
        entry->n_tl_data++;
    }

    /* fill in the record */

    if (tl_data->tl_data_contents)
        krb5_db_free(context, tl_data->tl_data_contents);

    tl_data->tl_data_type = new_tl_data->tl_data_type;
    tl_data->tl_data_length = new_tl_data->tl_data_length;
    tl_data->tl_data_contents = tmp;
    memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length);

    return (0);
}

/* change password functions */
krb5_error_code
krb5_dbe_cpw(krb5_context kcontext,
             krb5_keyblock * master_key,
             krb5_key_salt_tuple * ks_tuple,
             int ks_tuple_count,
             char *passwd,
             int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext,
                                                          master_key,
                                                          ks_tuple,
                                                          ks_tuple_count,
                                                          passwd,
                                                          new_kvno,
                                                          keepold, db_entry);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

/* policy management functions */
krb5_error_code
krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_get_policy(krb5_context kcontext, char *name,
                   osa_policy_ent_t * policy, int *cnt)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy,
                                                     cnt);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
                    osa_adb_iter_policy_func func, void *data)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry,
                                                      func, data);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

krb5_error_code
krb5_db_delete_policy(krb5_context kcontext, char *policy)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}

void
krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return;
}

krb5_error_code
krb5_db_promote(krb5_context kcontext, char **db_args)
{
    krb5_error_code status = 0;
    char   *section = NULL;
    kdb5_dal_handle *dal_handle;

    section = kdb_get_conf_section(kcontext);
    if (section == NULL) {
        status = KRB5_KDB_SERVER_INTERNAL_ERR;
        krb5_set_error_message (kcontext, status,
                gettext("unable to determine configuration section for realm %s\n"),
                kcontext->default_realm);
        goto clean_n_exit;
    }

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    status =
        dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args);
    get_errmsg(kcontext, status);
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    if (section)
        free(section);
    return status;
}

/*
 * Solaris Kerberos: support for iprop
 *
 * Not all KDB plugins support iprop.
 *
 * sets iprop_supported to 1 if iprop supportd, 0 otherwise.
 */
krb5_error_code
krb5_db_supports_iprop(krb5_context kcontext, int *iprop_supported)
{
    krb5_error_code status = 0;
    kdb5_dal_handle *dal_handle;

    if (kcontext->db_context == NULL) {
        status = kdb_setup_lib_handle(kcontext);
        if (status) {
            goto clean_n_exit;
        }
    }

    dal_handle = (kdb5_dal_handle *) kcontext->db_context;
    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
    if (status) {
        goto clean_n_exit;
    }

    *iprop_supported = dal_handle->lib_handle->vftabl.iprop_supported;
    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);

  clean_n_exit:
    return status;
}