/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 * 
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /afs/sipb/project/openafs/debian/cvs/openafs/src/libadmin/kas/afs_kasAdmin.c,v 1.1.1.5 2001/07/14 22:22:32 hartmans Exp $");

#include <stdio.h>
#include <afs/stds.h>
#include "afs_kasAdmin.h"
#include "../adminutil/afs_AdminInternal.h"
#include <afs/afs_AdminErrors.h>
#include <afs/afs_utilAdmin.h>
#include <afs/kauth.h>
#include <afs/kautils.h>
#include <afs/kaport.h>
#include <pthread.h>

#undef ENCRYPT

extern int ubik_Call();
extern int ubik_Call_New();
extern int ubik_CallIter();

typedef struct {
    int begin_magic;
    int is_valid;
    struct ubik_client *servers;
    char *cell;
    int end_magic;
} kas_server_t, *kas_server_p;

/*
 * IsValidServerHandle - verify the validity of a kas_server_t handle.
 *
 * PARAMETERS
 *
 * IN serverHandle - the handle to be verified.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
static int IsValidServerHandle(
  const kas_server_p serverHandle,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;

    /*
     * Validate input parameters
     */

    if (serverHandle == NULL) {
	tst = ADMKASSERVERHANDLENULL;
	goto fail_IsValidServerHandle;
    }

    if ((serverHandle->begin_magic != BEGIN_MAGIC) || 
	(serverHandle->end_magic != END_MAGIC)) {
	tst = ADMKASSERVERHANDLEBADMAGIC;
	goto fail_IsValidServerHandle;
    }

    if (!serverHandle->is_valid) {
	tst = ADMKASSERVERHANDLENOTVALID;
	goto fail_IsValidServerHandle;
    }

    if (serverHandle->servers == NULL) {
	tst = ADMKASSERVERHANDLENOSERVERS;
	goto fail_IsValidServerHandle;
    }
    rc = 1;

fail_IsValidServerHandle:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * IsValidCellHandle - verify the validity of a afs_cell_handle_t handle
 * for doing kas related functions.
 *
 * PARAMETERS
 *
 * IN cellHandle - the handle to be verified.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
static int IsValidCellHandle(
  const afs_cell_handle_p cellHandle,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;

    /*
     * Validate input parameters
     */

    if (!CellHandleIsValid((void *) cellHandle, &tst)) {
	goto fail_IsValidCellHandle;
    }

    if (!cellHandle->kas_valid) {
	tst = ADMCLIENTCELLKASINVALID;
	goto fail_IsValidCellHandle;
    }

    if (cellHandle->kas == NULL) {
	tst = ADMCLIENTCELLKASNULL;
	goto fail_IsValidCellHandle;
    }
    rc = 1;

fail_IsValidCellHandle:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * For all kas admin functions that take a cellHandle and a serverHandle,
 * the intention is that is the cellHandle is not NULL, we should use
 * it.  Otherwise, we use the serverHandle.  It is an error for both
 * of these parameters to be non-NULL.
 */

/*
 * ChooseValidServer - given a serverHandle and a cellHandle, choose the
 * one that is non-NULL, validate it, and return a ubik_client structure
 * that contains it.
 *
 * PARAMETERS
 *
 * IN cellHandle - the cell where kas calls are to be made
 *
 * IN serverHandle - the group of server(s) that should be used to satisfy
 * kas calls.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
static int ChooseValidServer(
  const afs_cell_handle_p cellHandle,
  const kas_server_p serverHandle,
  kas_server_p kasHandle,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;

    /*
     * Validate input parameters
     */

    if (kasHandle == NULL) {
	tst = ADMKASKASHANDLENULL;
	goto fail_ChooseValidServer;
    }

    /*
     * Only one of the input handle parameters to this function should
     * not be NULL
     */
    if ((cellHandle == NULL) && (serverHandle == NULL)) {
	tst = ADMKASCELLHANDLEANDSERVERHANDLENULL;
	goto fail_ChooseValidServer;
    }

    if ((cellHandle != NULL) && (serverHandle != NULL)) {
	tst = ADMKASCELLHANDLEANDSERVERHANDLENOTNULL;
	goto fail_ChooseValidServer;
    }

    /*
     * Validate the non-NULL handle
     */

    if (cellHandle != NULL) {
	if (IsValidCellHandle(cellHandle, &tst)) {
	    kasHandle->servers = cellHandle->kas;
	    kasHandle->cell = cellHandle->working_cell;
	} else {
	    goto fail_ChooseValidServer;
	}
    } else {
	if (IsValidServerHandle(serverHandle, &tst)) {
	    kasHandle->servers = serverHandle->servers;
	    kasHandle->cell = serverHandle->cell;
	} else {
	    goto fail_ChooseValidServer;
	}
    }

    kasHandle->begin_magic = BEGIN_MAGIC;
    kasHandle->end_magic = END_MAGIC;
    kasHandle->is_valid = 1;
    rc = 1;

fail_ChooseValidServer:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}


