kll.c   [plain text]


/*
 * $Header$
 *
 * Copyright 2006 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.
 */

#include <Security/Authorization.h>
#include <CoreFoundation/CoreFoundation.h>

#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <stdio.h>

#include <dispatch/dispatch.h>

#include "heim.h"
#include "mit-KerberosLogin.h"
#include "mit-CredentialsCache.h"

struct KLLoginOptions {
    krb5_get_init_creds_opt *opt;
    char *service;
};

/* 
 * Deprecated Error codes 
 */
enum {
    /* Carbon Dialog errors */
    klDialogDoesNotExistErr             = 19676,
    klDialogAlreadyExistsErr,
    klNotInForegroundErr,
    klNoAppearanceErr,
    klFatalDialogErr,
    klCarbonUnavailableErr    
};

krb5_get_init_creds_opt *__KLLoginOptionsGetKerberos5Options (KLLoginOptions ioOptions);
KLTime __KLLoginOptionsGetStartTime (KLLoginOptions ioOptions);
char *__KLLoginOptionsGetServiceName (KLLoginOptions ioOptions);

#define CHECK_VERSION(_vers) ((_vers) != kerberosVersion_V5 && (_vers) != kerberosVersion_All)


KLStatus
KLAcquireTickets (KLPrincipal   inPrincipal,
		  KLPrincipal  *outPrincipal,
		  char        **outCredCacheName)
{
    LOG_ENTRY();
    return KLAcquireInitialTickets (inPrincipal, 
				    NULL, 
				    outPrincipal, 
				    outCredCacheName);
}


KLStatus
KLAcquireNewTickets (KLPrincipal  inPrincipal,
		     KLPrincipal  *outPrincipal,
		     char        **outCredCacheName)
{
    LOG_ENTRY();
    return KLAcquireNewInitialTickets (inPrincipal, 
				       NULL, 
				       outPrincipal, 
				       outCredCacheName);
}


KLStatus
KLAcquireTicketsWithPassword (KLPrincipal      inPrincipal,
			      KLLoginOptions   inLoginOptions,
			      const char      *inPassword,
			      char           **outCredCacheName)
{
    LOG_ENTRY();
    return KLAcquireInitialTicketsWithPassword (inPrincipal, 
						inLoginOptions, 
						inPassword, 
						outCredCacheName);
}


KLStatus
KLAcquireNewTicketsWithPassword (KLPrincipal      inPrincipal,
				 KLLoginOptions   inLoginOptions,
				 const char      *inPassword,
				 char           **outCredCacheName)
{
    LOG_ENTRY();
    return KLAcquireNewInitialTicketsWithPassword (inPrincipal, 
						   inLoginOptions, 
						   inPassword, 
						   outCredCacheName);
}


KLStatus
KLSetApplicationOptions (const void *inAppOptions)
{
    return klNoErr;
}


KLStatus
KLGetApplicationOptions (void *outAppOptions)
{
    return klDialogDoesNotExistErr;
}

#define kCoreAuthPanelKerberosRight "com.apple.KerberosAgent"
#define kCoreAuthPanelKerberosPrincipal "principal"
#define kCoreAuthPanelKerberosOptions "kerberosOptions"

