svn rev #23843: branches/iakerb/src/ appl/gss-sample/ include/ include/krb5/ ...

ghudson@MIT.EDU ghudson at MIT.EDU
Mon Mar 29 12:18:20 EDT 2010


http://src.mit.edu/fisheye/changelog/krb5/?cs=23843
Commit By: ghudson
Log Message:
Merge changes from /users/lhoward/iakerb-refonly.



Changed Files:
U   branches/iakerb/src/appl/gss-sample/gss-client.c
U   branches/iakerb/src/appl/gss-sample/gss-server.c
U   branches/iakerb/src/include/k5-int.h
U   branches/iakerb/src/include/krb5/krb5.hin
U   branches/iakerb/src/lib/gssapi/generic/gssapi_ext.h
U   branches/iakerb/src/lib/gssapi/krb5/Makefile.in
U   branches/iakerb/src/lib/gssapi/krb5/accept_sec_context.c
U   branches/iakerb/src/lib/gssapi/krb5/acquire_cred.c
U   branches/iakerb/src/lib/gssapi/krb5/add_cred.c
U   branches/iakerb/src/lib/gssapi/krb5/gssapiP_krb5.h
U   branches/iakerb/src/lib/gssapi/krb5/gssapi_err_krb5.et
U   branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.c
U   branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.hin
A   branches/iakerb/src/lib/gssapi/krb5/iakerb.c
U   branches/iakerb/src/lib/gssapi/krb5/init_sec_context.c
U   branches/iakerb/src/lib/gssapi/krb5/inq_cred.c
U   branches/iakerb/src/lib/gssapi/krb5/inq_names.c
U   branches/iakerb/src/lib/gssapi/krb5/rel_cred.c
U   branches/iakerb/src/lib/gssapi/krb5/ser_sctx.c
U   branches/iakerb/src/lib/gssapi/libgssapi_krb5.exports
U   branches/iakerb/src/lib/gssapi/mechglue/Makefile.in
A   branches/iakerb/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
U   branches/iakerb/src/lib/gssapi/mechglue/g_initialize.c
U   branches/iakerb/src/lib/gssapi/mechglue/mglueP.h
U   branches/iakerb/src/lib/gssapi/spnego/gssapiP_spnego.h
U   branches/iakerb/src/lib/gssapi/spnego/spnego_mech.c
U   branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.c
U   branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.h
U   branches/iakerb/src/lib/krb5/asn.1/asn1_k_encode.c
U   branches/iakerb/src/lib/krb5/asn.1/krb5_decode.c
U   branches/iakerb/src/lib/krb5/error_tables/krb5_err.et
U   branches/iakerb/src/lib/krb5/krb/Makefile.in
U   branches/iakerb/src/lib/krb5/krb/gc_frm_kdc.c
A   branches/iakerb/src/lib/krb5/krb/gc_frm_kdc_step.c
U   branches/iakerb/src/lib/krb5/krb/int-proto.h
U   branches/iakerb/src/lib/krb5/krb/kfree.c
U   branches/iakerb/src/lib/krb5/libkrb5.exports
U   branches/iakerb/src/tests/asn.1/krb5_decode_leak.c
U   branches/iakerb/src/tests/asn.1/krb5_decode_test.c
U   branches/iakerb/src/tests/asn.1/krb5_encode_test.c
U   branches/iakerb/src/tests/asn.1/ktest.c
U   branches/iakerb/src/tests/asn.1/ktest.h
U   branches/iakerb/src/tests/asn.1/ktest_equal.c
U   branches/iakerb/src/tests/asn.1/ktest_equal.h
U   branches/iakerb/src/tests/asn.1/reference_encode.out
U   branches/iakerb/src/tests/asn.1/trval_reference.out
Modified: branches/iakerb/src/appl/gss-sample/gss-client.c
===================================================================
--- branches/iakerb/src/appl/gss-sample/gss-client.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/appl/gss-sample/gss-client.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -64,6 +64,8 @@
 #endif
 
 #include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_ext.h>
 #include "gss-misc.h"
 
 static int verbose = 1;
@@ -72,7 +74,7 @@
 usage()
 {
     fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n");
-    fprintf(stderr, "       [-seq] [-noreplay] [-nomutual]");
+    fprintf(stderr, "       [-seq] [-noreplay] [-nomutual] [-user user] [-pass pw]");
 #ifdef _WIN32
     fprintf(stderr, " [-threads num]");
 #endif
@@ -162,6 +164,7 @@
 static int
 client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
                          int auth_flag, int v1_format, gss_OID oid,
+                         char *username, char *password,
                          gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
 {
     if (auth_flag) {
@@ -169,7 +172,54 @@
         gss_name_t target_name;
         OM_uint32 maj_stat, min_stat, init_sec_min_stat;
         int token_flags;
+        gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+        gss_name_t gss_username = GSS_C_NO_NAME;
+        gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
 
+        if (oid != GSS_C_NO_OID) {
+            mechs.elements = oid;
+            mechs.count = 1;
+            mechsp = &mechs;
+        }
+
+        if (username != NULL) {
+            send_tok.value = username;
+            send_tok.length = strlen(username);
+
+            maj_stat = gss_import_name(&min_stat, &send_tok,
+                                       (gss_OID) gss_nt_user_name,
+                                       &gss_username);
+            if (maj_stat != GSS_S_COMPLETE) {
+                display_status("parsing client name", maj_stat, min_stat);
+                return -1;
+            }
+        }
+
+        if (password != NULL) {
+            gss_buffer_desc pwbuf;
+
+            pwbuf.value = password;
+            pwbuf.length = strlen(password);
+
+            maj_stat = gss_acquire_cred_with_password(&min_stat,
+                                                      gss_username,
+                                                      &pwbuf, 0,
+                                                      mechsp, GSS_C_INITIATE,
+                                                      &cred, NULL, NULL);
+        } else if (gss_username != GSS_C_NO_NAME) {
+            maj_stat = gss_acquire_cred(&min_stat,
+                                        gss_username, 0,
+                                        mechsp, GSS_C_INITIATE,
+                                        &cred, NULL, NULL);
+        } else
+            maj_stat = GSS_S_COMPLETE;
+        if (maj_stat != GSS_S_COMPLETE) {
+            display_status("acquiring creds", maj_stat, min_stat);
+            gss_release_name(&min_stat, &gss_username);
+            return -1;
+        }
+        gss_release_name(&min_stat, &gss_username);
+
         /*
          * Import the name into target_name.  Use send_tok to save
          * local variable space.
@@ -213,7 +263,7 @@
 
         do {
             maj_stat = gss_init_sec_context(&init_sec_min_stat,
-                                            GSS_C_NO_CREDENTIAL, gss_context,
+                                            cred, gss_context,
                                             target_name, oid, gss_flags, 0,
                                             NULL, /* channel bindings */
                                             token_ptr, NULL, /* mech type */
@@ -260,6 +310,7 @@
                 printf("\n");
         } while (maj_stat == GSS_S_CONTINUE_NEEDED);
 
+        (void) gss_release_cred(&min_stat, &cred);
         (void) gss_release_name(&min_stat, &target_name);
     } else {
         if (send_token(s, TOKEN_NOOP, empty_token) < 0)
@@ -344,7 +395,7 @@
 static int
 call_server(host, port, oid, service_name, gss_flags, auth_flag,
             wrap_flag, encrypt_flag, mic_flag, v1_format, msg, use_file,
-            mcount)
+            mcount, username, password)
     char   *host;
     u_short port;
     gss_OID oid;
@@ -355,6 +406,8 @@
     char   *msg;
     int     use_file;
     int     mcount;
+    char    *username;
+    char    *password;
 {
     gss_ctx_id_t context;
     gss_buffer_desc in_buf, out_buf;
@@ -380,7 +433,8 @@
 
     /* Establish context */
     if (client_establish_context(s, service_name, gss_flags, auth_flag,
-                                 v1_format, oid, &context, &ret_flags) < 0) {
+                                 v1_format, oid, username, password,
+                                 &context, &ret_flags) < 0) {
         (void) close(s);
         return -1;
     }
@@ -663,13 +717,15 @@
 static gss_OID oid = GSS_C_NULL_OID;
 static int mcount = 1, ccount = 1;
 static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;
+static char *username = NULL;
+static char *password = NULL;
 
 static void
 worker_bee(void *unused)
 {
     if (call_server(server_host, port, oid, service_name,
                     gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,
-                    v1_format, msg, use_file, mcount) < 0)
+                    v1_format, msg, use_file, mcount, username, password) < 0)
         exit(1);
 
 #ifdef _WIN32
@@ -705,17 +761,33 @@
             if (!argc)
                 usage();
             mechanism = *argv;
-        }
+        } else if (strcmp(*argv, "-user") == 0) {
+            argc--;
+            argv++;
+            if (!argc)
+                usage();
+            username = *argv;
+        } else if (strcmp(*argv, "-pass") == 0) {
+            argc--;
+            argv++;
+            if (!argc)
+                usage();
+            password = *argv;
+        } else if (strcmp(*argv, "-iakerb") == 0) {
+            mechanism = "{ 1 3 6 1 5 2 5 }";
+        } else if (strcmp(*argv, "-spnego") == 0) {
+            mechanism = "{ 1 3 6 1 5 5 2 }";
+        } else if (strcmp(*argv, "-krb5") == 0) {
+            mechanism = "{ 1 3 5 1 5 2 }";
 #ifdef _WIN32
-        else if (strcmp(*argv, "-threads") == 0) {
+        } else if (strcmp(*argv, "-threads") == 0) {
             argc--;
             argv++;
             if (!argc)
                 usage();
             max_threads = atoi(*argv);
-        }
 #endif
-        else if (strcmp(*argv, "-d") == 0) {
+        } else if (strcmp(*argv, "-d") == 0) {
             gss_flags |= GSS_C_DELEG_FLAG;
         } else if (strcmp(*argv, "-seq") == 0) {
             gss_flags |= GSS_C_SEQUENCE_FLAG;

Modified: branches/iakerb/src/appl/gss-sample/gss-server.c
===================================================================
--- branches/iakerb/src/appl/gss-sample/gss-server.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/appl/gss-sample/gss-server.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -58,6 +58,7 @@
 #include <ctype.h>
 
 #include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
 #include "gss-misc.h"
 
 #ifdef HAVE_STRING_H
@@ -75,7 +76,8 @@
 #endif
     fprintf(stderr, "\n");
     fprintf(stderr,
-            "       [-inetd] [-export] [-logfile file] service_name\n");
+            "       [-inetd] [-export] [-logfile file] [-keytab keytab]\n"
+            "       service_name\n");
     exit(1);
 }
 
@@ -690,6 +692,15 @@
                     exit(1);
                 }
             }
+        } else if (strcmp(*argv, "-keytab") == 0) {
+            argc--;
+            argv++;
+            if (!argc)
+                usage();
+            if (krb5_gss_register_acceptor_identity(*argv)) {
+                fprintf(stderr, "failed to register keytab\n");
+                exit(1);
+            }
         } else
             break;
         argc--;

Modified: branches/iakerb/src/include/k5-int.h
===================================================================
--- branches/iakerb/src/include/k5-int.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/include/k5-int.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -358,6 +358,10 @@
 #define KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED    79 /* missing paChecksum in PA-PK-AS-REQ */
 #define KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED 80 /* bad digest algorithm in SignedData */
 #define KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED 81
+#define KRB_AP_ERR_IAKERB_KDC_NOT_FOUND         85 /* The IAKERB proxy could
+not find a KDC */
+#define KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE       86 /* The KDC did not respond
+to the IAKERB proxy */
 
 /*
  * This structure is returned in the e-data field of the KRB-ERROR
@@ -1032,6 +1036,15 @@
     krb5_pa_data **method_data;
 } krb5_ad_signedpath;
 
+typedef struct _krb5_iakerb_header {
+    krb5_data target_realm;
+    krb5_data *cookie;
+} krb5_iakerb_header;
+
+typedef struct _krb5_iakerb_finished {
+    krb5_checksum checksum;
+} krb5_iakerb_finished;
+
 typedef krb5_error_code
 (*krb5_preauth_obtain_proc)(krb5_context, krb5_pa_data *,
                             krb5_etype_info, krb5_keyblock *,
@@ -1329,6 +1342,10 @@
 void KRB5_CALLCONV krb5_free_fast_response(krb5_context, krb5_fast_response *);
 void KRB5_CALLCONV krb5_free_ad_kdcissued(krb5_context, krb5_ad_kdcissued *);
 void KRB5_CALLCONV krb5_free_ad_signedpath(krb5_context, krb5_ad_signedpath *);
+void KRB5_CALLCONV krb5_free_iakerb_header
+(krb5_context, krb5_iakerb_header *);
+void KRB5_CALLCONV krb5_free_iakerb_finished
+(krb5_context, krb5_iakerb_finished *);
 
 /* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
 #include "com_err.h"
@@ -1740,6 +1757,10 @@
 
 krb5_error_code
 encode_krb5_pa_fx_fast_reply(const krb5_enc_data *, krb5_data **);
+krb5_error_code encode_krb5_iakerb_header
+(const krb5_iakerb_header *, krb5_data **);
+krb5_error_code encode_krb5_iakerb_finished
+(const krb5_iakerb_finished *, krb5_data **);
 
 krb5_error_code
 encode_krb5_fast_response(const krb5_fast_response *, krb5_data **);
@@ -1939,6 +1960,12 @@
 krb5_error_code
 decode_krb5_ad_signedpath(const krb5_data *, krb5_ad_signedpath **);
 
+krb5_error_code decode_krb5_iakerb_header
+(const krb5_data *, krb5_iakerb_header **);
+
+krb5_error_code decode_krb5_iakerb_finished
+(const krb5_data *, krb5_iakerb_finished **);
+
 struct _krb5_key_data;          /* kdb.h */
 
 struct ldap_seqof_key_data {

Modified: branches/iakerb/src/include/krb5/krb5.hin
===================================================================
--- branches/iakerb/src/include/krb5/krb5.hin	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/include/krb5/krb5.hin	2010-03-29 16:18:20 UTC (rev 23843)
@@ -637,6 +637,7 @@
 #define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY    27 /* XXX note conflict with above */
 
 #define KRB5_KEYUSAGE_AD_SIGNEDPATH             -21
+#define KRB5_KEYUSAGE_IAKERB_FINISHED           42
 #define KRB5_KEYUSAGE_PA_PKINIT_KX              44
 /* define in draft-ietf-krb-wg-preauth-framework*/
 #define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50
@@ -2408,7 +2409,34 @@
 krb5_init_creds_get_times(krb5_context context, krb5_init_creds_context ctx,
                           krb5_ticket_times *times);
 
+struct _krb5_tkt_creds_context;
+typedef struct _krb5_tkt_creds_context *krb5_tkt_creds_context;
+
 krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
+                    krb5_creds *creds, int kdcopt, krb5_tkt_creds_context *ctx);
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_get_creds(krb5_context context, krb5_tkt_creds_context ctx,
+                         krb5_creds *creds);
+
+void KRB5_CALLCONV
+krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context ctx);
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx,
+                    krb5_data *in, krb5_data *out, krb5_data *realm,
+                    unsigned int *flags);
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_store_creds(krb5_context context, krb5_tkt_creds_context ctx,
+                           krb5_ccache ccache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_get_times(krb5_context context, krb5_tkt_creds_context ctx,
+                         krb5_ticket_times *times);
+
+krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds,
                            krb5_principal client, krb5_keytab arg_keytab,
                            krb5_deltat start_time, char *in_tkt_service,

Modified: branches/iakerb/src/lib/gssapi/generic/gssapi_ext.h
===================================================================
--- branches/iakerb/src/lib/gssapi/generic/gssapi_ext.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/generic/gssapi_ext.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -47,6 +47,33 @@
 	 const char * /*username*/);
 #endif
 
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_with_password(
+    OM_uint32 *,        /* minor_status */
+    const gss_name_t,   /* desired_name */
+    const gss_buffer_t, /* password */
+    OM_uint32,          /* time_req */
+    const gss_OID_set,  /* desired_mechs */
+    gss_cred_usage_t,   /* cred_usage */
+    gss_cred_id_t *,    /* output_cred_handle */
+    gss_OID_set *,      /* actual_mechs */
+    OM_uint32 *);       /* time_rec */
+
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_with_password(
+    OM_uint32 *,        /* minor_status */
+    const gss_cred_id_t,/* input_cred_handle */
+    const gss_name_t,   /* desired_name */
+    const gss_OID,      /* desired_mech */
+    const gss_buffer_t, /* password */
+    gss_cred_usage_t,   /* cred_usage */
+    OM_uint32,          /* initiator_time_req */
+    OM_uint32,          /* acceptor_time_req */
+    gss_cred_id_t *,    /* output_cred_handle */
+    gss_OID_set *,      /* actual_mechs */
+    OM_uint32 *,        /* initiator_time_rec */
+    OM_uint32 *);       /* acceptor_time_rec */
+
 /*
  * GGF extensions
  */

Modified: branches/iakerb/src/lib/gssapi/krb5/Makefile.in
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/Makefile.in	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/Makefile.in	2010-03-29 16:18:20 UTC (rev 23843)
@@ -52,6 +52,7 @@
 	$(srcdir)/export_sec_context.c \
 	$(srcdir)/get_tkt_flags.c \
 	$(srcdir)/gssapi_krb5.c \
+	$(srcdir)/iakerb.c \
 	$(srcdir)/import_name.c \
 	$(srcdir)/import_sec_context.c \
 	$(srcdir)/indicate_mechs.c \
@@ -106,6 +107,7 @@
 	$(OUTPRE)export_sec_context.$(OBJEXT) \
 	$(OUTPRE)get_tkt_flags.$(OBJEXT) \
 	$(OUTPRE)gssapi_krb5.$(OBJEXT) \
+	$(OUTPRE)iakerb.$(OBJEXT) \
 	$(OUTPRE)import_name.$(OBJEXT) \
 	$(OUTPRE)import_sec_context.$(OBJEXT) \
 	$(OUTPRE)indicate_mechs.$(OBJEXT) \
@@ -163,6 +165,7 @@
 	export_sec_context.o \
 	get_tkt_flags.o \
 	gssapi_krb5.o \
+	iakerb.o \
 	import_name.o \
 	import_sec_context.o \
 	indicate_mechs.o \

Modified: branches/iakerb/src/lib/gssapi/krb5/accept_sec_context.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/accept_sec_context.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/accept_sec_context.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -105,7 +105,6 @@
 #endif
 #include <assert.h>
 
-
 #ifdef CFX_EXERCISE
 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
 #else
@@ -384,12 +383,49 @@
     return major_status;
 }
 
+static krb5_error_code
+kg_process_extension(krb5_context context,
+                     krb5_auth_context auth_context,
+                     int ext_type,
+                     krb5_data *ext_data,
+                     krb5_gss_ctx_ext_t exts)
+{
+    krb5_error_code code = 0;
+
+    assert(exts != NULL);
+
+    switch (ext_type) {
+    case KRB5_GSS_EXTS_IAKERB_FINISHED:
+        if (exts->iakerb.conv == NULL) {
+            code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
+        } else {
+            krb5_key key;
+
+            code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
+            if (code != 0)
+                break;
+
+            code = iakerb_verify_finished(context, key, exts->iakerb.conv,
+                                          ext_data);
+            if (code == 0)
+                exts->iakerb.verified = 1;
+
+            krb5_k_free_key(context, key);
+        }
+        break;
+    default:
+        break;
+    }
+
+    return code;
+}
+
 static OM_uint32
 kg_accept_krb5(minor_status, context_handle,
                verifier_cred_handle, input_token,
                input_chan_bindings, src_name, mech_type,
                output_token, ret_flags, time_rec,
-               delegated_cred_handle)
+               delegated_cred_handle, exts)
     OM_uint32 *minor_status;
     gss_ctx_id_t *context_handle;
     gss_cred_id_t verifier_cred_handle;
@@ -401,6 +437,7 @@
     OM_uint32 *ret_flags;
     OM_uint32 *time_rec;
     gss_cred_id_t *delegated_cred_handle;
