From d6819928bc2319313b5a8f664dc74a9c8826fd0d Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 30 Apr 2010 21:22:48 +0000
Subject: [PATCH 114/120] Add IAKERB mechanism and
 gss_acquire_cred_with_password

Merge branches/iakerb to trunk.  Includes the following:

* New IAKERB mechanism.
* New gss_acquire_cred_with_password mechglue function.
* ASN.1 encoders and decoders for IAKERB structures (with tests).
* New shortcuts in gss-sample client and server.
* Tests to exercise SPNEGO and IAKERB using gss-sample application.

ticket: 6712

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23960 dc483132-0cff-0310-8789-dd5450dbe970
---
 src/appl/gss-sample/gss-client.c                 |   90 +-
 src/appl/gss-sample/gss-server.c                 |   13 +-
 src/appl/gss-sample/t_gss_sample.py              |   80 ++
 src/include/k5-int.h                             |   28 +
 src/include/krb5/krb5.hin                        |    1 +
 src/lib/gssapi/generic/gssapi_ext.h              |   27 +
 src/lib/gssapi/krb5/Makefile.in                  |    3 +
 src/lib/gssapi/krb5/accept_sec_context.c         |  175 ++--
 src/lib/gssapi/krb5/acquire_cred.c               |  343 ++++---
 src/lib/gssapi/krb5/add_cred.c                   |    6 +-
 src/lib/gssapi/krb5/gssapiP_krb5.h               |  140 ++-
 src/lib/gssapi/krb5/gssapi_err_krb5.et           |    1 +
 src/lib/gssapi/krb5/gssapi_krb5.c                |   55 +-
 src/lib/gssapi/krb5/gssapi_krb5.hin              |    1 +
 src/lib/gssapi/krb5/iakerb.c                     | 1046 ++++++++++++++++++++++
 src/lib/gssapi/krb5/init_sec_context.c           |  184 ++--
 src/lib/gssapi/krb5/inq_cred.c                   |    4 +
 src/lib/gssapi/krb5/inq_names.c                  |    3 +-
 src/lib/gssapi/krb5/rel_cred.c                   |    5 +
 src/lib/gssapi/krb5/ser_sctx.c                   |    1 +
 src/lib/gssapi/libgssapi_krb5.exports            |    2 +
 src/lib/gssapi/mechglue/Makefile.in              |    3 +
 src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c |  544 +++++++++++
 src/lib/gssapi/mechglue/g_initialize.c           |   94 +-
 src/lib/gssapi/mechglue/mglueP.h                 |    3 +-
 src/lib/gssapi/spnego/gssapiP_spnego.h           |   12 +
 src/lib/gssapi/spnego/spnego_mech.c              |   64 ++
 src/lib/krb5/asn.1/asn1_k_decode.c               |   41 +
 src/lib/krb5/asn.1/asn1_k_decode.h               |    6 +
 src/lib/krb5/asn.1/asn1_k_encode.c               |   31 +
 src/lib/krb5/asn.1/krb5_decode.c                 |   24 +
 src/lib/krb5/error_tables/krb5_err.et            |    4 +-
 src/lib/krb5/krb/kfree.c                         |   19 +
 src/lib/krb5/libkrb5.exports                     |    6 +
 src/tests/asn.1/krb5_decode_leak.c               |   22 +
 src/tests/asn.1/krb5_decode_test.c               |   16 +
 src/tests/asn.1/krb5_encode_test.c               |   22 +
 src/tests/asn.1/ktest.c                          |   35 +
 src/tests/asn.1/ktest.h                          |    4 +
 src/tests/asn.1/ktest_equal.c                    |   23 +
 src/tests/asn.1/ktest_equal.h                    |    6 +
 src/tests/asn.1/reference_encode.out             |    2 +
 src/tests/asn.1/trval_reference.out              |   13 +
 43 files changed, 2887 insertions(+), 315 deletions(-)
 create mode 100644 src/appl/gss-sample/t_gss_sample.py
 create mode 100644 src/lib/gssapi/krb5/iakerb.c
 create mode 100644 src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c

diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c
index ad314c2..d922cc3 100644
--- a/src/appl/gss-sample/gss-client.c
+++ b/src/appl/gss-sample/gss-client.c
@@ -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 @@ static void
 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 @@ connect_to_server(char *host, u_short port)
 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,6 +172,53 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
         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