static OSStatus
acquireticket_ui(KLPrincipal inPrincipal,
		 KLLoginOptions inLoginOptions,
		 KLPrincipal *outPrincipal,
		 char **outCredCacheName)
{
    AuthorizationRef auth;
    OSStatus ret;
    char *princ = NULL;
    CFDataRef d = NULL;
    
    LOG_ENTRY();

    if (outPrincipal)
	*outPrincipal = NULL;
    if (outCredCacheName)
	*outCredCacheName = NULL;

    if (inPrincipal) {
	ret = heim_krb5_unparse_name(milcontext, inPrincipal, &princ);
	if (ret)
	    return ret;
    }
    

    ret = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &auth);
    if (ret) {
	free(princ);
	return ret;
    }
    
    AuthorizationItem rightItems[1] = { kCoreAuthPanelKerberosRight, 0, NULL, 0 };
    AuthorizationRights rights = { sizeof(rightItems[0])/sizeof(rightItems) , rightItems };
    AuthorizationItem envItems[3];
    AuthorizationEnvironment env = { 0 , envItems };
    AuthorizationFlags authFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;

    if (princ) {
	envItems[env.count].name = kCoreAuthPanelKerberosPrincipal;
	envItems[env.count].valueLength = strlen(princ);
	envItems[env.count].value = princ;
	envItems[env.count].flags = 0;
	env.count++;
    }

    if (inLoginOptions && inLoginOptions->opt) {
	CFMutableDictionaryRef dict;

	dict = CFDictionaryCreateMutable(NULL, 1,
					 &kCFTypeDictionaryKeyCallBacks,
					 &kCFTypeDictionaryValueCallBacks);
	if (dict == NULL)
	    goto out;
	
	if (inLoginOptions->opt->renew_life) {
	    CFStringRef t;
	    t = CFStringCreateWithFormat(NULL, 0, CFSTR("%ld"),(long)inLoginOptions->opt->renew_life);
	    CFDictionarySetValue(dict, CFSTR("renewTime"), t);
	    CFRelease(t);
	}

	d = CFPropertyListCreateData(NULL, dict, kCFPropertyListBinaryFormat_v1_0,
				     0, NULL);
	CFRelease(dict);

	envItems[env.count].name = kCoreAuthPanelKerberosOptions;
	envItems[env.count].valueLength = CFDataGetLength(d);
	envItems[env.count].value = (void *)CFDataGetBytePtr(d);
	envItems[env.count].flags = 0;
	env.count++;
    }
    
    ret = AuthorizationCopyRights(auth, &rights, &env, authFlags, NULL);

    if (ret == 0 && outPrincipal) {
	AuthorizationItemSet *info;
	UInt32 i;
	ret = AuthorizationCopyInfo(auth, NULL, &info);
	if (ret)
	    goto out;
	for(i = 0; i < info->count; i++) {
	    if (strcmp(info->items[i].name, "out-principal") == 0) {
		char *str;
		asprintf(&str, "%.*s", (int)info->items[i].valueLength, (char *)info->items[i].value);
		heim_krb5_parse_name(milcontext, str, outPrincipal);
	    } else if (strcmp(info->items[i].name, "out-cache-name") == 0) {
		asprintf(outCredCacheName, "%.*s", (int)info->items[i].valueLength, (char *)info->items[i].value);
	    }
	}
	AuthorizationFreeItemSet(info);
	if (*outPrincipal == NULL)
	    ret = EINVAL;
    }
out:
    if (d)
	CFRelease(d);
    AuthorizationFree(auth, kAuthorizationFlagDestroyRights);
    free(princ);
    return ret;
}

KLStatus
KLAcquireInitialTickets(KLPrincipal      inPrincipal,
			KLLoginOptions   inLoginOptions,
			KLPrincipal     *outPrincipal,
			char           **outCredCacheName)
{
    KLBoolean ValidTickets;
    KLStatus ret;

    LOG_ENTRY();

    ret = KLCacheHasValidTickets(inPrincipal,
				 kerberosVersion_V5,
				 &ValidTickets,
				 outPrincipal,
				 outCredCacheName);
    if (ret || !ValidTickets)
	ret = acquireticket_ui(inPrincipal, inLoginOptions, outPrincipal, outCredCacheName);

    return ret;
}


KLStatus
KLAcquireNewInitialTickets (KLPrincipal      inPrincipal,
			    KLLoginOptions   inLoginOptions,
			    KLPrincipal     *outPrincipal,
			    char           **outCredCacheName)
{
    LOG_ENTRY();

    return acquireticket_ui(inPrincipal, inLoginOptions, outPrincipal, outCredCacheName);
}


KLStatus
KLDestroyTickets(KLPrincipal inPrincipal)
{
    krb5_context context = mshim_ctx();
    krb5_error_code ret;
    krb5_ccache id;

    ret = heim_krb5_cc_cache_match(context, inPrincipal, &id);
    if (ret)
	return ret;

    return krb5_cc_destroy((mit_krb5_context)context, (mit_krb5_ccache)id);
}