+    krb5_gss_ctx_ext_t exts;
 {
     krb5_context context;
     unsigned char *ptr, *ptr2;
@@ -741,16 +778,11 @@
 
         /* if the checksum length > 24, there are options to process */
 
-        if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
-
-            i = authdat->checksum->length - 24;
-
+        i = authdat->checksum->length - 24;
+        if (i && (gss_flags & GSS_C_DELEG_FLAG)) {
             if (i >= 4) {
-
                 TREAD_INT16(ptr, option_id, bigend);
-
                 TREAD_INT16(ptr, option.length, bigend);
-
                 i -= 4;
 
                 if (i < option.length || option.length < 0) {
@@ -784,37 +816,41 @@
 
             } /* if i >= 4 */
             /* ignore any additional trailing data, for now */
-#ifdef CFX_EXERCISE
-            {
-                FILE *f = fopen("/tmp/gsslog", "a");
-                if (f) {
-                    fprintf(f,
-                            "initial context token with delegation, %d extra bytes\n",
-                            i);
-                    fclose(f);
-                }
+        }
+        while (i > 0) {
+            /* Process Type-Length-Data options */
+            if (i < 8) {
+                code = KG_BAD_LENGTH;
+                major_status = GSS_S_FAILURE;
+                goto fail;
             }
-#endif
-        } else {
-#ifdef CFX_EXERCISE
-            {
-                FILE *f = fopen("/tmp/gsslog", "a");
-                if (f) {
-                    if (gss_flags & GSS_C_DELEG_FLAG)
-                        fprintf(f,
-                                "initial context token, delegation flag but too small\n");
-                    else
-                        /* no deleg flag, length might still be too big */
-                        fprintf(f,
-                                "initial context token, %d extra bytes\n",
-                                authdat->checksum->length - 24);
-                    fclose(f);
-                }
+            TREAD_INT(ptr, option_id, 1);
+            TREAD_INT(ptr, option.length, 1);
+            i -= 8;
+            if (i < option.length) {
+                code = KG_BAD_LENGTH;
+                major_status = GSS_S_FAILURE;
+                goto fail;
             }
-#endif
+            TREAD_STR(ptr, ptr2, option.length);
+            option.data = (char *)ptr2;
+
+            i -= option.length;
+
+            code = kg_process_extension(context, auth_context,
+                                        option_id, &option, exts);
+            if (code != 0) {
+                major_status = GSS_S_FAILURE;
+                goto fail;
+            }
         }
     }
 
+    if (exts->iakerb.conv && !exts->iakerb.verified) {
+        major_status = GSS_S_BAD_SIG;
+        goto fail;
+    }
+
     /* only DCE_STYLE clients are allowed to send raw AP-REQs */
     if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
         major_status = GSS_S_DEFECTIVE_TOKEN;
@@ -831,6 +867,7 @@
     }
 
     memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+    ctx->magic = KG_CONTEXT;
     ctx->mech_used = (gss_OID) mech_used;
     ctx->auth_context = auth_context;
     ctx->initiate = 0;
@@ -1248,22 +1285,19 @@
 #endif /* LEAN_CLIENT */
 
 OM_uint32
-krb5_gss_accept_sec_context(minor_status, context_handle,
-                            verifier_cred_handle, input_token,
-                            input_chan_bindings, src_name, mech_type,
-                            output_token, ret_flags, time_rec,
-                            delegated_cred_handle)
-    OM_uint32 *minor_status;
-    gss_ctx_id_t *context_handle;
-    gss_cred_id_t verifier_cred_handle;
-    gss_buffer_t input_token;
-    gss_channel_bindings_t input_chan_bindings;
-    gss_name_t *src_name;
-    gss_OID *mech_type;
-    gss_buffer_t output_token;
-    OM_uint32 *ret_flags;
-    OM_uint32 *time_rec;
-    gss_cred_id_t *delegated_cred_handle;
+krb5_gss_accept_sec_context_ext(
+    OM_uint32 *minor_status,
+    gss_ctx_id_t *context_handle,
+    gss_cred_id_t verifier_cred_handle,
+    gss_buffer_t input_token,
+    gss_channel_bindings_t input_chan_bindings,
+    gss_name_t *src_name,
+    gss_OID *mech_type,
+    gss_buffer_t output_token,
+    OM_uint32 *ret_flags,
+    OM_uint32 *time_rec,
+    gss_cred_id_t *delegated_cred_handle,
+    krb5_gss_ctx_ext_t exts)
 {
     krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
 
@@ -1291,5 +1325,42 @@
                           verifier_cred_handle, input_token,
                           input_chan_bindings, src_name, mech_type,
                           output_token, ret_flags, time_rec,
-                          delegated_cred_handle);
+                          delegated_cred_handle, exts);
 }
+
+OM_uint32
+krb5_gss_accept_sec_context(minor_status, context_handle,
+                            verifier_cred_handle, input_token,
+                            input_chan_bindings, src_name, mech_type,
+                            output_token, ret_flags, time_rec,
+                            delegated_cred_handle)
+    OM_uint32 *minor_status;
+    gss_ctx_id_t *context_handle;
+    gss_cred_id_t verifier_cred_handle;
+    gss_buffer_t input_token;
+    gss_channel_bindings_t input_chan_bindings;
+    gss_name_t *src_name;
+    gss_OID *mech_type;
+    gss_buffer_t output_token;
+    OM_uint32 *ret_flags;
+    OM_uint32 *time_rec;
+    gss_cred_id_t *delegated_cred_handle;
+{
+    krb5_gss_ctx_ext_rec exts;
+
+    memset(&exts, 0, sizeof(exts));
+
+    return krb5_gss_accept_sec_context_ext(minor_status,
+                                           context_handle,
+                                           verifier_cred_handle,
+                                           input_token,
+                                           input_chan_bindings,
+                                           src_name,
+                                           mech_type,
+                                           output_token,
+                                           ret_flags,
+                                           time_rec,
+                                           delegated_cred_handle,
+                                           &exts);
+}
+

Modified: branches/iakerb/src/lib/gssapi/krb5/acquire_cred.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/acquire_cred.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/acquire_cred.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -222,16 +222,17 @@
                   OM_uint32 *minor_status,
                   krb5_gss_name_t desired_name,
                   krb5_gss_name_t *output_name,
+                  gss_buffer_t password,
                   krb5_gss_cred_id_rec *cred)
 {
     krb5_error_code code;
     krb5_ccache ccache;
-    krb5_principal princ, tmp_princ;
-    krb5_flags flags;
+    krb5_principal princ = NULL, tmp_princ;
     krb5_cc_cursor cur;
     krb5_creds creds;
     int got_endtime;
     int caller_provided_ccache_name = 0;
+    krb5_data password_data;
 
     cred->ccache = NULL;
 
@@ -335,24 +336,30 @@
     }
 
     /* turn off OPENCLOSE mode while extensive frobbing is going on */
-
-    flags = 0;           /* turns off OPENCLOSE mode */
-    if ((code = krb5_cc_set_flags(context, ccache, flags))) {
+    code = krb5_cc_set_flags(context, ccache, 0);
+    if (code == KRB5_FCC_NOFILE &&
+        password != GSS_C_NO_BUFFER && desired_name != NULL) {
+        /* Well, we can create a memory ccache. */
+        code = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
+        if (code == 0)
+            code = krb5_cc_initialize(context, ccache, desired_name->princ);
+    }
+    if (code != 0) {
         (void)krb5_cc_close(context, ccache);
         *minor_status = code;
         return(GSS_S_CRED_UNAVAIL);
     }
 
     /* get out the principal name and see if it matches */
-
-    if ((code = krb5_cc_get_principal(context, ccache, &princ))) {
+    code = krb5_cc_get_principal(context, ccache, &princ);
+    if (code != 0) {
         (void)krb5_cc_close(context, ccache);
         *minor_status = code;
         return(GSS_S_FAILURE);
     }
 
-    if (desired_name != (krb5_gss_name_t)NULL) {
-        if (! krb5_principal_compare(context, princ, desired_name->princ)) {
+    if (desired_name != NULL) {
+        if (!krb5_principal_compare(context, princ, desired_name->princ)) {
             (void)krb5_free_principal(context, princ);
             (void)krb5_cc_close(context, ccache);
             *minor_status = KG_CCACHE_NOMATCH;
@@ -369,8 +376,34 @@
             *minor_status = code;
             return(GSS_S_FAILURE);
         }
+        /* princ is now owned by output_name, it need not be freed here */
     }
 
+    if (password != GSS_C_NO_BUFFER) {
+        /* stash the password for later */
+        password_data.length = password->length;
+        password_data.data = (char *)password->value;
+
+        code = krb5int_copy_data_contents_add0(context, &password_data,
+                                               &cred->password);
+        if (code != 0) {
+            (void)krb5_cc_close(context, ccache);
+            *minor_status = code;
+            return GSS_S_FAILURE;
+        }
+
+        /* restore the OPENCLOSE flag */
+        code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
+        if (code != 0) {
+            (void)krb5_cc_close(context, ccache);
+            *minor_status = code;
+            return GSS_S_FAILURE;
+        }
+
+        cred->ccache = ccache;
+        return GSS_S_COMPLETE;
+    }
+
     /* iterate over the ccache, find the tgt */
 
     if ((code = krb5_cc_start_seq_get(context, ccache, &cur))) {
@@ -388,7 +421,7 @@
     code = krb5_build_principal_ext(context, &tmp_princ,
                                     krb5_princ_realm(context, princ)->length,
                                     krb5_princ_realm(context, princ)->data,
-                                    6, "krbtgt",
+                                    KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
                                     krb5_princ_realm(context, princ)->length,
                                     krb5_princ_realm(context, princ)->data,
                                     0);
@@ -433,8 +466,7 @@
             *minor_status = code;
             return(GSS_S_FAILURE);
         }
-        flags = KRB5_TC_OPENCLOSE;        /* turns on OPENCLOSE mode */
-        if ((code = krb5_cc_set_flags(context, ccache, flags))) {
+        if ((code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE))) {
             (void)krb5_cc_close(context, ccache);
             *minor_status = code;
             return(GSS_S_FAILURE);
@@ -449,38 +481,36 @@
 }
 
 /*ARGSUSED*/
-OM_uint32
-krb5_gss_acquire_cred(minor_status, desired_name, time_req,
-                      desired_mechs, cred_usage, output_cred_handle,
-                      actual_mechs, time_rec)
+static OM_uint32
+acquire_cred(minor_status, desired_name, password, time_req,
+             desired_mechs, cred_usage, output_cred_handle,
+             actual_mechs, time_rec, req_iakerb)
     OM_uint32 *minor_status;
-    gss_name_t desired_name;
+    const gss_name_t desired_name;
+    const gss_buffer_t password;
     OM_uint32 time_req;
-    gss_OID_set desired_mechs;
+    const gss_OID_set desired_mechs;
     gss_cred_usage_t cred_usage;
     gss_cred_id_t *output_cred_handle;
     gss_OID_set *actual_mechs;
     OM_uint32 *time_rec;
+    int req_iakerb;
 {
-    krb5_context context;
+    krb5_context context = NULL;
     size_t i;
-    krb5_gss_cred_id_t cred;
-    gss_OID_set ret_mechs = NULL;
+    krb5_gss_cred_id_t cred = NULL;
+    gss_OID_set ret_mechs = GSS_C_NO_OID_SET;
     int req_old, req_new;
     OM_uint32 ret;
-    krb5_error_code code;
+    krb5_error_code code = 0;
 
     code = gss_krb5int_initialize_library();
-    if (code) {
-        *minor_status = code;
-        return GSS_S_FAILURE;
-    }
+    if (code)
+        goto krb_error_out;
 
     code = krb5_gss_init_context(&context);
-    if (code) {
-        *minor_status = code;
-        return GSS_S_FAILURE;
-    }
+    if (code)
+        goto krb_error_out;
 
     /* make sure all outputs are valid */
 
@@ -495,11 +525,16 @@
     /*SUPPRESS 29*/
     if ((desired_name != GSS_C_NO_NAME) &&
         (! kg_validate_name(desired_name))) {
-        *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-        krb5_free_context(context);
-        return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
+        code = G_VALIDATE_FAILED;
+        goto krb_error_out;
     }
 
+    if (req_iakerb &&
+        (password == GSS_C_NO_BUFFER || cred_usage == GSS_C_BOTH)) {
+        code = G_BAD_USAGE;
+        goto krb_error_out;
+    }
+
     /* verify that the requested mechanism set is the default, or
        contains krb5 */
 
@@ -518,26 +553,21 @@
         }
 
         if (!req_old && !req_new) {
-            *minor_status = 0;
-            krb5_free_context(context);
-            return(GSS_S_BAD_MECH);
+            ret = GSS_S_BAD_MECH;
+            goto error_out;
         }
     }
 
     /* create the gss cred structure */
+    cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
+    if (code != 0)
+        goto krb_error_out;
 
-    if ((cred =
-         (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) {
-        *minor_status = ENOMEM;
-        krb5_free_context(context);
-        return(GSS_S_FAILURE);
-    }
-    memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
-
     cred->usage = cred_usage;
     cred->name = NULL;
-    cred->prerfc_mech = (req_old != 0);
-    cred->rfc_mech = (req_new != 0);
+    cred->prerfc_mech = (req_old != 0) && (req_iakerb == 0);
+    cred->rfc_mech = (req_new != 0) && (req_iakerb == 0);
+    cred->iakerb_mech = req_iakerb;
     cred->default_identity = (desired_name == GSS_C_NO_NAME);
 
 #ifndef LEAN_CLIENT
@@ -546,11 +576,9 @@
     cred->ccache = NULL;
 
     code = k5_mutex_init(&cred->lock);
-    if (code) {
-        *minor_status = code;
-        krb5_free_context(context);
-        return GSS_S_FAILURE;
-    }
+    if (code)
+        goto krb_error_out;
+
     /* Note that we don't need to lock this GSSAPI credential record
        here, because no other thread can gain access to it until we
        return it.  */
@@ -558,11 +586,8 @@
     if ((cred_usage != GSS_C_INITIATE) &&
         (cred_usage != GSS_C_ACCEPT) &&
         (cred_usage != GSS_C_BOTH)) {
-        k5_mutex_destroy(&cred->lock);
-        xfree(cred);
         *minor_status = (OM_uint32) G_BAD_USAGE;
-        krb5_free_context(context);
-        return(GSS_S_FAILURE);
+        goto error_out;
     }
 
     /* if requested, acquire credentials for accepting */
@@ -574,14 +599,7 @@
                                        (krb5_gss_name_t)desired_name,
                                        &cred->name, cred))
             != GSS_S_COMPLETE) {
-            if (cred->name)
-                kg_release_name(context, 0, &cred->name);
-            k5_mutex_destroy(&cred->lock);
-            xfree(cred);
-            /* minor_status set by acquire_accept_cred() */
-            save_error_info(*minor_status, context);
-            krb5_free_context(context);
-            return(ret);
+            goto error_out;
         }
 #endif /* LEAN_CLIENT */
 
@@ -589,46 +607,24 @@
     /* this will fill in cred->name if it wasn't set above, and
        the desired_name is not specified */
 
-    if ((cred_usage == GSS_C_INITIATE) ||
-        (cred_usage == GSS_C_BOTH))
-        if ((ret =
-             acquire_init_cred(context, minor_status,
-                               cred->name?cred->name:(krb5_gss_name_t)desired_name,
-                               &cred->name, cred))
-            != GSS_S_COMPLETE) {
-#ifndef LEAN_CLIENT
-            if (cred->keytab)
-                krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
-            if (cred->name)
-                kg_release_name(context, 0, &cred->name);
-            k5_mutex_destroy(&cred->lock);
-            xfree(cred);
-            /* minor_status set by acquire_init_cred() */
-            save_error_info(*minor_status, context);
-            krb5_free_context(context);
-            return(ret);
-        }
+    if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
+        ret = acquire_init_cred(context, minor_status,
+                                cred->name ?
+                                    cred->name : (krb5_gss_name_t)desired_name,
+                                &cred->name, password, cred);
+        if (ret != GSS_S_COMPLETE)
+            goto error_out;
+    }
 
     /* if the princ wasn't filled in already, fill it in now */
 
-    if (!cred->name && (desired_name != GSS_C_NO_NAME))
-        if ((code = kg_duplicate_name(context,
-                                      (krb5_gss_name_t)desired_name,
-                                      0, &cred->name))) {
-            if (cred->ccache)
-                (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
-            if (cred->keytab)
-                (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
-            k5_mutex_destroy(&cred->lock);
-            xfree(cred);
-            *minor_status = code;
-            save_error_info(*minor_status, context);
-            krb5_free_context(context);
-            return(GSS_S_FAILURE);
-        }
+    if (!cred->name && (desired_name != GSS_C_NO_NAME)) {
+        code = kg_duplicate_name(context,
+                                 (krb5_gss_name_t)desired_name,
+                                 0, &cred->name);
+        if (code != 0)
+            goto krb_error_out;
+    }
 
     /*** at this point, the cred structure has been completely created */
 
@@ -640,22 +636,9 @@
     } else {
         krb5_timestamp now;
 
-        if ((code = krb5_timeofday(context, &now))) {
-            if (cred->ccache)
-                (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
-            if (cred->keytab)
-                (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
-            if (cred->name)
-                kg_release_name(context, 0, &cred->name);
-            k5_mutex_destroy(&cred->lock);
-            xfree(cred);
-            *minor_status = code;
-            save_error_info(*minor_status, context);
-            krb5_free_context(context);
-            return(GSS_S_FAILURE);
-        }
+        code = krb5_timeofday(context, &now);
+        if (code != 0)
+            goto krb_error_out;
 
         if (time_rec)
             *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
@@ -673,44 +656,20 @@
             (cred->rfc_mech &&
              GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
                                                             gss_mech_krb5,
+                                                            &ret_mechs))) ||
+            (cred->iakerb_mech &&
+             GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                            gss_mech_iakerb,
                                                             &ret_mechs)))) {
-            if (cred->ccache)
-                (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
-            if (cred->keytab)
-                (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
-            if (cred->name)
-                kg_release_name(context, 0, &cred->name);
-            k5_mutex_destroy(&cred->lock);
-            xfree(cred);
-            /* *minor_status set above */
-            krb5_free_context(context);
-            return(ret);
+            goto error_out;
         }
     }
 
     /* intern the credential handle */
 
     if (! kg_save_cred_id((gss_cred_id_t) cred)) {
-        if (ret_mechs) {
-            free(ret_mechs->elements);
-            free(ret_mechs);
-        }
-        if (cred->ccache)
-            (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
-        if (cred->keytab)
-            (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
-        if (cred->name)
-            kg_release_name(context, 0, &cred->name);
-        k5_mutex_destroy(&cred->lock);
-        xfree(cred);
-        *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-        save_error_string(*minor_status, "error saving credentials");
-        krb5_free_context(context);
-        return(GSS_S_FAILURE);
+        ret = GSS_S_FAILURE;
+        goto error_out;
     }
 
     /* return success */
@@ -722,6 +681,29 @@
 
     krb5_free_context(context);
     return(GSS_S_COMPLETE);
+
+krb_error_out:
+    *minor_status = code;
+    ret = GSS_S_FAILURE;
+
+error_out:
+    if (ret_mechs != GSS_C_NO_OID_SET) {
+        free(ret_mechs->elements);
+        free(ret_mechs);
+    }
+    if (cred->ccache)
+        (void)krb5_cc_close(context, cred->ccache);
+#ifndef LEAN_CLIENT
+    if (cred->keytab)
+        (void)krb5_kt_close(context, cred->keytab);
+#endif /* LEAN_CLIENT */
+    if (cred->name)
+        kg_release_name(context, 0, &cred->name);
+    k5_mutex_destroy(&cred->lock);
+    xfree(cred);
+    save_error_info(*minor_status, context);
+    krb5_free_context(context);
+    return ret;
 }
 
 OM_uint32
@@ -768,3 +750,57 @@
     *minor_status = 0;
     return GSS_S_COMPLETE;
 }
+
+OM_uint32
+krb5_gss_acquire_cred(minor_status, desired_name, time_req,
+                      desired_mechs, cred_usage, output_cred_handle,
+                      actual_mechs, time_rec)
+    OM_uint32 *minor_status;
+    gss_name_t desired_name;
+    OM_uint32 time_req;
+    gss_OID_set desired_mechs;
+    gss_cred_usage_t cred_usage;
+    gss_cred_id_t *output_cred_handle;
+    gss_OID_set *actual_mechs;
+    OM_uint32 *time_rec;
+{
+    return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER,
+                        time_req, desired_mechs,
+                        cred_usage, output_cred_handle, actual_mechs,
+                        time_rec, 0);
+}
+
+OM_uint32
+krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+                                    const gss_name_t desired_name,
+                                    const gss_buffer_t password,
+                                    OM_uint32 time_req,
+                                    const gss_OID_set desired_mechs,
+                                    int cred_usage,
+                                    gss_cred_id_t *output_cred_handle,
+                                    gss_OID_set *actual_mechs,
+                                    OM_uint32 *time_rec)
+{
+    return acquire_cred(minor_status, desired_name, password,
+                        time_req, desired_mechs,
+                        cred_usage, output_cred_handle, actual_mechs,
+                        time_rec, 0);
+}
+
+OM_uint32
+iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+                                      const gss_name_t desired_name,
+                                      const gss_buffer_t password,
+                                      OM_uint32 time_req,
+                                      const gss_OID_set desired_mechs,
+                                      int cred_usage,
+                                      gss_cred_id_t *output_cred_handle,
+                                      gss_OID_set *actual_mechs,
+                                      OM_uint32 *time_rec)
+{
+    return acquire_cred(minor_status, desired_name, password,
+                        time_req, desired_mechs,
+                        cred_usage, output_cred_handle, actual_mechs,
+                        time_rec, 1);
+}
+

