--- /dev/null
+/*
+ * CCache-glue.c
+ *
+ * This file contains implementations of krb4 credentials cache operations in terms
+ * of the CCache API (<http://www.umich.edu/~sgr/v4Cache/>).
+ *
+ * $Header$
+ */
+
+
+#include <CoreServices/CoreServices.h>
+#include "kerberos.h"
+
+#ifdef USE_LOGIN_LIBRARY
+#include <Kerberos/KerberosLoginPrivate.h>
+#endif /* USE_LOGIN_LIBRARY */
+#include <Kerberos/CredentialsCache.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "Kerberos4Private.h"
+
+/*
+ * Psycho krb4 sources don't compile with these options on, so I use them just for this file
+ */
+
+#pragma require_prototypes on
+#pragma mpwc_relax off
+
+int INTERFACE
+krb_get_tf_realm (
+ const char* ticket_file,
+ char* realm);
+
+/*
+ * The way Kerberos v4 normally works is that at any given point in time there is a
+ * file where all the tickets go, determined by an environment variable. If a user kinits
+ * to a new principal, the existing tickets are replaced with new ones. At any point in time, there is a
+ * "current" or "default" principal, which is determined by the principal associated with
+ * the current ticket file.
+ *
+ * In the CCache API implementation, this corresponds to always having a "default"
+ * or "current" named cache. The default principal then corresponds to that cache.
+ *
+ * Unfortunately, Kerberos v4 also has this notion that the default cache exists (in the sense
+ * that its name is known) even before the actual file has been created.
+ *
+ * In addition to this, we cannot make the default cache system-wide global, because then
+ * we get all sorts of interesting scenarios in which context switches between processes
+ * can cause credentials to be stored in wrong caches.
+ *
+ * To solve all the problems, we have to emulate the concept of an environment variable,
+ * by having a system-wide concept of what a default credentials cache is; then, we copy
+ * the system-wide value into the per-process value when the application starts up.
+ *
+ * However, in order to allow applications to be able to sanely handle the user model we
+ * want to support, in which the user has some way of selecting the system-wide default
+ * user _without_ quitting and relaunching all applications (this is also necessary for
+ * KClient support), calls had to be added to the Kerberos v4 library to reset the
+ * per-process cached value of default cache.
+ */
+
+/*
+ * Name of the default cache
+ */
+char* gDefaultCacheName = NULL;
+char gDefaultName[ANAME_SZ];
+char gDefaultInstance[INST_SZ];
+char gDefaultRealm[REALM_SZ];
+Boolean gHaveDefaultPrincipal = false;
+
+/*
+ * Initialize credentials cache
+ *
+ * Creating the cache will blow away an existing one. The assumption is that
+ * whoever called us made sure that the one that we blow away if it exists
+ * is the right one to blow away.
+ */
+
+int
+in_tkt (
+ char* pname,
+ char* pinst,
+ char* realm)
+{
+ char principal [MAX_K_NAME_SZ + 1];
+ cc_int32 err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (err == ccNoError) {
+ sprintf (principal, "%s%s%s@%s", pname, (pinst [0] == '\0') ? "" : ".", pinst, realm);
+ }
+
+ if (err == ccNoError) {
+ err = cc_context_create_ccache (cc_context, TKT_FILE, cc_credentials_v4, principal, &ccache);
+ }
+
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (err != ccNoError)
+ return KFAILURE;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * Store a ticket into the default credentials cache
+ * cache must exist (if it didn't exist, it would have been created by in_tkt)
+ */
+int
+krb_save_credentials (
+ char* service,
+ char* instance,
+ char* realm,
+ C_Block session,
+ int lifetime,
+ int kvno,
+ KTEXT ticket,
+ long issue_date,
+ KRB_UINT32 local_address,
+ KRB_UINT32 stk_type)
+{
+#pragma unused (realm)
+
+ cc_int32 cc_err = ccNoError;
+ int kerr = KSUCCESS;
+ cc_credentials_v4_t v4creds;
+ cc_credentials_union creds;
+ cc_ccache_t ccache = NULL;
+ cc_string_t principal;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ /* First try existing cache */
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ /* Now we have a cache. Fill out the credentials and put them in the cache. */
+ /* To fill out the credentials, we need the principal */
+ cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal);
+ }
+
+ if (cc_err == ccNoError) {
+ kerr = kname_parse (v4creds.principal, v4creds.principal_instance, v4creds.realm, (char*) principal -> data);
+ cc_string_release (principal);
+ }
+
+ if ((cc_err == ccNoError) && (kerr == KSUCCESS)) {
+ strncpy (v4creds.service, service, SNAME_SZ);
+ strncpy (v4creds.service_instance, instance, INST_SZ);
+ strncpy (v4creds.realm, realm, REALM_SZ);
+ memmove (v4creds.session_key, session, sizeof (C_Block));
+ v4creds.kvno = kvno;
+ v4creds.string_to_key_type = stk_type;
+ v4creds.issue_date = issue_date;
+ v4creds.address = local_address;
+ v4creds.lifetime = lifetime;
+ v4creds.ticket_size = ticket -> length;
+ memmove (v4creds.ticket, ticket -> dat, ticket -> length);
+
+ creds.version = cc_credentials_v4;
+ creds.credentials.credentials_v4 = &v4creds;
+
+ cc_err = cc_ccache_store_credentials (ccache, &creds);
+ }
+
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (kerr != KSUCCESS)
+ return kerr;
+ if (cc_err != ccNoError)
+ return KFAILURE;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * Credentials file -> realm mapping
+ *
+ * Determine the realm by opening the named cache and parsing realm from the principal
+ */
+int INTERFACE
+krb_get_tf_realm (
+ const char* ticket_file,
+ char* realm)
+{
+ cc_string_t principal;
+ char pname [ANAME_SZ];
+ char pinst [INST_SZ];
+ char prealm [REALM_SZ];
+ int kerr = KSUCCESS;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version = NULL;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, ticket_file, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal);
+ }
+
+ if (cc_err == ccNoError) {
+ /* found cache. get princiapl and parse it */
+ kerr = kname_parse (pname, pinst, prealm, (char*) principal -> data);
+ cc_string_release (principal);
+ }
+
+ if ((cc_err == ccNoError) && (kerr == KSUCCESS)) {
+ strcpy (realm, prealm);
+ }
+
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (kerr != KSUCCESS)
+ return kerr;
+ if (cc_err != ccNoError)
+ return GC_NOTKT;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * Credentials file -> name, instance, realm mapping
+ */
+int INTERFACE
+krb_get_tf_fullname (
+ const char* ticket_file,
+ char* name,
+ char* instance,
+ char* realm)
+{
+ cc_string_t principal;
+ int kerr = KSUCCESS;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, ticket_file, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ /* found cache. get principal and parse it */
+ cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal);
+ }
+
+ if (cc_err == ccNoError) {
+ kerr = kname_parse (name, instance, realm, (char*) principal -> data);
+ cc_string_release (principal);
+ }
+
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (kerr != KSUCCESS)
+ return kerr;
+ if (cc_err != ccNoError)
+ return GC_NOTKT;
+ else
+ return KSUCCESS;
+}
+
+
+/*
+ * Retrieval from credentials cache
+ */
+int INTERFACE
+krb_get_cred (
+ char* service,
+ char* instance,
+ char* realm,
+ CREDENTIALS* creds)
+{
+ int kerr = KSUCCESS;
+ cc_int32 cc_err = ccNoError;
+ cc_credentials_t theCreds = NULL;
+ cc_credentials_iterator_t iterator = NULL;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+#ifdef USE_LOGIN_LIBRARY
+ // If we are requesting a tgt, prompt for it
+ if (strncmp (service, TICKET_GRANTING_TICKET, ANAME_SZ) == 0) {
+ OSStatus err;
+ char *cacheName;
+ KLPrincipal defaultPrincipal = nil;
+ KLPrincipal outPrincipal;
+
+ if (gHaveDefaultPrincipal) {
+ err = KLCreatePrincipalFromTriplet (gDefaultName, gDefaultInstance, gDefaultRealm, &defaultPrincipal);
+ if (err != klNoErr)
+ defaultPrincipal = nil;
+ }
+
+ err = __KLInternalAcquireInitialTicketsForCache (defaultPrincipal, NULL, TKT_FILE,
+ kerberosVersion_V4, &outPrincipal, &cacheName);
+ if (defaultPrincipal != nil)
+ KLDisposePrincipal (defaultPrincipal);
+
+ if (err == noErr) {
+ char* newName = nil;
+ char* newInstance = nil;
+ char* newRealm = nil;
+
+ gHaveDefaultPrincipal = false;
+ err = KLGetTripletFromPrincipal (outPrincipal, &newName, &newInstance, &newRealm);
+ if (err == noErr) {
+ // If this isn't a valid krb4 principal, don't store it or track the cache name
+ if ((strlen (newName) < ANAME_SZ) && (strlen (newInstance) < INST_SZ) &&
+ (strlen (newRealm) < REALM_SZ)) {
+ strcpy (gDefaultName, newName);
+ strcpy (gDefaultInstance, newInstance);
+ strcpy (gDefaultRealm, newRealm);
+ krb_set_tkt_string (cacheName); // Tickets for the krb4 principal went here
+
+ gHaveDefaultPrincipal = true;
+ }
+
+ KLDisposeString (newName);
+ KLDisposeString (newInstance);
+ KLDisposeString (newRealm);
+ }
+
+ KLDisposeString (cacheName);
+ KLDisposePrincipal (outPrincipal);
+ } else {
+ return GC_NOTKT;
+ }
+ }
+#endif /* USE_LOGIN_LIBRARY */
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator);
+ }
+
+ if (cc_err == ccNoError) {
+ for (;;) {
+ /* get next creds */
+ cc_err = cc_credentials_iterator_next (iterator, &theCreds);
+ if (cc_err == ccIteratorEnd) {
+ kerr = GC_NOTKT;
+ break;
+ }
+ if (cc_err != ccNoError) {
+ kerr = KFAILURE;
+ break;
+ }
+
+ /* version, service, instance, realm check */
+ if ((theCreds -> data -> version == cc_credentials_v4) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> service, service) == 0) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> service_instance, instance) == 0) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> realm, realm) == 0)) {
+
+ /* Match! */
+ strcpy (creds -> service, service);
+ strcpy (creds -> instance, instance);
+ strcpy (creds -> realm, realm);
+ memmove (creds -> session, theCreds -> data -> credentials.credentials_v4 -> session_key, sizeof (C_Block));
+ creds -> lifetime = theCreds -> data -> credentials.credentials_v4 -> lifetime;
+ creds -> kvno = theCreds -> data -> credentials.credentials_v4 -> kvno;
+ creds -> ticket_st.length = theCreds -> data -> credentials.credentials_v4 -> ticket_size;
+ memmove (creds -> ticket_st.dat, theCreds -> data -> credentials.credentials_v4 -> ticket, creds -> ticket_st.length);
+ creds -> issue_date = theCreds -> data -> credentials.credentials_v4 -> issue_date;
+ strcpy (creds -> pname, theCreds -> data -> credentials.credentials_v4 -> principal);
+ strcpy (creds -> pinst, theCreds -> data -> credentials.credentials_v4 -> principal_instance);
+ creds -> stk_type = theCreds -> data -> credentials.credentials_v4 -> string_to_key_type;
+
+ cc_credentials_release (theCreds);
+ kerr = KSUCCESS;
+ break;
+ } else {
+ cc_credentials_release (theCreds);
+ }
+ }
+ }
+
+ if (iterator != NULL)
+ cc_credentials_iterator_release (iterator);
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (kerr != KSUCCESS)
+ return kerr;
+ if (cc_err != ccNoError)
+ return GC_NOTKT;
+ else
+ return KSUCCESS;
+}
+
+
+/*
+ * Getting name of default credentials cache
+ */
+const char* INTERFACE
+tkt_string (void)
+{
+ if (gDefaultCacheName == NULL) {
+ UpdateDefaultCache ();
+ }
+ return gDefaultCacheName;
+}
+
+/*
+ * Synchronize default cache for this process with system default cache
+ */
+
+void
+UpdateDefaultCache (void)
+{
+ cc_string_t name;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_get_default_ccache_name (cc_context, &name);
+ }
+
+ if (cc_err == ccNoError) {
+ krb_set_tkt_string ((char*) name -> data);
+ cc_string_release (name);
+ }
+
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+}
+
+/*
+ * Setting name of default credentials cache
+ */
+void
+krb_set_tkt_string (
+ const char* val)
+{
+ /* If we get called with the return value of tkt_string, we
+ shouldn't dispose of the input string */
+ if (val != gDefaultCacheName) {
+ if (gDefaultCacheName != NULL)
+ free (gDefaultCacheName);
+
+ gDefaultCacheName = malloc (strlen (val) + 1);
+ if (gDefaultCacheName != NULL)
+ strcpy (gDefaultCacheName, val);
+ gHaveDefaultPrincipal = false;
+ }
+}
+
+/*
+ * Destroy credentials file
+ *
+ * Implementation in dest_tkt.c
+ */
+int INTERFACE
+dest_tkt (void)
+{
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_ccache_destroy (ccache);
+ }
+
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (cc_err != ccNoError)
+ return RET_TKFIL;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * The following functions are not part of the standard Kerberos v4 API.
+ * They were created for Mac implementation, and used by admin tools
+ * such as CNS-Config.
+ */
+
+/*
+ * Number of credentials in credentials cache
+ */
+int INTERFACE
+krb_get_num_cred (void)
+{
+ cc_credentials_t theCreds = NULL;
+ int count = 0;
+ cc_credentials_iterator_t iterator = NULL;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator);
+ }
+
+ if (cc_err == ccNoError) {
+ for (;;) {
+ /* get next creds */
+ cc_err = cc_credentials_iterator_next (iterator, &theCreds);
+ if (cc_err != ccNoError)
+ break;
+
+ if (theCreds -> data -> version == cc_credentials_v4)
+ count++;
+
+ cc_credentials_release (theCreds);
+ }
+ }
+
+ if (iterator != NULL)
+ cc_credentials_iterator_release (iterator);
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (cc_err != ccNoError)
+ return 0;
+ else
+ return count;
+}
+
+/*
+ * Retrieval from credentials file
+ * This function is _not_!! well-defined under CCache API, because
+ * there is no guarantee about order of credentials remaining the same.
+ */
+int INTERFACE
+krb_get_nth_cred (
+ char* sname,
+ char* sinstance,
+ char* srealm,
+ int n)
+{
+ cc_credentials_t theCreds = NULL;
+ int count = 0;
+ cc_credentials_iterator_t iterator = NULL;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ if (n < 1)
+ return KFAILURE;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator);
+ }
+
+ if (cc_err == ccNoError) {
+ for (count = 0; count < n;) {
+ /* get next creds */
+ cc_err = cc_credentials_iterator_next (iterator, &theCreds);
+ if (cc_err != ccNoError)
+ break;
+
+ if (theCreds -> data -> version == cc_credentials_v4)
+ count++;
+
+ if (count < n - 1)
+ cc_credentials_release (theCreds);
+ }
+ }
+
+ if (cc_err == ccNoError) {
+ strcpy (sname, theCreds -> data -> credentials.credentials_v4 -> service);
+ strcpy (sinstance, theCreds -> data -> credentials.credentials_v4 -> service_instance);
+ strcpy (srealm, theCreds -> data -> credentials.credentials_v4 -> realm);
+ }
+
+ if (theCreds != NULL)
+ cc_credentials_release (theCreds);
+ if (iterator != NULL)
+ cc_credentials_iterator_release (iterator);
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (cc_err != ccNoError)
+ return KFAILURE;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * Deletion from credentials file
+ */
+int INTERFACE
+krb_delete_cred (
+ char* sname,
+ char* sinstance,
+ char* srealm)
+{
+ cc_credentials_t theCreds = NULL;
+ cc_credentials_iterator_t iterator = NULL;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache);
+ }
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator);
+ }
+
+ if (cc_err == ccNoError) {
+ for (;;) {
+ /* get next creds */
+ cc_err = cc_credentials_iterator_next (iterator, &theCreds);
+ if (cc_err != ccNoError) {
+ break;
+ }
+
+ if ((theCreds -> data -> version == cc_credentials_v4) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> service, sname) == 0) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> service_instance, sinstance) == 0) &&
+ (strcmp (theCreds -> data -> credentials.credentials_v4 -> realm, srealm) == 0)) {
+
+ cc_ccache_remove_credentials (ccache, theCreds);
+ cc_credentials_release (theCreds);
+ break;
+ }
+
+ cc_credentials_release (theCreds);
+ }
+ }
+
+ if (iterator != NULL)
+ cc_credentials_iterator_release (iterator);
+ if (ccache != NULL)
+ cc_ccache_release (ccache);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if (cc_err != ccNoError)
+ return KFAILURE;
+ else
+ return KSUCCESS;
+}
+
+/*
+ * Destroy all credential caches
+ *
+ * Implementation in memcache.c
+ */
+int INTERFACE
+dest_all_tkts (void)
+{
+ int count = 0;
+ cc_ccache_iterator_t iterator = NULL;
+ cc_int32 cc_err = ccNoError;
+ cc_context_t cc_context = NULL;
+ cc_int32 cc_version;
+ cc_ccache_t ccache = NULL;
+
+ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL);
+
+ if (cc_err == ccNoError) {
+ cc_err = cc_context_new_ccache_iterator (cc_context, &iterator);
+ }
+
+ if (cc_err == ccNoError) {
+ for (;;) {
+ /* get next ccache */
+ cc_err = cc_ccache_iterator_next (iterator, &ccache);
+
+ if (cc_err != ccNoError)
+ break;
+
+ cc_ccache_destroy (ccache);
+ count++;
+ }
+ }
+
+ if (iterator != NULL)
+ cc_credentials_iterator_release (iterator);
+ if (cc_context != NULL)
+ cc_context_release (cc_context);
+
+ if ((cc_err == ccIteratorEnd) && (count == 0)) {
+ /* first time, nothing to destroy */
+ return KFAILURE;
+ } else {
+ if (cc_err == ccIteratorEnd) {
+ /* done */
+ return KSUCCESS;
+ } else {
+ /* error */
+ return KFAILURE;
+ }
+ }
+}