KLStatus KLChangePassword (KLPrincipal inPrincipal)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLStatus
KLAcquireInitialTicketsWithPassword(KLPrincipal      inPrincipal,
				    KLLoginOptions   inLoginOptions,
				    const char      *inPassword,
				    char           **outCredCacheName)
{
    KLStatus ret;
    KLBoolean ValidTickets;

    ret = KLCacheHasValidTickets(inPrincipal,
				 kerberosVersion_V5,
				 &ValidTickets,
				 NULL,
				 outCredCacheName);
    if (ret == 0) {
	if (ValidTickets)
	    return klNoErr; /* done */
	/* get credential */
	if (outCredCacheName)
	    free(*outCredCacheName);
    }
    return KLAcquireNewInitialTicketsWithPassword(inPrincipal, 
						  inLoginOptions,
						  inPassword,
						  outCredCacheName);
}

KLStatus
KLAcquireNewInitialTicketsWithPassword(KLPrincipal      inPrincipal,
				       KLLoginOptions   inLoginOptions,
				       const char      *inPassword,
				       char           **outCredCacheName)
{
    krb5_context context = mshim_ctx();
    krb5_error_code ret;
    krb5_ccache cache;
    krb5_creds creds;
    char *service = NULL;
    krb5_get_init_creds_opt *opt = NULL;

    LOG_ENTRY();

    if (inLoginOptions) {
	service = inLoginOptions->service;
	opt = inLoginOptions->opt;
    }

    ret = heim_krb5_get_init_creds_password(context, &creds,
					    inPrincipal, inPassword,
					    NULL, NULL, 0,
					    service,
					    opt);
    if (ret)
	return ret;

    ret = heim_krb5_cc_cache_match(context, inPrincipal, &cache);
    if (ret)
	ret = heim_krb5_cc_new_unique(context, NULL, NULL, &cache);
    if (ret)
	goto out;
	
    ret = heim_krb5_cc_initialize(context, cache, creds.client);
    if(ret)
	goto out;

    ret = heim_krb5_cc_store_cred(context, cache, &creds);
    if (ret)
	goto out;

    if (outCredCacheName)
	*outCredCacheName = strdup(heim_krb5_cc_get_name(context, cache));

 out:
    if (cache) {
	if (ret)
	    krb5_cc_destroy((mit_krb5_context)context, (mit_krb5_ccache)cache);
	else
	    heim_krb5_cc_close(context, cache);
    }
    heim_krb5_free_cred_contents(context, &creds);

    return ret;
}


KLStatus KLAcquireNewInitialTicketCredentialsWithPassword (KLPrincipal      inPrincipal,
                                                           KLLoginOptions   inLoginOptions,
                                                           const char      *inPassword,
                                                           void            *inV5Context,
                                                           KLBoolean       *outGotV4Credentials,
                                                           KLBoolean       *outGotV5Credentials,
                                                           void            *outV4Credentials,
                                                           void            *outV5Credentials)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLStatus KLStoreNewInitialTicketCredentials (KLPrincipal     inPrincipal,
                                             void           *inV5Context,
                                             void           *inV4Credentials,
                                             void           *inV5Credentials,
                                             char          **outCredCacheName)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLStatus KLVerifyInitialTickets (KLPrincipal   inPrincipal,
                                 KLBoolean     inFailIfNoHostKey,
                                 char        **outCredCacheName)
{
    LOG_UNIMPLEMENTED();
    return klNoErr;
}


KLStatus KLVerifyInitialTicketCredentials (void        *inV4Credentials,
                                           void        *inV5Credentials,
                                           KLBoolean    inFailIfNoHostKey)
{
    LOG_UNIMPLEMENTED();
    return klNoErr;
}


KLStatus KLAcquireNewInitialTicketsWithKeytab (KLPrincipal      inPrincipal,
                                               KLLoginOptions   inLoginOptions,
                                               const char      *inKeytabName,
                                               char           **outCredCacheName)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLStatus
KLRenewInitialTickets(KLPrincipal      inPrincipal,
		      KLLoginOptions   inLoginOptions,
		      KLPrincipal     *outPrincipal,
		      char           **outCredCacheName)
{
    krb5_context context = mshim_ctx();
    krb5_error_code ret;
    krb5_creds in, *cred = NULL;
    krb5_ccache id;
    krb5_kdc_flags flags;
    krb5_const_realm realm;
    krb5_principal principal = NULL;

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

    LOG_ENTRY();

    if (outPrincipal)
	*outPrincipal = NULL;
    if (outCredCacheName)
	*outCredCacheName = NULL;

    if (inPrincipal) {
	principal = inPrincipal;
    } else {
	ret = heim_krb5_get_default_principal(context, &principal);
	if (ret)
	    return ret;
    }

    ret = heim_krb5_cc_cache_match(context, principal, &id);
    if (ret) {
	if (inPrincipal == NULL)
	    heim_krb5_free_principal(context, principal);
	return ret;
    }

    in.client = principal;

    realm = heim_krb5_principal_get_realm(context, in.client);

    if (inLoginOptions && inLoginOptions->service)
	ret = heim_krb5_make_principal(context, &in.server, realm, inLoginOptions->service, NULL);
    else
	ret = heim_krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, realm, NULL);
    if (ret) {
	if (inPrincipal == NULL)
	    heim_krb5_free_principal(context, principal);
	heim_krb5_cc_close(context, id);
	return ret;
    }

    flags.i = 0;
    if (inLoginOptions)
	flags.i = inLoginOptions->opt->flags;

    /* Pull out renewable from previous ticket */
    ret = heim_krb5_get_credentials(context, KRB5_GC_CACHED, id, &in, &cred);
    if (inPrincipal == NULL)
	heim_krb5_free_principal(context, principal);
    if (ret == 0 && cred) {
	flags.b.renewable = cred->flags.b.renewable;
	heim_krb5_free_creds (context, cred);
	cred = NULL;
    }

    flags.b.renew = 1;

    ret = heim_krb5_get_kdc_cred(context, id, flags, NULL, NULL, &in, &cred);
    heim_krb5_free_principal(context, in.server);
    if (ret)
	goto out;
    ret = heim_krb5_cc_initialize(context, id, in.client);
    if (ret)
	goto out;
    ret = heim_krb5_cc_store_cred(context, id, cred);

 out:
    if (cred)
	heim_krb5_free_creds (context, cred);
    heim_krb5_cc_close(context, id);

    return ret;
}


KLStatus KLValidateInitialTickets (KLPrincipal      inPrincipal,
                                   KLLoginOptions   inLoginOptions,
                                   char           **outCredCacheName)
{
    LOG_UNIMPLEMENTED();
    return klNoErr;
}

static krb5_timestamp g_cc_change_time = 0;
static KLTime g_kl_change_time = 0;
static pthread_mutex_t g_change_time_mutex = PTHREAD_MUTEX_INITIALIZER;


KLStatus
KLLastChangedTime(KLTime *outLastChangedTime)
{
    krb5_timestamp ccChangeTime;
    KLStatus ret;
    
    LOG_ENTRY();

    if (outLastChangedTime == NULL)
	return klParameterErr;

    ret = heim_krb5_cccol_last_change_time(milcontext, "API", &ccChangeTime);
    if (ret)
	return ret;
	
    pthread_mutex_lock(&g_change_time_mutex);

    if (g_kl_change_time == 0)
	g_kl_change_time = (KLTime)ccChangeTime;
    
    if (ccChangeTime > g_cc_change_time) {

	if (ccChangeTime > g_kl_change_time)
	    g_kl_change_time = (KLTime)ccChangeTime;
	else
	    g_kl_change_time++;

	g_cc_change_time = ccChangeTime;
    }

    *outLastChangedTime = g_kl_change_time;

    pthread_mutex_unlock (&g_change_time_mutex);
    
    return klNoErr;
}


static krb5_error_code
fetch_creds(KLPrincipal inPrincipal, krb5_creds **ocreds,
	    char **outCredCacheName)
{	    
    krb5_context context = mshim_ctx();
    krb5_principal princ = NULL;
    krb5_creds in_creds;
    krb5_const_realm realm;
    krb5_error_code ret;
    krb5_ccache id = NULL;

    LOG_ENTRY();

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

    if (inPrincipal) {
	ret = heim_krb5_cc_cache_match(context, inPrincipal, &id);
    } else {
	ret = heim_krb5_cc_default(context, &id);
	if (ret == 0)
	    ret = heim_krb5_cc_get_principal(context, id, &princ);
	inPrincipal = princ;
    }
    if (ret)
	goto out;

    realm = heim_krb5_principal_get_realm(context, inPrincipal);
    ret = heim_krb5_make_principal(context, &in_creds.server, realm, KRB5_TGS_NAME, realm, NULL);
    if (ret)
	goto out;

    in_creds.client = inPrincipal;

    ret = heim_krb5_get_credentials(context, KRB5_GC_CACHED, id,
				    &in_creds, ocreds);
    heim_krb5_free_principal(context, in_creds.server);

    if (outCredCacheName)
	*outCredCacheName = strdup(heim_krb5_cc_get_name(context, id));

 out:
    if (id)
	heim_krb5_cc_close(context, id);
    if (princ)
	heim_krb5_free_principal(context, princ);

    return LOG_FAILURE(ret, "fetch_creds");
}