Modified: branches/iakerb/src/lib/gssapi/krb5/add_cred.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/add_cred.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/add_cred.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -145,7 +145,8 @@
     /* check that desired_mech isn't already in the credential */
 
     if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
-        (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
+        (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech) ||
+        (g_OID_equal(desired_mech, gss_mech_iakerb) && cred->iakerb_mech)) {
         *minor_status = 0;
         krb5_free_context(context);
         return(GSS_S_DUPLICATE_ELEMENT);
@@ -197,6 +198,7 @@
         new_cred->usage = cred_usage;
         new_cred->prerfc_mech = cred->prerfc_mech;
         new_cred->rfc_mech = cred->rfc_mech;
+        new_cred->iakerb_mech = cred->iakerb_mech;
         new_cred->tgt_expire = cred->tgt_expire;
 
         if (cred->name)
@@ -359,6 +361,8 @@
         cred->prerfc_mech = 1;
     else if (g_OID_equal(desired_mech, gss_mech_krb5))
         cred->rfc_mech = 1;
+    else if (g_OID_equal(desired_mech, gss_mech_iakerb))
+        cred->iakerb_mech = 1;
 
     /* set the outputs */
 

Modified: branches/iakerb/src/lib/gssapi/krb5/gssapiP_krb5.h
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/gssapiP_krb5.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/gssapiP_krb5.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -86,6 +86,9 @@
 #define GSS_MECH_KRB5_WRONG_OID_LENGTH 9
 #define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002"
 
+/* IAKERB variant */
+#define GSS_MECH_IAKERB_OID_LENGTH 6
+#define GSS_MECH_IAKERB_OID "\053\006\001\005\002\005"
 
 #define CKSUMTYPE_KG_CB         0x8003
 
@@ -100,6 +103,7 @@
 #define KG2_TOK_MIC_MSG         0x0404
 #define KG2_TOK_WRAP_MSG        0x0504
 #define KG2_TOK_DEL_CTX         0x0405
+#define IAKERB_TOK_PROXY        0x0501
 
 #define KRB5_GSS_FOR_CREDS_OPTION 1
 
@@ -170,6 +174,7 @@
     unsigned int rfc_mech : 1;
     unsigned int proxy_cred : 1;
     unsigned int default_identity : 1;
+    unsigned int iakerb_mech : 1;
 
     /* keytab (accept) data */
     krb5_keytab keytab;
@@ -179,9 +184,18 @@
     krb5_ccache ccache;
     krb5_timestamp tgt_expire;
     krb5_enctype *req_enctypes;  /* limit negotiated enctypes to this list */
+    krb5_data password;
 } krb5_gss_cred_id_rec, *krb5_gss_cred_id_t;
 
+typedef struct _krb5_gss_ctx_ext_rec {
+    struct {
+        krb5_data *conv;
+        int verified;
+    } iakerb;
+} krb5_gss_ctx_ext_rec, *krb5_gss_ctx_ext_t;
+
 typedef struct _krb5_gss_ctx_id_rec {
+    krb5_magic magic;
     unsigned int initiate : 1;   /* nonzero if initiating, zero if accepting */
     unsigned int established : 1;
     unsigned int big_endian : 1;
@@ -239,7 +253,8 @@
 
 #define kg_validate_name(name)          g_validate_name(&kg_vdb,name)
 #define kg_validate_cred_id(cred)       g_validate_cred_id(&kg_vdb,cred)
-#define kg_validate_ctx_id(ctx)         g_validate_ctx_id(&kg_vdb,ctx)
+#define kg_validate_ctx_id(ctx)         (g_validate_ctx_id(&kg_vdb,ctx) && \
+                                         ((krb5_gss_ctx_id_t)ctx)->magic == KG_CONTEXT)
 #define kg_validate_lucidctx_id(lctx)   g_validate_lucidctx_id(&kg_vdb,lctx)
 
 #define kg_delete_name(name)            g_delete_name(&kg_vdb,name)
@@ -492,7 +507,8 @@
     OM_uint32 *ret_flags,
     OM_uint32 *time_rec,
     krb5_context context,
-    int default_mech);
+    int default_mech,
+    krb5_gss_ctx_ext_t exts);
 
 /** declarations of internal name mechanism functions **/
 
@@ -507,6 +523,30 @@
  OM_uint32*        /* time_rec */
 );
 
+OM_uint32
+krb5_gss_acquire_cred_with_password(
+    OM_uint32 *minor_status,
+    const gss_name_t desired_name,
+    const gss_buffer_t password,
+    OM_uint32 time_req,
+    const gss_OID_set desired_mechs,
+    int cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec);
+
+OM_uint32
+iakerb_gss_acquire_cred_with_password(
+    OM_uint32 *minor_status,
+    const gss_name_t desired_name,
+    const gss_buffer_t password,
+    OM_uint32 time_req,
+    const gss_OID_set desired_mechs,
+    int cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec);
+
 OM_uint32 krb5_gss_release_cred
 (OM_uint32*,       /* minor_status */
  gss_cred_id_t*    /* cred_handle */
@@ -529,6 +569,24 @@
  OM_uint32*        /* time_rec */
 );
 
+OM_uint32 krb5_gss_init_sec_context_ext
+(OM_uint32*,       /* minor_status */
+ gss_cred_id_t,    /* claimant_cred_handle */
+ gss_ctx_id_t*,    /* context_handle */
+ gss_name_t,       /* target_name */
+ gss_OID,          /* mech_type */
+ OM_uint32,        /* req_flags */
+ OM_uint32,        /* time_req */
+ gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_buffer_t,     /* input_token */
+ gss_OID*,         /* actual_mech_type */
+ gss_buffer_t,     /* output_token */
+ OM_uint32*,       /* ret_flags */
+ OM_uint32*,       /* time_rec */
+ krb5_gss_ctx_ext_t /* exts */
+);
+
 #ifndef LEAN_CLIENT
 OM_uint32 krb5_gss_accept_sec_context
 (OM_uint32*,       /* minor_status */
@@ -544,6 +602,22 @@
  OM_uint32*,       /* time_rec */
  gss_cred_id_t*    /* delegated_cred_handle */
 );
+
+OM_uint32 krb5_gss_accept_sec_context_ext
+(OM_uint32*,       /* minor_status */
+ gss_ctx_id_t*,    /* context_handle */
+ gss_cred_id_t,    /* verifier_cred_handle */
+ gss_buffer_t,     /* input_token_buffer */
+ gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_name_t*,      /* src_name */
+ gss_OID*,         /* mech_type */
+ gss_buffer_t,     /* output_token */
+ OM_uint32*,       /* ret_flags */
+ OM_uint32*,       /* time_rec */
+ gss_cred_id_t*,   /* delegated_cred_handle */
+ krb5_gss_ctx_ext_t/*exts */
+);
 #endif /* LEAN_CLIENT */
 
 OM_uint32 krb5_gss_process_context_token
@@ -1084,4 +1158,53 @@
 #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10
 #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID  "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04"
 
+/* IAKERB */
+
+OM_uint32
+iakerb_gss_init_sec_context(OM_uint32 *minor_status,
+                            gss_cred_id_t claimant_cred_handle,
+                            gss_ctx_id_t *context_handle,
+                            gss_name_t target_name,
+                            gss_OID mech_type,
+                            OM_uint32 req_flags,
+                            OM_uint32 time_req,
+                            gss_channel_bindings_t input_chan_bindings,
+                            gss_buffer_t input_token,
+                            gss_OID *actual_mech_type,
+                            gss_buffer_t output_token,
+                            OM_uint32 *ret_flags,
+                            OM_uint32 *time_rec);
+
+OM_uint32
+iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
+                              gss_ctx_id_t *context_handler,
+                              gss_cred_id_t verifier_cred_handle,
+                              gss_buffer_t input_token,
+                              gss_channel_bindings_t input_chan_bindings,
+                              gss_name_t *src_name,
+                              gss_OID *mech_type,
+                              gss_buffer_t output_token,
+                              OM_uint32 *ret_flags,
+                              OM_uint32 *time_rec,
+                              gss_cred_id_t *delegated_cred_handle);
+
+OM_uint32
+iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
+                              gss_ctx_id_t *context_handle,
+                              gss_buffer_t output_token);
+
+krb5_error_code
+iakerb_make_finished(krb5_context context,
+                     krb5_key key,
+                     const krb5_data *conv,
+                     krb5_data **finished);
+
+krb5_error_code
+iakerb_verify_finished(krb5_context context,
+                       krb5_key key,
+                       const krb5_data *conv,
+                       const krb5_data *finished);
+
+#define KRB5_GSS_EXTS_IAKERB_FINISHED 1
+
 #endif /* _GSSAPIP_KRB5_H_ */

Modified: branches/iakerb/src/lib/gssapi/krb5/gssapi_err_krb5.et
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/gssapi_err_krb5.et	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/gssapi_err_krb5.et	2010-03-29 16:18:20 UTC (rev 23843)
@@ -38,4 +38,5 @@
 error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types"
 error_code KG_LUCID_VERSION, "Requested lucid context version not supported"
 error_code KG_INPUT_TOO_LONG, "PRF input too long"
+error_code KG_IAKERB_CONTEXT, "Bad magic number for iakerb_ctx_id_t"
 end

Modified: branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -127,34 +127,36 @@
     {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID},
     /* this is the unofficial, incorrect mech OID emitted by MS */
     {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID},
+    /* IAKERB OID */
+    {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID},
     /* this is the v2 assigned OID */
     {9, "\052\206\110\206\367\022\001\002\003"},
     /* these two are name type OID's */
-
     /* 2.1.1. Kerberos Principal Name Form:  (rfc 1964)
      * This name form shall be represented by the Object Identifier {iso(1)
      * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
      * krb5(2) krb5_name(1)}.  The recommended symbolic name for this type
      * is "GSS_KRB5_NT_PRINCIPAL_NAME". */
     {10, "\052\206\110\206\367\022\001\002\002\001"},
-
     /* gss_nt_krb5_principal.  Object identifier for a krb5_principal. Do not use. */
     {10, "\052\206\110\206\367\022\001\002\002\002"},
-
     { 0, 0 }
 };
 
 const gss_OID_desc * const gss_mech_krb5              = krb5_gss_oid_array+0;
 const gss_OID_desc * const gss_mech_krb5_old          = krb5_gss_oid_array+1;
 const gss_OID_desc * const gss_mech_krb5_wrong        = krb5_gss_oid_array+2;
-const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+4;
-const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+5;
-const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+4;
+const gss_OID_desc * const gss_mech_iakerb            = krb5_gss_oid_array+3;
 
+
+const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+5;
+const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+6;
+const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+5;
+
 static const gss_OID_set_desc oidsets[] = {
-    {1, (gss_OID) krb5_gss_oid_array+0},
-    {1, (gss_OID) krb5_gss_oid_array+1},
-    {3, (gss_OID) krb5_gss_oid_array+0},
+    {1, (gss_OID) krb5_gss_oid_array+0}, /* RFC OID */
+    {1, (gss_OID) krb5_gss_oid_array+1}, /* pre-RFC OID */
+    {4, (gss_OID) krb5_gss_oid_array+0}, /* includes wrong OID & IAKERB */
     {1, (gss_OID) krb5_gss_oid_array+2},
     {3, (gss_OID) krb5_gss_oid_array+0},
 };
@@ -697,18 +699,47 @@
     NULL,               /* set_neg_mechs */
 };
 
+static struct gss_config_ext krb5_mechanism_ext = {
+    krb5_gss_acquire_cred_with_password,
+};
 
+static struct gss_config_ext iakerb_mechanism_ext = {
+    iakerb_gss_acquire_cred_with_password,
+};
+
 #ifdef _GSS_STATIC_LINK
 #include "mglueP.h"