@@ -213,7 +263,7 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
 
         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 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
                 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 @@ read_file(file_name, in_buf)
 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 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
     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 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
 
     /* 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 OM_uint32 min_stat;
 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 @@ main(argc, argv)
             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;
diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c
index 0e2a275..0b8a0e1 100644
--- a/src/appl/gss-sample/gss-server.c
+++ b/src/appl/gss-sample/gss-server.c
@@ -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 @@ usage()
 #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 @@ main(int argc, char **argv)
                     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--;
diff --git a/src/appl/gss-sample/t_gss_sample.py b/src/appl/gss-sample/t_gss_sample.py
new file mode 100644
index 0000000..09d0803
--- /dev/null
+++ b/src/appl/gss-sample/t_gss_sample.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2010 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.
+
+#!/usr/bin/python
+from k5test import *
+
+appdir = os.path.join(buildtop, 'appl', 'gss-sample')
+gss_client = os.path.join(appdir, 'gss-client')
+gss_server = os.path.join(appdir, 'gss-server')
+
+# Run a gss-server process and a gss-client process, with additional
+# gss-client flags given by options.  Verify that gss-client displayed
+# the expected output for a successful negotiation, and that we
+# obtained credentials for the host service.
+def server_client_test(realm, options):
+    portstr = str(realm.server_port())
+    server = realm.start_server([gss_server, '-port', portstr, 'host'],
+                                'starting...')
+    output = realm.run_as_client([gss_client, '-port', portstr] + options +
+                                 [hostname, 'host', 'testmsg'])
+    if 'Signature verified.' not in output:
+        fail('Expected message not seen in gss-client output')
+    stop_daemon(server)
+    realm.klist(realm.user_princ, realm.host_princ)
+
+# Make up a filename to hold user's initial credentials.
+def ccache_savefile(realm):
+    return os.path.join(realm.testdir, 'ccache.copy')
+
+# Move user's initial credentials into the save file.
+def ccache_save(realm):
+    os.rename(realm.ccache, ccache_savefile(realm))
+
+# Copy user's initial credentials from the save file into the ccache.
+def ccache_restore(realm):
+    shutil.copyfile(ccache_savefile(realm), realm.ccache)
+
+# Perform a regular (TGS path) test of the server and client.
+def tgs_test(realm, options):
+    ccache_restore(realm)
+    server_client_test(realm, options)
+
+# Perform a test of the server and client with initial credentials
+# obtained through gss_acquire_cred_with_password().
+def as_test(realm, options):
+    os.remove(realm.ccache)
+    server_client_test(realm, options + ['-user', realm.user_princ,
+                                         '-pass', password('user')])
+
+for realm in multipass_realms():
+    ccache_save(realm)
+
+    tgs_test(realm, ['-krb5'])
+    tgs_test(realm, ['-spnego'])
+    tgs_test(realm, ['-iakerb'])
+
+    as_test(realm, ['-krb5'])
+    as_test(realm, ['-spnego'])
+    as_test(realm, ['-iakerb'])
+
+success('GSS sample application')
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 5dc2d49..96c7d77 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -356,6 +356,10 @@ typedef INT64_TYPE krb5_int64;
 #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
@@ -1030,6 +1034,15 @@ typedef struct _krb5_ad_signedpath {
     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 *,
@@ -1327,6 +1340,9 @@ void KRB5_CALLCONV krb5_free_fast_finished(krb5_context, krb5_fast_finished *);
 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 +1756,12 @@ 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 **);
 
 krb5_error_code
@@ -1941,6 +1963,12 @@ decode_krb5_ad_kdcissued(const krb5_data *, krb5_ad_kdcissued **);
 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 {
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 98ffb70..1c89f9e 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -637,6 +637,7 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
 #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
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
index 1221677..8e5c7c9 100644
--- a/src/lib/gssapi/generic/gssapi_ext.h
+++ b/src/lib/gssapi/generic/gssapi_ext.h
@@ -47,6 +47,33 @@ int KRB5_CALLCONV __gss_userok
 	 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
  */
diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in
index 01591b2..2f4e969 100644
--- a/src/lib/gssapi/krb5/Makefile.in
+++ b/src/lib/gssapi/krb5/Makefile.in
@@ -52,6 +52,7 @@ SRCS = \
 	$(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 @@ OBJS = \
 	$(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 @@ STLIBOBJS = \
 	export_sec_context.o \
 	get_tkt_flags.o \
 	gssapi_krb5.o \
+	iakerb.o \
 	import_name.o \
 	import_sec_context.o \
 	indicate_mechs.o \
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 48307a2..9158ecf 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -105,7 +105,6 @@
 #endif
 #include <assert.h>
 
-
 #ifdef CFX_EXERCISE
 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
 #else
@@ -384,12 +383,49 @@ fail:
     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 @@ kg_accept_krb5(minor_status, context_handle,
     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;
@@ -757,16 +794,11 @@ kg_accept_krb5(minor_status, context_handle,
 
         /* 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) {
@@ -800,37 +832,41 @@ kg_accept_krb5(minor_status, context_handle,
 
             } /* 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;
+            }
+            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;
             }
-#endif
         }
     }
 
+    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;
@@ -847,6 +883,7 @@ kg_accept_krb5(minor_status, context_handle,
     }
 
     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;
@@ -1264,22 +1301,19 @@ done:
 #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;
 
@@ -1307,5 +1341,42 @@ 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);
+                          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);
 }
+
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index 2ccf7db..c3e8481 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -222,16 +222,17 @@ acquire_init_cred(krb5_context context,
                   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 @@ acquire_init_cred(krb5_context context,
     }
 
     /* 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) {
+        /* We will get initial creds later. */
+        code = krb5_cc_initialize(context, ccache, desired_name->princ);
+        if (code == 0)
+            code = krb5_cc_set_flags(context, ccache, 0);
+    }
+    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,6 +376,32 @@ acquire_init_cred(krb5_context context,
             *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 */
@@ -388,7 +421,7 @@ acquire_init_cred(krb5_context context,
     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 @@ acquire_init_cred(krb5_context context,
             *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 @@ acquire_init_cred(krb5_context context,
 }
 
 /*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,9 +525,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
     /*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;
     }
 
     /* verify that the requested mechanism set is the default, or
@@ -518,26 +547,21 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
         }
 
         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 */
-
-    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 = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
+    if (code != 0)
+        goto krb_error_out;
 
     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 +570,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
     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 +580,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
     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 +593,7 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
                                        (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 +601,24 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
     /* 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 +630,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
     } 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 +650,20 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
             (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 +675,29 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
 
     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 +744,76 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
     *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
+iakerb_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, 1);
+}
+
+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);
+}
+
diff --git a/src/lib/gssapi/krb5/add_cred.c b/src/lib/gssapi/krb5/add_cred.c
index 6f6707c..4897036 100644
--- a/src/lib/gssapi/krb5/add_cred.c
+++ b/src/lib/gssapi/krb5/add_cred.c
@@ -145,7 +145,8 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
     /* 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 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
         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 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
         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 */
 
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 2ac4b3a..1ec7be3 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -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 @@ typedef struct _krb5_gss_cred_id_rec {
     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 @@ typedef struct _krb5_gss_cred_id_rec {
     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,9 @@ extern k5_mutex_t gssint_krb5_keytab_lock;
 
 #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 +508,8 @@ kg_new_connection(
     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 +524,42 @@ OM_uint32 krb5_gss_acquire_cred
  OM_uint32*        /* time_rec */
 );
 
+OM_uint32
+iakerb_gss_acquire_cred
+(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 */
+);
+
+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 +582,24 @@ OM_uint32 krb5_gss_init_sec_context
  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 +615,22 @@ OM_uint32 krb5_gss_accept_sec_context
  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 +1171,53 @@ extern void krb5_gss_delete_error_info(void *p);
 #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_ */
diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et
index 4cfd68a..6396d59 100644
--- a/src/lib/gssapi/krb5/gssapi_err_krb5.et
+++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et
@@ -38,4 +38,5 @@ error_code KG_EMPTY_CCACHE, "Credential cache is empty"
 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
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c
index 0420085..f209986 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.c
+++ b/src/lib/gssapi/krb5/gssapi_krb5.c
@@ -127,34 +127,36 @@ const gss_OID_desc krb5_gss_oid_array[] = {
     {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,48 @@ static struct gss_config krb5_mechanism = {
     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;
+    iakerb_mechanism.gss_acquire_cred       = iakerb_gss_acquire_cred;
+
+    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";
@@ -769,6 +801,9 @@ int gss_krb5int_lib_init(void)
     err = gss_krb5mechglue_init();
     if (err)
         return err;
+    err = gss_iakerbmechglue_init();
+    if (err)
+        return err;
 #endif
 
     return 0;
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.hin b/src/lib/gssapi/krb5/gssapi_krb5.hin
index 783387a..ce96454 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.hin
+++ b/src/lib/gssapi/krb5/gssapi_krb5.hin
@@ -75,6 +75,7 @@ GSS_DLLIMP extern const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME;
 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;
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
new file mode 100644
index 0000000..3463a7f
--- /dev/null
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -0,0 +1,1046 @@
+/* -*- 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"
+
+/*
+ * 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;            /* Initiator only */
+    krb5_init_creds_context icc;        /* Initiator only */
+    krb5_tkt_creds_context tcc;         /* Initiator only */
+    gss_ctx_id_t gssc;
+    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;
+
+    krb5_init_creds_free(ctx->k5c, ctx->icc);
+    krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
+    krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL);
+    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_iakerb_finished iaf;
+
+    *finished = NULL;
+
+    memset(&iaf, 0, sizeof(iaf));
+
+    if (key == NULL)
+        return KRB5KDC_ERR_NULL_KEY;
+
+    code = krb5_k_make_checksum(context, 0, 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 = empty_data(), reply = empty_data();
+    krb5_data realm = empty_data();
+    OM_uint32 tmp;
+    int tcp_only, use_master;
+    krb5_ui_4 kdc_code;
+
+    output_token->length = 0;
+    output_token->value = NULL;
+
+    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;
+
+    for (tcp_only = 0; tcp_only <= 1; tcp_only++) {
+        use_master = 0;
+        code = krb5_sendto_kdc(ctx->k5c, &request, &realm,
+                               &reply, &use_master, tcp_only);
+        if (code == 0 && krb5_is_krb_error(&reply)) {
+            krb5_error *error;
+
+            code = decode_krb5_error(&reply, &error);
+            if (code != 0)
+                goto cleanup;
+            kdc_code = error->error;
+            krb5_free_error(ctx->k5c, error);
+            if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) {
+                krb5_free_data_contents(ctx->k5c, &reply);
+                reply = empty_data();
+                continue;
+            }
+        }
+        break;
+    }
+
+    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)
+        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->icc);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_init_creds_set_password(ctx->k5c, ctx->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->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->icc == NULL) {
+            code = iakerb_init_creds_ctx(ctx, cred, time_req);
+            if (code != 0)
+                goto cleanup;
+        }
+
+        code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm,
+                                    &flags);
+        if (code != 0)
+            goto cleanup;
+        if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
+            krb5_init_creds_get_times(ctx->k5c, ctx->icc, &times);
+            cred->tgt_expire = times.endtime;
+
+            krb5_init_creds_free(ctx->k5c, ctx->icc);
+            ctx->icc = NULL;
+
+            ctx->state = IAKERB_TGS_REQ;
+        } else
+            break;
+        in = empty_data();
+    case IAKERB_TGS_REQ:
+        if (ctx->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->tcc, &in, &out, &realm,
+                                   &flags);
+        if (code != 0)
+            goto cleanup;
+        if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) {
+            krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, &times);
+            cred->tgt_expire = times.endtime;
+
+            krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
+            ctx->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;
+
+    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 || code == KRB5_CC_NOT_KTYPE) {
+        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;
+
+        /* It would be nice if we could return KRB5KRB_AP_ERR_TKT_EXPIRED if
+         * the TGT is expired, for consistency with the krb5 mech.  As it
+         * stands, we won't see the expired TGT and will return
+         * KRB5_CC_NOTFOUND. */
+        code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
+                                    cred->ccache,
+                                    &in_creds, &out_creds);
+        if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL) {
+            *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->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->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->gssc;
+            ctx->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;
+    OM_uint32 tmpmin;
+    krb5_error_code code;
+    iakerb_ctx_id_t ctx;
+    gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
+    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 {
+        major_status = iakerb_gss_acquire_cred(minor_status, NULL,
+                                               GSS_C_INDEFINITE,
+                                               GSS_C_NULL_OID_SET,
+                                               GSS_C_INITIATE,
+                                               &defcred, NULL, NULL);
+        if (GSS_ERROR(major_status))
+            goto cleanup;
+        kcred = (krb5_gss_cred_id_t)defcred;
+    }
+
+    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->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,
+                                                     (gss_cred_id_t) kcred,
+                                                     &ctx->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->gssc;
+            ctx->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;
+    }
+    krb5_gss_release_cred(&tmpmin, &defcred);
+
+    return major_status;
+}
+
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 9b049ed..4a4cf7e 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -197,8 +197,38 @@ static krb5_error_code get_credentials(context, cred, server, now,
             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, &result_creds);
+    if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL &&
+        !cred->iakerb_mech) {
+        krb5_creds tgt_creds;
+
+        memset(&tgt_creds, 0, sizeof(tgt_creds));
+
+        /* No TGT in the ccache, but we can get one with the password. */
+        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, &result_creds);
+    }
     if (code)
         goto cleanup;
 