KLStatus KLCacheHasValidTickets (KLPrincipal         inPrincipal,
                                 KLKerberosVersion   inKerberosVersion,
                                 KLBoolean          *outFoundValidTickets,
                                 KLPrincipal        *outPrincipal,
                                 char              **outCredCacheName)
{
    krb5_error_code ret;
    krb5_creds *ocreds = NULL;

    LOG_ENTRY();

    if (outPrincipal)
	*outPrincipal = NULL;

    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");

    ret = fetch_creds(inPrincipal, &ocreds, outCredCacheName);

    if (ret == 0) {
	time_t t = time(NULL);
	/* consinder tickets that are slightly too young as valid
	 * since might just have fetched them */
	*outFoundValidTickets =
	       (ocreds->times.starttime - 10 < t)
	    && (t < ocreds->times.endtime);

	if (outPrincipal)
	    (void)heim_krb5_copy_principal(milcontext, ocreds->client, outPrincipal);
	heim_krb5_free_creds(milcontext, ocreds);
    } else {
	LOG_FAILURE(ret, "fetch tickets failed");
	ret = 0;
	*outFoundValidTickets = 0;
    }


    return LOG_FAILURE(ret, "KLCacheHasValidTickets");
}


KLStatus KLTicketStartTime (KLPrincipal        inPrincipal,
                            KLKerberosVersion  inKerberosVersion,
                            KLTime            *outStartTime)
{
    krb5_creds *creds;
    krb5_error_code ret;

    LOG_ENTRY();

    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");

    ret = fetch_creds(inPrincipal, &creds, NULL);
    if (ret)
	return LOG_FAILURE(ret, "fetch tickets failed");

    *outStartTime = (KLTime)creds->times.starttime;

    heim_krb5_free_creds(milcontext, creds);
    return klNoErr;
}


KLStatus KLTicketExpirationTime (KLPrincipal        inPrincipal,
                                 KLKerberosVersion  inKerberosVersion,
                                 KLTime            *outExpirationTime)
{
    krb5_error_code ret;
    krb5_creds *creds;

    LOG_ENTRY();

    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    
    ret = fetch_creds(inPrincipal, &creds, NULL);
    if (ret)
	return LOG_FAILURE(ret, "fetch tickets failed");

    *outExpirationTime = (KLTime)creds->times.endtime;

    heim_krb5_free_creds(milcontext, creds);
    return klNoErr;
}


KLStatus
KLSetSystemDefaultCache (KLPrincipal inPrincipal)
{
    krb5_context context = mshim_ctx();
    krb5_error_code ret;
    krb5_ccache id;

    LOG_ENTRY();

    ret = heim_krb5_cc_cache_match(context, inPrincipal, &id);
    if (ret)
	return LOG_FAILURE(ret, "ccache match");
    ret = heim_krb5_cc_switch(context, id);
    heim_krb5_cc_close(context, id);
    if (ret)
	return LOG_FAILURE(ret, "cc switch");
    return klNoErr;
}


KLStatus KLHandleError (KLStatus           inError,
                        KLDialogIdentifier inDialogIdentifier,
                        KLBoolean          inShowAlert)
{
    LOG_UNIMPLEMENTED();
    return klNoErr;
}


KLStatus KLGetErrorString (KLStatus   inError,
                           char     **outErrorString)
{
    const char *msg;
    LOG_ENTRY();

    msg = heim_krb5_get_error_message(milcontext, (krb5_error_code)inError);
    if (msg) {
	*outErrorString = strdup(msg);
	krb5_free_error_message((mit_krb5_context)milcontext, msg);
    } else
	asprintf(outErrorString, "unknown error: %d\n", (int)inError);
    if (*outErrorString == NULL)
	return klMemFullErr;
    return klNoErr;
}


KLStatus KLCancelAllDialogs (void)
{
    return klNoErr;
}


/* Kerberos change password dialog low level functions */