+static int gss_iakerbmechglue_init(void)
+{
+    struct gss_mech_config mech_iakerb;
+    struct gss_config iakerb_mechanism = krb5_mechanism;
+
+    /* IAKERB mechanism mirrors krb5, but with different context SPIs */
+    iakerb_mechanism.gss_accept_sec_context = iakerb_gss_accept_sec_context;
+    iakerb_mechanism.gss_init_sec_context   = iakerb_gss_init_sec_context;
+    iakerb_mechanism.gss_delete_sec_context = iakerb_gss_delete_sec_context;
+
+    memset(&mech_iakerb, 0, sizeof(mech_iakerb));
+    mech_iakerb.mech = &iakerb_mechanism;
+    mech_iakerb.mech_ext = &iakerb_mechanism_ext;
+
+    mech_iakerb.mechNameStr = "iakerb";
+    mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb;
+    gssint_register_mechinfo(&mech_iakerb);
+
+    return 0;
+}
+
 static int gss_krb5mechglue_init(void)
 {
     struct gss_mech_config mech_krb5;
 
     memset(&mech_krb5, 0, sizeof(mech_krb5));
     mech_krb5.mech = &krb5_mechanism;
+    mech_krb5.mech_ext = &krb5_mechanism_ext;
+
     mech_krb5.mechNameStr = "kerberos_v5";
     mech_krb5.mech_type = (gss_OID)gss_mech_krb5;
-
     gssint_register_mechinfo(&mech_krb5);
 
     mech_krb5.mechNameStr = "kerberos_v5_old";
@@ -766,6 +797,9 @@
         return err;
 #endif
 #ifdef _GSS_STATIC_LINK
+    err = gss_iakerbmechglue_init();
+    if (err)
+        return err;
     err = gss_krb5mechglue_init();
     if (err)
         return err;

Modified: branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.hin
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.hin	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/gssapi_krb5.hin	2010-03-29 16:18:20 UTC (rev 23843)
@@ -75,6 +75,7 @@
 GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5;
 GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_old;
 GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_wrong;
+GSS_DLLIMP extern const gss_OID_desc * const gss_mech_iakerb;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_old;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_both;

Added: branches/iakerb/src/lib/gssapi/krb5/iakerb.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/iakerb.c	                        (rev 0)
+++ branches/iakerb/src/lib/gssapi/krb5/iakerb.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -0,0 +1,1081 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009  by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+#include "k5-int.h"
+#include "gssapiP_krb5.h"
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <assert.h>
+
+/*
+ * IAKERB implementation
+ */
+
+extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
+
+enum iakerb_state {
+    IAKERB_AS_REQ,      /* acquiring ticket with initial creds */
+    IAKERB_TGS_REQ,     /* acquiring ticket with TGT */
+    IAKERB_AP_REQ       /* hand-off to normal GSS AP-REQ exchange */
+};
+
+struct _iakerb_ctx_id_rec {
+    krb5_magic magic;                   /* KG_IAKERB_CONTEXT */
+    krb5_context k5c;
+    enum iakerb_state state;            /* discriminant for union below */
+    union {
+        krb5_init_creds_context icc;    /* IAKERB_AS_REQ */
+        krb5_tkt_creds_context tcc;     /* IAKERB_TGS_REQ */
+        gss_ctx_id_t gssc;              /* IAKERB_AP_REQ */
+    } u;
+    krb5_data conv;                     /* conversation for checksumming */
+    unsigned int count;                 /* number of round trips */
+    krb5_get_init_creds_opt *gic_opts;
+};
+
+#define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS )
+
+typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec;
+typedef iakerb_ctx_id_rec *iakerb_ctx_id_t;
+
+/*
+ * Release an IAKERB context
+ */
+static void
+iakerb_release_context(iakerb_ctx_id_t ctx)
+{
+    OM_uint32 tmp;
+
+    if (ctx == NULL)
+        return;
+
+    switch (ctx->state) {
+    case IAKERB_AS_REQ:
+        krb5_init_creds_free(ctx->k5c, ctx->u.icc);
+        break;
+    case IAKERB_TGS_REQ:
+        krb5_tkt_creds_free(ctx->k5c, ctx->u.tcc);
+        break;
+    case IAKERB_AP_REQ:
+        krb5_gss_delete_sec_context(&tmp, &ctx->u.gssc, NULL);
+        break;
+    }
+    krb5_free_data_contents(ctx->k5c, &ctx->conv);
+    krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts);
+    krb5_free_context(ctx->k5c);
+    free(ctx);
+}
+
+/*
+ * Create a IAKERB-FINISHED structure containing a checksum of
+ * the entire IAKERB exchange.
+ */
+krb5_error_code
+iakerb_make_finished(krb5_context context,
+                     krb5_key key,
+                     const krb5_data *conv,
+                     krb5_data **finished)
+{
+    krb5_error_code code;
+    krb5_cksumtype cksumtype;
+    krb5_iakerb_finished iaf;
+
+    *finished = NULL;
+
+    memset(&iaf, 0, sizeof(iaf));
+
+    if (key == NULL)
+        return KRB5KDC_ERR_NULL_KEY;
+
+    code = krb5int_c_mandatory_cksumtype(context,
+                                         krb5_k_key_enctype(context, key),
+                                         &cksumtype);
+    if (code != 0)
+        return code;
+
+    code = krb5_k_make_checksum(context, cksumtype,
+                                key, KRB5_KEYUSAGE_IAKERB_FINISHED,
+                                conv, &iaf.checksum);
+    if (code != 0)
+        return code;
+
+    code = encode_krb5_iakerb_finished(&iaf, finished);
+
+    krb5_free_checksum_contents(context, &iaf.checksum);
+
+    return code;
+}
+
+/*
+ * Verify a IAKERB-FINISHED structure submitted by the initiator
+ */
+krb5_error_code
+iakerb_verify_finished(krb5_context context,
+                       krb5_key key,
+                       const krb5_data *conv,
+                       const krb5_data *finished)
+{
+    krb5_error_code code;
+    krb5_iakerb_finished *iaf;
+    krb5_boolean valid = FALSE;
+
+    if (key == NULL)
+        return KRB5KDC_ERR_NULL_KEY;
+
+    code = decode_krb5_iakerb_finished(finished, &iaf);
+    if (code != 0)
+        return code;
+
+    code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
+                                  conv, &iaf->checksum, &valid);
+    if (code == 0 && valid == FALSE)
+        code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+    krb5_free_iakerb_finished(context, iaf);
+
+    return code;
+}
+
+/*
+ * Save a token for future checksumming.
+ */
+static krb5_error_code
+iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token)
+{
+    char *p;
+
+    p = realloc(ctx->conv.data, ctx->conv.length + token->length);
+    if (p == NULL)
+        return ENOMEM;
+
+    memcpy(p + ctx->conv.length, token->value, token->length);
+    ctx->conv.data = p;
+    ctx->conv.length += token->length;
+
+    return 0;
+}
+
+/*
+ * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP
+ */
+static krb5_error_code
+iakerb_parse_token(iakerb_ctx_id_t ctx,
+                   int initialContextToken,
+                   const gss_buffer_t token,
+                   krb5_data *realm,
+                   krb5_data **cookie,
+                   krb5_data *request)
+{
+    krb5_error_code code;
+    krb5_iakerb_header *iah = NULL;
+    unsigned int bodysize, lenlen;
+    int length;
+    unsigned char *ptr;
+    int flags = 0;
+    krb5_data data;
+
+    if (token == GSS_C_NO_BUFFER || token->length == 0) {
+        code = KRB5_BAD_MSIZE;
+        goto cleanup;
+    }
+
+    if (initialContextToken)
+        flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED;
+
+    ptr = token->value;
+
+    code = g_verify_token_header(gss_mech_iakerb,
+                                 &bodysize, &ptr,
+                                 IAKERB_TOK_PROXY,
+                                 token->length, flags);
+    if (code != 0)
+        goto cleanup;
+
+    data.data = (char *)ptr;
+
+    if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) {
+        code = ASN1_BAD_ID;
+        goto cleanup;
+    }
+
+    length = gssint_get_der_length(&ptr, bodysize, &lenlen);
+    if (length < 0 || bodysize - lenlen < (unsigned int)length) {
+        code = KRB5_BAD_MSIZE;
+        goto cleanup;
+    }
+    data.length = 1 /* SEQUENCE */ + lenlen + length;
+
+    ptr += length;
+    bodysize -= (lenlen + length);
+
+    code = decode_krb5_iakerb_header(&data, &iah);
+    if (code != 0)
+        goto cleanup;
+
+    if (realm != NULL) {
+        *realm = iah->target_realm;
+        iah->target_realm.data = NULL;
+    }
+
+    if (cookie != NULL) {
+        *cookie = iah->cookie;
+        iah->cookie = NULL;
+    }
+
+    request->data = (char *)ptr;
+    request->length = bodysize;
+
+    assert(request->data + request->length ==
+           (char *)token->value + token->length);
+
+cleanup:
+    krb5_free_iakerb_header(ctx->k5c, iah);
+
+    return code;
+}
+
+/*
+ * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP
+ */
+static krb5_error_code
+iakerb_make_token(iakerb_ctx_id_t ctx,
+                  krb5_data *realm,
+                  krb5_data *cookie,
+                  krb5_data *request,
+                  int initialContextToken,
+                  gss_buffer_t token)
+{
+    krb5_error_code code;
+    krb5_iakerb_header iah;
+    krb5_data *data = NULL;
+    char *p;
+    unsigned int tokenSize;
+    unsigned char *q;
+
+    token->value = NULL;
+    token->length = 0;
+
+    /*
+     * Assemble the IAKERB-HEADER from the realm and cookie
+     */
+    memset(&iah, 0, sizeof(iah));
+    iah.target_realm = *realm;
+    iah.cookie = cookie;
+
+    code = encode_krb5_iakerb_header(&iah, &data);
+    if (code != 0)
+        goto cleanup;
+
+    /*
+     * Concatenate Kerberos request.
+     */
+    p = realloc(data->data, data->length + request->length);
+    if (p == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    data->data = p;
+
+    memcpy(data->data + data->length, request->data, request->length);
+    data->length += request->length;
+
+    if (initialContextToken)
+        tokenSize = g_token_size(gss_mech_iakerb, data->length);
+    else
+        tokenSize = 2 + data->length;
+
+    token->value = q = k5alloc(tokenSize, &code);
+    if (code != 0)
+        goto cleanup;
+    token->length = tokenSize;
+
+    if (initialContextToken) {
+        g_make_token_header(gss_mech_iakerb, data->length, &q,
+                            IAKERB_TOK_PROXY);
+    } else {
+        store_16_be(IAKERB_TOK_PROXY, q);
+        q += 2;
+    }
+    memcpy(q, data->data, data->length);
+    q += data->length;
+
+    assert(q == (unsigned char *)token->value + token->length);
+
+cleanup:
+    krb5_free_data(ctx->k5c, data);
+
+    return code;
+}
+
+/*
+ * Parse the IAKERB token in input_token and send the contained KDC
+ * request to the KDC for the realm.
+ *
+ * Wrap the KDC reply in output_token.
+ */
+static krb5_error_code
+iakerb_acceptor_step(iakerb_ctx_id_t ctx,
+                     int initialContextToken,
+                     const gss_buffer_t input_token,
+                     gss_buffer_t output_token)
+{
+    krb5_error_code code;
+    krb5_data request, reply, realm;
+    OM_uint32 tmp;
+    int tcpOnly = 0, useMaster;
+
+    output_token->length = 0;
+    output_token->value = NULL;
+
+    request.data = NULL;
+    request.length = 0;
+    reply.data = NULL;
+    reply.length = 0;
+    realm.data = NULL;
+    realm.length = 0;
+
+    if (ctx->count >= IAKERB_MAX_HOPS) {
+        code = KRB5_KDC_UNREACH;
+        goto cleanup;
+    }
+
+    code = iakerb_parse_token(ctx,
+                              initialContextToken,
+                              input_token,
+                              &realm,
+                              NULL,
+                              &request);
+    if (code != 0)
+        goto cleanup;
+
+    if (realm.length == 0 || request.length == 0) {
+        code = KRB5_BAD_MSIZE;
+        goto cleanup;
+    }
+
+    code = iakerb_save_token(ctx, input_token);
+    if (code != 0)
+        goto cleanup;
+
+send_again:
+    useMaster = 0;
+    code = krb5_sendto_kdc(ctx->k5c, &request, &realm,
+                           &reply, &useMaster, tcpOnly);
+    if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) {
+        krb5_error error;
+
+        memset(&error, 0, sizeof(error));
+        if (code == KRB5_KDC_UNREACH)
+            error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE;
+        else if (code == KRB5_REALM_UNKNOWN)
+            error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;
+
+        code = krb5_mk_error(ctx->k5c, &error, &reply);
+        if (code != 0)
+            goto cleanup;
+    } else if (code == 0 && krb5_is_krb_error(&reply)) {
+        krb5_error *error;
+
+        code = decode_krb5_error(&reply, &error);
+        if (code != 0)
+            goto cleanup;
+
+        if (error && error->error == KRB_ERR_RESPONSE_TOO_BIG &&
+            tcpOnly == 0) {
+            tcpOnly = 1;
+            krb5_free_error(ctx->k5c, error);
+            krb5_free_data_contents(ctx->k5c, &reply);
+            goto send_again;
+        }
+    } else if (code != 0)
+        goto cleanup;
+
+    code = iakerb_make_token(ctx, &realm, NULL, &reply,
+                             0, output_token);
+    if (code != 0)
+        goto cleanup;
+
+    code = iakerb_save_token(ctx, output_token);
+    if (code != 0)
+        goto cleanup;
+
+    ctx->count++;
+
+cleanup:
+    if (code != 0)
+        gss_release_buffer(&tmp, output_token);
+    /* request is a pointer into input_token, no need to free */
+    krb5_free_data_contents(ctx->k5c, &realm);
+    krb5_free_data_contents(ctx->k5c, &reply);
+
+    return code;
+}
+
+/*
+ * Initialise the krb5_init_creds context for the IAKERB context
+ */
+static krb5_error_code
+iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
+                      krb5_gss_cred_id_t cred,
+                      OM_uint32 time_req)
+{
+    krb5_error_code code;
+
+    if (cred->iakerb_mech == 0 || cred->password.data == NULL) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    assert(cred->name != NULL);
+    assert(cred->name->princ != NULL);
+
+    code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts);
+    if (code != 0)
+        goto cleanup;
+
+    if (time_req != 0 && time_req != GSS_C_INDEFINITE)
+        krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req);
+
+    code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts,
+                                                  cred->ccache);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_init_creds_init(ctx->k5c,
+                                cred->name->princ,
+                                NULL,   /* prompter */
+                                NULL,   /* data */
+                                0,      /* start_time */
+                                ctx->gic_opts,
+                                &ctx->u.icc);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_init_creds_set_password(ctx->k5c,
+                                        ctx->u.icc,
+                                        cred->password.data);
+    if (code != 0)
+        goto cleanup;
+
+cleanup:
+    return code;
+}
+
+/*
+ * Initialise the krb5_tkt_creds context for the IAKERB context
+ */
+static krb5_error_code
+iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx,
+                     krb5_gss_cred_id_t cred,
+                     krb5_gss_name_t name,
+                     OM_uint32 time_req)
+
+{
+    krb5_error_code code;
+    krb5_creds creds;
+    krb5_timestamp now;
+
+    assert(cred->name != NULL);
+    assert(cred->name->princ != NULL);
+
+    memset(&creds, 0, sizeof(creds));
+
+    creds.client = cred->name->princ;
+    creds.server = name->princ;
+
+    if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
+        code = krb5_timeofday(ctx->k5c, &now);
+        if (code != 0)
+            goto cleanup;
+
+        creds.times.endtime = now + time_req;
+    }
+
+    if (cred->name->ad_context != NULL) {
+        code = krb5_authdata_export_authdata(ctx->k5c,
+                                             cred->name->ad_context,
+                                             AD_USAGE_TGS_REQ,
+                                             &creds.authdata);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    code = krb5_tkt_creds_init(ctx->k5c, cred->ccache,
+                               &creds, 0, &ctx->u.tcc);
+    if (code != 0)
+        goto cleanup;
+
+cleanup:
+    krb5_free_authdata(ctx->k5c, creds.authdata);
+
+    return code;
+}
+
+/*
+ * Parse the IAKERB token in input_token and process the KDC
+ * response.
+ *
+ * Emit the next KDC request, if any, in output_token.
+ */
+static krb5_error_code
+iakerb_initiator_step(iakerb_ctx_id_t ctx,
+                      krb5_gss_cred_id_t cred,
+                      krb5_gss_name_t name,
+                      OM_uint32 time_req,
+                      const gss_buffer_t input_token,
+                      gss_buffer_t output_token)
+{
+    krb5_error_code code;
+    krb5_data in, out, realm, *cookie = NULL;
+    OM_uint32 tmp;
+    int initialContextToken = (input_token == GSS_C_NO_BUFFER);
+    unsigned int flags = 0;
+    krb5_ticket_times times;
+
+    output_token->length = 0;
+    output_token->value = NULL;
+
+    in.data = NULL;
+    in.length = 0;
+    out.data = NULL;
+    out.length = 0;
+    realm.data = NULL;
+    realm.length = 0;
+
+    if (initialContextToken) {
+        in.data = NULL;
+        in.length = 0;
+    } else {
+        code = iakerb_parse_token(ctx,
+                                  0,
+                                  input_token,
+                                  NULL,
+                                  &cookie,
+                                  &in);
+        if (code != 0)
+            goto cleanup;
+
+        code = iakerb_save_token(ctx, input_token);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    switch (ctx->state) {
+    case IAKERB_AS_REQ:
+        if (ctx->u.icc == NULL) {
+            code = iakerb_init_creds_ctx(ctx, cred, time_req);
+            if (code != 0)
+                goto cleanup;
+        }
+
+        code = krb5_init_creds_step(ctx->k5c,
+                                    ctx->u.icc,
+                                    &in,
+                                    &out,
+                                    &realm,
+                                    &flags);
+        if (code != 0)
+            goto cleanup;
+        if (flags != 0) {
+            krb5_init_creds_get_times(ctx->k5c, ctx->u.icc, &times);
+            cred->tgt_expire = times.endtime;
+
+            krb5_init_creds_free(ctx->k5c, ctx->u.icc);
+            ctx->u.icc = NULL;
+
+            ctx->state = IAKERB_TGS_REQ;
+        } else
+            break;
+    case IAKERB_TGS_REQ:
+        if (ctx->u.tcc == NULL) {
+            code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req);
+            if (code != 0)
+                goto cleanup;
+        }
+
+        code = krb5_tkt_creds_step(ctx->k5c,
+                                   ctx->u.tcc,
+                                   flags ? NULL : &in,
+                                   &out,
+                                   &realm,
+                                   &flags);
+        if (code != 0) {
+            /* we failed, but store any referrals in our ccache */
+            krb5_tkt_creds_store_creds(ctx->k5c, ctx->u.tcc, NULL);
+            goto cleanup;
+        }
+        if (flags != 0) {
+            code = krb5_tkt_creds_store_creds(ctx->k5c, ctx->u.tcc, NULL);
+            if (code != 0)
+                goto cleanup;
+
+            krb5_tkt_creds_get_times(ctx->k5c, ctx->u.tcc, &times);
+            cred->tgt_expire = times.endtime;
+
+            krb5_tkt_creds_free(ctx->k5c, ctx->u.tcc);
+            ctx->u.tcc = NULL;
+
+            ctx->state = IAKERB_AP_REQ;
+        } else
+            break;
+    case IAKERB_AP_REQ:
+        break;
+    }
+
+    if (out.length != 0) {
+        assert(ctx->state != IAKERB_AP_REQ);
+
+        code = iakerb_make_token(ctx, &realm, cookie, &out,
+                                 (input_token == GSS_C_NO_BUFFER),
+                                 output_token);
+        if (code != 0)
+            goto cleanup;
+
+        /* Save the token for generating a future checksum */
+        code = iakerb_save_token(ctx, output_token);
+        if (code != 0)
+            goto cleanup;
+
+        ctx->count++;
+    }
+
+cleanup:
+    if (code != 0)
+        gss_release_buffer(&tmp, output_token);
+    krb5_free_data(ctx->k5c, cookie);
+    krb5_free_data_contents(ctx->k5c, &out);
+    krb5_free_data_contents(ctx->k5c, &realm);
+
+    return code;
+}
+
+/*
+ * Determine the starting IAKERB state for a context. If we already
+ * have a ticket, we may not need to do IAKERB at all.
+ */
+static krb5_error_code
+iakerb_get_initial_state(iakerb_ctx_id_t ctx,
+                         krb5_gss_cred_id_t cred,
+                         krb5_gss_name_t target,
+                         OM_uint32 time_req,
+                         enum iakerb_state *state)
+{
+    krb5_creds in_creds, *out_creds = NULL;
+    krb5_error_code code;
+
+    if (cred == NULL || cred->iakerb_mech == 0) {
+        *state = IAKERB_AP_REQ;
+        return 0;
+    }
+
+    memset(&in_creds, 0, sizeof(in_creds));
+
+    in_creds.client = cred->name->princ;
+    in_creds.server = target->princ;
+
+    if (cred->name->ad_context != NULL) {
+        code = krb5_authdata_export_authdata(ctx->k5c,
+                                             cred->name->ad_context,
+                                             AD_USAGE_TGS_REQ,
+                                             &in_creds.authdata);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
+        krb5_timestamp now;
+
+        code = krb5_timeofday(ctx->k5c, &now);
+        if (code != 0)
+            goto cleanup;
+
+        in_creds.times.endtime = now + time_req;
+    }
+
+    code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
+                                cred->ccache,
+                                &in_creds, &out_creds);
+    if (code == KRB5_CC_NOTFOUND) {
+        krb5_principal tgs;
+        krb5_data *realm = krb5_princ_realm(ctx->k5c, in_creds.client);
+
+        /* If we have a TGT for the client realm, can proceed to TGS-REQ. */
+        code = krb5_build_principal_ext(ctx->k5c,
+                                        &tgs,
+                                        realm->length,
+                                        realm->data,
+                                        KRB5_TGS_NAME_SIZE,
+                                        KRB5_TGS_NAME,
+                                        realm->length,
+                                        realm->data,
+                                        NULL);
+        if (code != 0)
+            goto cleanup;
+
+        in_creds.server = tgs;
+
+        code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
+                                    cred->ccache,
+                                    &in_creds, &out_creds);
+        if (code == KRB5_CC_NOTFOUND) {
+            *state = IAKERB_AS_REQ;
+            code = 0;
+        } else if (code == 0) {
+            *state = IAKERB_TGS_REQ;
+            krb5_free_creds(ctx->k5c, out_creds);
+        }
+        krb5_free_principal(ctx->k5c, tgs);
+    } else if (code == 0) {
+        *state = IAKERB_AP_REQ;
+        krb5_free_creds(ctx->k5c, out_creds);
+    }
+
+cleanup:
+    krb5_free_authdata(ctx->k5c, in_creds.authdata);
+
+    return code;
+}
+
+/*
+ * Allocate and initialise an IAKERB context
+ */
+static krb5_error_code
+iakerb_alloc_context(iakerb_ctx_id_t *pctx)
+{
+    iakerb_ctx_id_t ctx;
+    krb5_error_code code;
+
+    *pctx = NULL;
+
+    ctx = k5alloc(sizeof(*ctx), &code);
+    if (ctx == NULL)
+        goto cleanup;
+    ctx->magic = KG_IAKERB_CONTEXT;
+    ctx->state = IAKERB_AS_REQ;
+    ctx->count = 0;
+
+    code = krb5_gss_init_context(&ctx->k5c);
+    if (code != 0)
+        goto cleanup;
+
+    *pctx = ctx;
+
+cleanup:
+    if (code != 0)
+        iakerb_release_context(ctx);
+
+    return code;
+}
+
+/*
+ * Delete an IAKERB context. This can also accept Kerberos context
+ * handles. The heuristic is similar to SPNEGO's delete_sec_context.
+ */
+OM_uint32
+iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
+                              gss_ctx_id_t *context_handle,
+                              gss_buffer_t output_token)
+{
+    OM_uint32 major_status = GSS_S_COMPLETE;
+
+    if (output_token != GSS_C_NO_BUFFER) {
+        output_token->length = 0;
+        output_token->value = NULL;
+    }
+
+    *minor_status = 0;
+
+    if (*context_handle != GSS_C_NO_CONTEXT) {
+        iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle;
+
+        if (iakerb_ctx->magic == KG_IAKERB_CONTEXT) {
+            iakerb_release_context(iakerb_ctx);
+            *context_handle = GSS_C_NO_CONTEXT;
+        } else {
+            assert(iakerb_ctx->magic == KG_CONTEXT);
+
+            major_status = krb5_gss_delete_sec_context(minor_status,
+                                                       context_handle,
+                                                       output_token);
+        }
+    }
+
+    return major_status;
+}
+
+static krb5_boolean
+iakerb_is_iakerb_token(const gss_buffer_t token)
+{
+    krb5_error_code code;
+    unsigned int bodysize = token->length;
+    unsigned char *ptr = token->value;
+
+    code = g_verify_token_header(gss_mech_iakerb,
+                                 &bodysize, &ptr,
+                                 IAKERB_TOK_PROXY,
+                                 token->length, 0);
+
+    return (code == 0);
+}
+
+static void
+iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts)
+{
+    memset(exts, 0, sizeof(*exts));
+
+    if (ctx->conv.length != 0)
+        exts->iakerb.conv = &ctx->conv;
+}
+
+/*
+ *
+ */
+OM_uint32
+iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
+                              gss_ctx_id_t *context_handle,
+                              gss_cred_id_t verifier_cred_handle,
+                              gss_buffer_t input_token,
+                              gss_channel_bindings_t input_chan_bindings,
+                              gss_name_t *src_name,
+                              gss_OID *mech_type,
+                              gss_buffer_t output_token,
+                              OM_uint32 *ret_flags,
+                              OM_uint32 *time_rec,
+                              gss_cred_id_t *delegated_cred_handle)
+{
+    OM_uint32 major_status = GSS_S_FAILURE;
+    OM_uint32 code;
+    iakerb_ctx_id_t ctx;
+    int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
+
+    if (initialContextToken) {
+        code = iakerb_alloc_context(&ctx);
+        if (code != 0)
+            goto cleanup;
+
+    } else
+        ctx = (iakerb_ctx_id_t)*context_handle;
+
+    if (iakerb_is_iakerb_token(input_token)) {
+        if (ctx->u.gssc != GSS_C_NO_CONTEXT) {
+            /* We shouldn't get an IAKERB token now. */
+            code = G_WRONG_TOKID;
+            major_status = GSS_S_DEFECTIVE_TOKEN;
+            goto cleanup;
+        }
+        code = iakerb_acceptor_step(ctx, initialContextToken,
+                                    input_token, output_token);
+        if (code == (OM_uint32)KRB5_BAD_MSIZE)
+            major_status = GSS_S_DEFECTIVE_TOKEN;
+        if (code != 0)
+            goto cleanup;
+        if (initialContextToken) {
+            *context_handle = (gss_ctx_id_t)ctx;
+            ctx = NULL;
+        }
+        if (src_name != NULL)
+            *src_name = GSS_C_NO_NAME;
+        if (mech_type != NULL)
+            *mech_type = (gss_OID)gss_mech_iakerb;
+        if (ret_flags != NULL)
+            *ret_flags = 0;
+        if (time_rec != NULL)
+            *time_rec = 0;
+        if (delegated_cred_handle != NULL)
+            *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+        major_status = GSS_S_CONTINUE_NEEDED;
+    } else {
+        krb5_gss_ctx_ext_rec exts;
+
+        iakerb_make_exts(ctx, &exts);
+
+        major_status = krb5_gss_accept_sec_context_ext(&code,
+                                                       &ctx->u.gssc,
+                                                       verifier_cred_handle,
+                                                       input_token,
+                                                       input_chan_bindings,
+                                                       src_name,
+                                                       mech_type,
+                                                       output_token,
+                                                       ret_flags,
+                                                       time_rec,
+                                                       delegated_cred_handle,
+                                                       &exts);
+        if (major_status == GSS_S_COMPLETE) {
+            *context_handle = ctx->u.gssc;
+            ctx->u.gssc = NULL;
+            iakerb_release_context(ctx);
+        }
+    }
+
+cleanup:
+    if (initialContextToken && GSS_ERROR(major_status)) {
+        iakerb_release_context(ctx);
+        *context_handle = GSS_C_NO_CONTEXT;
+    }
+
+    *minor_status = code;
+    return major_status;
+}
+
+OM_uint32
+iakerb_gss_init_sec_context(OM_uint32 *minor_status,
+                            gss_cred_id_t claimant_cred_handle,
+                            gss_ctx_id_t *context_handle,
+                            gss_name_t target_name,
+                            gss_OID mech_type,
+                            OM_uint32 req_flags,
+                            OM_uint32 time_req,
+                            gss_channel_bindings_t input_chan_bindings,
+                            gss_buffer_t input_token,
+                            gss_OID *actual_mech_type,
+                            gss_buffer_t output_token,
+                            OM_uint32 *ret_flags,
+                            OM_uint32 *time_rec)
+{
+    OM_uint32 major_status = GSS_S_FAILURE;
+    krb5_error_code code;
+    iakerb_ctx_id_t ctx;
+    krb5_gss_cred_id_t kcred;
+    krb5_gss_name_t kname;
+    int credLocked = 0;
+    int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
+
+    if (initialContextToken) {
+        code = iakerb_alloc_context(&ctx);
+        if (code != 0) {
+            *minor_status = code;
+            goto cleanup;
+        }
+    } else
+        ctx = (iakerb_ctx_id_t)*context_handle;
+
+    if (!kg_validate_name(target_name)) {
+        *minor_status = G_VALIDATE_FAILED;
+        major_status = GSS_S_CALL_BAD_STRUCTURE | GSS_S_BAD_NAME;
+        goto cleanup;
+    }
+
+    kname = (krb5_gss_name_t)target_name;
+
+    if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) {
+        major_status = krb5_gss_validate_cred_1(minor_status,
+                                                claimant_cred_handle,
+                                                ctx->k5c);
+        if (GSS_ERROR(major_status))
+            goto cleanup;
+
+        credLocked = 1;
+        kcred = (krb5_gss_cred_id_t)claimant_cred_handle;
+    } else
+        kcred = NULL;
+
+    major_status = GSS_S_FAILURE;
+
+    if (initialContextToken) {
+        code = iakerb_get_initial_state(ctx, kcred, kname, time_req,
+                                        &ctx->state);
+        if (code != 0) {
+            *minor_status = code;
+            goto cleanup;
+        }
+        *context_handle = (gss_ctx_id_t)ctx;
+    }
+
+    if (ctx->state != IAKERB_AP_REQ) {
+        /* We need to do IAKERB. */
+        code = iakerb_initiator_step(ctx,
+                                     kcred,
+                                     kname,
+                                     time_req,
+                                     input_token,
+                                     output_token);
+        if (code == KRB5_BAD_MSIZE)
+            major_status = GSS_S_DEFECTIVE_TOKEN;
+        if (code != 0) {
+            *minor_status = code;
+            goto cleanup;
+        }
+    }
+
+    if (ctx->state == IAKERB_AP_REQ) {
+        krb5_gss_ctx_ext_rec exts;
+
+        /* Ensure cred is marked as usable for Kerberos mechanism */
+        if (kcred != NULL) {
+            if (kcred->iakerb_mech)
+                kcred->rfc_mech = 1;
+
+            k5_mutex_unlock(&kcred->lock);
+            credLocked = 0;
+        }
+
+        iakerb_make_exts(ctx, &exts);
+
+        if (ctx->u.gssc == GSS_C_NO_CONTEXT)
+            input_token = GSS_C_NO_BUFFER;
+
+        /* IAKERB is finished, or we skipped to Kerberos directly. */
+        major_status = krb5_gss_init_sec_context_ext(minor_status,
+                                                     claimant_cred_handle,
+                                                     &ctx->u.gssc,
+                                                     target_name,
+                                                     GSS_C_NULL_OID,
+                                                     req_flags,
+                                                     time_req,
+                                                     input_chan_bindings,
+                                                     input_token,
+                                                     actual_mech_type,
+                                                     output_token,
+                                                     ret_flags,
+                                                     time_rec,
+                                                     &exts);
+        if (major_status == GSS_S_COMPLETE) {
+            *context_handle = ctx->u.gssc;
+            ctx->u.gssc = GSS_C_NO_CONTEXT;
+            iakerb_release_context(ctx);
+        }
+    } else {
+        if (actual_mech_type != NULL)
+            *actual_mech_type = (gss_OID)gss_mech_iakerb;
+        if (ret_flags != NULL)
+            *ret_flags = 0;
+        if (time_rec != NULL)
+            *time_rec = 0;
+        major_status = GSS_S_CONTINUE_NEEDED;
+    }
+
+cleanup:
+    if (credLocked)
+        k5_mutex_unlock(&kcred->lock);
+    if (initialContextToken && GSS_ERROR(major_status)) {
+        iakerb_release_context(ctx);
+        *context_handle = GSS_C_NO_CONTEXT;
+    }
+
+    return major_status;
+}
+