static int kaentryinfo_to_kas_principalEntry_t(
  struct kaentryinfo *from,
  kas_principalEntry_p to,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    int short reuse;
    unsigned char misc_stuff[4];

    if (from == NULL) {
	tst = ADMKASFROMNULL;
	goto fail_kaentryinfo_to_kas_principalEntry_t;
    }

    if (to == NULL) {
	tst = ADMKASTONULL;
	goto fail_kaentryinfo_to_kas_principalEntry_t;
    }

    if (from->flags & KAFADMIN) {
	to->adminSetting = KAS_ADMIN;
    } else {
	to->adminSetting = NO_KAS_ADMIN;
    }

    if (from->flags & KAFNOTGS) {
	to->tgsSetting = NO_TGS;
    } else {
	to->tgsSetting = TGS;
    }

    if (from->flags & KAFNOSEAL) {
	to->encSetting = NO_ENCRYPT;
    } else {
	to->encSetting = ENCRYPT;
    }

    if (from->flags & KAFNOCPW) {
	to->cpwSetting = NO_CHANGE_PASSWORD;
    } else {
	to->cpwSetting = CHANGE_PASSWORD;
    }

    reuse = (short) from->reserved3;
    if (!reuse) {
	to->rpwSetting = REUSE_PASSWORD;
    } else {
	to->rpwSetting = NO_REUSE_PASSWORD;
    }


    if (from->user_expiration == NEVERDATE) {
	to->userExpiration = 0;
    } else {
	to->userExpiration = from->user_expiration;
    }
    
    to->lastModTime = from->modification_time;
    strcpy(to->lastModPrincipal.principal, from->modification_user.name);
    strcpy(to->lastModPrincipal.instance, from->modification_user.instance);
    to->lastChangePasswordTime = from->change_password_time;
    to->maxTicketLifetime = from->max_ticket_lifetime;
    to->keyVersion = from->key_version;
    memcpy(&to->key, &from->key, sizeof(to->key));
    to->keyCheckSum = from->keyCheckSum;

    unpack_long(from->misc_auth_bytes, misc_stuff);
    to->daysToPasswordExpire = misc_stuff[0];
    to->failLoginCount = misc_stuff[2];
    to->lockTime = misc_stuff[3] << 9;
    rc = 1;

fail_kaentryinfo_to_kas_principalEntry_t:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_ServerOpen - open a handle to a set of kaserver's.
 *
 * PARAMETERS
 *
 * IN cellHandle - a previously opened cellHandle that corresponds
 * to the cell where the server(s) live.
 *
 * IN serverList - a NULL terminated list (a la argv) of server's that 
 * should be opened.
 *
 * OUT serverHandleP - a pointer to a void pointer that upon successful
 * completion contains serverHandle that can be used in other kas functions.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 * 
 * ASSUMPTIONS
 * 
 * This function make some assumptions about the afsconf_cell used by 
 * ka_AuthSpecificServersConn (since I just wrote ka_AuthSpecificServersConn).
 * It only fills in the fields that are required.
 *
 * Also we assume that the servers listed are members of the cell in
 * cellHandle without verifying that this is in fact the case.  kas itself
 * always assumes that the -servers parameter lists servers in the current
 * cell without verifying, so I am no worse than the current
 * implementation.  In fact I'm actually a little more flexible since you
 * can actually use my serverList to play with servers in another cell.
 * You can't do that with kas.  For certain functions in kas the same
 * cell assumption can cause things to fail (the ka_StringToKey function in
 * UserCreate).
 */
 
int ADMINAPI kas_ServerOpen(
  const void *cellHandle,
  const char **serverList,
  void **serverHandleP,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    int server_count=0, server_addr;
    struct afsconf_cell server_info;
    kas_server_p k_handle = (kas_server_p) malloc (sizeof(kas_server_t));

    /*
     * Validate input parameters
     */

    if (c_handle == NULL) {
	tst = ADMCLIENTCELLHANDLENULL;
	goto fail_kas_ServerOpen;
    }

    if (c_handle->kas_valid == 0) {
	tst = ADMCLIENTCELLKASINVALID;
	goto fail_kas_ServerOpen;
    }

    if (serverList == NULL) {
	tst = ADMKASSERVERLISTNULL;
	goto fail_kas_ServerOpen;
    }

    if (serverHandleP == NULL) {
	tst = ADMKASSERVERHANDLEPNULL;
	goto fail_kas_ServerOpen;
    }

    if (k_handle == NULL) {
	tst = ADMNOMEM;
	goto fail_kas_ServerOpen;
    }

    k_handle->begin_magic = BEGIN_MAGIC;
    k_handle->end_magic = END_MAGIC;
    k_handle->is_valid = 0;
    k_handle->servers = NULL;

    /*
     * Convert serverList to numeric addresses
     */

    for(server_count=0; serverList[server_count] != NULL; server_count++) {
	if (server_count >= MAXHOSTSPERCELL) {
	    tst = ADMKASSERVERLISTTOOLONG;
	    goto fail_kas_ServerOpen;
	}
	if (util_AdminServerAddressGetFromName(serverList[server_count],&server_addr,&tst)) {
	    server_info.hostAddr[server_count].sin_addr.s_addr = htonl(server_addr);
	    server_info.hostAddr[server_count].sin_port = htons(AFSCONF_KAUTHPORT);
	} else {
	    goto fail_kas_ServerOpen;
	}
    }

    if (server_count == 0) {
	tst = ADMKASSERVERLISTEMPTY;
	goto fail_kas_ServerOpen;
    }

    /*
     * Get a ubik_client handle for the specified servers
     */
    server_info.numServers = server_count;
    if (!(tst = ka_AuthSpecificServersConn(KA_MAINTENANCE_SERVICE, &c_handle->tokens->kas_token, &server_info, &k_handle->servers))) {
	k_handle->is_valid = 1;
	k_handle->cell = c_handle->working_cell;
	*serverHandleP = (void *) k_handle;
    } else {
	goto fail_kas_ServerOpen;
    }
    rc = 1;

fail_kas_ServerOpen:

    if ((rc == 0) && (k_handle != NULL)) {
	free(k_handle);
    }

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}
 
/*
 * kas_ServerClose - close a serverHandle.
 *
 * PARAMETERS
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_ServerClose(
  const void *serverHandle,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    kas_server_p k_handle = (kas_server_p) serverHandle;
 
    if (!IsValidServerHandle(k_handle, &tst)) {
	goto fail_kas_ServerClose;
    }

    tst = ubik_ClientDestroy(k_handle->servers);
    if (tst) {
	goto fail_kas_ServerClose;
    }

    k_handle->is_valid = 0;
    free(k_handle);
    rc = 1;

fail_kas_ServerClose:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalCreate - create a new principal.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - a kas_identity_p containing the identity of the new principal
 * to be created.
 *
 * IN password - the new principal's initial password.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_PrincipalCreate(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  const char *password,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    struct kas_encryptionKey key;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalCreate;
    }

    if (password == NULL) {
	tst = ADMKASPASSWORDNULL;
	goto fail_kas_PrincipalCreate;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalCreate;
    }

    if (!kas_StringToKey(kaserver.cell, password, &key, &tst)) {
	goto fail_kas_PrincipalCreate;
    }

    tst = ubik_Call(KAM_CreateUser, kaserver.servers, 0,
		    who->principal, who->instance, key);
    if (tst) {
	goto fail_kas_PrincipalCreate;
    }
    rc = 1;


fail_kas_PrincipalCreate:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalDelete - delete an existing principal.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - a kas_identity_p containing the identity of the principal
 * to be deleted.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_PrincipalDelete(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalDelete;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalDelete;
    }
    tst = ubik_Call(KAM_DeleteUser, kaserver.servers, 0,
		    who->principal, who->instance);
    if (tst) {
	goto fail_kas_PrincipalDelete;
    }
    rc = 1;

fail_kas_PrincipalDelete:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * GetPrincipalLockStatus - get the lock status of a principal.
 *
 * PARAMETERS
 *
 * IN kaserver - a valid kaserver handle previously returned by 
 * ChooseValidServer
 *
 * IN who - a kas_identity_p containing the identity of the principal
 * to be queried.
 *
 * OUT lockedUntil - the remaining number of seconds the principal is locked.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
static int GetPrincipalLockStatus(
  const kas_server_p kaserver,
  const kas_identity_p who,
  unsigned int *lockedUntil,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    unsigned int locked;
    int count=0;
    int once = 0;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (kaserver == NULL) {
	tst = ADMKASKASERVERNULL;
	goto fail_GetPrincipalLockStatus;
    }

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_GetPrincipalLockStatus;
    }

    if (lockedUntil == NULL) {
	tst = ADMKASLOCKEDUNTILNULL;
	goto fail_GetPrincipalLockStatus;
    }

    /*
     * Unlike every other kas rpc we make here, the lock/unlock rpc's
     * aren't ubik based.  So instead of calling ubik_Call, we use
     * ubik_CallIter.  ubik_CallIter steps through the list of hosts
     * in the ubik_client and calls them one at a time.  Since there's
     * no synchronization of this data across the servers we have to 
     * manually keep track of the shortest time to unlock the user ourselves.
     *
     * The original inspiration for this function is ka_islocked
     * in admin_tools.c.  I think that function is totally bogus so I'm
     * rewriting it here.
     *
     * This function should contact all the kaservers and request the lock
     * status of the principal.  If any of the servers say the principal is
     * unlocked, we report it as unlocked.  If all the servers say the
     * principal is locked, we find the server with the shortest lock time
     * remaining on the principal and return that time.
     *
     * This is different than kas, but I think kas is buggy.
     */

    *lockedUntil = 0;
    do {
	locked = 0;
	tst = ubik_CallIter(KAM_LockStatus, kaserver->servers, UPUBIKONLY,
			    &count, who->principal, who->instance,
			    &locked, 0, 0, 0, 0);
	if (tst == 0) {
	    if (locked) {
		if ((locked < *lockedUntil) || !once) {
		    *lockedUntil = locked;
		    once++;
		}
	    }
	}
    } while ((tst != UNOSERVERS) && (locked != 0));

    /*
     * Check to see if any server reported this principal unlocked.
     */

    if ((tst == 0) && (locked == 0)) {
	*lockedUntil = 0;
    }
    if ((tst == 0) || (tst == UNOSERVERS)) {
	rc = 1;
    }

fail_GetPrincipalLockStatus:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalGet - retrieve information about a single principal.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - a kas_identity_p containing the identity of the principal
 * to be retrieved.
 *
 * OUT principal - upon successful completion contains information
 * about who.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_PrincipalGet(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  kas_principalEntry_p principal,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    struct kaentryinfo entry;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalGet;
    }

    if (principal == NULL) {
	tst = ADMKASPRINCIPALNULL;
	goto fail_kas_PrincipalGet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalGet;
    }

    tst = ubik_Call(KAM_GetEntry, kaserver.servers, 0,
		    who->principal, who->instance, KAMAJORVERSION, &entry);
    if (tst) {
	goto fail_kas_PrincipalGet;
    }

    /*
     * copy the kaentryinfo structure to our kas_principalEntry_t
     * format
     */
    if (!kaentryinfo_to_kas_principalEntry_t(&entry, principal, &tst)) {
	goto fail_kas_PrincipalGet;
    }
    rc = 1;

fail_kas_PrincipalGet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

typedef struct principal_get {
    int current;
    int next;
    int count;
    kas_server_t kaserver;
    kas_identity_t principal[CACHED_ITEMS];
} principal_get_t, *principal_get_p;

static int DeletePrincipalSpecificData(
    void *rpc_specific,
    afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    principal_get_p prin = (principal_get_p) rpc_specific;

    prin->kaserver.is_valid = 0;
    rc = 1;

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

static int GetPrincipalRPC(
    void *rpc_specific,
    int slot,
    int *last_item,
    int *last_item_contains_data,
    afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    principal_get_p prin = (principal_get_p) rpc_specific;

    tst = ubik_Call(KAM_ListEntry, prin->kaserver.servers, 0,
		    prin->current, &prin->next, &prin->count,
		    &prin->principal[slot]);
    if (tst == 0) {
	prin->current = prin->next;
	if (prin->next == 0) {
	    *last_item = 1;
	    *last_item_contains_data = 0;
	}
	rc = 1;
    }

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

static int GetPrincipalFromCache(
    void *rpc_specific,
    int slot,
    void *dest,
    afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    principal_get_p prin = (principal_get_p) rpc_specific;

    memcpy(dest, &prin->principal[slot], sizeof(kas_identity_t));
    rc = 1;

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalGetBegin - start the process of iterating over the entire
 * kas database.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * OUT iterationIdP - upon successful completion contains a iterator that
 * can be passed to kas_PrincipalGetNext.
 *
 * LOCKS
 *
 * No locks are obtained or released by this function
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 *
 * ASSUMPTIONS
 */
 
int ADMINAPI kas_PrincipalGetBegin(
  const void *cellHandle,
  const void *serverHandle,
  void **iterationIdP,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    afs_admin_iterator_p iter = (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
    principal_get_p principal = (principal_get_p) malloc(sizeof(principal_get_t));
 
    /*
     * Validate arguments
     */

    if (iter == NULL) {
	tst = ADMNOMEM;
	goto fail_kas_PrincipalGetBegin;
    }

    if (principal == NULL) {
	tst = ADMNOMEM;
	goto fail_kas_PrincipalGetBegin;
    }

    if (iterationIdP == NULL) {
	tst = ADMITERATIONIDPNULL;
	goto fail_kas_PrincipalGetBegin;
    }

    if (!ChooseValidServer(c_handle, k_handle, &principal->kaserver, &tst)) {
	goto fail_kas_PrincipalGetBegin;
    }

    /*
     * Initialize the iterator structure
     */

    principal->current = 0;
    principal->next = 0;
    principal->count = 0;
    if (IteratorInit(iter, (void *) principal, GetPrincipalRPC,
		     GetPrincipalFromCache, NULL,
		     DeletePrincipalSpecificData, &tst)) {
	*iterationIdP = (void *) iter;
	rc = 1;
    }

fail_kas_PrincipalGetBegin:

    if (rc == 0) {
	if (iter != NULL) {
	    free(iter);
	}
	if (principal != NULL) {
	    free(principal);
	}
    }

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalGetNext - retrieve the next principal from the kaserver.
 *
 * PARAMETERS
 *
 * IN iterationId - an iterator previously returned by kas_PrincipalGetBegin
 *
 * OUT who - upon successful completion contains the next principal from the
 * kaserver
 *
 * LOCKS
 *
 * Hold the iterator mutex across the call to the kaserver.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 * Returns 0 and st == ADMITERATORDONE when the last entry is returned.
 */
 
int ADMINAPI kas_PrincipalGetNext(
  const void *iterationId,
  kas_identity_p who,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
 
    /*
     * Validate arguments
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalGetNext;
    }

    if (iter == NULL) {
	tst = ADMITERATORNULL;
	goto fail_kas_PrincipalGetNext;
    }

    rc = IteratorNext(iter, (void *) who, &tst);

fail_kas_PrincipalGetNext:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalGetDone - finish using a principal iterator
 *
 * PARAMETERS
 *
 * IN iterationId - an iterator previously returned by kas_PrincipalGetBegin
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 *
 * ASSUMPTIONS
 *
 * It is the user's responsibility to make sure kas_PrincipalGetDone
 * is called only once for each iterator.
 */
 
int ADMINAPI kas_PrincipalGetDone(
  const void *iterationIdP,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationIdP;
 
    /*
     * Validate argument
     */

    if (iter == NULL) {
	tst = ADMITERATORNULL;
	goto fail_kas_PrincipalGetDone;
    }

    rc = IteratorDone(iter, &tst);

fail_kas_PrincipalGetDone:
 
    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalKeySet - set a principal's password to a known value.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - the principal for whom the password is being set.
 *
 * IN keyVersion - the version number of the new key.
 *
 * IN key - the new password.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_PrincipalKeySet(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  int keyVersion,
  const kas_encryptionKey_p key,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalKeySet;
    }

    if (key == NULL) {
	tst = ADMKASKEYNULL;
	goto fail_kas_PrincipalKeySet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalKeySet;
    }

    tst = ubik_Call(KAM_SetPassword, kaserver.servers, 0,
		    who->principal, who->instance, keyVersion, *key);
    if (tst) {
	goto fail_kas_PrincipalKeySet;
    }

    /* If we failed to fail we must have succeeded */
    rc = 1;
 
fail_kas_PrincipalKeySet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalLockStatusGet - determine a principal's lock status.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - the principal whose lock status is being checked.
 *
 * OUT lock_end_timeP - the number of seconds until the principal is unlocked.
 * If 0 => user is unlocked.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 *
 * ASSUMPTIONS
 *
 * See the comments in GetPrincipalLockStatus regarding how the locking data
 * is kept INconsistently between servers.
 */
 
int ADMINAPI kas_PrincipalLockStatusGet(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  unsigned int *lock_end_timeP,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;

    /*
     * Validate input arguments and make rpc.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalLockStatusGet;
    }

    if (lock_end_timeP == NULL) {
	tst = ADMKASLOCKENDTIMEPNULL;
	goto fail_kas_PrincipalLockStatusGet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalLockStatusGet;
    }

    rc = GetPrincipalLockStatus(&kaserver, who, lock_end_timeP, &tst);
 
fail_kas_PrincipalLockStatusGet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalUnlock - unlock a principal.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - the principal who is being unlocked.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 *
 * ASSUMPTIONS
 *
 * See the comments in GetPrincipalLockStatus regarding how the locking data
 * is kept INconsistently between servers.
 */
 
int ADMINAPI kas_PrincipalUnlock(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    int count = 0;
    afs_status_t save_tst = 0;

    /*
     * Validate input arguments and make rpc.
     */
 
    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalUnlock;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_PrincipalUnlock;
    }

    do {
	  tst = ubik_CallIter(KAM_Unlock, kaserver.servers, 0,
			    &count, who->principal, who->instance,
			    0, 0, 0, 0);
	if (tst && (tst != UNOSERVERS)) {
	    if (save_tst == 0) {
		save_tst = tst; /* save the first failure */
	    }
	}
    } while (tst != UNOSERVERS);

    if ((tst == 0) || (tst == UNOSERVERS)){
	rc = 1;
    }

fail_kas_PrincipalUnlock:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}
 
static int getPrincipalFlags(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  afs_int32 *cur_flags,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    struct kaentryinfo tentry;

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_getPrincipalFlags;
    }

    tst = ubik_Call(KAM_GetEntry, kaserver.servers, 0, who->principal,
		    who->instance, KAMAJORVERSION, &tentry);
    if (tst == 0) {
	*cur_flags = tentry.flags;
	rc = 1;
    }

fail_getPrincipalFlags:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_PrincipalFieldsSet - modify an existing principal.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * IN who - the principal who is being modified.
 *
 * IN isAdmin - the admin status of the principal.
 *
 * IN grantTickets - should the TGS issue tickets for the principal.
 *
 * IN canEncrypt - should the TGS allow the use of encryption via the 
 * principal's key.
 *
 * IN canChangePassword - should the principal be allowed to change their
 * own password?
 *
 * IN expirationDate - the date when the principal will expire.
 *
 * IN maxTicketLifetime - the maximum lifetime of a ticket issued for
 * the principal.
 *
 * IN passwordExpires - the maximum number of days a particular
 * password can be used.  The limit is 255, 0 => no expiration.
 *
 * IN passwordReuse - can a password be reused by this principal.
 *
 * IN failedPasswordAttempts - number of failed login attempts before
 * a principal is locked.  The limit is 255, 0 => no limit.
 *
 * IN failedPasswordLockTime - the number of seconds a principal is
 * locked once failedPasswordAttempts is reached.  Some bizarre rounding
 * occurs for this value, see kas for more details.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 *
 * ASSUMPTIONS
 *
 * See the comments in GetPrincipalLockStatus regarding how the locking data
 * is kept INconsistently between servers.
 */
 
int ADMINAPI kas_PrincipalFieldsSet(
  const void *cellHandle,
  const void *serverHandle,
  const kas_identity_p who,
  const kas_admin_p isAdmin,
  const kas_tgs_p grantTickets,
  const kas_enc_p canEncrypt,
  const kas_cpw_p canChangePassword,
  const unsigned int *expirationDate,
  const unsigned int *maxTicketLifetime,
  const unsigned int *passwordExpires,
  const kas_rpw_p passwordReuse,
  const unsigned int *failedPasswordAttempts,
  const unsigned int *failedPasswordLockTime,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    afs_int32 flags = 0;
    Date expiration = 0;
    afs_int32 lifetime = 0;
    int was_spare;
    char spare_bytes[4] = {0,0,0,0};
    int somethings_changing = 0;

    /*
     * Validate input arguments.
     */

    if (who == NULL) {
	tst = ADMKASWHONULL;
	goto fail_kas_PrincipalFieldsSet;
    }

    /*
     * set flags based upon input
     *
     * If we're changing the flags, we need to get the current value of
     * the flags first and then make the changes
     */

    if ((isAdmin != NULL) || (grantTickets != NULL) ||
	(canEncrypt != NULL) || (canChangePassword != NULL)) {
	if (!getPrincipalFlags(cellHandle, serverHandle, who, &flags,
			       &tst)) {
	    goto fail_kas_PrincipalFieldsSet;
	}
    }

    if (isAdmin != NULL) {
	somethings_changing = 1;
	if (*isAdmin == KAS_ADMIN) {
	    flags |= KAFADMIN;
	} else {
	    flags &= ~KAFADMIN;
	}
    }

    if (grantTickets != NULL) {
	somethings_changing = 1;
	if (*grantTickets == NO_TGS) {
	    flags |= KAFNOTGS;
	} else {
	    flags &= ~KAFNOTGS;
	}
    }

    if (canEncrypt != NULL) {
	somethings_changing = 1;
	if (*canEncrypt == NO_ENCRYPT) {
	    flags |= KAFNOSEAL;
	} else {
	    flags &= ~KAFNOSEAL;
	}
    }

    if (canChangePassword != NULL) {
	somethings_changing = 1;
	if (*canChangePassword == NO_CHANGE_PASSWORD) {
	    flags |= KAFNOCPW;
	} else {
	    flags &= ~KAFNOCPW;
	}
    }

    flags = (flags & KAF_SETTABLE_FLAGS) | KAFNORMAL;

    if (expirationDate != NULL) {
	somethings_changing = 1;
	expiration = *expirationDate;
    }

    if (maxTicketLifetime != NULL) {
	somethings_changing = 1;
	lifetime = *maxTicketLifetime;
    }

    if (passwordExpires != NULL) {
	if (*passwordExpires > 255) {
	    tst = ADMKASPASSWORDEXPIRESTOOBIG;
	    goto fail_kas_PrincipalFieldsSet;
	}
	somethings_changing = 1;
	spare_bytes[0] = *passwordExpires + 1;
    }

    if (passwordReuse != NULL) {
	somethings_changing = 1;
	if (*passwordReuse == REUSE_PASSWORD) {
	    spare_bytes[1] = KA_REUSEPW;
	} else {
	    spare_bytes[1] = KA_NOREUSEPW;
	}
    }

    if (failedPasswordAttempts != NULL) {
	if (*failedPasswordAttempts > 255) {
	    tst = ADMKASFAILEDPASSWORDATTEMPTSTOOBIG;
	    goto fail_kas_PrincipalFieldsSet;
	}
	somethings_changing = 1;
	spare_bytes[2] = *failedPasswordAttempts + 1;
    }

    if (failedPasswordLockTime != NULL) {
	if (*failedPasswordLockTime > 36*60*60) {
	    tst = ADMKASFAILEDPASSWORDLOCKTIME;
	    goto fail_kas_PrincipalFieldsSet;
	}
	somethings_changing = 1;
	spare_bytes[3] = ((*failedPasswordLockTime + 511) >> 9) + 1;
    }

    was_spare = pack_long(spare_bytes);

    if (somethings_changing) {
	if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	    goto fail_kas_PrincipalFieldsSet;
	}
	tst = ubik_Call(KAM_SetFields, kaserver.servers, 0, who->principal,
			who->instance, flags, expiration, lifetime,
			-1, was_spare, 0);
	if (tst == 0) {
	    rc = 1;
	}
    } else {
	tst = ADMKASPRINCIPALFIELDSNOCHANGE;
    }
 
fail_kas_PrincipalFieldsSet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_ServerStatsGet - get server statistics.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * OUT stats - the statistics retrieved.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_ServerStatsGet(
  const void *cellHandle,
  const void *serverHandle,
  kas_serverStats_p stats,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    afs_int32 admins;
    kasstats statics;
    kadstats dynamics;
    size_t i;

    /*
     * Validate input arguments and make rpc.
     */

    if (stats == NULL) {
	tst = ADMKASSTATSNULL;
	goto fail_kas_ServerStatsGet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_ServerStatsGet;
    }

    tst = ubik_Call(KAM_GetStats, kaserver.servers, 0, KAMAJORVERSION, &admins, 
		    &statics, &dynamics);
    if (tst) {
	goto fail_kas_ServerStatsGet;
    }

    stats->allocations = statics.allocs;
    stats->frees = statics.frees;
    stats->changePasswordRequests = statics.cpws;
    stats->adminAccounts = admins;
    stats->host = dynamics.host;
    stats->serverStartTime = dynamics.start_time;
    stats->hashTableUtilization = dynamics.hashTableUtilization;

    i = sizeof(kas_serverProcStats_t);
    memcpy(&stats->authenticate, &dynamics.Authenticate, i);
    memcpy(&stats->changePassword, &dynamics.ChangePassword, i);
    memcpy(&stats->getTicket, &dynamics.GetTicket, i);
    memcpy(&stats->createUser, &dynamics.CreateUser, i);
    memcpy(&stats->setPassword, &dynamics.SetPassword, i);
    memcpy(&stats->setFields, &dynamics.SetFields, i);
    memcpy(&stats->deleteUser, &dynamics.DeleteUser, i);
    memcpy(&stats->getEntry, &dynamics.GetEntry, i);
    memcpy(&stats->listEntry, &dynamics.ListEntry, i);
    memcpy(&stats->getStats, &dynamics.GetStats, i);
    memcpy(&stats->getPassword, &dynamics.GetPassword, i);
    memcpy(&stats->getRandomKey, &dynamics.GetRandomKey, i);
    memcpy(&stats->debug, &dynamics.Debug, i);
    memcpy(&stats->udpAuthenticate, &dynamics.UAuthenticate, i);
    memcpy(&stats->udpGetTicket, &dynamics.UGetTicket, i);
    memcpy(&stats->unlock, &dynamics.Unlock, i);
    memcpy(&stats->lockStatus, &dynamics.LockStatus, i);

    stats->stringChecks = dynamics.string_checks;
    rc = 1;

fail_kas_ServerStatsGet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_ServerDebugGet - get server debug info.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * OUT stats - the debug info retrieved.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_ServerDebugGet(
  const void *cellHandle,
  const void *serverHandle,
  kas_serverDebugInfo_p debug,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
    struct ka_debugInfo info;
    int i;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (debug == NULL) {
	tst = ADMKASDEBUGNULL;
	goto fail_kas_ServerDebugGet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_ServerDebugGet;
    }
    tst = ubik_Call(KAM_Debug, kaserver.servers, 0, KAMAJORVERSION, 0, &info);
    if (tst) {
	goto fail_kas_ServerDebugGet;
    }
    debug->host = info.host;
    debug->serverStartTime = info.startTime;
    debug->currentTime = info.reserved1;
    debug->noAuth = info.noAuth;
    debug->lastTransaction = info.lastTrans;
    strcpy(debug->lastOperation, info.lastOperation);
    strcpy(debug->lastPrincipalAuth, info.lastAuth);
    strcpy(debug->lastPrincipalUDPAuth, info.lastUAuth);
    strcpy(debug->lastPrincipalTGS, info.lastTGS);
    strcpy(debug->lastPrincipalUDPTGS, info.lastUTGS);
    strcpy(debug->lastPrincipalAdmin, info.lastAdmin);
    strcpy(debug->lastServerTGS, info.lastTGSServer);
    strcpy(debug->lastServerUDPTGS, info.lastUTGSServer);
    debug->nextAutoCheckPointWrite = info.nextAutoCPW;
    debug->updatesRemainingBeforeAutoCheckPointWrite = info.updatesRemaining;
    debug->dbHeaderRead = info.dbHeaderRead;
    debug->dbVersion = info.dbVersion;
    debug->dbFreePtr = info.dbFreePtr;
    debug->dbEOFPtr = info.dbEofPtr;
    debug->dbKvnoPtr = info.dbKvnoPtr;
    debug->dbSpecialKeysVersion = info.dbSpecialKeysVersion;
    debug->dbHeaderLock = info.cheader_lock;
    debug->keyCacheLock = info.keycache_lock;
    debug->keyCacheVersion = info.kcVersion;
    debug->keyCacheSize = info.kcSize;
    debug->keyCacheUsed = info.kcUsed;
    for(i=0;i<info.kcUsed;i++) {
	debug->keyCache[i].lastUsed = info.kcInfo[i].used;
	debug->keyCache[i].keyVersionNumber = info.kcInfo[i].kvno;
	debug->keyCache[i].primary = info.kcInfo[i].primary;
	debug->keyCache[i].keyCheckSum = info.kcInfo[i].keycksum;
	strcpy(debug->keyCache[i].principal,info.kcInfo[i].principal);
    }
    rc = 1;

fail_kas_ServerDebugGet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_ServerRandomKeyGet - get a random key from a server.
 *
 * PARAMETERS
 *
 * IN cellHandle - a cellHandle previously returned by afsclient_CellOpen.
 *
 * IN serverHandle - a serverHandle previously returned by kas_ServerOpen.
 *
 * OUT key - a random key.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_ServerRandomKeyGet(
  const void *cellHandle,
  const void *serverHandle,
  kas_encryptionKey_p key,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
    afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
    kas_server_p k_handle = (kas_server_p) serverHandle;
    kas_server_t kaserver;
 
    /*
     * Validate input arguments and make rpc.
     */

    if (key == NULL) {
	tst = ADMKASKEYNULL;
	goto fail_kas_ServerRandomKeyGet;
    }

    if (!ChooseValidServer(c_handle, k_handle, &kaserver, &tst)) {
	goto fail_kas_ServerRandomKeyGet;
    }

    tst = ubik_Call(KAM_GetRandomKey, kaserver.servers, 0, key);
    if (tst) {
	goto fail_kas_ServerRandomKeyGet;
    }
    rc = 1;

fail_kas_ServerRandomKeyGet:

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}

/*
 * kas_StringToKey - turn a string key into a key.
 *
 * PARAMETERS
 *
 * IN cellName - the name of the cell where the key will be used.
 *
 * IN string - the string to be converted.
 *
 * OUT key - the encryption key.
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_StringToKey(
  const char *cellName,
  const char *string,
  kas_encryptionKey_p key,
  afs_status_p st)
{
    int rc = 0;
    afs_status_t tst = 0;
 
    ka_StringToKey(string, cellName, (struct ktc_encryptionKey *) key);
    rc = 1;

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}


/*
 * kas_KeyCheckSum - compute the checksum of an encryption key.
 *
 * PARAMETERS
 *
 * IN key - the encryption key.
 *
 * OUT cksumP - key checksum
 *
 * LOCKS
 *
 * No locks are held by this function.
 *
 * RETURN CODES
 *
 * Returns != 0 upon successful completion.
 */
 
int ADMINAPI kas_KeyCheckSum(
  const kas_encryptionKey_p key,
  unsigned int *cksumP,
  afs_status_p st
) {
    int rc = 0;
    afs_status_t tst = 0;
    afs_uint32 cksum32;
 
    if ((tst = ka_KeyCheckSum((char *)key, &cksum32)) == 0) {
	*cksumP = cksum32;
	rc = 1;
    }

    if (st != NULL) {
        *st = tst;
    }
    return rc;
}