@@ -238,6 +268,7 @@ struct gss_checksum_data {
     krb5_gss_cred_id_t cred;
     krb5_checksum md5;
     krb5_data checksum_data;
+    krb5_gss_ctx_ext_t exts;
 };
 
 #include "../../krb5/krb/auth_con.h"
@@ -252,6 +283,7 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
     krb5_data credmsg;
     unsigned int junk;
     krb5_key send_subkey;
+    krb5_data *finished = NULL;
 
     data->checksum_data.data = 0;
     credmsg.data = 0;
@@ -293,8 +325,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
             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;
@@ -316,6 +348,26 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
     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
@@ -323,9 +375,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
 
     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;
@@ -341,19 +392,25 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
         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;
@@ -362,13 +419,13 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
     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;
     struct gss_checksum_data cksum_struct;
     krb5_checksum md5;
     krb5_data ap_req;
-    krb5_data *checksum_data = NULL;
     unsigned char *ptr;
     unsigned char *t;
     unsigned int tlen;
@@ -387,22 +444,9 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
     cksum_struct.ctx = ctx;
     cksum_struct.cred = cred;
     cksum_struct.checksum_data.data = NULL;
-    switch (k_cred->keyblock.enctype) {
-    case ENCTYPE_DES_CBC_CRC:
-    case ENCTYPE_DES_CBC_MD4:
-    case ENCTYPE_DES_CBC_MD5:
-    case ENCTYPE_DES3_CBC_SHA1:
-        code = make_gss_checksum(context, ctx->auth_context, &cksum_struct,
-                                 &checksum_data);
-        if (code)
-            goto cleanup;
-        break;
-    default:
-        krb5_auth_con_set_checksum_func(context, ctx->auth_context,
-                                        make_gss_checksum, &cksum_struct);
-        break;
-    }
-
+    cksum_struct.exts = exts;
+    krb5_auth_con_set_checksum_func(context, ctx->auth_context,
+                                    make_gss_checksum, &cksum_struct);
 
     /* call mk_req.  subkey and ap_req need to be used or destroyed */
 