Modified: branches/iakerb/src/lib/gssapi/krb5/init_sec_context.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/init_sec_context.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/init_sec_context.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -195,8 +195,36 @@
             goto cleanup;
     }
 
+    /* Don't go out over the network if we used IAKERB */
+    if (cred->iakerb_mech)
+        flags |= KRB5_GC_CACHED;
+
     code = krb5_get_credentials(context, flags, cred->ccache,
                                 &in_creds, out_creds);
+    if (code == KRB5_NO_TKT_IN_RLM && cred->password.data != NULL) {
+        krb5_creds tgt_creds;
+
+        memset(&tgt_creds, 0, sizeof(tgt_creds));
+
+        code = krb5_get_init_creds_password(context, &tgt_creds,
+                                            in_creds.client,
+                                            cred->password.data,
+                                            NULL, NULL,
+                                            0, NULL, NULL);
+        if (code)
+            goto cleanup;
+
+        code = krb5_cc_store_cred(context, cred->ccache, &tgt_creds);
+        if (code) {
+            krb5_free_cred_contents(context, &tgt_creds);
+            goto cleanup;
+        }
+        cred->tgt_expire = tgt_creds.times.endtime;
+        krb5_free_cred_contents(context, &tgt_creds);
+
+        code = krb5_get_credentials(context, flags, cred->ccache,
+                                    &in_creds, out_creds);
+    }
     if (code)
         goto cleanup;
 
@@ -232,11 +260,9 @@
     krb5_gss_cred_id_t cred;
     krb5_checksum md5;
     krb5_data checksum_data;
+    krb5_gss_ctx_ext_t exts;
 };
 
-#ifdef CFX_EXERCISE
-#include "../../krb5/krb/auth_con.h"
-#endif
 static krb5_error_code KRB5_CALLCONV
 make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
                    void *cksum_data, krb5_data **out)
@@ -247,6 +273,7 @@
     struct gss_checksum_data *data = cksum_data;
     krb5_data credmsg;
     unsigned int junk;
+    krb5_data *finished = NULL;
 
     data->checksum_data.data = 0;
     credmsg.data = 0;
@@ -279,8 +306,8 @@
             data->checksum_data.length = 24;
         } else {
             if (credmsg.length+28 > KRB5_INT16_MAX) {
-                krb5_free_data_contents(context, &credmsg);
-                return(KRB5KRB_ERR_FIELD_TOOLONG);
+                code = KRB5KRB_ERR_FIELD_TOOLONG;
+                goto cleanup;
             }
 
             data->checksum_data.length = 28+credmsg.length;
@@ -302,6 +329,26 @@
     junk = 0;
 #endif
 
+    assert(data->exts != NULL);
+
+    if (data->exts->iakerb.conv) {
+        krb5_key key;
+
+        code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
+        if (code != 0)
+            goto cleanup;
+
+        code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
+                                    &finished);
+        if (code != 0) {
+            krb5_k_free_key(context, key);
+            goto cleanup;
+        }
+
+        krb5_k_free_key(context, key);
+        data->checksum_data.length += 8 + finished->length;
+    }
+
     data->checksum_data.length += junk;
 
     /* now allocate a buffer to hold the checksum data and
@@ -309,9 +356,8 @@
 
     if ((data->checksum_data.data =
          (char *) xmalloc(data->checksum_data.length)) == NULL) {
-        if (credmsg.data)
-            krb5_free_data_contents(context, &credmsg);
-        return(ENOMEM);
+        code = ENOMEM;
+        goto cleanup;
     }
 
     ptr = (unsigned char *)data->checksum_data.data;
@@ -327,19 +373,25 @@
         TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
         TWRITE_INT16(ptr, credmsg.length, 0);
         TWRITE_STR(ptr, credmsg.data, credmsg.length);
-
-        /* free credmsg data */
-        krb5_free_data_contents(context, &credmsg);
     }
+    if (data->exts->iakerb.conv) {
+        TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1);
+        TWRITE_INT(ptr, finished->length, 1);
+        TWRITE_STR(ptr, finished->data, finished->length);
+    }
     if (junk)
         memset(ptr, 'i', junk);
     *out = &data->checksum_data;
-    return 0;
+    code = 0;
+cleanup:
+    krb5_free_data_contents(context, &credmsg);
+    krb5_free_data(context, finished);
+    return code;
 }
 
 static krb5_error_code
 make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
-               chan_bindings, mech_type, token)
+               chan_bindings, mech_type, token, exts)
     krb5_context context;
     krb5_gss_ctx_id_rec *ctx;
     krb5_gss_cred_id_t cred;
@@ -348,6 +400,7 @@
     gss_channel_bindings_t chan_bindings;
     gss_OID mech_type;
     gss_buffer_t token;
+    krb5_gss_ctx_ext_t exts;
 {
     krb5_flags mk_req_flags = 0;
     krb5_error_code code;
@@ -373,6 +426,7 @@
     cksum_struct.ctx = ctx;
     cksum_struct.cred = cred;
     cksum_struct.checksum_data.data = NULL;
+    cksum_struct.exts = exts;
     switch (k_cred->keyblock.enctype) {
     case ENCTYPE_DES_CBC_CRC:
     case ENCTYPE_DES_CBC_MD4:
@@ -474,7 +528,8 @@
     OM_uint32 *ret_flags,
     OM_uint32 *time_rec,
     krb5_context context,
-    int default_mech)
+    int default_mech,
+    krb5_gss_ctx_ext_t exts)
 {
     OM_uint32 major_status;
     krb5_error_code code;
@@ -514,6 +569,7 @@
 
     /* fill in the ctx */
     memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+    ctx->magic = KG_CONTEXT;
     ctx_free = ctx;
     if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
         goto fail;
@@ -592,7 +648,7 @@
         if ((code = make_ap_req_v1(context, ctx,
                                    cred, k_cred, ctx->here->ad_context,
                                    input_chan_bindings,
-                                   mech_type, &token))) {
+                                   mech_type, &token, exts))) {
             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
                 (code == KG_EMPTY_CCACHE))
                 major_status = GSS_S_NO_CRED;
@@ -703,7 +759,8 @@
     gss_buffer_t output_token,
     OM_uint32 *ret_flags,
     OM_uint32 *time_rec,
-    krb5_context context)
+    krb5_context context,
+    krb5_gss_ctx_ext_t exts)
 {
     OM_uint32 major_status;
     unsigned char *ptr;
@@ -878,24 +935,21 @@
 }
 
 OM_uint32
-krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
-                          context_handle, target_name, mech_type,
-                          req_flags, time_req, input_chan_bindings,
-                          input_token, actual_mech_type, output_token,
-                          ret_flags, time_rec)
-    OM_uint32 *minor_status;
-    gss_cred_id_t claimant_cred_handle;
-    gss_ctx_id_t *context_handle;
-    gss_name_t target_name;
-    gss_OID mech_type;
-    OM_uint32 req_flags;
-    OM_uint32 time_req;
-    gss_channel_bindings_t input_chan_bindings;
-    gss_buffer_t input_token;
-    gss_OID *actual_mech_type;
-    gss_buffer_t output_token;
-    OM_uint32 *ret_flags;
-    OM_uint32 *time_rec;
+krb5_gss_init_sec_context_ext(
+    OM_uint32 *minor_status,
+    gss_cred_id_t claimant_cred_handle,
+    gss_ctx_id_t *context_handle,
+    gss_name_t target_name,
+    gss_OID mech_type,
+    OM_uint32 req_flags,
+    OM_uint32 time_req,
+    gss_channel_bindings_t input_chan_bindings,
+    gss_buffer_t input_token,
+    gss_OID *actual_mech_type,
+    gss_buffer_t output_token,
+    OM_uint32 *ret_flags,
+    OM_uint32 *time_rec,
+    krb5_gss_ctx_ext_t exts)
 {
     krb5_context context;
     krb5_gss_cred_id_t cred;
@@ -1013,7 +1067,7 @@
                                          time_req, input_chan_bindings,
                                          input_token, actual_mech_type,
                                          output_token, ret_flags, time_rec,
-                                         context, default_mech);
+                                         context, default_mech, exts);
         k5_mutex_unlock(&cred->lock);
         if (*context_handle == GSS_C_NO_CONTEXT) {
             save_error_info (*minor_status, context);
@@ -1028,7 +1082,7 @@
                                    time_req, input_chan_bindings,
                                    input_token, actual_mech_type,
                                    output_token, ret_flags, time_rec,
-                                   context);
+                                   context, exts);
         /* If context_handle is now NO_CONTEXT, mutual_auth called
            delete_sec_context, which would've zapped the krb5 context
            too.  */
@@ -1093,3 +1147,44 @@
     return GSS_S_COMPLETE;
 }
 #endif
+
+OM_uint32
+krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
+                          context_handle, target_name, mech_type,
+                          req_flags, time_req, input_chan_bindings,
+                          input_token, actual_mech_type, output_token,
+                          ret_flags, time_rec)
+    OM_uint32 *minor_status;
+    gss_cred_id_t claimant_cred_handle;
+    gss_ctx_id_t *context_handle;
+    gss_name_t target_name;
+    gss_OID mech_type;
+    OM_uint32 req_flags;
+    OM_uint32 time_req;
+    gss_channel_bindings_t input_chan_bindings;
+    gss_buffer_t input_token;
+    gss_OID *actual_mech_type;
+    gss_buffer_t output_token;
+    OM_uint32 *ret_flags;
+    OM_uint32 *time_rec;
+{
+    krb5_gss_ctx_ext_rec exts;
+
+    memset(&exts, 0, sizeof(exts));
+
+    return krb5_gss_init_sec_context_ext(minor_status,
+                                         claimant_cred_handle,
+                                         context_handle,
+                                         target_name,
+                                         mech_type,
+                                         req_flags,
+                                         time_req,
+                                         input_chan_bindings,
+                                         input_token,
+                                         actual_mech_type,
+                                         output_token,
+                                         ret_flags,
+                                         time_rec,
+                                         &exts);
+}
+