KLStatus KLChangePasswordWithPasswords (KLPrincipal   inPrincipal,
                                        const char   *inOldPassword,
                                        const char   *inNewPassword,
                                        KLBoolean    *outRejected,
                                        char        **outRejectionError,
                                        char        **outRejectionDescription)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


/* Application Configuration functions */

KLStatus KLSetIdleCallback (const KLIdleCallback inCallback,
                            const KLRefCon inRefCon)
{
    return klNoErr;
}


KLStatus KLGetIdleCallback (KLIdleCallback* inCallback,
                            KLRefCon* inRefCon)
{
    return klNoErr;
}


/* Library configuration functions */
/* Deprecated options which we now ignore */
enum {
    loginOption_ShowOptions                = 'sopt',
    loginOption_RememberShowOptions        = 'ropt',
    loginOption_LongTicketLifetimeDisplay  = 'hms ',
    loginOption_RememberPassword           = 'pass'
};



KLStatus KLGetDefaultLoginOption (const KLDefaultLoginOption  inOption,
                                  void                       *ioBuffer,
                                  KLSize                     *ioBufferSize)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLStatus KLSetDefaultLoginOption (const KLDefaultLoginOption  inOption,
                                  const void                 *inBuffer,
                                  const KLSize                inBufferSize)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


/* Realm configuration functions */

KLStatus KLFindKerberosRealmByName (const char *inRealmName,
                                    KLIndex    *outIndex)
{
    KLStatus ret;
    char *realm = NULL;

    ret = KLGetKerberosDefaultRealmByName (&realm);
    if (ret == klNoErr) {
        if (!strcmp (inRealmName, realm)) {
            *outIndex = 0;
        } else {
            ret = klRealmDoesNotExistErr;
        }
	free(realm);
    }

    
    return ret;
}


KLStatus KLGetKerberosRealm (KLIndex   inIndex,
                             char    **outRealmName)
{
    return KLGetKerberosDefaultRealmByName (outRealmName);
}


KLStatus KLSetKerberosRealm (KLIndex     inIndex,
                             const char *inRealmName)
{
    return klNoErr;
}


KLStatus KLRemoveKerberosRealm (KLIndex inIndex)
{
    return klNoErr;
}


KLStatus KLInsertKerberosRealm (KLIndex     inInsertBeforeIndex,
                                const char *inRealmName)
{
    return klNoErr;
}


KLStatus KLRemoveAllKerberosRealms (void)
{
    return klNoErr;
}


KLSize KLCountKerberosRealms (void)
{
    return 1;
}


KLStatus KLGetKerberosDefaultRealm(KLIndex *outIndex)
{
    if(outIndex)
	*outIndex = 0;

    return klNoErr;
}


KLStatus KLGetKerberosDefaultRealmByName (char **outRealmName)
{
    LOG_ENTRY();

    return krb5_get_default_realm((mit_krb5_context)milcontext, outRealmName);
}


KLStatus KLSetKerberosDefaultRealm (KLIndex inIndex)
{
    return klNoErr;
}


KLStatus KLSetKerberosDefaultRealmByName (const char *inRealm)
{
    return klNoErr;
}


/* KLPrincipal functions */

KLStatus
KLCreatePrincipalFromTriplet (const char  *inName,
			      const char  *inInstance,
			      const char  *inRealm,
			      KLPrincipal *outPrincipal)
{
    return heim_krb5_make_principal(milcontext, outPrincipal, inRealm, inName, inInstance, NULL);
}


KLStatus
KLCreatePrincipalFromString (const char        *inFullPrincipal,
			     KLKerberosVersion  inKerberosVersion,
			     KLPrincipal       *outPrincipal)
{
    LOG_ENTRY();

    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    
    if (inFullPrincipal == NULL)
	return klParameterErr;
    return heim_krb5_parse_name(milcontext, inFullPrincipal, outPrincipal);
}


KLStatus KLCreatePrincipalFromKerberos5Principal (void           *inKerberos5Principal,
                                                  KLPrincipal    *outPrincipal)
{
    if (inKerberos5Principal == NULL)
	return klParameterErr;
    return heim_krb5_copy_principal(milcontext, inKerberos5Principal, outPrincipal);
}


KLStatus KLCreatePrincipalFromPrincipal (KLPrincipal inPrincipal,
                                         KLPrincipal *outPrincipal)
{
    if (inPrincipal == NULL)
	return klParameterErr;
    return heim_krb5_copy_principal(milcontext, inPrincipal, outPrincipal);
}