@@ -413,7 +457,7 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
 
     krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
     code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
-                                checksum_data, k_cred, &ap_req);
+                                NULL, k_cred, &ap_req);
     krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
     krb5_free_data_contents(context, &cksum_struct.checksum_data);
     if (code)
@@ -459,8 +503,6 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
     code = 0;
 
 cleanup:
-    if (checksum_data && checksum_data->data)
-        krb5_free_data_contents(context, checksum_data);
     if (ap_req.data)
         krb5_free_data_contents(context, &ap_req);
 
@@ -488,7 +530,8 @@ kg_new_connection(
     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;
@@ -528,6 +571,7 @@ kg_new_connection(
 
     /* 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;
@@ -606,7 +650,7 @@ kg_new_connection(
         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;
@@ -892,24 +936,21 @@ fail:
 }
 
 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;
@@ -1027,7 +1068,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
                                          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);
@@ -1107,3 +1148,44 @@ krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
     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);
+}
+
diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c
index 9af0e4e..8083b27 100644
--- a/src/lib/gssapi/krb5/inq_cred.c
+++ b/src/lib/gssapi/krb5/inq_cred.c
@@ -166,6 +166,10 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
             (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)
diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c
index a3de420..19c477f 100644
--- a/src/lib/gssapi/krb5/inq_names.c
+++ b/src/lib/gssapi/krb5/inq_names.c
@@ -44,7 +44,8 @@ krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types)
      */
     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);
     }
diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c
index 05e24b2..d1c571a 100644
--- a/src/lib/gssapi/krb5/rel_cred.c
+++ b/src/lib/gssapi/krb5/rel_cred.c
@@ -77,6 +77,11 @@ krb5_gss_release_cred(minor_status, cred_handle)
     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;
diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c
index f5c1081..63a7bd8 100644
--- a/src/lib/gssapi/krb5/ser_sctx.c
+++ b/src/lib/gssapi/krb5/ser_sctx.c
@@ -610,6 +610,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
              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 */
diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports
index e038f50..5b00fc2 100644
--- a/src/lib/gssapi/libgssapi_krb5.exports
+++ b/src/lib/gssapi/libgssapi_krb5.exports
@@ -9,6 +9,7 @@ GSS_C_NT_USER_NAME
 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 @@ gss_krb5int_unseal_token_v3
 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
diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in
index f435711..92cd6c9 100644
--- a/src/lib/gssapi/mechglue/Makefile.in
+++ b/src/lib/gssapi/mechglue/Makefile.in
@@ -12,6 +12,7 @@ DEFS=-D_GSS_STATIC_LINK=1
 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 @@ SRCS = \
 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 @@ OBJS = \
 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 \
diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
new file mode 100644
index 0000000..f866303
--- /dev/null
+++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
@@ -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);
+}
diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
index 70c2203..a393a53 100644
--- a/src/lib/gssapi/mechglue/g_initialize.c
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -609,6 +609,10 @@ releaseMechInfo(gss_mech_info *pCf)
 		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 @@ gssint_register_mechinfo(gss_mech_info template)
 	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 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
 	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)
 {
@@ -906,59 +935,50 @@ gssint_get_mechanism_ext(oid)
 const gss_OID oid;
 {
 	gss_mech_info aMech;
-	gss_mechanism_ext mech_ext;
 
 	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);
-
-	if (aMech->dl_handle == NULL)
-		return (NULL);
-
-	/* Load the gss_config_ext struct for this mech */
-
-	mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
-
-	if (mech_ext == NULL)
-		return (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_*.
+	 * might need to re-read the configuration file before loading
+	 * the mechanism to ensure we have the latest info.
 	 */
-	mech_ext->gss_acquire_cred_with_password =
-		(gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle,
-			"gssspi_acquire_cred_with_password");
-#endif
+	updateMechList();
+
+	aMech = searchMechList(oid);
+
+	/* 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);
+	}
 
-	/* Set aMech->mech_ext */
-	(void) k5_mutex_lock(&g_mechListLock);
+	/* has another thread loaded the mech */
+	if (aMech->mech_ext) {
+		(void) k5_mutex_unlock(&g_mechListLock);
+		return (aMech->mech_ext);
+	}
 
-	if (aMech->mech_ext == NULL)
-		aMech->mech_ext = mech_ext;
-	else
-		free(mech_ext);	/* we raced and lost; don't leak */
+	/* 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);
+	}
 
 	(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.
  *
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index 3769caf..f219290 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -77,7 +77,6 @@ typedef struct gss_cred_id_struct {
 } 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 @@ typedef struct gss_config {
 
 /* 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;
 
 /*
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
index a1cd2b4..d72c85d 100644
--- a/src/lib/gssapi/spnego/gssapiP_spnego.h
+++ b/src/lib/gssapi/spnego/gssapiP_spnego.h
@@ -458,6 +458,18 @@ spnego_gss_acquire_cred_impersonate_name(
     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,
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 6c4ea0a..62e7099 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -274,6 +274,11 @@ static struct gss_config spnego_mechanism =
 	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 @@ static int gss_spnegomechglue_init(void)
 
 	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;
 
@@ -2398,6 +2404,64 @@ spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
 }
 
 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, tmpmin;
+	gss_OID_set amechs = GSS_C_NULL_OID_SET, dmechs;
+	gss_cred_id_t mcred = NULL;
+	spnego_gss_cred_id_t spcred = NULL;
+
+	dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
+
+	if (actual_mechs)
+		*actual_mechs = NULL;
+
+	if (time_rec)
+		*time_rec = 0;
+
+	dmechs = desired_mechs;
+	if (desired_mechs == GSS_C_NULL_OID_SET) {
+		status = get_available_mechs(minor_status, desired_name,
+					     cred_usage, NULL, &amechs);
+		dmechs = amechs;
+	}
+
+	status = gss_acquire_cred_with_password(minor_status, desired_name,
+						password, time_req, dmechs,
+						cred_usage, &mcred,
+						actual_mechs, time_rec);
+	if (status != GSS_S_COMPLETE)
+	    goto cleanup;
+
+	spcred = malloc(sizeof(spnego_gss_cred_id_rec));
+	if (spcred == NULL) {
+		*minor_status = ENOMEM;
+		status = GSS_S_FAILURE;
+		goto cleanup;
+	}
+	spcred->neg_mechs = GSS_C_NULL_OID_SET;
+	spcred->mcred = mcred;
+	mcred = GSS_C_NO_CREDENTIAL;
+	*output_cred_handle = (gss_cred_id_t)spcred;
+
+cleanup:
+
+	(void) gss_release_oid_set(&tmpmin, &amechs);
+	(void) gss_release_cred(&tmpmin, &mcred);
+
+	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,
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 60d9455..c2dd5f6 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -1899,6 +1899,47 @@ error_out:
     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 */
 
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h
index 1b0aa75..79a4a05 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -271,4 +271,10 @@ asn1_error_code asn1_decode_ad_kdcissued_ptr(asn1buf *buf,
 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
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index a35f561..d334ae6 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1394,6 +1394,35 @@ static unsigned int ad_signedpath_optional(const void *p)
 
 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_fast_response, fast_response);
 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);
 
 
 
diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c
index 542a626..7aeb6bf 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -1218,6 +1218,30 @@ decode_krb5_ad_signedpath(const krb5_data *code, krb5_ad_signedpath **repptr)
     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,
diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et
index bf3404d..56434ad 100644
--- a/src/lib/krb5/error_tables/krb5_err.et
+++ b/src/lib/krb5/error_tables/krb5_err.et
@@ -126,8 +126,8 @@ error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED,	"Public key encrypti
 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"
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index 6af3601..4f38f34 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -927,3 +927,22 @@ krb5_free_ad_signedpath(krb5_context context, krb5_ad_signedpath *val)
     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);
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index cab6e9b..fedecf6 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -22,6 +22,8 @@ decode_krb5_error
 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_error
 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_etype_info
 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