Modified: branches/iakerb/src/lib/gssapi/krb5/inq_cred.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/inq_cred.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/inq_cred.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -166,6 +166,10 @@
             (cred->rfc_mech &&
              GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
                                                             gss_mech_krb5,
+                                                            &mechs))) ||
+            (cred->iakerb_mech &&
+             GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                            gss_mech_iakerb,
                                                             &mechs)))) {
             k5_mutex_unlock(&cred->lock);
             if (ret_name)

Modified: branches/iakerb/src/lib/gssapi/krb5/inq_names.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/inq_names.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/inq_names.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -44,7 +44,8 @@
      */
     if ((mechanism != GSS_C_NULL_OID) &&
         !g_OID_equal(gss_mech_krb5, mechanism) &&
-        !g_OID_equal(gss_mech_krb5_old, mechanism)) {
+        !g_OID_equal(gss_mech_krb5_old, mechanism) &&
+        !g_OID_equal(gss_mech_iakerb, mechanism)) {
         *minor_status = 0;
         return(GSS_S_BAD_MECH);
     }

Modified: branches/iakerb/src/lib/gssapi/krb5/rel_cred.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/rel_cred.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/rel_cred.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -77,6 +77,11 @@
     if (cred->req_enctypes)
         free(cred->req_enctypes);
 
+    if (cred->password.data) {
+        zap(cred->password.data, cred->password.length);
+        krb5_free_data_contents(context, &cred->password);
+    }
+
     xfree(cred);
 
     *cred_handle = NULL;

Modified: branches/iakerb/src/lib/gssapi/krb5/ser_sctx.c
===================================================================
--- branches/iakerb/src/lib/gssapi/krb5/ser_sctx.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/krb5/ser_sctx.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -610,6 +610,7 @@
              xmalloc(sizeof(krb5_gss_ctx_id_rec)))) {
             memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
 
+            ctx->magic = ibuf;
             ctx->k5_context = kcontext;
 
             /* Get static data */

Modified: branches/iakerb/src/lib/gssapi/libgssapi_krb5.exports
===================================================================
--- branches/iakerb/src/lib/gssapi/libgssapi_krb5.exports	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/libgssapi_krb5.exports	2010-03-29 16:18:20 UTC (rev 23843)
@@ -9,6 +9,7 @@
 GSS_KRB5_NT_PRINCIPAL_NAME
 gss_accept_sec_context
 gss_acquire_cred
+gss_acquire_cred_with_password
 gss_acquire_cred_impersonate_name
 gss_add_buffer_set_member
 gss_add_cred
@@ -54,6 +55,7 @@
 gsskrb5_extract_authtime_from_sec_context
 gsskrb5_extract_authz_data_from_sec_context
 gss_map_name_to_any
+gss_mech_iakerb
 gss_mech_krb5
 gss_mech_krb5_old
 gss_mech_set_krb5

Modified: branches/iakerb/src/lib/gssapi/mechglue/Makefile.in
===================================================================
--- branches/iakerb/src/lib/gssapi/mechglue/Makefile.in	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/mechglue/Makefile.in	2010-03-29 16:18:20 UTC (rev 23843)
@@ -12,6 +12,7 @@
 SRCS = \
 	$(srcdir)/g_accept_sec_context.c \
 	$(srcdir)/g_acquire_cred.c \
+	$(srcdir)/g_acquire_cred_with_pw.c \
 	$(srcdir)/g_acquire_cred_imp_name.c \
 	$(srcdir)/g_buffer_set.c \
 	$(srcdir)/g_canon_name.c \
@@ -67,6 +68,7 @@
 OBJS = \
 	$(OUTPRE)g_accept_sec_context.$(OBJEXT) \
 	$(OUTPRE)g_acquire_cred.$(OBJEXT) \
+	$(OUTPRE)g_acquire_cred_with_pw.$(OBJEXT) \
 	$(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \
 	$(OUTPRE)g_buffer_set.$(OBJEXT) \
 	$(OUTPRE)g_canon_name.$(OBJEXT) \
@@ -122,6 +124,7 @@
 STLIBOBJS = \
 	g_accept_sec_context.o \
 	g_acquire_cred.o \
+	g_acquire_cred_with_pw.o \
 	g_acquire_cred_imp_name.o \
 	g_buffer_set.o \
 	g_canon_name.o \

Added: branches/iakerb/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
===================================================================
--- branches/iakerb/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c	                        (rev 0)
+++ branches/iakerb/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -0,0 +1,544 @@
+/* #pragma ident	"@(#)g_acquire_cred.c	1.22	04/02/23 SMI" */
+
+/*
+ * Copyright 1996 by Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Sun Microsystems not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Sun Microsystems makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ *  glue routine for gss_acquire_cred_with_password
+ */
+
+#include "mglueP.h"
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+static OM_uint32
+val_acq_cred_pw_args(
+    OM_uint32 *minor_status,
+    const gss_name_t desired_name,
+    const gss_buffer_t password,
+    OM_uint32 time_req,
+    const gss_OID_set desired_mechs,
+    int cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec)
+{
+
+    /* Initialize outputs. */
+
+    if (minor_status != NULL)
+	*minor_status = 0;
+
+    if (output_cred_handle != NULL)
+	*output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+    if (actual_mechs != NULL)
+	*actual_mechs = GSS_C_NULL_OID_SET;
+
+    if (time_rec != NULL)
+	*time_rec = 0;
+
+    /* Validate arguments. */
+
+    if (minor_status == NULL)
+	return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (output_cred_handle == NULL)
+	return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (cred_usage != GSS_C_ACCEPT
+	&& cred_usage != GSS_C_INITIATE
+	&& cred_usage != GSS_C_BOTH) {
+	if (minor_status) {
+	    *minor_status = EINVAL;
+	    map_errcode(minor_status);
+	}
+	return GSS_S_FAILURE;
+    }
+
+    if (password == GSS_C_NO_BUFFER ||
+        password->length == 0 ||
+        password->value == NULL) {
+	if (minor_status) {
+	    *minor_status = EINVAL;
+	    map_errcode(minor_status);
+	}
+	return GSS_S_FAILURE;
+    }
+
+    return (GSS_S_COMPLETE);
+}
+
+
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_with_password(
+    minor_status,
+    desired_name,
+    password,
+    time_req,
+    desired_mechs,
+    cred_usage,
+    output_cred_handle,
+    actual_mechs,
+    time_rec)
+
+OM_uint32 *		minor_status;
+const gss_name_t	desired_name;
+const gss_buffer_t	password;
+OM_uint32		time_req;
+const gss_OID_set	desired_mechs;
+int			cred_usage;
+gss_cred_id_t *		output_cred_handle;
+gss_OID_set *		actual_mechs;
+OM_uint32 *		time_rec;
+{
+    OM_uint32 major = GSS_S_FAILURE;
+    OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
+    gss_OID_set_desc default_OID_set;
+    gss_OID_set mechs;
+    gss_OID_desc default_OID;
+    gss_mechanism mech;
+    unsigned int i;
+    gss_union_cred_t creds;
+
+    major = val_acq_cred_pw_args(minor_status,
+			         desired_name,
+			         password,
+			         time_req,
+			         desired_mechs,
+			         cred_usage,
+			         output_cred_handle,
+			         actual_mechs,
+			         time_rec);
+    if (major != GSS_S_COMPLETE)
+	return (major);
+
+    /* Initial value needed below. */
+    major = GSS_S_FAILURE;
+
+    /*
+     * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
+     * appropriate default.  We use the first mechanism in the
+     * mechansim list as the default. This set is created with
+     * statics thus needs not be freed
+     */
+    if(desired_mechs == GSS_C_NULL_OID_SET) {
+	mech = gssint_get_mechanism(NULL);
+	if (mech == NULL)
+	    return (GSS_S_BAD_MECH);
+
+	mechs = &default_OID_set;
+	default_OID_set.count = 1;
+	default_OID_set.elements = &default_OID;
+	default_OID.length = mech->mech_type.length;
+	default_OID.elements = mech->mech_type.elements;
+    } else
+	mechs = desired_mechs;
+
+    if (mechs->count == 0)
+	return (GSS_S_BAD_MECH);
+
+    /* allocate the output credential structure */
+    creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
+    if (creds == NULL)
+	return (GSS_S_FAILURE);
+
+    /* initialize to 0s */
+    (void) memset(creds, 0, sizeof (gss_union_cred_desc));
+    creds->loopback = creds;
+
+    /* for each requested mech attempt to obtain a credential */
+    for (i = 0; i < mechs->count; i++) {
+	major = gss_add_cred_with_password(minor_status, (gss_cred_id_t)creds,
+			     desired_name, 
+			     &mechs->elements[i],
+			     password,
+			     cred_usage, time_req, time_req, NULL,
+			     NULL, &initTimeOut, &acceptTimeOut);
+	if (major == GSS_S_COMPLETE) {
+	    /* update the credential's time */
+	    if (cred_usage == GSS_C_ACCEPT) {
+		if (outTime > acceptTimeOut)
+		    outTime = acceptTimeOut;
+	    } else if (cred_usage == GSS_C_INITIATE) {
+		if (outTime > initTimeOut)
+		    outTime = initTimeOut;
+	    } else {
+		/*
+		 * time_rec is the lesser of the
+		 * init/accept times
+		 */
+		if (initTimeOut > acceptTimeOut)
+		    outTime = (outTime > acceptTimeOut) ?
+			acceptTimeOut : outTime;
+		else
+		    outTime = (outTime > initTimeOut) ?
+			initTimeOut : outTime;
+	    }
+	}
+    } /* for */
+
+    /* ensure that we have at least one credential element */
+    if (creds->count < 1) {
+	free(creds);
+	return (major);
+    }
+
+    /*
+     * fill in output parameters
+     * setup the actual mechs output parameter
+     */
+    if (actual_mechs != NULL) {
+	gss_OID_set_desc oids;
+
+	oids.count = creds->count;
+	oids.elements = creds->mechs_array;
+
+	major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+	if (GSS_ERROR(major)) {
+	    (void) gss_release_cred(minor_status,
+				    (gss_cred_id_t *)&creds);
+	    return (major);
+	}
+    }
+
+    if (time_rec)
+	*time_rec = outTime;
+
+
+    creds->loopback = creds;
+    *output_cred_handle = (gss_cred_id_t)creds;
+    return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+val_add_cred_pw_args(
+    OM_uint32 *minor_status,
+    gss_cred_id_t input_cred_handle,
+    const gss_name_t desired_name,
+    const gss_OID desired_mech,
+    const gss_buffer_t password,
+    gss_cred_usage_t cred_usage,
+    OM_uint32 initiator_time_req,
+    OM_uint32 acceptor_time_req,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *initiator_time_rec,
+    OM_uint32 *acceptor_time_rec)
+{
+
+    /* Initialize outputs. */
+
+    if (minor_status != NULL)
+	*minor_status = 0;
+
+    if (output_cred_handle != NULL)
+	*output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+    if (actual_mechs != NULL)
+	*actual_mechs = GSS_C_NO_OID_SET;
+
+    if (acceptor_time_rec != NULL)
+	*acceptor_time_rec = 0;
+
+    if (initiator_time_rec != NULL)
+	*initiator_time_rec = 0;
+
+    /* Validate arguments. */
+
+    if (minor_status == NULL)
+	return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
+	output_cred_handle == NULL)
+	return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
+
+    if (cred_usage != GSS_C_ACCEPT
+	&& cred_usage != GSS_C_INITIATE
+	&& cred_usage != GSS_C_BOTH) {
+	if (minor_status) {
+	    *minor_status = EINVAL;
+	    map_errcode(minor_status);
+	}
+	return GSS_S_FAILURE;
+    }
+
+    if (password == GSS_C_NO_BUFFER ||
+        password->length == 0 ||
+        password->value == NULL) {
+	if (minor_status) {
+	    *minor_status = EINVAL;
+	    map_errcode(minor_status);
+	}
+	return GSS_S_FAILURE;
+    }
+
+
+    return (GSS_S_COMPLETE);
+}
+
+
+/* V2 KRB5_CALLCONV */
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_with_password(minor_status, input_cred_handle,
+		  desired_name, desired_mech, password, cred_usage,
+		  initiator_time_req, acceptor_time_req,
+		  output_cred_handle, actual_mechs,
+		  initiator_time_rec, acceptor_time_rec)
+    OM_uint32		*minor_status;
+    const gss_cred_id_t	input_cred_handle;
+    const gss_name_t	desired_name;
+    const gss_OID	desired_mech;
+    const gss_buffer_t	password;
+    gss_cred_usage_t	cred_usage;
+    OM_uint32		initiator_time_req;
+    OM_uint32		acceptor_time_req;
+    gss_cred_id_t	*output_cred_handle;
+    gss_OID_set		*actual_mechs;
+    OM_uint32		*initiator_time_rec;
+    OM_uint32		*acceptor_time_rec;
+{
+    OM_uint32		status, temp_minor_status;
+    OM_uint32		time_req, time_rec;
+    gss_union_name_t	union_name;
+    gss_union_cred_t	new_union_cred, union_cred;
+    gss_name_t		internal_name = GSS_C_NO_NAME;
+    gss_name_t		allocated_name = GSS_C_NO_NAME;
+    gss_mechanism       mech;
+    gss_mechanism_ext	mech_ext;
+    gss_cred_id_t	cred = NULL;
+    gss_OID		new_mechs_array = NULL;
+    gss_cred_id_t *	new_cred_array = NULL;
+
+    status = val_add_cred_pw_args(minor_status,
+			          input_cred_handle,
+			          desired_name,
+			          desired_mech,
+			          password,
+			          cred_usage,
+			          initiator_time_req,
+			          acceptor_time_req,
+			          output_cred_handle,
+			          actual_mechs,
+			          initiator_time_rec,
+			          acceptor_time_rec);
+    if (status != GSS_S_COMPLETE)
+	return (status);
+
+    mech = gssint_get_mechanism(desired_mech);
+    if (!mech)
+	return GSS_S_BAD_MECH;
+
+    mech_ext = gssint_get_mechanism_ext(desired_mech);
+    if (!mech_ext || !mech_ext->gssspi_acquire_cred_with_password)
+	return GSS_S_UNAVAILABLE;
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
+	union_cred = malloc(sizeof (gss_union_cred_desc));
+	if (union_cred == NULL)
+	    return (GSS_S_FAILURE);
+
+	(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
+
+	/* for default credentials we will use GSS_C_NO_NAME */
+	internal_name = GSS_C_NO_NAME;
+    } else {
+	union_cred = (gss_union_cred_t)input_cred_handle;
+	if (gssint_get_mechanism_cred(union_cred, desired_mech) !=
+	    GSS_C_NO_CREDENTIAL)
+	    return (GSS_S_DUPLICATE_ELEMENT);
+
+	/* may need to create a mechanism specific name */
+	if (desired_name) {
+	    union_name = (gss_union_name_t)desired_name;
+	    if (union_name->mech_type &&
+		g_OID_equal(union_name->mech_type,
+			    &mech->mech_type))
+		internal_name = union_name->mech_name;
+	    else {
+		if (gssint_import_internal_name(minor_status,
+					        &mech->mech_type, union_name,
+					        &allocated_name) != GSS_S_COMPLETE)
+		    return (GSS_S_BAD_NAME);
+		internal_name = allocated_name;
+	    }
+	}
+    }
+
+
+    if (cred_usage == GSS_C_ACCEPT)
+	time_req = acceptor_time_req;
+    else if (cred_usage == GSS_C_INITIATE)
+	time_req = initiator_time_req;
+    else if (cred_usage == GSS_C_BOTH)
+	time_req = (acceptor_time_req > initiator_time_req) ?
+	    acceptor_time_req : initiator_time_req;
+    else
+	time_req = 0;
+
+    status = mech_ext->gssspi_acquire_cred_with_password(minor_status,
+				                         internal_name,
+				                         password,
+                                                         time_req,
+				                         GSS_C_NULL_OID_SET,
+                                                         cred_usage,
+				                         &cred,
+                                                         NULL,
+                                                         &time_rec);
+    if (status != GSS_S_COMPLETE) {
+	map_error(minor_status, mech);
+	goto errout;
+    }
+
+    /* may need to set credential auxinfo strucutre */
+    if (union_cred->auxinfo.creation_time == 0) {
+	union_cred->auxinfo.creation_time = time(NULL);
+	union_cred->auxinfo.time_rec = time_rec;
+	union_cred->auxinfo.cred_usage = cred_usage;
+
+	/*
+	 * we must set the name; if name is not supplied
+	 * we must do inquire cred to get it
+	 */
+	if (internal_name == NULL) {
+	    if (mech->gss_inquire_cred == NULL ||
+		((status = mech->gss_inquire_cred(
+		      &temp_minor_status, cred,
+		      &allocated_name, NULL, NULL,
+		      NULL)) != GSS_S_COMPLETE))
+		goto errout;
+	    internal_name = allocated_name;
+	}
+
+	if (internal_name != GSS_C_NO_NAME) {
+	    status = mech->gss_display_name(&temp_minor_status, internal_name,
+					    &union_cred->auxinfo.name,
+					    &union_cred->auxinfo.name_type);
+
+	    if (status != GSS_S_COMPLETE)
+		goto errout;
+	}
+    }
+
+    /* now add the new credential elements */
+    new_mechs_array = (gss_OID)
+	malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
+
+    new_cred_array = (gss_cred_id_t *)
+	malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
+
+    if (!new_mechs_array || !new_cred_array) {
+	status = GSS_S_FAILURE;
+	goto errout;
+    }
+
+    if (acceptor_time_rec)
+	if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
+	    *acceptor_time_rec = time_rec;
+    if (initiator_time_rec)
+	if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
+	    *initiator_time_rec = time_rec;
+
+    /*
+     * OK, expand the mechanism array and the credential array
+     */
+    (void) memcpy(new_mechs_array, union_cred->mechs_array,
+		  sizeof (gss_OID_desc) * union_cred->count);
+    (void) memcpy(new_cred_array, union_cred->cred_array,
+		  sizeof (gss_cred_id_t) * union_cred->count);
+
+    new_cred_array[union_cred->count] = cred;
+    if ((new_mechs_array[union_cred->count].elements =
+	 malloc(mech->mech_type.length)) == NULL)
+	goto errout;
+
+    g_OID_copy(&new_mechs_array[union_cred->count],
+	       &mech->mech_type);
+
+    if (actual_mechs != NULL) {
+	gss_OID_set_desc oids;
+
+	oids.count = union_cred->count + 1;
+	oids.elements = new_mechs_array;
+
+	status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+	if (GSS_ERROR(status)) {
+	    free(new_mechs_array[union_cred->count].elements);
+	    goto errout;
+	}
+    }
+
+    if (output_cred_handle == NULL) {
+	free(union_cred->mechs_array);
+	free(union_cred->cred_array);
+	new_union_cred = union_cred;
+    } else {
+	new_union_cred = malloc(sizeof (gss_union_cred_desc));
+	if (new_union_cred == NULL) {
+	    free(new_mechs_array[union_cred->count].elements);
+	    goto errout;
+	}
+	*new_union_cred = *union_cred;
+	*output_cred_handle = (gss_cred_id_t)new_union_cred;
+    }
+
+    new_union_cred->mechs_array = new_mechs_array;
+    new_union_cred->cred_array = new_cred_array;
+    new_union_cred->count++;
+    new_union_cred->loopback = new_union_cred;
+
+    /* We're done with the internal name. Free it if we allocated it. */
+
+    if (allocated_name)
+	(void) gssint_release_internal_name(&temp_minor_status,
+					   &mech->mech_type,
+					   &allocated_name);
+
+    return (GSS_S_COMPLETE);
+
+errout:
+    if (new_mechs_array)
+	free(new_mechs_array);
+    if (new_cred_array)
+	free(new_cred_array);
+
+    if (cred != NULL && mech->gss_release_cred)
+	mech->gss_release_cred(&temp_minor_status, &cred);
+
+    if (allocated_name)
+	(void) gssint_release_internal_name(&temp_minor_status,
+					   &mech->mech_type,
+					   &allocated_name);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
+	if (union_cred->auxinfo.name.value)
+	    free(union_cred->auxinfo.name.value);
+	free(union_cred);
+    }
+
+    return (status);
+}

Modified: branches/iakerb/src/lib/gssapi/mechglue/g_initialize.c
===================================================================
--- branches/iakerb/src/lib/gssapi/mechglue/g_initialize.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/mechglue/g_initialize.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -609,6 +609,10 @@
 		memset(cf->mech, 0, sizeof(*cf->mech));
 		free(cf->mech);
 	}
+	if (cf->mech_ext != NULL) {
+		memset(cf->mech_ext, 0, sizeof(*cf->mech_ext));
+		free(cf->mech_ext);
+	}
 	if (cf->dl_handle != NULL)
 		krb5int_close_plugin(cf->dl_handle);
 
@@ -646,6 +650,16 @@
 	new_cf->freeMech = 1;
 	new_cf->next = NULL;
 
+	if (template->mech_ext != NULL) {
+		new_cf->mech_ext = (gss_mechanism_ext)calloc(1,
+						sizeof(struct gss_config_ext));
+		if (new_cf->mech_ext == NULL) {
+			releaseMechInfo(&new_cf);
+			return ENOMEM;
+		}
+		*new_cf->mech_ext = *template->mech_ext;
+	}
+
 	if (template->kmodName != NULL) {
 		new_cf->kmodName = strdup(template->kmodName);
 		if (new_cf->kmodName == NULL) {
@@ -785,6 +799,21 @@
 	return mech;
 }
 
+static gss_mechanism_ext
+build_dynamicMechExt(void *dl, const gss_OID mech_type)
+{
+	gss_mechanism_ext mech_ext;
+
+	mech_ext = (gss_mechanism_ext)calloc(1, sizeof(*mech_ext));
+	if (mech_ext == NULL) {
+		return NULL;
+	}
+
+	GSS_ADD_DYNAMIC_METHOD(dl, mech_ext, gssspi_acquire_cred_with_password);
+
+	return mech_ext;
+}
+
 static void
 freeMechList(void)
 {
@@ -828,8 +857,8 @@
 gssint_get_mechanism(oid)
 const gss_OID oid;
 {
-	gss_mech_info aMech;
-	gss_mechanism (*sym)(const gss_OID);
+        gss_mech_info aMech;
+        gss_mechanism (*sym)(const gss_OID);
 	struct plugin_file_handle *dl;
 	struct errinfo errinfo;
 
@@ -905,60 +934,51 @@
 gssint_get_mechanism_ext(oid)
 const gss_OID oid;
 {
-	gss_mech_info aMech;
-	gss_mechanism_ext mech_ext;
+        gss_mech_info aMech;
 
 	if (gssint_mechglue_initialize_library() != 0)
 		return (NULL);
 
+	if (k5_mutex_lock(&g_mechListLock) != 0)
+		return NULL;
 	/* check if the mechanism is already loaded */
-	if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL)
+	if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext) {
+		(void) k5_mutex_unlock(&g_mechListLock);
 		return (aMech->mech_ext);
+	}
 
-	if (gssint_get_mechanism(oid) == NULL)
-		return (NULL);
+	/*
+	 * might need to re-read the configuration file before loading
+	 * the mechanism to ensure we have the latest info.
+	 */
+	updateMechList();
 
-	if (aMech->dl_handle == NULL)
-		return (NULL);
+	aMech = searchMechList(oid);
 
-	/* Load the gss_config_ext struct for this mech */
+	/* is the mechanism present in the list ? */
+	if (aMech == NULL || aMech->dl_handle == NULL) {
+		(void) k5_mutex_unlock(&g_mechListLock);
+		return ((gss_mechanism_ext)NULL);
+	}
 
-	mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
+	/* has another thread loaded the mech */
+	if (aMech->mech_ext) {
+		(void) k5_mutex_unlock(&g_mechListLock);
+		return (aMech->mech_ext);
+	}
 
-	if (mech_ext == NULL)
-		return (NULL);
+	/* Try dynamic dispatch table */
+	aMech->mech_ext = build_dynamicMechExt(aMech->dl_handle,
+                                               aMech->mech_type);
+	if (aMech->mech_ext == NULL) {
+		(void) k5_mutex_unlock(&g_mechListLock);
+		return ((gss_mechanism_ext)NULL);
+	}
 
-#if 0
-	/*
-	 * dlsym() the mech's 'method' functions for the extended APIs
-	 *
-	 * NOTE:  Until the void *context argument is removed from the
-	 * SPI method functions' signatures it will be necessary to have
-	 * different function pointer typedefs and function names for
-	 * the SPI methods than for the API.  When this argument is
-	 * removed it will be possible to rename gss_*_sfct to gss_*_fct
-	 * and and gssspi_* to gss_*.
-	 */
-	mech_ext->gss_acquire_cred_with_password =
-		(gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle,
-			"gssspi_acquire_cred_with_password");
-#endif
-
-	/* Set aMech->mech_ext */
-	(void) k5_mutex_lock(&g_mechListLock);
-
-	if (aMech->mech_ext == NULL)
-		aMech->mech_ext = mech_ext;
-	else
-		free(mech_ext);	/* we raced and lost; don't leak */
-
 	(void) k5_mutex_unlock(&g_mechListLock);
-
 	return (aMech->mech_ext);
-
 } /* gssint_get_mechanism_ext */
 
-
 /*
  * this routine is used for searching the list of mechanism data.
  *

Modified: branches/iakerb/src/lib/gssapi/mechglue/mglueP.h
===================================================================
--- branches/iakerb/src/lib/gssapi/mechglue/mglueP.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/mechglue/mglueP.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -77,7 +77,6 @@
 } gss_union_cred_desc, *gss_union_cred_t;
 
 typedef	OM_uint32	    (*gss_acquire_cred_with_password_sfct)(
-		    void *,		/* context */
 		    OM_uint32 *,	/* minor_status */
 		    const gss_name_t,	/* desired_name */
 		    const gss_buffer_t, /* password */
@@ -593,7 +592,7 @@
 
 /* This structure MUST NOT be used by any code outside libgss */
 typedef struct gss_config_ext {
-    gss_acquire_cred_with_password_sfct	gss_acquire_cred_with_password;
+    gss_acquire_cred_with_password_sfct	gssspi_acquire_cred_with_password;
 } *gss_mechanism_ext;
 
 /*

Modified: branches/iakerb/src/lib/gssapi/spnego/gssapiP_spnego.h
===================================================================
--- branches/iakerb/src/lib/gssapi/spnego/gssapiP_spnego.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/spnego/gssapiP_spnego.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -458,6 +458,18 @@
     OM_uint32 *);	    /* time_rec */
 
 OM_uint32
+spnego_gss_acquire_cred_with_password(
+    OM_uint32 *minor_status,
+    const gss_name_t desired_name,
+    const gss_buffer_t password,
+    OM_uint32 time_req,
+    const gss_OID_set desired_mechs,
+    gss_cred_usage_t cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec);
+
+OM_uint32
 spnego_gss_display_name_ext
 (
 	OM_uint32 *minor_status,

Modified: branches/iakerb/src/lib/gssapi/spnego/spnego_mech.c
===================================================================
--- branches/iakerb/src/lib/gssapi/spnego/spnego_mech.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/gssapi/spnego/spnego_mech.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -274,6 +274,11 @@
 	spnego_gss_set_neg_mechs,
 };
 
+static struct gss_config_ext spnego_mechanism_ext =
+{
+	spnego_gss_acquire_cred_with_password
+};
+
 #ifdef _GSS_STATIC_LINK
 #include "mglueP.h"
 
@@ -283,6 +288,7 @@
 
 	memset(&mech_spnego, 0, sizeof(mech_spnego));
 	mech_spnego.mech = &spnego_mechanism;
+	mech_spnego.mech_ext = &spnego_mechanism_ext;
 	mech_spnego.mechNameStr = "spnego";
 	mech_spnego.mech_type = GSS_C_NO_OID;
 
@@ -852,7 +858,7 @@
 		 * token back if this is the first token or if a MIC exchange
 		 * is required.
 		 */
-		if (*send_token == CONT_TOKEN_SEND &&
+		if (mechtok_in->length != 0 &&
 		    mechtok_out->length == 0 &&
 		    (!sc->mic_reqd ||
 		     !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
@@ -873,7 +879,8 @@
 			*send_token = ERROR_TOKEN_SEND;
 		}
 		*negState = REJECT;
-	}
+	} else if (*send_token == NO_TOKEN_SEND)
+		*send_token = CONT_TOKEN_SEND;
 	return ret;
 }
 