KLStatus
KLGetTripletFromPrincipal(KLPrincipal inPrincipal,
			  char **outName,
			  char **outInstance,
			  char **outRealm)
{
    LOG_ENTRY();

    if (outName)
	*outName = NULL;
    if (outInstance)
	*outInstance = NULL;
    if (outRealm)
	*outRealm = NULL;

    if (inPrincipal == NULL)
	return klParameterErr;

    switch (inPrincipal->name.name_string.len) {
    case 2:
	if (outInstance)
	    *outInstance = strdup(inPrincipal->name.name_string.val[1]);
    case 1:
	if (outName)
	    *outName = strdup(inPrincipal->name.name_string.val[0]);
	break;
    default:
	return klInvalidOptionErr;
    }
    if (outRealm)
	*outRealm = strdup(inPrincipal->realm);

    return klNoErr;
}

KLStatus
KLGetStringFromPrincipal (KLPrincipal         inPrincipal,
			  KLKerberosVersion   inKerberosVersion,
			  char              **outFullPrincipal)
{
    LOG_ENTRY();
    if (inPrincipal == NULL)
	return klParameterErr;
    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    return heim_krb5_unparse_name(milcontext, inPrincipal, outFullPrincipal);
}


KLStatus
KLGetDisplayStringFromPrincipal (KLPrincipal         inPrincipal,
				 KLKerberosVersion   inKerberosVersion,
				 char              **outFullPrincipal)
{
    LOG_ENTRY();
    if (inPrincipal == NULL)
	return klParameterErr;
    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    return heim_krb5_unparse_name(milcontext, inPrincipal, outFullPrincipal);
}


KLStatus
KLComparePrincipal (KLPrincipal  inFirstPrincipal,
		    KLPrincipal  inSecondPrincipal,
		    KLBoolean   *outAreEquivalent)
{
    LOG_ENTRY();
    if (inFirstPrincipal == NULL || inSecondPrincipal == NULL)
	return klParameterErr;
    *outAreEquivalent = heim_krb5_principal_compare(milcontext, inFirstPrincipal, inSecondPrincipal);
    return klNoErr;
}


KLStatus
KLDisposePrincipal (KLPrincipal inPrincipal)
{
    LOG_ENTRY();
    if (inPrincipal == NULL)
	return klNoErr;
    heim_krb5_free_principal(milcontext, inPrincipal);
    return klNoErr;
}


/* KLLoginOptions functions */

KLStatus
KLCreateLoginOptions (KLLoginOptions *outOptions)
{
    struct KLLoginOptions *opt;
    krb5_error_code ret;

    *outOptions = NULL;

    LOG_ENTRY();

    opt = calloc(1, sizeof(*opt));

    ret = heim_krb5_get_init_creds_opt_alloc(milcontext, &opt->opt);
    if (ret) {
	free(opt);
	return ret; /* XXX */
    }
    *outOptions = opt;
    return klNoErr;
}


KLStatus KLLoginOptionsSetTicketLifetime (KLLoginOptions ioOptions,
                                          KLLifetime     inTicketLifetime)
{
    LOG_ENTRY();
    heim_krb5_get_init_creds_opt_set_tkt_life(ioOptions->opt, inTicketLifetime);
    return klNoErr;
}


KLStatus KLLoginOptionsSetForwardable (KLLoginOptions ioOptions,
                                       KLBoolean      inForwardable)
{
    LOG_ENTRY();
    heim_krb5_get_init_creds_opt_set_forwardable(ioOptions->opt, inForwardable);
    return klNoErr;
}


KLStatus KLLoginOptionsSetProxiable (KLLoginOptions ioOptions,
                                     KLBoolean      inProxiable)
{
    LOG_ENTRY();
    heim_krb5_get_init_creds_opt_set_proxiable(ioOptions->opt, inProxiable);
    return klNoErr;
}


KLStatus KLLoginOptionsSetRenewableLifetime (KLLoginOptions ioOptions,
                                             KLLifetime     inRenewableLifetime)
{
    LOG_ENTRY();
    heim_krb5_get_init_creds_opt_set_renew_life(ioOptions->opt, inRenewableLifetime);
    return klNoErr;
}