diff --git a/src/tests/asn.1/krb5_decode_leak.c b/src/tests/asn.1/krb5_decode_leak.c
index ab46fb9..ac7f5bf 100644
--- a/src/tests/asn.1/krb5_decode_leak.c
+++ b/src/tests/asn.1/krb5_decode_leak.c
@@ -704,6 +704,28 @@ main(int argc, char **argv)
                   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;
 }
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index 2ef70cf..b3480a6 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -916,6 +916,22 @@ int main(argc, argv)
         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 */
     {
diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
index 9694746..784d203 100644
--- a/src/tests/asn.1/krb5_encode_test.c
+++ b/src/tests/asn.1/krb5_encode_test.c
@@ -740,6 +740,28 @@ main(argc, argv)
                    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;
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index 370b640..f51273e 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -892,6 +892,28 @@ krb5_error_code ktest_make_sample_ad_signedpath(p)
     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)
 {
@@ -1534,6 +1556,19 @@ void ktest_empty_ad_signedpath(p)
     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;
diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
index 5f9b5ca..c4059a7 100644
--- a/src/tests/asn.1/ktest.h
+++ b/src/tests/asn.1/ktest.h
@@ -109,6 +109,8 @@ krb5_error_code ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 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_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
 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);
diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
index f84357b..0a92804 100644
--- a/src/tests/asn.1/ktest_equal.c
+++ b/src/tests/asn.1/ktest_equal.c
@@ -600,6 +600,29 @@ int ktest_equal_ad_signedpath(ref, var)
     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;
diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
index 80c38b6..4b55e23 100644
--- a/src/tests/asn.1/ktest_equal.h
+++ b/src/tests/asn.1/ktest_equal.h
@@ -106,6 +106,12 @@ int ktest_equal_ad_signedpath_data
 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);
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index c6bcf61..92d21b1 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -60,3 +60,5 @@ encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30
 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
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index 38e5b99..cc1daf3 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1332,3 +1332,16 @@ encode_krb5_ad_signedpath:
 .  .  .  [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"
+
-- 
2.6.6