@@ -2447,6 +2454,47 @@
 }
 
 OM_uint32
+spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+				      const gss_name_t desired_name,
+				      const gss_buffer_t password,
+				      OM_uint32 time_req,
+				      const gss_OID_set desired_mechs,
+				      gss_cred_usage_t cred_usage,
+				      gss_cred_id_t *output_cred_handle,
+				      gss_OID_set *actual_mechs,
+				      OM_uint32 *time_rec)
+{
+	OM_uint32 status;
+	gss_OID_set amechs = GSS_C_NULL_OID_SET;
+
+	dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
+
+	if (actual_mechs)
+		*actual_mechs = NULL;
+
+	if (time_rec)
+		*time_rec = 0;
+
+	if (desired_mechs == GSS_C_NULL_OID_SET) {
+		status = get_available_mechs(minor_status,
+				desired_name, cred_usage,
+				output_cred_handle, &amechs);
+	}
+
+	status = gss_acquire_cred_with_password(minor_status,
+			desired_name, password, time_req,
+			desired_mechs ? desired_mechs : amechs, cred_usage,
+			output_cred_handle, actual_mechs,
+			time_rec);
+
+	if (amechs != GSS_C_NULL_OID_SET)
+		(void) gss_release_oid_set(minor_status, &amechs);
+
+	dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
+	return (status);
+}
+
+OM_uint32
 spnego_gss_display_name_ext(OM_uint32 *minor_status,
 			    gss_name_t name,
 			    gss_OID display_as_name_type,

Modified: branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.c
===================================================================
--- branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -1899,6 +1899,47 @@
     return retval;
 }
 
+asn1_error_code asn1_decode_iakerb_header
+(asn1buf *buf, krb5_iakerb_header *val)
+{
+    setup();
+    val->target_realm.data = NULL;
+    val->target_realm.length = 0;
+    val->cookie = NULL;
+    {
+        begin_structure();
+        get_lenfield(val->target_realm.length, val->target_realm.data,
+                     1, asn1_decode_charstring);
+        if (tagnum == 2) {
+            alloc_data(val->cookie);
+            get_lenfield(val->cookie->length, val->cookie->data,
+                         2, asn1_decode_charstring);
+        }
+        end_structure();
+    }
+    return 0;
+error_out:
+    krb5_free_data_contents(NULL, &val->target_realm);
+    krb5_free_data(NULL, val->cookie);
+    return retval;
+}
+
+asn1_error_code asn1_decode_iakerb_finished
+(asn1buf *buf, krb5_iakerb_finished *val)
+{
+    setup();
+    val->checksum.contents = NULL;
+    {
+        begin_structure();
+        get_field(val->checksum, 1, asn1_decode_checksum);
+        end_structure();
+    }
+    return 0;
+error_out:
+    krb5_free_checksum_contents(NULL, &val->checksum);
+    return retval;
+}
+
 #ifndef DISABLE_PKINIT
 /* PKINIT */
 

Modified: branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.h
===================================================================
--- branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/asn.1/asn1_k_decode.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -271,4 +271,10 @@
 asn1_error_code asn1_decode_ad_signedpath(asn1buf *buf,
                                           krb5_ad_signedpath *val);
 
+asn1_error_code asn1_decode_iakerb_header(asn1buf *buf,
+                                          krb5_iakerb_header *val);
+
+asn1_error_code asn1_decode_iakerb_finished(asn1buf *buf,
+                                            krb5_iakerb_finished *val);
+
 #endif

Modified: branches/iakerb/src/lib/krb5/asn.1/asn1_k_encode.c
===================================================================
--- branches/iakerb/src/lib/krb5/asn.1/asn1_k_encode.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/asn.1/asn1_k_encode.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -1394,6 +1394,35 @@
 
 DEFSEQTYPE(ad_signedpath, krb5_ad_signedpath, ad_signedpath_fields, ad_signedpath_optional);
 
+static const struct field_info iakerb_header_fields[] = {
+    FIELDOF_NORM(krb5_iakerb_header, ostring_data, target_realm, 1),
+    FIELDOF_OPT(krb5_iakerb_header, ostring_data_ptr, cookie, 2, 2),
+};
+
+static unsigned int iakerb_header_optional(const void *p)
+{
+    unsigned int optional = 0;
+    const krb5_iakerb_header *val = p;
+    if (val->cookie && val->cookie->data)
+        optional |= (1u << 2);
+    return optional;
+}
+
+DEFSEQTYPE(iakerb_header, krb5_iakerb_header, iakerb_header_fields, iakerb_header_optional);
+
+static const struct field_info iakerb_finished_fields[] = {
+    FIELDOF_NORM(krb5_iakerb_finished, checksum, checksum, 1),
+};
+
+static unsigned int iakerb_finished_optional(const void *p)
+{
+    unsigned int optional = 0;
+    return optional;
+}
+
+DEFSEQTYPE(iakerb_finished, krb5_iakerb_finished, iakerb_finished_fields,
+iakerb_finished_optional);
+
 /* Exported complete encoders -- these produce a krb5_data with
    the encoding in the correct byte order.  */
 
@@ -1472,6 +1501,8 @@
 MAKE_FULL_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
 MAKE_FULL_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data);
 MAKE_FULL_ENCODER(encode_krb5_ad_signedpath, ad_signedpath);
+MAKE_FULL_ENCODER(encode_krb5_iakerb_header, iakerb_header);
+MAKE_FULL_ENCODER(encode_krb5_iakerb_finished, iakerb_finished);
 
 
 

Modified: branches/iakerb/src/lib/krb5/asn.1/krb5_decode.c
===================================================================
--- branches/iakerb/src/lib/krb5/asn.1/krb5_decode.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/asn.1/krb5_decode.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -1218,6 +1218,30 @@
     cleanup(free);
 }
 
+krb5_error_code decode_krb5_iakerb_header
+(const krb5_data *code, krb5_iakerb_header **repptr)
+{
+    setup_buf_only(krb5_iakerb_header *);
+    alloc_field(rep);
+
+    retval = asn1_decode_iakerb_header(&buf, rep);
+    if (retval) clean_return(retval);
+
+    cleanup(free);
+}
+
+krb5_error_code decode_krb5_iakerb_finished
+(const krb5_data *code, krb5_iakerb_finished **repptr)
+{
+    setup_buf_only(krb5_iakerb_finished *);
+    alloc_field(rep);
+
+    retval = asn1_decode_iakerb_finished(&buf, rep);
+    if (retval) clean_return(retval);
+
+    cleanup(free);
+}
+
 krb5_error_code
 krb5int_get_authdata_containee_types(krb5_context context,
                                      const krb5_authdata *authdata,

Modified: branches/iakerb/src/lib/krb5/error_tables/krb5_err.et
===================================================================
--- branches/iakerb/src/lib/krb5/error_tables/krb5_err.et	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/error_tables/krb5_err.et	2010-03-29 16:18:20 UTC (rev 23843)
@@ -126,8 +126,8 @@
 error_code KRB5PLACEHOLD_82,	"KRB5 error code 82"
 error_code KRB5PLACEHOLD_83,	"KRB5 error code 83"
 error_code KRB5PLACEHOLD_84,	"KRB5 error code 84"
-error_code KRB5PLACEHOLD_85,	"KRB5 error code 85"
-error_code KRB5PLACEHOLD_86,	"KRB5 error code 86"
+error_code KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND,         "The IAKERB proxy could not find a KDC"
+error_code KRB5KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE,	"The KDC did not respond to the IAKERB proxy"
 error_code KRB5PLACEHOLD_87,	"KRB5 error code 87"
 error_code KRB5PLACEHOLD_88,	"KRB5 error code 88"
 error_code KRB5PLACEHOLD_89,	"KRB5 error code 89"

Modified: branches/iakerb/src/lib/krb5/krb/Makefile.in
===================================================================
--- branches/iakerb/src/lib/krb5/krb/Makefile.in	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/krb/Makefile.in	2010-03-29 16:18:20 UTC (rev 23843)
@@ -48,6 +48,7 @@
 	fast.o \
 	fwd_tgt.o	\
 	gc_frm_kdc.o	\
+	gc_frm_kdc_step.o	\
 	gc_via_tkt.o	\
 	gen_seqnum.o	\
 	gen_subkey.o	\
@@ -147,6 +148,7 @@
 	$(OUTPRE)fast.$(OBJEXT) \
 	$(OUTPRE)fwd_tgt.$(OBJEXT)	\
 	$(OUTPRE)gc_frm_kdc.$(OBJEXT)	\
+	$(OUTPRE)gc_frm_kdc_step.$(OBJEXT)	\
 	$(OUTPRE)gc_via_tkt.$(OBJEXT)	\
 	$(OUTPRE)gen_seqnum.$(OBJEXT)	\
 	$(OUTPRE)gen_subkey.$(OBJEXT)	\
@@ -247,6 +249,7 @@
 	$(srcdir)/fast.c \
 	$(srcdir)/fwd_tgt.c	\
 	$(srcdir)/gc_frm_kdc.c	\
+	$(srcdir)/gc_frm_kdc_step.c	\
 	$(srcdir)/gc_via_tkt.c	\
 	$(srcdir)/gen_seqnum.c	\
 	$(srcdir)/gen_subkey.c	\

Modified: branches/iakerb/src/lib/krb5/krb/gc_frm_kdc.c
===================================================================
--- branches/iakerb/src/lib/krb5/krb/gc_frm_kdc.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/krb/gc_frm_kdc.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -152,8 +152,6 @@
 /*
  * Prototypes of helper functions
  */
-static krb5_error_code tgt_mcred(krb5_context, krb5_principal,
-                                 krb5_principal, krb5_principal, krb5_creds *);
 static krb5_error_code retr_local_tgt(struct tr_state *, krb5_principal);
 static krb5_error_code try_ccache(struct tr_state *, krb5_creds *);
 static krb5_error_code find_nxt_realm(struct tr_state *);
@@ -295,10 +293,10 @@
  * failure.  The peculiar ordering of DST and SRC args is for
  * consistency with krb5int_tgtname().
  */
-static krb5_error_code
-tgt_mcred(krb5_context ctx, krb5_principal client,
-          krb5_principal dst, krb5_principal src,
-          krb5_creds *mcreds)
+krb5_error_code
+krb5int_tgt_mcred(krb5_context ctx, krb5_principal client,
+                  krb5_principal dst, krb5_principal src,
+                  krb5_creds *mcreds)
 {
     krb5_error_code retval;
 
@@ -365,7 +363,7 @@
     krb5_creds tgtq;
 
     memset(&tgtq, 0, sizeof(tgtq));
-    retval = tgt_mcred(ts->ctx, client, client, client, &tgtq);
+    retval = krb5int_tgt_mcred(ts->ctx, client, client, client, &tgtq);
     if (retval)
         return retval;
 
@@ -997,7 +995,7 @@
      * local realm in the default (referral) case or for the remote
      * realm if we're starting someplace non-local.
      */
-    retval = tgt_mcred(context, client, server, client, &tgtq);
+    retval = krb5int_tgt_mcred(context, client, server, client, &tgtq);
     if (retval)
         goto cleanup;
 
@@ -1235,7 +1233,7 @@
      */
 
     krb5_free_cred_contents(context, &tgtq);
-    retval = tgt_mcred(context, client, server, client, &tgtq);
+    retval = krb5int_tgt_mcred(context, client, server, client, &tgtq);
     if (retval)
         goto cleanup;
 

Added: branches/iakerb/src/lib/krb5/krb/gc_frm_kdc_step.c
===================================================================
--- branches/iakerb/src/lib/krb5/krb/gc_frm_kdc_step.c	                        (rev 0)
+++ branches/iakerb/src/lib/krb5/krb/gc_frm_kdc_step.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -0,0 +1,450 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1990-2009 by the Massachusetts Institute of Technology.
+ * Copyright (c) 1994 CyberSAFE Corporation
+ * Copyright (c) 1993 Open Computing Security Group
+ * 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.
+ * Neither M.I.T., the Open Computing Security Group, nor
+ * CyberSAFE Corporation make any representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * krb5_tkt_creds_step() and related functions:
+ *
+ * Get credentials from some KDC somewhere, possibly accumulating TGTs
+ * along the way. This is asychronous version of the API in gc_frm_kdc.c.
+ * It requires that the KDC support cross-realm referrals.
+ */
+
+#include "k5-int.h"
+#include <stdio.h>
+#include "int-proto.h"
+
+#define KRB5_TKT_CREDS_STEP_FLAG_COMPLETE       0x1
+#define KRB5_TKT_CREDS_STEP_FLAG_CTX_KTYPES     0x2
+
+struct _krb5_tkt_creds_context {
+    krb5_ccache ccache;
+    krb5_creds in_cred;
+    krb5_principal client;
+    krb5_principal server;
+    krb5_principal req_server;
+    int req_kdcopt;
+
+    unsigned int flags;
+    krb5_creds cc_tgt;
+    krb5_creds *tgtptr;
+    unsigned int referral_count;
+    krb5_creds *referral_tgts[KRB5_REFERRAL_MAXHOPS];
+    krb5_boolean default_use_conf_ktypes;
+    krb5_timestamp timestamp;
+    krb5_int32 nonce;
+    int kdcopt;
+    krb5_keyblock *subkey;
+    krb5_data encoded_previous_request;
+
+    krb5_creds *out_cred;
+};
+
+/* Convert ticket flags to necessary KDC options */
+#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
+
+static krb5_error_code
+tkt_make_tgs_request(krb5_context context,
+                     krb5_tkt_creds_context ctx,
+                     krb5_creds *tgt,
+                     krb5_creds *in_cred,
+                     krb5_data *req)
+{
+    krb5_error_code code;
+
+    /* These flags are always included */
+    ctx->kdcopt |= FLAGS2OPTS(tgt->ticket_flags);
+
+    if ((ctx->kdcopt & KDC_OPT_ENC_TKT_IN_SKEY) == 0)
+        in_cred->is_skey = FALSE;
+
+    if (!krb5_c_valid_enctype(tgt->keyblock.enctype))
+        return KRB5_PROG_ETYPE_NOSUPP;
+
+    code = krb5int_make_tgs_request(context, tgt, ctx->kdcopt,
+                                   tgt->addresses, NULL,
+                                   in_cred, NULL, NULL, req,
+                                   &ctx->timestamp, &ctx->nonce, &ctx->subkey);
+    return code;
+}
+
+static krb5_error_code
+tkt_process_tgs_reply(krb5_context context,
+                      krb5_tkt_creds_context ctx,
+                      krb5_data *rep,
+                      krb5_creds *tgt,
+                      krb5_creds *in_cred,
+                      krb5_creds **out_cred)
+{
+    krb5_error_code code;
+
+    code = krb5int_process_tgs_reply(context,
+                                    rep,
+                                    tgt,
+                                    ctx->kdcopt,
+                                    tgt->addresses,
+                                    NULL,
+                                    in_cred,
+                                    ctx->timestamp,
+                                    ctx->nonce,
+                                    ctx->subkey,
+                                    NULL,
+                                    NULL,
+                                    out_cred);
+
+    return code;
+}
+
+/*
+ * Asynchronous API
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_init(krb5_context context,
+                    krb5_ccache ccache,
+                    krb5_creds *creds,
+                    int kdcopt,
+                    krb5_tkt_creds_context *pctx)
+{
+    krb5_error_code code;
+    krb5_tkt_creds_context ctx = NULL;
+    krb5_creds tgtq;
+    krb5_flags flags = KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES;
+
+    memset(&tgtq, 0, sizeof(tgtq));
+
+    ctx = k5alloc(sizeof(*ctx), &code);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5int_copy_creds_contents(context, creds, &ctx->in_cred);
+    if (code != 0)
+        goto cleanup;
+
+    ctx->ccache = ccache; /* XXX */
+
+    ctx->req_kdcopt = kdcopt;
+    ctx->default_use_conf_ktypes = context->use_conf_ktypes;
+    ctx->client = ctx->in_cred.client;
+    ctx->server = ctx->in_cred.server;
+
+    code = krb5_copy_principal(context, ctx->server, &ctx->req_server);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5int_tgt_mcred(context, ctx->client, ctx->client,
+                             ctx->client, &tgtq);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_cc_retrieve_cred(context, ctx->ccache, flags,
+                                 &tgtq, &ctx->cc_tgt);
+    if (code != 0)
+        goto cleanup;
+
+    ctx->tgtptr = &ctx->cc_tgt;
+
+    *pctx = ctx;
+
+cleanup:
+    if (code != 0)
+        krb5_tkt_creds_free(context, ctx);
+    krb5_free_cred_contents(context, &tgtq);
+
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_get_creds(krb5_context context,
+                         krb5_tkt_creds_context ctx,
+                         krb5_creds *creds)
+{
+    krb5_error_code code;
+
+    if (ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE)
+        code = krb5int_copy_creds_contents(context, ctx->out_cred, creds);
+    else
+        code = KRB5_NO_TKT_SUPPLIED;
+
+    return code;
+}
+
+/*
+ * Store credentials in credentials cache. If ccache is NULL, the
+ * credentials cache associated with the context is used. This can
+ * be called on an incomplete context, in which case the referral
+ * TGT only will be stored.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_store_creds(krb5_context context,
+                           krb5_tkt_creds_context ctx,
+                           krb5_ccache ccache)
+{
+    krb5_error_code code;
+
+    if (ccache == NULL)
+        ccache = ctx->ccache;
+
+    /* Only store the referral from our local KDC */
+    if (ctx->referral_tgts[0] != NULL)
+        krb5_cc_store_cred(context, ccache, ctx->referral_tgts[0]);
+
+    if (ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE)
+        code = krb5_cc_store_cred(context, ccache, ctx->out_cred);
+    else
+        code = KRB5_NO_TKT_SUPPLIED;
+
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_get_times(krb5_context context,
+                         krb5_tkt_creds_context ctx,
+                         krb5_ticket_times *times)
+{
+    if ((ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE) == 0)
+        return KRB5_NO_TKT_SUPPLIED;
+
+    *times = ctx->out_cred->times;
+
+    return 0;
+}
+
+void KRB5_CALLCONV
+krb5_tkt_creds_free(krb5_context context,
+                    krb5_tkt_creds_context ctx)
+{
+    int i;
+
+    if (ctx == NULL)
+        return;
+
+    krb5_free_principal(context, ctx->req_server);
+    krb5_free_cred_contents(context, &ctx->in_cred);
+    krb5_free_creds(context, ctx->out_cred);
+    krb5_free_data_contents(context, &ctx->encoded_previous_request);
+    krb5_free_keyblock(context, ctx->subkey);
+
+    /* Free referral TGTs list. */
+    for (i = 0; i < KRB5_REFERRAL_MAXHOPS; i++) {
+        if (ctx->referral_tgts[i] != NULL) {
+            krb5_free_creds(context, ctx->referral_tgts[i]);
+            ctx->referral_tgts[i] = NULL;
+        }
+    }
+
+    free(ctx);
+}
+
+static krb5_error_code
+tkt_creds_step_request(krb5_context context,
+                       krb5_tkt_creds_context ctx,
+                       krb5_data *req)
+{
+    krb5_error_code code;
+
+    if (ctx->referral_count >= KRB5_REFERRAL_MAXHOPS)
+        return KRB5_KDC_UNREACH;
+
+    assert(ctx->tgtptr != NULL);
+
+    /* Copy krbtgt realm to server principal */
+    krb5_free_data_contents(context, &ctx->server->realm);
+    code = krb5int_copy_data_contents(context,
+                                      &ctx->tgtptr->server->data[1],
+                                      &ctx->server->realm);
+    if (code != 0)
+        return code;
+
+    ctx->kdcopt = ctx->req_kdcopt | KDC_OPT_CANONICALIZE;
+
+    if (ctx->in_cred.second_ticket.length != 0 &&
+        (ctx->kdcopt & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
+        ctx->kdcopt |= KDC_OPT_ENC_TKT_IN_SKEY;
+    }
+
+    if ((ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_CTX_KTYPES) == 0)
+        context->use_conf_ktypes = 1;
+
+    code = tkt_make_tgs_request(context, ctx, ctx->tgtptr,
+                                &ctx->in_cred, req);
+
+    context->use_conf_ktypes = ctx->default_use_conf_ktypes;
+
+    return code;
+}
+
+static krb5_error_code
+tkt_creds_step_reply(krb5_context context,
+                     krb5_tkt_creds_context ctx,
+                     krb5_data *rep)
+{
+    krb5_error_code code;
+    unsigned int i;
+    krb5_boolean got_tkt = FALSE;
+
+    krb5_free_creds(context, ctx->out_cred);
+    ctx->out_cred = NULL;
+
+    code = tkt_process_tgs_reply(context, ctx, rep, ctx->tgtptr,
+                                 &ctx->in_cred, &ctx->out_cred);
+    if (code != 0)
+        goto cleanup;
+
+    /*
+     * Referral request succeeded; let's see what it is
+     */
+    if (krb5_principal_compare(context, ctx->server, ctx->out_cred->server)) {
+        /*
+         * Check if the return enctype is one that we requested if
+         * needed.
+         */
+        if (ctx->default_use_conf_ktypes || context->tgs_etypes == NULL)
+            got_tkt = TRUE;
+        else
+            for (i = 0; context->tgs_etypes[i] != ENCTYPE_NULL; i++) {
+                if (ctx->out_cred->keyblock.enctype == context->tgs_etypes[i]) {
+                    /* Found an allowable etype, so we're done */
+                    got_tkt = TRUE;
+                    break;
+                }
+            }
+
+        if (got_tkt == FALSE)
+            ctx->flags |= KRB5_TKT_CREDS_STEP_FLAG_CTX_KTYPES; /* try again */
+    } else if (IS_TGS_PRINC(context, ctx->out_cred->server)) {
+        krb5_data *r1, *r2;
+
+        if (ctx->referral_count == 0)
+            r1 = &ctx->tgtptr->server->data[1];
+        else
+            r1 = &ctx->referral_tgts[ctx->referral_count - 1]->server->data[1];
+
+        r2 = &ctx->out_cred->server->data[1];
+        if (data_eq(*r1, *r2)) {
+            code = KRB5_KDC_UNREACH;
+            goto cleanup;
+        }
+
+        /* Check for referral routing loop. */
+        for (i = 0; i < ctx->referral_count; i++) {
+            if (krb5_principal_compare(context,
+                                       ctx->out_cred->server,
+                                       ctx->referral_tgts[i]->server)) {
+                code = KRB5_KDC_UNREACH;
+                goto cleanup;
+            }
+        }
+        /* Point current tgt pointer at newly-received TGT. */
+        ctx->tgtptr = ctx->out_cred;
+
+        /* avoid multiple copies of authdata */
+        ctx->out_cred->authdata = ctx->in_cred.authdata;
+        ctx->in_cred.authdata = NULL;
+
+        ctx->referral_tgts[ctx->referral_count++] = ctx->out_cred;
+        ctx->out_cred = NULL;
+    } else {
+        code = KRB5KRB_AP_ERR_NO_TGT;
+    }
+
+    assert(ctx->tgtptr == NULL || code == 0);
+
+    if (code == 0 && got_tkt == TRUE) {
+        krb5_free_principal(context, ctx->out_cred->server);
+        ctx->out_cred->server = ctx->req_server;
+        ctx->req_server = NULL;
+
+        if (ctx->in_cred.authdata != NULL) {
+            code = krb5_copy_authdata(context, ctx->in_cred.authdata,
+                                      &ctx->out_cred->authdata);
+        }
+
+        ctx->flags |= KRB5_TKT_CREDS_STEP_FLAG_COMPLETE;
+    }
+
+cleanup:
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_tkt_creds_step(krb5_context context,
+                    krb5_tkt_creds_context ctx,
+                    krb5_data *in,
+                    krb5_data *out,
+                    krb5_data *realm,
+                    unsigned int *flags)
+{
+    krb5_error_code code, code2;
+
+    *flags = 0;
+
+    out->data = NULL;
+    out->length = 0;
+
+    realm->data = NULL;
+    realm->length = 0;
+
+    if (ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE)
+        goto cleanup;
+
+    if (in != NULL && in->length != 0) {
+        code = tkt_creds_step_reply(context, ctx, in);
+        if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
+            code2 = krb5int_copy_data_contents(context,
+                                               &ctx->encoded_previous_request,
+                                               out);
+            if (code2 != 0)
+                code = code2;
+            goto copy_realm;
+        }
+        if (code != 0 || (ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE))
+            goto cleanup;
+    }
+
+    code = tkt_creds_step_request(context, ctx, out);
+    if (code != 0)
+        goto cleanup;
+
+    assert(out->length != 0);
+
+    code = krb5int_copy_data_contents(context,
+                                      out,
+                                      &ctx->encoded_previous_request);
+    if (code != 0)
+        goto cleanup;
+
+copy_realm:
+    code2 = krb5int_copy_data_contents(context, &ctx->server->realm, realm);
+    if (code2 != 0) {
+        code = code2;
+        goto cleanup;
+    }
+
+cleanup:
+    *flags = (ctx->flags & KRB5_TKT_CREDS_STEP_FLAG_COMPLETE);
+
+    return code;
+}
+

Modified: branches/iakerb/src/lib/krb5/krb/int-proto.h
===================================================================
--- branches/iakerb/src/lib/krb5/krb/int-proto.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/krb/int-proto.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -142,6 +142,11 @@
                           krb5_pa_data ***out_enc_padata,
                           krb5_creds **out_cred);
 
+krb5_error_code
+krb5int_tgt_mcred(krb5_context ctx, krb5_principal client,
+                  krb5_principal dst, krb5_principal src,
+                  krb5_creds *mcreds);
+
 krb5_error_code krb5int_send_tgs(krb5_context, krb5_flags,
                                  const krb5_ticket_times *,
                                  const krb5_enctype *,

Modified: branches/iakerb/src/lib/krb5/krb/kfree.c
===================================================================
--- branches/iakerb/src/lib/krb5/krb/kfree.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/krb/kfree.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -913,3 +913,22 @@
     krb5_free_pa_data(context, val->method_data);
     free(val);
 }
+
+void KRB5_CALLCONV
+krb5_free_iakerb_header(krb5_context context, krb5_iakerb_header *val)
+{
+    if (val == NULL)
+        return ;
+
+    krb5_free_data_contents(context, &val->target_realm);
+    krb5_free_data(context, val->cookie);
+}
+
+void KRB5_CALLCONV
+krb5_free_iakerb_finished(krb5_context context, krb5_iakerb_finished *val)
+{
+    if (val == NULL)
+        return ;
+
+    krb5_free_checksum_contents(context, &val->checksum);
+}

Modified: branches/iakerb/src/lib/krb5/libkrb5.exports
===================================================================
--- branches/iakerb/src/lib/krb5/libkrb5.exports	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/lib/krb5/libkrb5.exports	2010-03-29 16:18:20 UTC (rev 23843)
@@ -22,6 +22,8 @@
 decode_krb5_etype_info
 decode_krb5_etype_info2
 decode_krb5_fast_req
+decode_krb5_iakerb_finished
+decode_krb5_iakerb_header
 decode_krb5_kdc_req_body
 decode_krb5_pa_enc_ts
 decode_krb5_pa_for_user
@@ -67,6 +69,8 @@
 encode_krb5_etype_info
 encode_krb5_etype_info2
 encode_krb5_fast_response
+encode_krb5_iakerb_finished
+encode_krb5_iakerb_header
 encode_krb5_kdc_req_body
 encode_krb5_pa_enc_ts
 encode_krb5_pa_for_user
@@ -266,6 +270,8 @@
 krb5_free_fast_armored_req
 krb5_free_fast_req
 krb5_free_host_realm
+krb5_free_iakerb_finished
+krb5_free_iakerb_header
 krb5_free_kdc_rep
 krb5_free_kdc_req
 krb5_free_keyblock
@@ -550,6 +556,12 @@
 krb5_string_to_timestamp
 krb5_sync_disk_file
 krb5int_tgtname
+krb5_tkt_creds_free
+krb5_tkt_creds_get_creds
+krb5_tkt_creds_get_times
+krb5_tkt_creds_init
+krb5_tkt_creds_step
+krb5_tkt_creds_store_creds
 krb5_timeofday
 krb5_timestamp_to_sfstring
 krb5_timestamp_to_string

Modified: branches/iakerb/src/tests/asn.1/krb5_decode_leak.c
===================================================================
--- branches/iakerb/src/tests/asn.1/krb5_decode_leak.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/krb5_decode_leak.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -704,6 +704,28 @@
                   krb5_free_ad_signedpath);
         ktest_empty_ad_signedpath(&sp);
     }