KLStatus
KLLoginOptionsSetAddressless (KLLoginOptions ioOptions,
			      KLBoolean      inAddressless)
{
    LOG_ENTRY();
    return klNoErr;
}


KLStatus
KLLoginOptionsSetTicketStartTime (KLLoginOptions ioOptions,
				  KLTime         inStartTime)
{
    LOG_ENTRY();
    return klNoErr;
}


KLStatus
KLLoginOptionsSetServiceName(KLLoginOptions  ioOptions,
			     const char     *inServiceName)
{
    LOG_ENTRY();
    if (ioOptions->service)
	free(ioOptions->service);
    ioOptions->service = strdup(inServiceName);
    return klNoErr;
}


KLStatus KLDisposeLoginOptions(KLLoginOptions ioOptions)
{
    LOG_ENTRY();
    heim_krb5_get_init_creds_opt_free(milcontext, ioOptions->opt);
    if (ioOptions->service)
	free(ioOptions->service);
    free(ioOptions);
    return klNoErr;
}


KLStatus KLDisposeString (char *inStringToDispose)
{
    free(inStringToDispose);
    return klNoErr;
}

KLStatus __KLSetApplicationPrompter(int inPrompter)
{
    /* Deprecated */
    return klNoErr;
}

KLStatus __KLSetHomeDirectoryAccess (KLBoolean inAllowHomeDirectoryAccess)
{
    return heim_krb5_set_home_dir_access(NULL, inAllowHomeDirectoryAccess);
}


KLBoolean __KLAllowHomeDirectoryAccess (void)
{
    return 1;
}


KLStatus __KLSetAutomaticPrompting (KLBoolean inAllowAutomaticPrompting)
{
    return klNoErr;
}


KLBoolean __KLAllowAutomaticPrompting (void)
{
    return 0;
}


KLStatus __KLSetPromptMechanism (int inPromptMechanism)
{
    return klNoErr;
}


int __KLPromptMechanism (void)
{
    return klPromptMechanism_None;
}


KLBoolean __KLAllowRememberPassword (void)
{
    return klNoErr;
}


KLStatus __KLCreatePrincipalFromTriplet (const char  *inName,
                                         const char  *inInstance,
                                         const char  *inRealm,
                                         KLKerberosVersion  inKerberosVersion,
                                         KLPrincipal *outPrincipal)
{
    LOG_UNIMPLEMENTED();
    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    return EINVAL;
}


KLStatus __KLGetTripletFromPrincipal (KLPrincipal         inPrincipal,
                                      KLKerberosVersion   inKerberosVersion,
                                      char              **outName,
                                      char              **outInstance,
                                      char              **outRealm)
{
    LOG_UNIMPLEMENTED();
    if (CHECK_VERSION(inKerberosVersion))
	return LOG_FAILURE(klInvalidVersionErr, "wrong version");
    return EINVAL;
}


KLStatus __KLCreatePrincipalFromKerberos5Principal (krb5_principal inPrincipal,
                                                    KLPrincipal *outPrincipal)
{
    return KLCreatePrincipalFromKerberos5Principal (inPrincipal, outPrincipal);
}


KLStatus __KLGetKerberos5PrincipalFromPrincipal (KLPrincipal     inPrincipal, 
                                                 krb5_context    inContext, 
                                                 krb5_principal *outKrb5Principal)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}


KLBoolean __KLPrincipalIsTicketGrantingService (KLPrincipal inPrincipal)
{
    LOG_UNIMPLEMENTED();
    return klNoErr;
}


KLStatus __KLGetKeychainPasswordForPrincipal (KLPrincipal   inPrincipal,
                                              char        **outPassword)
{
    LOG_UNIMPLEMENTED();
    return EINVAL;
}



KLStatus __KLPrincipalSetKeychainPassword (KLPrincipal  inPrincipal,
                                           const char  *inPassword)
{
    return klNoErr;
}


KLStatus __KLRemoveKeychainPasswordForPrincipal (KLPrincipal inPrincipal)
{
    return klNoErr;
}

krb5_get_init_creds_opt *
__KLLoginOptionsGetKerberos5Options (KLLoginOptions ioOptions)
{
    return NULL;
}

KLTime __KLLoginOptionsGetStartTime (KLLoginOptions ioOptions)
{
    return klNoErr;
}

char *__KLLoginOptionsGetServiceName (KLLoginOptions ioOptions)
{
    return NULL;
}