+    /****************************************************************/
+    /* encode_krb5_iakerb_header */
+    {
+        krb5_iakerb_header ih, *tmp;
+        setup(ih, "iakerb_header",
+              ktest_make_sample_iakerb_header);
+        leak_test(ih, encode_krb5_iakerb_header,
+                  decode_krb5_iakerb_header,
+                  krb5_free_iakerb_header);
+        ktest_empty_iakerb_header(&ih);
+    }
+    /****************************************************************/
+    /* encode_krb5_iakerb_finished */
+    {
+        krb5_iakerb_finished ih, *tmp;
+        setup(ih, "iakerb_finished",
+              ktest_make_sample_iakerb_finished);
+        leak_test(ih, encode_krb5_iakerb_finished,
+                  decode_krb5_iakerb_finished,
+                  krb5_free_iakerb_finished);
+        ktest_empty_iakerb_finished(&ih);
+    }
     krb5_free_context(test_context);
     return 0;
 }

Modified: branches/iakerb/src/tests/asn.1/krb5_decode_test.c
===================================================================
--- branches/iakerb/src/tests/asn.1/krb5_decode_test.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/krb5_decode_test.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -916,6 +916,22 @@
         ktest_empty_ad_signedpath(&ref);
     }
 
+    /****************************************************************/
+    /* decode_iakerb_header */
+    {
+        setup(krb5_iakerb_header,"krb5_iakerb_header",ktest_make_sample_iakerb_header);
+        decode_run("iakerb_header","","30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61",decode_krb5_iakerb_header,ktest_equal_iakerb_header,krb5_free_iakerb_header);
+        ktest_empty_iakerb_header(&ref);
+    }
+
+    /****************************************************************/
+    /* decode_iakerb_finished */
+    {
+        setup(krb5_iakerb_finished,"krb5_iakerb_finished",ktest_make_sample_iakerb_finished);
+        decode_run("iakerb_finished","","30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34",decode_krb5_iakerb_finished,ktest_equal_iakerb_finished,krb5_free_iakerb_finished);
+        ktest_empty_iakerb_finished(&ref);
+    }
+
 #ifdef ENABLE_LDAP
     /* ldap sequence_of_keys */
     {

Modified: branches/iakerb/src/tests/asn.1/krb5_encode_test.c
===================================================================
--- branches/iakerb/src/tests/asn.1/krb5_encode_test.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/krb5_encode_test.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -740,6 +740,28 @@
                    encode_krb5_ad_signedpath);
         ktest_empty_ad_signedpath(&sp);
     }
+    /****************************************************************/
+    /* encode_krb5_iakerb_header */
+    {
+        krb5_iakerb_header ih;
+        setup(ih,krb5_ad_signedpath,"iakerb_header",
+              ktest_make_sample_iakerb_header);
+        encode_run(ih,krb5_iakerb_header,
+                   "iakerb_header","",
+                   encode_krb5_iakerb_header);
+        ktest_empty_iakerb_header(&ih);
+    }
+    /****************************************************************/
+    /* encode_krb5_iakerb_finished */
+    {
+        krb5_iakerb_finished ih;
+        setup(ih,krb5_ad_signedpath,"iakerb_finished",
+              ktest_make_sample_iakerb_finished);
+        encode_run(ih,krb5_iakerb_finished,
+                   "iakerb_finished","",
+                   encode_krb5_iakerb_finished);
+        ktest_empty_iakerb_finished(&ih);
+    }
 #ifdef ENABLE_LDAP
     {
         ldap_seqof_key_data skd;

Modified: branches/iakerb/src/tests/asn.1/ktest.c
===================================================================
--- branches/iakerb/src/tests/asn.1/ktest.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/ktest.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -890,6 +890,28 @@
     return retval;
 }
 
+krb5_error_code ktest_make_sample_iakerb_header(ih)
+    krb5_iakerb_header *ih;
+{
+    krb5_error_code retval;
+    retval = ktest_make_sample_data(&(ih->target_realm));
+    if (retval) return retval;
+    ih->cookie = k5alloc(sizeof(krb5_data), &retval);
+    if (retval) return retval;
+    retval = ktest_make_sample_data(ih->cookie);
+    if (retval) return retval;
+    return retval;
+}
+
+krb5_error_code ktest_make_sample_iakerb_finished(ih)
+    krb5_iakerb_finished *ih;
+{
+    krb5_error_code retval;
+    retval = ktest_make_sample_checksum(&ih->checksum);
+    if (retval) return retval;
+    return retval;
+}
+
 #ifdef ENABLE_LDAP
 static krb5_error_code ktest_make_sample_key_data(krb5_key_data *p, int i)
 {
@@ -1532,6 +1554,19 @@
     ktest_destroy_pa_data_array(&p->method_data);
 }
 
+void ktest_empty_iakerb_header(p)
+    krb5_iakerb_header *p;
+{
+    krb5_free_data_contents(NULL, &p->target_realm);
+    krb5_free_data(NULL, p->cookie);
+}
+
+void ktest_empty_iakerb_finished(p)
+    krb5_iakerb_finished *p;
+{
+    krb5_free_checksum_contents(NULL, &p->checksum);
+}
+
 #ifdef ENABLE_LDAP
 void ktest_empty_ldap_seqof_key_data(ctx, p)
     krb5_context ctx;

Modified: branches/iakerb/src/tests/asn.1/ktest.h
===================================================================
--- branches/iakerb/src/tests/asn.1/ktest.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/ktest.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -109,6 +109,8 @@
 krb5_error_code ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p);
 krb5_error_code ktest_make_sample_ad_signedpath_data(krb5_ad_signedpath_data *p);
 krb5_error_code ktest_make_sample_ad_signedpath(krb5_ad_signedpath *p);
+krb5_error_code ktest_make_sample_iakerb_header(krb5_iakerb_header *p);
+krb5_error_code ktest_make_sample_iakerb_finished(krb5_iakerb_finished *p);
 
 #ifdef ENABLE_LDAP
 krb5_error_code ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data * p);
@@ -221,6 +223,8 @@
 void ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p);
 void ktest_empty_ad_signedpath_data(krb5_ad_signedpath_data *p);
 void ktest_empty_ad_signedpath(krb5_ad_signedpath *p);
+void ktest_empty_iakerb_header(krb5_iakerb_header *p);
+void ktest_empty_iakerb_finished(krb5_iakerb_finished *p);
 
 #ifdef ENABLE_LDAP
 void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);

Modified: branches/iakerb/src/tests/asn.1/ktest_equal.c
===================================================================
--- branches/iakerb/src/tests/asn.1/ktest_equal.c	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/ktest_equal.c	2010-03-29 16:18:20 UTC (rev 23843)
@@ -600,6 +600,29 @@
     return p;
 }
 
+int ktest_equal_iakerb_header(ref, var)
+    krb5_iakerb_header *ref;
+    krb5_iakerb_header *var;
+{
+    int p = TRUE;
+    if (ref == var) return TRUE;
+    else if (ref == NULL || var == NULL) return FALSE;
+    p=p&&struct_equal(target_realm,ktest_equal_data);
+    p=p&&ptr_equal(cookie,ktest_equal_data);
+    return p;
+}
+
+int ktest_equal_iakerb_finished(ref, var)
+    krb5_iakerb_finished *ref;
+    krb5_iakerb_finished *var;
+{
+    int p = TRUE;
+    if (ref == var) return TRUE;
+    else if (ref == NULL || var == NULL) return FALSE;
+    p=p&&struct_equal(checksum,ktest_equal_checksum);
+    return p;
+}
+
 #ifdef ENABLE_LDAP
 static int equal_key_data(ref, var)
     krb5_key_data *ref;

Modified: branches/iakerb/src/tests/asn.1/ktest_equal.h
===================================================================
--- branches/iakerb/src/tests/asn.1/ktest_equal.h	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/ktest_equal.h	2010-03-29 16:18:20 UTC (rev 23843)
@@ -106,6 +106,12 @@
 int ktest_equal_ad_signedpath
     (krb5_ad_signedpath *ref,
 		    krb5_ad_signedpath *var);
+int ktest_equal_iakerb_header
+    (krb5_iakerb_header *ref,
+		    krb5_iakerb_header *var);
+int ktest_equal_iakerb_finished
+    (krb5_iakerb_finished *ref,
+		    krb5_iakerb_finished *var);
 
 int ktest_equal_ldap_sequence_of_keys(ldap_seqof_key_data *ref,
 				      ldap_seqof_key_data *var);

Modified: branches/iakerb/src/tests/asn.1/reference_encode.out
===================================================================
--- branches/iakerb/src/tests/asn.1/reference_encode.out	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/reference_encode.out	2010-03-29 16:18:20 UTC (rev 23843)
@@ -60,3 +60,5 @@
 encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
 encode_krb5_ad_signedpath_data: 30 81 C7 A0 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 32 30 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
 encode_krb5_ad_signedpath: 30 3E A0 03 02 01 01 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61
+encode_krb5_iakerb_header: 30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61
+encode_krb5_iakerb_finished: 30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34

Modified: branches/iakerb/src/tests/asn.1/trval_reference.out
===================================================================
--- branches/iakerb/src/tests/asn.1/trval_reference.out	2010-03-29 15:34:58 UTC (rev 23842)
+++ branches/iakerb/src/tests/asn.1/trval_reference.out	2010-03-29 16:18:20 UTC (rev 23843)
@@ -1332,3 +1332,16 @@
 .  .  .  [1] [Integer] 13
 .  .  .  [2] [Octet String] "pa-data"
 
+encode_krb5_iakerb_header:
+
+[Sequence/Sequence Of] 
+.  [1] [Octet String] "krb5data"
+.  [2] [Octet String] "krb5data"
+
+encode_krb5_iakerb_finished:
+
+[Sequence/Sequence Of] 
+.  [1] [Sequence/Sequence Of] 
+.  .  [0] [Integer] 1
+.  .  [1] [Octet String] "1234"
+




More information about the cvs-krb5 mailing list