Merge branch 'fastreauth'
authorLuke Howard <lukeh@padl.com>
Thu, 23 Sep 2010 23:42:23 +0000 (01:42 +0200)
committerLuke Howard <lukeh@padl.com>
Thu, 23 Sep 2010 23:42:23 +0000 (01:42 +0200)
Conflicts:
mech_eap/util_cred.c

28 files changed:
Makefile.am
TODO
accept_sec_context.c
authdata_plugin.h [new file with mode: 0644]
configure.ac
display_status.c
eap_mech.c
export_sec_context.c
gssapiP_eap.h
import_sec_context.c
init_sec_context.c
mech
radius_ad.exports [new file with mode: 0644]
set_cred_option.c
store_cred.c
unwrap.c
unwrap_iov.c
util.h
util_adshim.c [new file with mode: 0644]
util_context.c
util_cred.c
util_name.c
util_reauth.c [new file with mode: 0644]
util_reauth.h [new file with mode: 0644]
util_token.c
wrap.c
wrap_iov.c
wrap_iov_length.c

index aa22204..9f2167a 100644 (file)
@@ -1,21 +1,21 @@
 AUTOMAKE_OPTIONS = foreign
 
 gssdir = $(libdir)/gss
+gss_LTLIBRARIES = mech_eap.la
 
-gss_LTLIBRARIES = libmech_eap.la
+mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\"
+mech_eap_la_CFLAGS   = -g -Wall -fno-strict-aliasing \
+                       @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@
+mech_eap_la_CXXFLAGS = -g -Wall \
+                       @EAP_CFLAGS@ @KRB5_CFLAGS@ @SHIBSP_CXXFLAGS@ \
+                       @SHIBRESOLVER_CXXFLAGS@ @TARGET_CFLAGS@
+mech_eap_la_LDFLAGS  = -avoid-version -module \
+                       -export-symbols mech_eap.exports -no-undefined \
+                       @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@
+mech_eap_la_LIBADD   = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ \
+                      @SHIBRESOLVER_LIBS@ -lfreeradius-client
 
-libmech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\"
-libmech_eap_la_CFLAGS   = -g -Wall -fno-strict-aliasing \
-                         @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@
-libmech_eap_la_CXXFLAGS = -g -Wall \
-                         @EAP_CFLAGS@ @KRB5_CFLAGS@ @SHIBSP_CXXFLAGS@ \
-                         @SHIBRESOLVER_CXXFLAGS@ @TARGET_CFLAGS@
-libmech_eap_la_LDFLAGS  = -export-symbols mech_eap.exports -version-info 0:0:0 \
-                         -no-undefined \
-                         @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@
-libmech_eap_la_LIBADD   = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ @SHIBRESOLVER_LIBS@ -lfreeradius-client
-
-libmech_eap_la_SOURCES =                       \
+mech_eap_la_SOURCES =                          \
        accept_sec_context.c                    \
        acquire_cred.c                          \
        acquire_cred_with_password.c            \
@@ -81,3 +81,16 @@ libmech_eap_la_SOURCES =                     \
        wrap_iov_length.c                       \
        wrap_size_limit.c
 
+if GSSEAP_ENABLE_REAUTH
+mech_eap_la_SOURCES += util_reauth.c
+
+krb5pluginsdir = $(libdir)/krb5/plugins/authdata
+krb5plugins_LTLIBRARIES = radius_ad.la
+
+radius_ad_la_CFLAGS  = -g -Wall -fno-strict-aliasing \
+                         @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@
+radius_ad_la_LDFLAGS = -avoid-version -module \
+                      -export-symbols radius_ad.exports -no-undefined
+radius_ad_la_LIBADD  = @KRB5_LIBS@
+radius_ad_la_SOURCES = util_adshim.c
+endif
diff --git a/TODO b/TODO
index bbfbc03..cdc5491 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
 - hook up libradius, AVP parsing logic
 - better handling of mechanism-specific error namespace
 - better interfaces for initiator EAP configuration/credential management
-- libradius library handle is a global variable
-- radius expiry time
+- make CBT ASN.1
index bc2f295..ba0f021 100644 (file)
 
 #include "gssapiP_eap.h"
 
+#ifdef GSSEAP_ENABLE_REAUTH
+static OM_uint32
+eapGssSmAcceptGssReauth(OM_uint32 *minor,
+                        gss_ctx_id_t ctx,
+                        gss_cred_id_t cred,
+                        gss_buffer_t inputToken,
+                        gss_channel_bindings_t chanBindings,
+                        gss_buffer_t outputToken);
+#endif
+
 /*
  * Mark a context as ready for cryptographic operations
  */
 static OM_uint32
-acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
+acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
 {
     OM_uint32 major, tmpMinor;
     VALUE_PAIR *vp;
@@ -261,7 +271,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         pos[0] == EAP_TYPE_IDENTITY) {
         /*
          * XXX TODO do we really need to set User-Name? FreeRADIUS does
-         * not appear to require it.
+         * not require it but some other RADIUS servers might.
          */
         major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, 0, &nameBuf);
         if (GSS_ERROR(major))
@@ -322,11 +332,11 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         ctx->acceptorCtx.avps = received;
         received = NULL;
 
-        major = acceptReady(minor, ctx, cred);
+        major = acceptReadyEap(minor, ctx, cred);
         if (GSS_ERROR(major))
             goto cleanup;
 
-        ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
+        ctx->state = EAP_STATE_EXTENSIONS_REQ;
     }
 
     major = GSS_S_CONTINUE_NEEDED;
@@ -339,56 +349,99 @@ cleanup:
 }
 
 static OM_uint32
-eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
-                                 gss_ctx_id_t ctx,
-                                 gss_cred_id_t cred,
-                                 gss_buffer_t inputToken,
-                                 gss_channel_bindings_t chanBindings,
-                                 gss_buffer_t outputToken)
+acceptGssChannelBindings(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         gss_cred_id_t cred,
+                         gss_buffer_t inputToken,
+                         gss_channel_bindings_t chanBindings)
 {
-    OM_uint32 major;
+    OM_uint32 major, tmpMinor;
     gss_iov_buffer_desc iov[2];
 
-    outputToken->length = 0;
-    outputToken->value = NULL;
+    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+    iov[0].buffer.length = 0;
+    iov[0].buffer.value = NULL;
 
-    if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) {
-        ctx->state = EAP_STATE_ESTABLISHED;
-        return GSS_S_COMPLETE;
-    }
+    iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
+    iov[1].buffer = *inputToken;
 
-    if (inputToken->length < 14) {
-        return GSS_S_DEFECTIVE_TOKEN;
+    major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+                                    iov, 2, TOK_TYPE_WRAP);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
+        !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
+        major = GSS_S_BAD_BINDINGS;
+    } else {
+        major = GSS_S_CONTINUE_NEEDED;
     }
 
-    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
-    iov[0].buffer.length = 0;
-    iov[0].buffer.value = NULL;
+    gss_release_buffer(&tmpMinor, &iov[0].buffer);
 
-    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
-        iov[0].buffer = chanBindings->application_data;
+    return major;
+}
 
-    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
-    iov[1].buffer.length = 16;
-    iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
+static OM_uint32
+eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
+                            gss_ctx_id_t ctx,
+                            gss_cred_id_t cred,
+                            gss_buffer_t inputToken,
+                            gss_channel_bindings_t chanBindings,
+                            gss_buffer_t outputToken)
+{
+    OM_uint32 major;
 
-    assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
+    outputToken->length = 0;
+    outputToken->value = NULL;
 
-    iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
-    iov[2].buffer.length = inputToken->length - 14;
-    iov[2].buffer.value = (unsigned char *)inputToken->value + 14;
+    major = acceptGssChannelBindings(minor, ctx, cred, inputToken,
+                                     chanBindings);
+    if (GSS_ERROR(major))
+        return major;
 
-    major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
-                                    iov, 3, TOK_TYPE_GSS_CB);
-    if (major == GSS_S_COMPLETE) {
-        ctx->state = EAP_STATE_ESTABLISHED;
+    ctx->state = EAP_STATE_EXTENSIONS_RESP;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
+                             gss_ctx_id_t ctx,
+                             gss_cred_id_t cred,
+                             gss_buffer_t inputToken,
+                             gss_channel_bindings_t chanBindings,
+                             gss_buffer_t outputToken)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc credsToken = GSS_C_EMPTY_BUFFER;
+
+    /*
+     * If we're built with fast reauthentication enabled, then
+     * fabricate a ticket from the initiator to ourselves.
+     * Otherwise return an empty token.
+     */
+#ifdef GSSEAP_ENABLE_REAUTH
+    major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken);
+    if (GSS_ERROR(major))
+        return major;
+#else
+    credsToken.value = "";
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+    major = duplicateBuffer(minor, &credsToken, outputToken);
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &credsToken);
+        return major;
     }
 
-#if 0
-    gss_release_buffer(&tmpMinor, &iov[0].buffer);
+#ifdef GSSEAP_ENABLE_REAUTH
+    gss_release_buffer(&tmpMinor, &credsToken);
 #endif
 
-    return major;
+    ctx->state = EAP_STATE_ESTABLISHED;
+
+    return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -414,10 +467,14 @@ static struct gss_eap_acceptor_sm {
                               gss_channel_bindings_t,
                               gss_buffer_t);
 } eapGssAcceptorSm[] = {
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptIdentity           },
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate       },
-    { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     eapGssSmAcceptGssChannelBindings },
-    { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished        },
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,    eapGssSmAcceptIdentity           },
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,    eapGssSmAcceptAuthenticate       },
+    { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,       eapGssSmAcceptExtensionsReq      },
+    { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,   eapGssSmAcceptExtensionsResp     },
+    { TOK_TYPE_NONE,        TOK_TYPE_NONE,       eapGssSmAcceptEstablished        },
+#ifdef GSSEAP_ENABLE_REAUTH
+    { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth          },
+#endif
 };
 
 OM_uint32
@@ -439,6 +496,8 @@ gss_accept_sec_context(OM_uint32 *minor,
     struct gss_eap_acceptor_sm *sm = NULL;
     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
+    enum gss_eap_token_type tokType;
+    int initialContextToken = 0;
 
     *minor = 0;
 
@@ -458,6 +517,7 @@ gss_accept_sec_context(OM_uint32 *minor,
         if (GSS_ERROR(major))
             return major;
 
+        initialContextToken = 1;
         *context_handle = ctx;
     }
 
@@ -466,12 +526,29 @@ gss_accept_sec_context(OM_uint32 *minor,
     sm = &eapGssAcceptorSm[ctx->state];
 
     major = gssEapVerifyToken(minor, ctx, input_token,
-                              sm->inputTokenType, &innerInputToken);
+                              &tokType, &innerInputToken);
     if (GSS_ERROR(major))
         goto cleanup;
 
+#ifdef GSSEAP_ENABLE_REAUTH
+    /*
+     * If we're built with fast reauthentication support, it's valid
+     * for an initiator to send a GSS reauthentication token as its
+     * initial context token, causing us to short-circuit the state
+     * machine and process Kerberos GSS messages instead.
+     */
+    if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) {
+        ctx->state = EAP_STATE_KRB_REAUTH_GSS;
+    } else
+#endif
+    if (tokType != sm->inputTokenType) {
+        major = GSS_S_DEFECTIVE_TOKEN;
+        goto cleanup;
+    }
+
     /* If credentials were provided, check they're usable with this mech */
-    if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+    if (cred != GSS_C_NO_CREDENTIAL &&
+        !gssEapCredAvailable(cred, ctx->mechanismUsed)) {
         major = GSS_S_BAD_MECH;
         goto cleanup;
     }
@@ -493,7 +570,7 @@ gss_accept_sec_context(OM_uint32 *minor,
         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
     }
-    if (innerOutputToken.length != 0) {
+    if (innerOutputToken.value != NULL) {
         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
                                    sm->outputTokenType, output_token);
         if (GSS_ERROR(tmpMajor)) {
@@ -530,3 +607,75 @@ cleanup:
     return major;
 }
 
+#ifdef GSSEAP_ENABLE_REAUTH
+static OM_uint32
+acceptReadyKrb(OM_uint32 *minor,
+               gss_ctx_id_t ctx,
+               gss_cred_id_t cred,
+               const gss_name_t initiator,
+               const gss_OID mech,
+               OM_uint32 timeRec)
+{
+    OM_uint32 major;
+
+    major = gssEapGlueToMechName(minor, initiator, &ctx->initiatorName);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
+        major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
+    if (GSS_ERROR(major))
+        return major;
+
+    ctx->state = EAP_STATE_ESTABLISHED;
+
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+eapGssSmAcceptGssReauth(OM_uint32 *minor,
+                        gss_ctx_id_t ctx,
+                        gss_cred_id_t cred,
+                        gss_buffer_t inputToken,
+                        gss_channel_bindings_t chanBindings,
+                        gss_buffer_t outputToken)
+{
+    OM_uint32 major, tmpMinor;
+    gss_cred_id_t krbCred = GSS_C_NO_CREDENTIAL;
+    gss_name_t krbInitiator = GSS_C_NO_NAME;
+    gss_OID mech = GSS_C_NO_OID;
+    OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
+
+    ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS;
+
+    if (cred != GSS_C_NO_CREDENTIAL)
+        krbCred = cred->krbCred;
+
+    major = gssAcceptSecContext(minor,
+                                &ctx->kerberosCtx,
+                                krbCred,
+                                inputToken,
+                                chanBindings,
+                                &krbInitiator,
+                                &mech,
+                                outputToken,
+                                &gssFlags,
+                                &timeRec,
+                                NULL);
+    if (major == GSS_S_COMPLETE) {
+        major = acceptReadyKrb(minor, ctx, cred,
+                               krbInitiator, mech, timeRec);
+    }
+
+    ctx->gssFlags = gssFlags;
+
+    gssReleaseName(&tmpMinor, &krbInitiator);
+
+    return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
diff --git a/authdata_plugin.h b/authdata_plugin.h
new file mode 100644 (file)
index 0000000..32bff2f
--- /dev/null
@@ -0,0 +1,331 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * krb5/authdata_plugin.h
+ *
+ * Copyright (C) 2007 Apple Inc.  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.
+ *
+ * AuthorizationData plugin definitions for Kerberos 5.
+ */
+
+/*
+ * This is considered an INTERNAL interface at this time.
+ *
+ * Some work is needed before exporting it:
+ *
+ * + Documentation.
+ * + Sample code.
+ * + Test cases (preferably automated testing under "make check").
+ * + Hook into TGS exchange too; will change API.
+ * + Examine memory management issues, especially for Windows; may
+ *   change API.
+ *
+ * Other changes that would be nice to have, but not necessarily
+ * before making this interface public:
+ *
+ * + Library support for AD-IF-RELEVANT and similar wrappers.  (We can
+ *   make the plugin construct them if it wants them.)
+ * + KDC could combine/optimize wrapped AD elements provided by
+ *   multiple plugins, e.g., two IF-RELEVANT sequences could be
+ *   merged.  (The preauth plugin API also has this bug, we're going
+ *   to need a general fix.)
+ */
+
+#ifndef KRB5_AUTHDATA_PLUGIN_H_INCLUDED
+#define KRB5_AUTHDATA_PLUGIN_H_INCLUDED
+#include <krb5/krb5.h>
+
+/*
+ * While arguments of these types are passed-in, for the most part a
+ * authorization data module can treat them as opaque.  If we need
+ * keying data, we can ask for it directly.
+ */
+struct _krb5_db_entry_new;
+
+/*
+ * The function table / structure which an authdata server module must export as
+ * "authdata_server_0".  NOTE: replace "0" with "1" for the type and
+ * variable names if this gets picked up by upstream.  If the interfaces work
+ * correctly, future versions of the table will add either more callbacks or
+ * more arguments to callbacks, and in both cases we'll be able to wrap the v0
+ * functions.
+ */
+/* extern krb5plugin_authdata_ftable_v0 authdata_server_0; */
+typedef struct krb5plugin_authdata_server_ftable_v0 {
+    /* Not-usually-visible name. */
+    char *name;
+
+    /*
+     * Per-plugin initialization/cleanup.  The init function is called
+     * by the KDC when the plugin is loaded, and the fini function is
+     * called before the plugin is unloaded.  Both are optional.
+     */
+    krb5_error_code (*init_proc)(krb5_context, void **);
+    void (*fini_proc)(krb5_context, void *);
+    /*
+     * Actual authorization data handling function.  If this field
+     * holds a null pointer, this mechanism will be skipped, and the
+     * init/fini functions will not be run.
+     *
+     * This function should only modify the field
+     * enc_tkt_reply->authorization_data.  All other values should be
+     * considered inputs only.  And, it should *modify* the field, not
+     * overwrite it and assume that there are no other authdata
+     * plugins in use.
+     *
+     * Memory management: authorization_data is a malloc-allocated,
+     * null-terminated sequence of malloc-allocated pointers to
+     * authorization data structures.  This plugin code currently
+     * assumes the libraries, KDC, and plugin all use the same malloc
+     * pool, which may be a problem if/when we get the KDC code
+     * running on Windows.
+     *
+     * If this function returns a non-zero error code, a message
+     * is logged, but no other action is taken.  Other authdata
+     * plugins will be called, and a response will be sent to the
+     * client (barring other problems).
+     */
+    krb5_error_code (*authdata_proc)(krb5_context,
+                                     struct _krb5_db_entry_new *client,
+                                     krb5_data *req_pkt,
+                                     krb5_kdc_req *request,
+                                     krb5_enc_tkt_part *enc_tkt_reply);
+} krb5plugin_server_authdata_ftable_v0;
+
+typedef krb5plugin_server_authdata_ftable_v0 krb5plugin_authdata_ftable_v0;
+
+typedef struct krb5plugin_authdata_server_ftable_v2 {
+    /* Not-usually-visible name. */
+    char *name;
+
+    /*
+     * Per-plugin initialization/cleanup.  The init function is called
+     * by the KDC when the plugin is loaded, and the fini function is
+     * called before the plugin is unloaded.  Both are optional.
+     */
+    krb5_error_code (*init_proc)(krb5_context, void **);
+    void (*fini_proc)(krb5_context, void *);
+    /*
+     * Actual authorization data handling function.  If this field
+     * holds a null pointer, this mechanism will be skipped, and the
+     * init/fini functions will not be run.
+     *
+     * This function should only modify the field
+     * enc_tkt_reply->authorization_data.  All other values should be
+     * considered inputs only.  And, it should *modify* the field, not
+     * overwrite it and assume that there are no other authdata
+     * plugins in use.
+     *
+     * Memory management: authorization_data is a malloc-allocated,
+     * null-terminated sequence of malloc-allocated pointers to
+     * authorization data structures.  This plugin code currently
+     * assumes the libraries, KDC, and plugin all use the same malloc
+     * pool, which may be a problem if/when we get the KDC code
+     * running on Windows.
+     *
+     * If this function returns a non-zero error code, a message
+     * is logged, but no other action is taken.  Other authdata
+     * plugins will be called, and a response will be sent to the
+     * client (barring other problems).
+     */
+    krb5_error_code (*authdata_proc)(krb5_context,
+                                     unsigned int flags,
+                                     struct _krb5_db_entry_new *client,
+                                     struct _krb5_db_entry_new *server,
+                                     struct _krb5_db_entry_new *tgs,
+                                     krb5_keyblock *client_key,
+                                     krb5_keyblock *server_key,
+                                     krb5_keyblock *tgs_key,
+                                     krb5_data *req_pkt,
+                                     krb5_kdc_req *request,
+                                     krb5_const_principal for_user_princ,
+                                     krb5_enc_tkt_part *enc_tkt_request,
+                                     krb5_enc_tkt_part *enc_tkt_reply);
+} krb5plugin_authdata_server_ftable_v2;
+
+typedef krb5plugin_authdata_server_ftable_v2 krb5plugin_authdata_ftable_v2;
+
+typedef krb5_error_code
+(*authdata_client_plugin_init_proc)(krb5_context context,
+                                    void **plugin_context);
+
+#define AD_USAGE_AS_REQ         0x01
+#define AD_USAGE_TGS_REQ        0x02
+#define AD_USAGE_AP_REQ         0x04
+#define AD_USAGE_KDC_ISSUED     0x08
+#define AD_USAGE_MASK           0x0F
+#define AD_INFORMATIONAL        0x10
+
+struct _krb5_authdata_context;
+
+typedef void
+(*authdata_client_plugin_flags_proc)(krb5_context kcontext,
+                                     void *plugin_context,
+                                     krb5_authdatatype ad_type,
+                                     krb5_flags *flags);
+
+typedef void
+(*authdata_client_plugin_fini_proc)(krb5_context kcontext,
+                                    void *plugin_context);
+
+typedef krb5_error_code
+(*authdata_client_request_init_proc)(krb5_context kcontext,
+                                     struct _krb5_authdata_context *context,
+                                     void *plugin_context,
+                                     void **request_context);
+
+typedef void
+(*authdata_client_request_fini_proc)(krb5_context kcontext,
+                                     struct _krb5_authdata_context *context,
+                                     void *plugin_context,
+                                     void *request_context);
+
+typedef krb5_error_code
+(*authdata_client_import_authdata_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_authdata **authdata,
+                                        krb5_boolean kdc_issued_flag,
+                                        krb5_const_principal issuer);
+
+typedef krb5_error_code
+(*authdata_client_export_authdata_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_flags usage,
+                                        krb5_authdata ***authdata);
+
+typedef krb5_error_code
+(*authdata_client_get_attribute_types_proc)(krb5_context kcontext,
+                                            struct _krb5_authdata_context *context,
+                                            void *plugin_context,
+                                            void *request_context,
+                                            krb5_data **attrs);
+
+typedef krb5_error_code
+(*authdata_client_get_attribute_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      const krb5_data *attribute,
+                                      krb5_boolean *authenticated,
+                                      krb5_boolean *complete,
+                                      krb5_data *value,
+                                      krb5_data *display_value,
+                                      int *more);
+
+typedef krb5_error_code
+(*authdata_client_set_attribute_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      krb5_boolean complete,
+                                      const krb5_data *attribute,
+                                      const krb5_data *value);
+
+typedef krb5_error_code
+(*authdata_client_delete_attribute_proc)(krb5_context kcontext,
+                                         struct _krb5_authdata_context *context,
+                                         void *plugin_context,
+                                         void *request_context,
+                                         const krb5_data *attribute);
+
+typedef krb5_error_code
+(*authdata_client_export_internal_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_boolean restrict_authenticated,
+                                        void **ptr);
+
+typedef void
+(*authdata_client_free_internal_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      void *ptr);
+
+typedef krb5_error_code
+(*authdata_client_verify_proc)(krb5_context kcontext,
+                               struct _krb5_authdata_context *context,
+                               void *plugin_context,
+                               void *request_context,
+                               const krb5_auth_context *auth_context,
+                               const krb5_keyblock *key,
+                               const krb5_ap_req *req);
+
+typedef krb5_error_code
+(*authdata_client_size_proc)(krb5_context kcontext,
+                             struct _krb5_authdata_context *context,
+                             void *plugin_context,
+                             void *request_context,
+                             size_t *sizep);
+
+typedef krb5_error_code
+(*authdata_client_externalize_proc)(krb5_context kcontext,
+                                    struct _krb5_authdata_context *context,
+                                    void *plugin_context,
+                                    void *request_context,
+                                    krb5_octet **buffer,
+                                    size_t *lenremain);
+
+typedef krb5_error_code
+(*authdata_client_internalize_proc)(krb5_context kcontext,
+                                    struct _krb5_authdata_context *context,
+                                    void *plugin_context,
+                                    void *request_context,
+                                    krb5_octet **buffer,
+                                    size_t *lenremain);
+
+typedef krb5_error_code
+(*authdata_client_copy_proc)(krb5_context kcontext,
+                             struct _krb5_authdata_context *context,
+                             void *plugin_context,
+                             void *request_context,
+                             void *dst_plugin_context,
+                             void *dst_request_context);
+
+typedef struct krb5plugin_authdata_client_ftable_v0 {
+    char *name;
+    krb5_authdatatype *ad_type_list;
+    authdata_client_plugin_init_proc init;
+    authdata_client_plugin_fini_proc fini;
+    authdata_client_plugin_flags_proc flags;
+    authdata_client_request_init_proc request_init;
+    authdata_client_request_fini_proc request_fini;
+    authdata_client_get_attribute_types_proc get_attribute_types;
+    authdata_client_get_attribute_proc get_attribute;
+    authdata_client_set_attribute_proc set_attribute;
+    authdata_client_delete_attribute_proc delete_attribute;
+    authdata_client_export_authdata_proc export_authdata;
+    authdata_client_import_authdata_proc import_authdata;
+    authdata_client_export_internal_proc export_internal;
+    authdata_client_free_internal_proc free_internal;
+    authdata_client_verify_proc verify;
+    authdata_client_size_proc size;
+    authdata_client_externalize_proc externalize;
+    authdata_client_internalize_proc internalize;
+    authdata_client_copy_proc copy; /* optional */
+} krb5plugin_authdata_client_ftable_v0;
+
+#endif /* KRB5_AUTHDATA_PLUGIN_H_INCLUDED */
index 9f95e44..43ca2ec 100644 (file)
@@ -26,6 +26,23 @@ dnl     TARGET_CFLAGS="-Wall -pedantic -pthread"
 dnl     TARGET_LDFLAGS=""
 dnl   esac
 
+reauth=no
+AC_ARG_ENABLE(reauth,
+  [  --enable-reauth whether to enable fast reauthentication protocol: yes/no; default no ],
+  [ if test "x$enableval" = "xyes" -o "x$enableval" = "xno" ; then
+      reauth=$enableval
+    else
+      echo "--enable-reauth argument must be yes or no"
+      exit -1
+    fi
+  ])
+
+if test "x$reauth" = "xyes" ; then
+  echo "Fast reauthentication protocol enabled"
+  TARGET_CFLAGS="$TARGET_CFLAGS -DGSSEAP_ENABLE_REAUTH"
+fi
+AM_CONDITIONAL(GSSEAP_ENABLE_REAUTH, test "$reauth" = "yes")
+
 AC_SUBST(TARGET_CFLAGS)
 AC_SUBST(TARGET_LDFLAGS)
 AX_CHECK_KRB5
index 3b872dc..f931be4 100644 (file)
@@ -40,7 +40,7 @@ gss_display_status(OM_uint32 *minor,
                    OM_uint32 *message_context,
                    gss_buffer_t status_string)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
     krb5_context krbContext;
     const char *errMsg;
 
index ea43ad3..26ee5ec 100644 (file)
@@ -284,6 +284,11 @@ gssEapInit(void)
 
     major = gssEapLocalAttrProviderInit(&minor);
     assert(major == GSS_S_COMPLETE);
+
+#ifdef GSSEAP_ENABLE_REAUTH
+    major = gssEapReauthInitialize(&minor);
+    assert(major == GSS_S_COMPLETE);
+#endif
 }
 
 static void
index 5fc98aa..021e366 100644 (file)
@@ -81,12 +81,14 @@ gssEapExportSecContext(OM_uint32 *minor,
      * contexts.
      */
     if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) {
+        assert((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) == 0);
+
         major = gssEapExportPartialContext(minor, ctx, &partialCtx);
         if (GSS_ERROR(major))
             goto cleanup;
     }
 
-    length  = 16;                               /* version, state, flags, etc */
+    length  = 16;                               /* version, state, flags, */
     length += 4 + ctx->mechanismUsed->length;   /* mechanismUsed */
     length += 12 + key.length;                  /* rfc3961Key.value */
     length += 4 + initiatorName.length;         /* initiatorName.value */
index f93c105..b46c2bc 100644 (file)
@@ -42,6 +42,7 @@
 
 /* GSS includes */
 #include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
 #include <gssapi/gssapi_ext.h>
 #include "gssapi_eap.h"
 
@@ -91,17 +92,26 @@ struct gss_cred_id_struct {
     gss_OID_set mechanisms;
     time_t expiryTime;
     char *radiusConfigFile;
+#ifdef GSSEAP_ENABLE_REAUTH
+    krb5_ccache krbCredCache;
+    gss_cred_id_t krbCred;
+#endif
 };
 
 #define CTX_FLAG_INITIATOR                  0x00000001
+#define CTX_FLAG_KRB_REAUTH_GSS             0x00000002
 
 #define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
 
 enum gss_eap_state {
     EAP_STATE_IDENTITY = 0,
     EAP_STATE_AUTHENTICATE,
-    EAP_STATE_GSS_CHANNEL_BINDINGS,
-    EAP_STATE_ESTABLISHED
+    EAP_STATE_EXTENSIONS_REQ,
+    EAP_STATE_EXTENSIONS_RESP,
+    EAP_STATE_ESTABLISHED,
+#ifdef GSSEAP_ENABLE_REAUTH
+    EAP_STATE_KRB_REAUTH_GSS
+#endif
 };
 
 #define CTX_IS_ESTABLISHED(ctx)             ((ctx)->state == EAP_STATE_ESTABLISHED)
@@ -153,6 +163,10 @@ struct gss_ctx_id_struct {
         #define initiatorCtx         ctxU.initiator
         struct gss_eap_acceptor_ctx  acceptor;
         #define acceptorCtx          ctxU.acceptor
+#ifdef GSSEAP_ENABLE_REAUTH
+        gss_ctx_id_t                 kerberos;
+        #define kerberosCtx          ctxU.kerberos
+#endif
     } ctxU;
 };
 
@@ -193,5 +207,16 @@ gssEapWrapIovLength(OM_uint32 *minor,
                     int *conf_state,
                     gss_iov_buffer_desc *iov,
                     int iov_count);
+OM_uint32
+gssEapWrap(OM_uint32 *minor,
+           gss_ctx_id_t ctx,
+           int conf_req_flag,
+           gss_qop_t qop_req,
+           gss_buffer_t input_message_buffer,
+           int *conf_state,
+           gss_buffer_t output_message_buffer);
+
+unsigned char
+rfc4121Flags(gss_ctx_id_t ctx, int receiving);
 
 #endif /* _GSSAPIP_EAP_H_ */
index 8aeb29c..219bfe0 100644 (file)
@@ -269,6 +269,8 @@ gssEapImportContext(OM_uint32 *minor,
      * acceptor contexts.
      */
     if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) {
+        assert((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) == 0);
+
         major = gssEapImportPartialContext(minor, &p, &remain, ctx);
         if (GSS_ERROR(major))
             return major;
index 5fcbec8..e3b4d63 100644 (file)
 
 #include "gssapiP_eap.h"
 
+#ifdef GSSEAP_ENABLE_REAUTH
+static int
+canReauthP(gss_cred_id_t cred);
+
+static OM_uint32
+eapGssSmInitGssReauth(OM_uint32 *minor,
+                      gss_cred_id_t cred,
+                      gss_ctx_id_t ctx,
+                      gss_name_t target,
+                      gss_OID mech,
+                      OM_uint32 reqFlags,
+                      OM_uint32 timeReq,
+                      gss_channel_bindings_t chanBindings,
+                      gss_buffer_t inputToken,
+                      gss_buffer_t outputToken);
+#endif
+
 static OM_uint32
 policyVariableToFlag(enum eapol_bool_var variable)
 {
@@ -287,35 +304,31 @@ initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
 }
 
 static OM_uint32
-eapGssSmInitIdentity(OM_uint32 *minor,
-                     gss_cred_id_t cred,
-                     gss_ctx_id_t ctx,
-                     gss_name_t target,
-                     gss_OID mech,
-                     OM_uint32 reqFlags,
-                     OM_uint32 timeReq,
-                     gss_channel_bindings_t chanBindings,
-                     gss_buffer_t inputToken,
-                     gss_buffer_t outputToken)
+initBegin(OM_uint32 *minor,
+          gss_cred_id_t cred,
+          gss_ctx_id_t ctx,
+          gss_name_t target,
+          gss_OID mech,
+          OM_uint32 reqFlags,
+          OM_uint32 timeReq,
+          gss_channel_bindings_t chanBindings,
+          gss_buffer_t inputToken,
+          gss_buffer_t outputToken)
 {
-    time_t now;
     OM_uint32 major;
-    int initialContextToken;
 
-    initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
-                           inputToken->length == 0);
-    if (!initialContextToken)
-        return GSS_S_DEFECTIVE_TOKEN;
-
-    time(&now);
-    if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
+    if (cred != GSS_C_NO_CREDENTIAL && cred->expiryTime)
+        ctx->expiryTime = cred->expiryTime;
+    else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
         ctx->expiryTime = 0;
     else
-        ctx->expiryTime = now + timeReq;
+        ctx->expiryTime = time(NULL) + timeReq;
 
-    major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
-    if (GSS_ERROR(major))
-        return major;
+    if (cred != GSS_C_NO_CREDENTIAL) {
+        major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
+        if (GSS_ERROR(major))
+            return major;
+    }
 
     major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
     if (GSS_ERROR(major))
@@ -336,6 +349,34 @@ eapGssSmInitIdentity(OM_uint32 *minor,
     if (!gssEapCredAvailable(cred, ctx->mechanismUsed))
         return GSS_S_BAD_MECH;
 
+    return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+eapGssSmInitIdentity(OM_uint32 *minor,
+                     gss_cred_id_t cred,
+                     gss_ctx_id_t ctx,
+                     gss_name_t target,
+                     gss_OID mech,
+                     OM_uint32 reqFlags,
+                     OM_uint32 timeReq,
+                     gss_channel_bindings_t chanBindings,
+                     gss_buffer_t inputToken,
+                     gss_buffer_t outputToken)
+{
+    OM_uint32 major;
+    int initialContextToken;
+
+    initialContextToken = (inputToken->length == 0);
+    if (!initialContextToken)
+        return GSS_S_DEFECTIVE_TOKEN;
+
+    major = initBegin(minor, cred, ctx, target, mech,
+                      reqFlags, timeReq, chanBindings,
+                      inputToken, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     ctx->state = EAP_STATE_AUTHENTICATE;
 
     return GSS_S_CONTINUE_NEEDED;
@@ -404,7 +445,7 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
 
         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
         major = GSS_S_CONTINUE_NEEDED;
-        ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
+        ctx->state = EAP_STATE_EXTENSIONS_REQ;
     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
         major = GSS_S_DEFECTIVE_CREDENTIAL;
     } else if (code == 0 && initialContextToken) {
@@ -438,55 +479,81 @@ cleanup:
 }
 
 static OM_uint32
-eapGssSmInitGssChannelBindings(OM_uint32 *minor,
-                               gss_cred_id_t cred,
-                               gss_ctx_id_t ctx,
-                               gss_name_t target,
-                               gss_OID mech,
-                               OM_uint32 reqFlags,
-                               OM_uint32 timeReq,
-                               gss_channel_bindings_t chanBindings,
-                               gss_buffer_t inputToken,
-                               gss_buffer_t outputToken)
+initGssChannelBindings(OM_uint32 *minor,
+                       gss_ctx_id_t ctx,
+                       gss_channel_bindings_t chanBindings,
+                       gss_buffer_t outputToken)
 {
     OM_uint32 major;
-    gss_iov_buffer_desc iov[2];
-    gss_buffer_desc buf;
-
-    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
-    iov[0].buffer.length = 0;
-    iov[0].buffer.value = NULL;
+    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
 
-    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
-    iov[1].buffer.length = 0;
-    iov[1].buffer.value = NULL;
 
     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
-        iov[0].buffer = chanBindings->application_data;
+        buffer = chanBindings->application_data;
 
-    major = gssEapWrapOrGetMIC(minor, ctx, FALSE, FALSE, iov, 2,
-                               TOK_TYPE_GSS_CB);
+    major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
+                       &buffer, NULL, outputToken);
     if (GSS_ERROR(major))
-        goto cleanup;
+        return major;                       
 
-    /* Skip past token ID */
-    assert(iov[1].buffer.length > 2);
-    assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
+    return GSS_S_CONTINUE_NEEDED;
+}
 
-    buf.length = iov[1].buffer.length - 2;
-    buf.value = (unsigned char *)iov[1].buffer.value + 2;
+static OM_uint32
+eapGssSmInitExtensionsReq(OM_uint32 *minor,
+                          gss_cred_id_t cred,
+                          gss_ctx_id_t ctx,
+                          gss_name_t target,
+                          gss_OID mech,
+                          OM_uint32 reqFlags,
+                          OM_uint32 timeReq,
+                          gss_channel_bindings_t chanBindings,
+                          gss_buffer_t inputToken,
+                          gss_buffer_t outputToken)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc cbToken = GSS_C_EMPTY_BUFFER;
 
-    major = duplicateBuffer(minor, &buf, outputToken);
+    major = initGssChannelBindings(minor, ctx, chanBindings, &cbToken);
     if (GSS_ERROR(major))
-        goto cleanup;
+        return major;
 
-    major = GSS_S_COMPLETE;
-    ctx->state = EAP_STATE_ESTABLISHED;
+    ctx->state = EAP_STATE_EXTENSIONS_RESP;
 
-cleanup:
-    gssEapReleaseIov(iov, 2);
+    major = duplicateBuffer(minor, &cbToken, outputToken);
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &cbToken);
+        return major;
+    }
 
-    return major;
+    gss_release_buffer(&tmpMinor, &cbToken);
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+eapGssSmInitExtensionsResp(OM_uint32 *minor,
+                           gss_cred_id_t cred,
+                           gss_ctx_id_t ctx,
+                           gss_name_t target,
+                           gss_OID mech,
+                           OM_uint32 reqFlags,
+                           OM_uint32 timeReq,
+                           gss_channel_bindings_t chanBindings,
+                           gss_buffer_t inputToken,
+                           gss_buffer_t outputToken)
+{
+#ifdef GSSEAP_ENABLE_REAUTH
+    OM_uint32 major;
+
+    major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+    if (GSS_ERROR(major))
+        return major;
+#endif
+
+    ctx->state = EAP_STATE_ESTABLISHED;
+
+    return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -520,10 +587,14 @@ static struct gss_eap_initiator_sm {
                               gss_buffer_t,
                               gss_buffer_t);
 } eapGssInitiatorSm[] = {
-    { TOK_TYPE_NONE,    TOK_TYPE_EAP_RESP,  eapGssSmInitIdentity            },
-    { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP,  eapGssSmInitAuthenticate        },
-    { TOK_TYPE_NONE,    TOK_TYPE_GSS_CB,    eapGssSmInitGssChannelBindings  },
-    { TOK_TYPE_NONE,    TOK_TYPE_NONE,      eapGssSmInitEstablished         },
+    { TOK_TYPE_NONE,    TOK_TYPE_EAP_RESP,      eapGssSmInitIdentity            },
+    { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP,      eapGssSmInitAuthenticate        },
+    { TOK_TYPE_NONE,    TOK_TYPE_EXT_REQ,       eapGssSmInitExtensionsReq       },
+    { TOK_TYPE_EXT_RESP,TOK_TYPE_NONE,          eapGssSmInitExtensionsResp      },
+    { TOK_TYPE_NONE,    TOK_TYPE_NONE,          eapGssSmInitEstablished         },
+#ifdef GSSEAP_ENABLE_REAUTH
+    { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth           },
+#endif
 };
 
 OM_uint32
@@ -547,6 +618,7 @@ gss_init_sec_context(OM_uint32 *minor,
     struct gss_eap_initiator_sm *sm = NULL;
     gss_buffer_desc innerInputToken;
     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
+    enum gss_eap_token_type tokType;
 
     *minor = 0;
 
@@ -568,6 +640,11 @@ gss_init_sec_context(OM_uint32 *minor,
 
         ctx->flags |= CTX_FLAG_INITIATOR;
 
+#ifdef GSSEAP_ENABLE_REAUTH
+        if (canReauthP(cred))
+            ctx->state = EAP_STATE_KRB_REAUTH_GSS;
+#endif
+
         *context_handle = ctx;
     }
 
@@ -577,9 +654,14 @@ gss_init_sec_context(OM_uint32 *minor,
 
     if (input_token != GSS_C_NO_BUFFER) {
         major = gssEapVerifyToken(minor, ctx, input_token,
-                                  sm->inputTokenType, &innerInputToken);
+                                  &tokType, &innerInputToken);
         if (GSS_ERROR(major))
             goto cleanup;
+
+        if (tokType != sm->inputTokenType) {
+            major = GSS_S_DEFECTIVE_TOKEN;
+            goto cleanup;
+        }
     } else {
         innerInputToken.length = 0;
         innerInputToken.value = NULL;
@@ -636,3 +718,79 @@ cleanup:
 
     return major;
 }
+
+#ifdef GSSEAP_ENABLE_REAUTH
+static int
+canReauthP(gss_cred_id_t cred)
+{
+    return (cred != GSS_C_NO_CREDENTIAL &&
+            cred->krbCred != GSS_C_NO_CREDENTIAL &&
+            cred->expiryTime > time(NULL));
+}
+
+static OM_uint32
+eapGssSmInitGssReauth(OM_uint32 *minor,
+                      gss_cred_id_t cred,
+                      gss_ctx_id_t ctx,
+                      gss_name_t target,
+                      gss_OID mech,
+                      OM_uint32 reqFlags,
+                      OM_uint32 timeReq,
+                      gss_channel_bindings_t chanBindings,
+                      gss_buffer_t inputToken,
+                      gss_buffer_t outputToken)
+{
+    OM_uint32 major, tmpMinor;
+    gss_name_t mechTarget = GSS_C_NO_NAME;
+    gss_OID actualMech = GSS_C_NO_OID;
+    OM_uint32 gssFlags, timeRec;
+
+    assert(cred != GSS_C_NO_CREDENTIAL);
+
+    ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS;
+
+    if (inputToken->length == 0) {
+        major = initBegin(minor, cred, ctx, target, mech,
+                          reqFlags, timeReq, chanBindings,
+                          inputToken, outputToken);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    major = gssEapMechToGlueName(minor, target, &mechTarget);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = gssInitSecContext(minor,
+                              cred->krbCred,
+                              &ctx->kerberosCtx,
+                              mechTarget,
+                              (gss_OID)gss_mech_krb5,
+                              reqFlags, /* | GSS_C_DCE_STYLE, */
+                              timeReq,
+                              chanBindings,
+                              inputToken,
+                              &actualMech,
+                              outputToken,
+                              &gssFlags,
+                              &timeRec);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    ctx->gssFlags = gssFlags;
+
+    if (major == GSS_S_COMPLETE) {
+        major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
+        if (GSS_ERROR(major))
+            goto cleanup;
+        ctx->state = EAP_STATE_ESTABLISHED;
+    }
+
+cleanup:
+    gssReleaseName(&tmpMinor, &mechTarget);
+
+    return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+
diff --git a/mech b/mech
index 89c5512..0b0fe7f 100644 (file)
--- a/mech
+++ b/mech
@@ -3,8 +3,8 @@
 # Any encryption type supported by Kerberos can be defined as the
 # last element of the OID arc.
 #
-eap                    1.3.6.1.4.1.5322.21.1           libmech_eap.dylib
-eap-des3-cbc-sha1      1.3.6.1.4.1.5322.21.1.16        libmech_eap.dylib
-eap-aes128             1.3.6.1.4.1.5322.21.1.17        libmech_eap.dylib
-eap-aes256             1.3.6.1.4.1.5322.21.1.18        libmech_eap.dylib
-eap-rc4-hmac           1.3.6.1.4.1.5322.21.1.23        libmech_eap.dylib
+eap                    1.3.6.1.4.1.5322.21.1           mech_eap.so
+eap-des3-cbc-sha1      1.3.6.1.4.1.5322.21.1.16        mech_eap.so
+eap-aes128             1.3.6.1.4.1.5322.21.1.17        mech_eap.so
+eap-aes256             1.3.6.1.4.1.5322.21.1.18        mech_eap.so
+eap-rc4-hmac           1.3.6.1.4.1.5322.21.1.23        mech_eap.so
diff --git a/radius_ad.exports b/radius_ad.exports
new file mode 100644 (file)
index 0000000..8d5d5c4
--- /dev/null
@@ -0,0 +1 @@
+authdata_client_0
index 48d2ceb..44ebebc 100644 (file)
@@ -105,16 +105,19 @@ gss_OID GSS_EAP_CRED_SET_CRED_FLAG     = &setCredOps[1].oid;
 
 OM_uint32
 gssspi_set_cred_option(OM_uint32 *minor,
-                       gss_cred_id_t cred,
+                       gss_cred_id_t *cred,
                        const gss_OID desired_object,
                        const gss_buffer_t value)
 {
     OM_uint32 major = GSS_S_UNAVAILABLE;
     int i;
 
+    if (*cred == GSS_C_NO_CREDENTIAL)
+        return GSS_S_UNAVAILABLE;
+
     for (i = 0; i < sizeof(setCredOps) / sizeof(setCredOps[0]); i++) {
         if (oidEqual(&setCredOps[i].oid, desired_object)) {
-            major = (*setCredOps[i].setOption)(minor, cred,
+            major = (*setCredOps[i].setOption)(minor, *cred,
                                               desired_object, value);
             break;
         }
index 4ddac48..b1e5e54 100644 (file)
@@ -42,6 +42,27 @@ gss_store_cred(OM_uint32 *minor,
                gss_OID_set *elements_stored,
                gss_cred_usage_t *cred_usage_stored)
 {
+    if (elements_stored != NULL)
+        *elements_stored = GSS_C_NO_OID_SET;
+    if (cred_usage_stored != NULL)
+        *cred_usage_stored = input_usage;
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL)
+        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED;
+
+#ifdef GSSEAP_ENABLE_REAUTH
+    if (input_cred_handle->krbCred != GSS_C_NO_CREDENTIAL) {
+        return gssStoreCred(minor,
+                            input_cred_handle->krbCred,
+                            input_usage,
+                            gss_mech_krb5,
+                            overwrite_cred,
+                            default_cred,
+                            elements_stored,
+                            cred_usage_stored);
+    }
+#endif
+
     *minor = 0;
     return GSS_S_UNAVAILABLE;
 }
index ca10b32..844c762 100644 (file)
--- a/unwrap.c
+++ b/unwrap.c
@@ -43,6 +43,9 @@ gss_unwrap(OM_uint32 *minor,
     OM_uint32 major, tmpMinor;
     gss_iov_buffer_desc iov[2];
 
+    if (!CTX_IS_ESTABLISHED(ctx))
+        return GSS_S_NO_CONTEXT;
+
     iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
     iov[0].buffer = *input_message_buffer;
 
index 1624308..7e6eb87 100644 (file)
@@ -73,7 +73,7 @@ unwrapToken(OM_uint32 *minor,
     gss_iov_buffer_t header;
     gss_iov_buffer_t padding;
     gss_iov_buffer_t trailer;
-    unsigned char acceptorFlag;
+    unsigned char flags;
     unsigned char *ptr = NULL;
     int keyUsage;
     size_t rrc, ec;
@@ -99,40 +99,27 @@ unwrapToken(OM_uint32 *minor,
 
     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
 
-    acceptorFlag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0;
-    switch (toktype) {
-    case TOK_TYPE_WRAP:
+    flags = rfc4121Flags(ctx, TRUE);
+
+    if (toktype == TOK_TYPE_WRAP) {
         keyUsage = !CTX_IS_INITIATOR(ctx)
                    ? KEY_USAGE_INITIATOR_SEAL
                    : KEY_USAGE_ACCEPTOR_SEAL;
-        break;
-    case TOK_TYPE_GSS_CB:
-        keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
-        break;
-    case TOK_TYPE_MIC:
-    default:
+    } else {
         keyUsage = !CTX_IS_INITIATOR(ctx)
                    ? KEY_USAGE_INITIATOR_SIGN
                    : KEY_USAGE_ACCEPTOR_SIGN;
-        break;
     }
 
     gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
 
     ptr = (unsigned char *)header->buffer.value;
 
-    if (header->buffer.length < 16) {
-        *minor = 0;
+    if (header->buffer.length < 16)
         return GSS_S_DEFECTIVE_TOKEN;
-    }
 
-    if ((ptr[2] & TOK_FLAG_SENDER_IS_ACCEPTOR) != acceptorFlag) {
+    if ((ptr[2] & flags) != flags)
         return GSS_S_BAD_SIG;
-    }
-
-    if (ptr[2] & TOK_FLAG_ACCEPTOR_SUBKEY) {
-        return GSS_S_BAD_SIG;
-    }
 
     if (toktype == TOK_TYPE_WRAP) {
         unsigned int krbTrailerLen;
@@ -219,7 +206,7 @@ unwrapToken(OM_uint32 *minor,
         }
 
         code = sequenceCheck(minor, &ctx->seqState, seqnum);
-    } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
+    } else if (toktype == TOK_TYPE_MIC) {
         if (load_uint16_be(ptr) != toktype)
             goto defective;
 
@@ -235,8 +222,7 @@ unwrapToken(OM_uint32 *minor,
             *minor = code;
             return GSS_S_BAD_SIG;
         }
-        if (toktype != TOK_TYPE_GSS_CB)
-            code = sequenceCheck(minor, &ctx->seqState, seqnum);
+        code = sequenceCheck(minor, &ctx->seqState, seqnum);
     } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
         if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
             goto defective;
@@ -307,7 +293,7 @@ unwrapStream(OM_uint32 *minor,
 
     assert(toktype == TOK_TYPE_WRAP);
 
-    if (toktype != TOK_TYPE_WRAP || (ctx->gssFlags & GSS_C_DCE_STYLE)) {
+    if (toktype != TOK_TYPE_WRAP) {
         code = EINVAL;
         goto cleanup;
     }
@@ -477,9 +463,6 @@ gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
 {
     OM_uint32 major;
 
-    if (!CTX_IS_ESTABLISHED(ctx))
-        return GSS_S_NO_CONTEXT;
-
     if (ctx->encryptionType == ENCTYPE_NULL)
         return GSS_S_UNAVAILABLE;
 
@@ -502,6 +485,9 @@ gss_unwrap_iov(OM_uint32 *minor,
                gss_iov_buffer_desc *iov,
                int iov_count)
 {
+    if (!CTX_IS_ESTABLISHED(ctx))
+        return GSS_S_NO_CONTEXT;
+
     return gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
                                    iov, iov_count, TOK_TYPE_WRAP);
 }
diff --git a/util.h b/util.h
index 74641c7..6b47283 100644 (file)
--- a/util.h
+++ b/util.h
@@ -88,7 +88,9 @@ enum gss_eap_token_type {
     TOK_TYPE_DELETE_CONTEXT          = 0x0405,  /* RFC 2743 delete context */
     TOK_TYPE_EAP_RESP                = 0x0601,  /* draft-howlett-eap-gss */
     TOK_TYPE_EAP_REQ                 = 0x0602,  /* draft-howlett-eap-gss */
-    TOK_TYPE_GSS_CB                  = 0x0603,  /* draft-howlett-eap-gss */
+    TOK_TYPE_EXT_REQ                 = 0x0603,  /* draft-howlett-eap-gss */
+    TOK_TYPE_EXT_RESP                = 0x0604,  /* to be specified */
+    TOK_TYPE_GSS_REAUTH              = 0x0605,  /* to be specified */
 };
 
 #define EAP_EXPORT_CONTEXT_V1           1
@@ -169,7 +171,7 @@ OM_uint32
 gssEapVerifyToken(OM_uint32 *minor,
                   gss_ctx_id_t ctx,
                   const gss_buffer_t inputToken,
-                  enum gss_eap_token_type tokenType,
+                  enum gss_eap_token_type *tokenType,
                   gss_buffer_t innerInputToken);
 
 OM_uint32
@@ -396,7 +398,7 @@ verifyTokenHeader(OM_uint32 *minor,
                   size_t *body_size,
                   unsigned char **buf_in,
                   size_t toksize_in,
-                  enum gss_eap_token_type tok_type);
+                  enum gss_eap_token_type *ret_tok_type);
 
 /* Helper macros */
 #define GSSEAP_CALLOC(count, size)      (calloc((count), (size)))
@@ -547,10 +549,20 @@ krbDataToGssBuffer(krb5_data *data, gss_buffer_t buffer)
     buffer->length = data->length;
 }
 
+static inline void
+gssBufferToKrbData(gss_buffer_t buffer, krb5_data *data)
+{
+    data->data = (char *)buffer->value;
+    data->length = buffer->length;
+}
+
 #ifdef __cplusplus
 }
 #endif
 
 #include "util_attr.h"
+#ifdef GSSEAP_ENABLE_REAUTH
+#include "util_reauth.h"
+#endif
 
 #endif /* _UTIL_H_ */
diff --git a/util_adshim.c b/util_adshim.c
new file mode 100644 (file)
index 0000000..c0b6837
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2010, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gssapiP_eap.h"
+#include "authdata_plugin.h"
+
+/*
+ * This rubbish is necessary because MIT doesn't provide another way
+ * to access verified AD-KDCIssued elements. We can't verify them
+ * ourselves because they're signed in the ticket session key, which
+ * is destroyed immediately after the AP-REQ is processed.
+ */
+
+struct radius_ad_context {
+    krb5_data avpdata;
+    krb5_boolean verified;
+};
+
+static krb5_data radius_ad_attr = {
+    KV5M_DATA, sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" };
+
+static krb5_error_code
+radius_ad_init(krb5_context kcontext, void **plugin_context)
+{
+    *plugin_context = 0;
+    return 0;
+}
+
+static void
+radius_ad_flags(krb5_context kcontext,
+                void *plugin_context,
+                krb5_authdatatype ad_type,
+                krb5_flags *flags)
+{
+    *flags = AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL;
+}
+
+static void
+radius_ad_fini(krb5_context kcontext, void *plugin_context)
+{
+    return;
+}
+
+static krb5_error_code
+radius_ad_request_init(krb5_context kcontext,
+                       struct _krb5_authdata_context *context,
+                       void *plugin_context,
+                       void **request_context)
+{
+    struct radius_ad_context *ctx;
+
+    ctx = GSSEAP_CALLOC(1, sizeof(*ctx));
+    if (ctx == NULL)
+        return ENOMEM;
+
+    *request_context = ctx;
+
+    return 0;
+}
+
+static krb5_error_code
+radius_ad_export_authdata(krb5_context kcontext,
+                          struct _krb5_authdata_context *context,
+                          void *plugin_context,
+                          void *request_context,
+                          krb5_flags usage,
+                          krb5_authdata ***out_authdata)
+{
+    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
+    krb5_authdata *data[2];
+    krb5_authdata datum;
+
+    datum.ad_type = KRB5_AUTHDATA_RADIUS_AVP;
+    datum.length = radius_ad->avpdata.length;
+    datum.contents = (krb5_octet *)radius_ad->avpdata.data;
+
+    data[0] = &datum;
+    data[1] = NULL;
+
+    return krb5_copy_authdata(kcontext, data, out_authdata);
+}
+
+static krb5_error_code
+radius_ad_import_authdata(krb5_context kcontext,
+                          struct _krb5_authdata_context *context,
+                          void *plugin_context,
+                          void *request_context,
+                          krb5_authdata **authdata,
+                          krb5_boolean kdc_issued_flag,
+                          krb5_const_principal issuer)
+{
+    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
+
+    krb5_free_data_contents(kcontext, &radius_ad->avpdata);
+    radius_ad->verified = FALSE;
+
+    assert(authdata[0] != NULL);
+
+    radius_ad->avpdata.data = GSSEAP_MALLOC(authdata[0]->length);
+    if (radius_ad->avpdata.data == NULL)
+        return ENOMEM;
+
+    memcpy(radius_ad->avpdata.data, authdata[0]->contents,
+           authdata[0]->length);
+    radius_ad->avpdata.length = authdata[0]->length;
+
+    radius_ad->verified = kdc_issued_flag;
+
+    return 0;
+}
+
+static void
+radius_ad_request_fini(krb5_context kcontext,
+                       struct _krb5_authdata_context *context,
+                       void *plugin_context,
+                       void *request_context)
+{
+    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
+
+    if (radius_ad != NULL) {
+        krb5_free_data_contents(kcontext, &radius_ad->avpdata);
+        GSSEAP_FREE(radius_ad);
+    }
+}
+
+static krb5_error_code
+radius_ad_get_attribute(krb5_context kcontext,
+                        struct _krb5_authdata_context *context,
+                        void *plugin_context,
+                        void *request_context,
+                        const krb5_data *attribute,
+                        krb5_boolean *authenticated,
+                        krb5_boolean *complete,
+                        krb5_data *value,
+                        krb5_data *display_value,
+                        int *more)
+{
+    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
+
+    if (attribute->length != radius_ad_attr.length ||
+        memcmp(attribute->data, radius_ad_attr.data,
+               radius_ad_attr.length) != 0)
+        return ENOENT;
+
+    *authenticated = radius_ad->verified;
+    *complete = TRUE;
+    *more = 0;
+
+    value->data = GSSEAP_MALLOC(radius_ad->avpdata.length);
+    if (value->data == NULL)
+        return ENOMEM;
+
+    memcpy(value->data, radius_ad->avpdata.data, radius_ad->avpdata.length);
+    value->length = radius_ad->avpdata.length;
+
+    return 0;
+}
+
+static krb5_error_code
+radius_ad_copy(krb5_context kcontext,
+               struct _krb5_authdata_context *context,
+               void *plugin_context,
+               void *request_context,
+               void *dst_plugin_context,
+               void *dst_request_context)
+{
+    struct radius_ad_context *radius_ad_src =
+        (struct radius_ad_context *)request_context;
+    struct radius_ad_context *radius_ad_dst =
+        (struct radius_ad_context *)dst_request_context;
+
+    radius_ad_dst->avpdata.data = GSSEAP_MALLOC(radius_ad_src->avpdata.length);
+    if (radius_ad_dst->avpdata.data == NULL)
+        return ENOMEM;
+
+    memcpy(radius_ad_dst->avpdata.data, radius_ad_src->avpdata.data,
+           radius_ad_src->avpdata.length);
+    radius_ad_dst->avpdata.length = radius_ad_src->avpdata.length;
+    radius_ad_dst->verified = radius_ad_src->verified;
+
+    return 0;
+}
+
+static krb5_authdatatype radius_ad_ad_types[] =
+    { KRB5_AUTHDATA_RADIUS_AVP, 0 };
+
+krb5plugin_authdata_client_ftable_v0 authdata_client_0 = {
+    "radius_ad",
+    radius_ad_ad_types,
+    radius_ad_init,
+    radius_ad_fini,
+    radius_ad_flags,
+    radius_ad_request_init,
+    radius_ad_request_fini,
+    NULL,
+    radius_ad_get_attribute,
+    NULL,
+    NULL,
+    radius_ad_export_authdata,
+    radius_ad_import_authdata,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    radius_ad_copy
+};
index a96b452..37edb63 100644 (file)
@@ -106,6 +106,11 @@ gssEapReleaseContext(OM_uint32 *minor,
 
     gssEapKerberosInit(&tmpMinor, &krbContext);
 
+#ifdef GSSEAP_ENABLE_REAUTH
+    if (ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) {
+        gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+    } else
+#endif
     if (CTX_IS_INITIATOR(ctx)) {
         releaseInitiatorContext(&ctx->initiatorCtx);
     } else {
@@ -156,7 +161,7 @@ OM_uint32
 gssEapVerifyToken(OM_uint32 *minor,
                   gss_ctx_id_t ctx,
                   const gss_buffer_t inputToken,
-                  enum gss_eap_token_type tokenType,
+                  enum gss_eap_token_type *actualToken,
                   gss_buffer_t innerInputToken)
 {
     OM_uint32 major;
@@ -174,9 +179,9 @@ gssEapVerifyToken(OM_uint32 *minor,
     }
 
     major = verifyTokenHeader(minor, oid, &bodySize, &p,
-                              inputToken->length, tokenType);
+                              inputToken->length, actualToken);
     if (GSS_ERROR(major))
-        return GSS_S_DEFECTIVE_TOKEN;
+        return major;
 
     if (ctx->mechanismUsed == GSS_C_NO_OID) {
         if (!gssEapIsConcreteMechanismOid(oid))
index 176bb1e..3893ff6 100644 (file)
@@ -63,11 +63,14 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
 {
     OM_uint32 tmpMinor;
     gss_cred_id_t cred = *pCred;
+    krb5_context krbContext = NULL;
 
     if (cred == GSS_C_NO_CREDENTIAL) {
         return GSS_S_COMPLETE;
     }
 
+    GSSEAP_KRB_INIT(&krbContext);
+
     gssEapReleaseName(&tmpMinor, &cred->name);
 
     if (cred->password.value != NULL) {
@@ -78,6 +81,13 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
     if (cred->radiusConfigFile != NULL)
         GSSEAP_FREE(cred->radiusConfigFile);
 
+#ifdef GSSEAP_ENABLE_REAUTH
+    if (cred->krbCredCache != NULL)
+        krb5_cc_destroy(krbContext, cred->krbCredCache);
+    if (cred->krbCred != GSS_C_NO_CREDENTIAL)
+        gssReleaseCred(&tmpMinor, &cred->krbCred);
+#endif
+
     GSSEAP_MUTEX_DESTROY(&cred->mutex);
     memset(cred, 0, sizeof(*cred));
     GSSEAP_FREE(cred);
index f4a6338..6063e7e 100644 (file)
@@ -514,7 +514,7 @@ gssEapDisplayName(OM_uint32 *minor,
                   gss_buffer_t output_name_buffer,
                   gss_OID *output_name_type)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
     krb5_context krbContext;
     char *krbName;
 
diff --git a/util_reauth.c b/util_reauth.c
new file mode 100644 (file)
index 0000000..a552fcb
--- /dev/null
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2010, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gssapiP_eap.h"
+
+#include <dlfcn.h>
+
+/*
+ * Fast reauthentication support for EAP GSS.
+ */
+
+krb5_error_code
+krb5_encrypt_tkt_part(krb5_context, const krb5_keyblock *, krb5_ticket *);
+
+krb5_error_code
+encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code);
+
+static OM_uint32
+gssDisplayName(OM_uint32 *minor,
+               gss_name_t name,
+               gss_buffer_t buffer,
+               gss_OID *name_type);
+
+static OM_uint32
+gssImportName(OM_uint32 *minor,
+              gss_buffer_t buffer,
+              gss_OID name_type,
+              gss_name_t *name);
+
+static krb5_error_code
+getAcceptorKey(krb5_context krbContext,
+               gss_ctx_id_t ctx,
+               gss_cred_id_t cred,
+               krb5_principal *princ,
+               krb5_keyblock *key)
+{
+    krb5_error_code code;
+    krb5_keytab keytab = NULL;
+    krb5_keytab_entry ktent = { 0 };
+    krb5_kt_cursor cursor = NULL;
+
+    *princ = NULL;
+    memset(key, 0, sizeof(*key));
+
+    code = krb5_kt_default(krbContext, &keytab);
+    if (code != 0)
+        goto cleanup;
+
+    if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
+        code = krb5_kt_get_entry(krbContext, keytab,
+                                 cred->name->krbPrincipal, 0,
+                                 ctx->encryptionType, &ktent);
+        if (code != 0)
+            goto cleanup;
+    } else {
+        code = krb5_kt_start_seq_get(krbContext, keytab, &cursor);
+        if (code != 0)
+            goto cleanup;
+
+        while ((code = krb5_kt_next_entry(krbContext, keytab,
+                                          &ktent, &cursor)) == 0) {
+            if (ktent.key.enctype == ctx->encryptionType)
+                break;
+            else
+                krb5_free_keytab_entry_contents(krbContext, &ktent);
+        }
+    }
+
+    if (code == 0) {
+        *princ = ktent.principal;
+        *key = ktent.key;
+    }
+
+cleanup:
+    if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME)
+        krb5_kt_end_seq_get(krbContext, keytab, &cursor);
+    krb5_kt_close(krbContext, keytab);
+
+    if (code != 0)
+        krb5_free_keytab_entry_contents(krbContext, &ktent);
+
+    return code;
+}
+
+static OM_uint32
+freezeAttrContext(OM_uint32 *minor,
+                  gss_name_t initiatorName,
+                  krb5_const_principal acceptorPrinc,
+                  krb5_keyblock *session,
+                  krb5_authdata ***authdata)
+{
+    OM_uint32 major, tmpMinor;
+    krb5_error_code code;
+    gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER;
+    krb5_authdata *authData[2], authDatum = { 0 };
+    krb5_context krbContext;
+
+    GSSEAP_KRB_INIT(&krbContext);
+
+    major = gssEapExportAttrContext(minor, initiatorName, &attrBuf);
+    if (GSS_ERROR(major))
+        return major;
+
+    authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP;
+    authDatum.length = attrBuf.length;
+    authDatum.contents = attrBuf.value;
+    authData[0] = &authDatum;
+    authData[1] = NULL;
+
+    code = krb5_make_authdata_kdc_issued(krbContext, session, acceptorPrinc,
+                                         authData, authdata);
+    if (code != 0) {
+        major = GSS_S_FAILURE;
+        *minor = code;
+    } else {
+        major = GSS_S_COMPLETE;
+    }
+
+    gss_release_buffer(&tmpMinor, &attrBuf);
+
+    return major;
+}
+
+/*
+ * Fabricate a ticket to ourselves given a GSS EAP context.
+ */
+OM_uint32
+gssEapMakeReauthCreds(OM_uint32 *minor,
+                      gss_ctx_id_t ctx,
+                      gss_cred_id_t cred,
+                      gss_buffer_t credBuf)
+{
+    OM_uint32 major = GSS_S_COMPLETE;
+    krb5_error_code code;
+    krb5_context krbContext = NULL;
+    krb5_ticket ticket = { 0 };
+    krb5_keyblock session = { 0 }, acceptorKey = { 0 };
+    krb5_enc_tkt_part enc_part = { 0 };
+    krb5_data *ticketData = NULL, *credsData = NULL;
+    krb5_creds creds = { 0 };
+    krb5_auth_context authContext = NULL;
+
+    credBuf->length = 0;
+    credBuf->value = NULL;
+
+    GSSEAP_KRB_INIT(&krbContext);
+
+    code = getAcceptorKey(krbContext, ctx, cred,
+                          &ticket.server, &acceptorKey);
+    if (code == KRB5_KT_NOTFOUND) {
+        gss_buffer_desc emptyToken = { 0, "" };
+
+        /*
+         * If we can't produce the KRB-CRED message, we need to
+         * return an empty (not NULL) token to the caller so we
+         * don't change the number of authentication legs.
+         */
+        return duplicateBuffer(minor, &emptyToken, credBuf);
+    } else if (code != 0)
+        goto cleanup;
+
+    enc_part.flags = TKT_FLG_INITIAL;
+
+    /*
+     * Generate a random session key to place in the ticket and
+     * sign the "KDC-Issued" authorization data element.
+     */
+    code = krb5_c_make_random_key(krbContext, ctx->encryptionType,
+                                  &session);
+    if (code != 0)
+        goto cleanup;
+
+    enc_part.session = &session;
+    enc_part.client = ctx->initiatorName->krbPrincipal;
+    enc_part.times.authtime = time(NULL);
+    enc_part.times.starttime = enc_part.times.authtime;
+    enc_part.times.endtime = ctx->expiryTime
+                             ? ctx->expiryTime
+                             : KRB5_INT32_MAX;
+    enc_part.times.renew_till = 0;
+
+    major = freezeAttrContext(minor, ctx->initiatorName, ticket.server,
+                              &session, &enc_part.authorization_data);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    ticket.enc_part2 = &enc_part;
+
+    code = krb5_encrypt_tkt_part(krbContext, &acceptorKey, &ticket);
+    if (code != 0)
+        goto cleanup;
+
+    code = encode_krb5_ticket(&ticket, &ticketData);
+    if (code != 0)
+        goto cleanup;
+
+    creds.client = enc_part.client;
+    creds.server = ticket.server;
+    creds.keyblock = session;
+    creds.times = enc_part.times;
+    creds.ticket_flags = enc_part.flags;
+    creds.ticket = *ticketData;
+    creds.authdata = enc_part.authorization_data;
+
+    code = krb5_auth_con_init(krbContext, &authContext);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_auth_con_setflags(krbContext, authContext, 0);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_auth_con_setsendsubkey(krbContext, authContext,
+                                       &ctx->rfc3961Key);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_mk_1cred(krbContext, authContext, &creds, &credsData, NULL);
+    if (code != 0)
+        goto cleanup;
+
+    krbDataToGssBuffer(credsData, credBuf);
+
+cleanup:
+    if (ticket.enc_part.ciphertext.data != NULL)
+        GSSEAP_FREE(ticket.enc_part.ciphertext.data);
+    krb5_free_keyblock_contents(krbContext, &session);
+    krb5_free_keyblock_contents(krbContext, &acceptorKey);
+    krb5_free_data(krbContext, ticketData);
+    krb5_auth_con_free(krbContext, authContext);
+    krb5_free_authdata(krbContext, enc_part.authorization_data);
+    if (credsData != NULL)
+        GSSEAP_FREE(credsData);
+
+    if (major == GSS_S_COMPLETE) {
+        *minor = code;
+        major = code != 0 ? GSS_S_FAILURE : GSS_S_COMPLETE;
+    }
+
+    return major;
+}
+
+static int
+isTicketGrantingServiceP(krb5_context krbContext,
+                         krb5_const_principal principal)
+{
+    if (krb5_princ_size(krbContext, principal) == 2 &&
+        krb5_princ_component(krbContext, principal, 0)->length == 6 &&
+        memcmp(krb5_princ_component(krbContext,
+                                    principal, 0)->data, "krbtgt", 6) == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+/*
+ * Store re-authentication (Kerberos) credentials in a credential handle.
+ */
+OM_uint32
+gssEapStoreReauthCreds(OM_uint32 *minor,
+                       gss_ctx_id_t ctx,
+                       gss_cred_id_t cred,
+                       gss_buffer_t credBuf)
+{
+    OM_uint32 major = GSS_S_COMPLETE, code;
+    krb5_context krbContext = NULL;
+    krb5_auth_context authContext = NULL;
+    krb5_data credData = { 0 };
+    krb5_creds **creds = NULL;
+    krb5_principal canonPrinc;
+    int i;
+
+    if (credBuf->length == 0 || cred == GSS_C_NO_CREDENTIAL)
+        return GSS_S_COMPLETE;
+
+    GSSEAP_KRB_INIT(&krbContext);
+
+    code = krb5_auth_con_init(krbContext, &authContext);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_auth_con_setflags(krbContext, authContext, 0);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_auth_con_setrecvsubkey(krbContext, authContext,
+                                       &ctx->rfc3961Key);
+    if (code != 0)
+        goto cleanup;
+
+    gssBufferToKrbData(credBuf, &credData);
+
+    code = krb5_rd_cred(krbContext, authContext, &credData, &creds, NULL);
+    if (code != 0)
+        goto cleanup;
+
+    if (creds == NULL || creds[0] == NULL)
+        goto cleanup;
+
+    code = krb5_copy_principal(krbContext, creds[0]->client, &canonPrinc);
+    if (code != 0)
+        goto cleanup;
+
+    krb5_free_principal(krbContext, cred->name->krbPrincipal);
+    cred->name->krbPrincipal = canonPrinc;
+
+    cred->expiryTime = creds[0]->times.endtime;
+
+    code = krb5_cc_new_unique(krbContext, "MEMORY", NULL, &cred->krbCredCache);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_cc_initialize(krbContext, cred->krbCredCache,
+                              creds[0]->client);
+    if (code != 0)
+        goto cleanup;
+
+    for (i = 0; creds[i] != NULL; i++) {
+        krb5_creds kcred = *(creds[i]);
+
+        /*
+         * Swap in the acceptor name the client asked for so
+         * get_credentials() works. We're making the assumption that
+         * any service tickets returned are for us. We'll need to
+         * reflect some more on whether that is a safe assumption.
+         */
+        if (!isTicketGrantingServiceP(krbContext, kcred.server))
+            kcred.server = ctx->acceptorName->krbPrincipal;
+
+        code = krb5_cc_store_cred(krbContext, cred->krbCredCache, &kcred);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    /*
+     * To turn a credentials cache into a GSS credentials handle, we
+     * require the gss_krb5_import_cred() API (present in Heimdal, but
+     * not shipped in MIT yet).
+     */
+    major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL,
+                                 &cred->krbCred);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+cleanup:
+    *minor = code;
+
+    krb5_auth_con_free(krbContext, authContext);
+    if (creds != NULL) {
+        for (i = 0; creds[i] != NULL; i++)
+            krb5_free_creds(krbContext, creds[i]);
+    }
+    if (major == GSS_S_COMPLETE)
+        major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;
+
+    return major;
+}
+
+static gss_buffer_desc radiusAvpKrbAttr = {
+    sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp"
+};
+
+/*
+ * Unfortunately extracting an AD-KDCIssued authorization data element
+ * is pretty implementation-dependent. It's not possible to verify the
+ * signature ourselves because the ticket session key is not exposed
+ * outside GSS. In an ideal world, all AD-KDCIssued elements would be
+ * verified by the Kerberos library and authentication would fail if
+ * verification failed. We're not quite there yet and as a result have
+ * to go through some hoops to get this to work. The alternative would
+ * be to sign the authorization data with our long-term key, but it
+ * seems a pity to compromise the design because of current implementation
+ * limitations.
+ *
+ * (Specifically, the hoops involve a libkrb5 authorisation data plugin
+ * that exposes the verified and serialised attribute context through
+ * the Kerberos GSS mechanism's naming extensions API.)
+ */
+static OM_uint32
+defrostAttrContext(OM_uint32 *minor,
+                   gss_name_t glueName,
+                   gss_name_t mechName)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc authData = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc authDataDisplay = GSS_C_EMPTY_BUFFER;
+    int more = -1;
+    int authenticated, complete;
+
+    major = gssGetNameAttribute(minor, glueName, &radiusAvpKrbAttr,
+                                &authenticated, &complete,
+                                &authData, &authDataDisplay, &more);
+    if (major == GSS_S_COMPLETE) {
+        if (authenticated == 0)
+            major = GSS_S_BAD_NAME;
+        else
+            major = gssEapImportAttrContext(minor, &authData, mechName);
+    } else if (major == GSS_S_UNAVAILABLE) {
+        major = GSS_S_COMPLETE;
+    }
+
+    gss_release_buffer(&tmpMinor, &authData);
+    gss_release_buffer(&tmpMinor, &authDataDisplay);
+
+    return major;
+}
+
+/*
+ * Convert a mechanism glue to an EAP mechanism name by displaying and
+ * importing it. This also handles the RADIUS attributes.
+ */
+OM_uint32
+gssEapGlueToMechName(OM_uint32 *minor,
+                     gss_name_t glueName,
+                     gss_name_t *pMechName)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+
+    *pMechName = GSS_C_NO_NAME;
+
+    major = gssDisplayName(minor, glueName, &nameBuf, NULL);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
+                             pMechName);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = defrostAttrContext(minor, glueName, *pMechName);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+cleanup:
+    if (GSS_ERROR(major)) {
+        gssReleaseName(&tmpMinor, pMechName);
+        *pMechName = GSS_C_NO_NAME;
+    }
+
+    gss_release_buffer(&tmpMinor, &nameBuf);
+
+    return major;
+}
+
+/*
+ * Convert an EAP mechanism name to a mechanism glue name by displaying
+ * and importing it.
+ */
+OM_uint32
+gssEapMechToGlueName(OM_uint32 *minor,
+                     gss_name_t mechName,
+                     gss_name_t *pGlueName)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+
+    *pGlueName = GSS_C_NO_NAME;
+
+    major = gssEapDisplayName(minor, mechName, &nameBuf, NULL);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = gssImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
+                          pGlueName);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+cleanup:
+    gss_release_buffer(&tmpMinor, &nameBuf);
+
+    return major;
+}
+
+/*
+ * Suck out the analgous elements of a Kerberos GSS context into an EAP
+ * one so that the application doesn't know the difference.
+ */
+OM_uint32
+gssEapReauthComplete(OM_uint32 *minor,
+                    gss_ctx_id_t ctx,
+                    gss_cred_id_t cred,
+                    const gss_OID mech,
+                    OM_uint32 timeRec)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_set_t keyData = GSS_C_NO_BUFFER_SET;
+
+    if (!oidEqual(mech, gss_mech_krb5)) {
+        major = GSS_S_BAD_MECH;
+        goto cleanup;
+    }
+
+    /* Get the raw subsession key and encryptino type*/
+    major = gssInquireSecContextByOid(minor, ctx->kerberosCtx,
+                                      GSS_C_INQ_SSPI_SESSION_KEY, &keyData);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    {
+        gss_OID_desc oid;
+        int suffix;
+
+        oid.length = keyData->elements[1].length;
+        oid.elements = keyData->elements[1].value;
+
+        /* GSS_KRB5_SESSION_KEY_ENCTYPE_OID */
+        major = decomposeOid(minor,
+                             "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04",
+                             10, &oid, &suffix);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        ctx->encryptionType = suffix;
+    }
+
+    {
+        krb5_context krbContext = NULL;
+        krb5_keyblock key;
+
+        GSSEAP_KRB_INIT(&krbContext);
+
+        KRB_KEY_LENGTH(&key) = keyData->elements[0].length;
+        KRB_KEY_DATA(&key)   = keyData->elements[0].value;
+        KRB_KEY_TYPE(&key)   = ctx->encryptionType;
+
+        *minor = krb5_copy_keyblock_contents(krbContext,
+                                             &key, &ctx->rfc3961Key);
+        if (*minor != 0) {
+            major = GSS_S_FAILURE;
+            goto cleanup;
+        }
+    }
+
+    major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
+                                      &ctx->checksumType);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    if (timeRec != GSS_C_INDEFINITE)
+        ctx->expiryTime = time(NULL) + timeRec;
+
+    /* Initialize our sequence state */
+    major = sequenceInit(minor,
+                         &ctx->seqState, ctx->recvSeq,
+                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
+                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
+                         TRUE);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = GSS_S_COMPLETE;
+
+cleanup:
+    gss_release_buffer_set(&tmpMinor, &keyData);
+
+    return major;
+}
+
+/*
+ * The remainder of this file consists of wrappers so we can call into the
+ * mechanism glue without calling ourselves.
+ */
+static OM_uint32
+(*gssInitSecContextNext)(OM_uint32 *,
+                         gss_cred_id_t,
+                         gss_ctx_id_t *,
+                         gss_name_t,
+                         gss_OID,
+                         OM_uint32,
+                         OM_uint32,
+                         gss_channel_bindings_t,
+                         gss_buffer_t,
+                         gss_OID *,
+                         gss_buffer_t,
+                         OM_uint32 *,
+                         OM_uint32 *);
+
+static OM_uint32
+(*gssAcceptSecContextNext)(OM_uint32 *,
+                           gss_ctx_id_t *,
+                           gss_cred_id_t,
+                           gss_buffer_t,
+                           gss_channel_bindings_t,
+                           gss_name_t *,
+                           gss_OID *,
+                           gss_buffer_t,
+                           OM_uint32 *,
+                           OM_uint32 *,
+                           gss_cred_id_t *);
+
+static OM_uint32
+(*gssReleaseCredNext)(OM_uint32 *, gss_cred_id_t *);
+
+static OM_uint32
+(*gssReleaseNameNext)(OM_uint32 *, gss_name_t *);
+
+static OM_uint32
+(*gssInquireSecContextByOidNext)(OM_uint32 *,
+                                 const gss_ctx_id_t,
+                                 const gss_OID,
+                                 gss_buffer_set_t *);
+
+static OM_uint32
+(*gssDeleteSecContextNext)(OM_uint32 *,
+                          gss_ctx_id_t *,
+                          gss_buffer_t);
+
+static OM_uint32
+(*gssDisplayNameNext)(OM_uint32 *,
+                      gss_name_t,
+                      gss_buffer_t,
+                      gss_OID *);
+
+static OM_uint32
+(*gssImportNameNext)(OM_uint32 *,
+                     gss_buffer_t,
+                     gss_OID,
+                     gss_name_t *);
+
+static OM_uint32
+(*gssStoreCredNext)(OM_uint32 *,
+                    const gss_cred_id_t,
+                    gss_cred_usage_t,
+                    const gss_OID,
+                    OM_uint32,
+                    OM_uint32,
+                    gss_OID_set *,
+                    gss_cred_usage_t *);
+
+static OM_uint32
+(*gssGetNameAttributeNext)(OM_uint32 *,
+                          gss_name_t,
+                          gss_buffer_t,
+                          int *,
+                          int *,
+                          gss_buffer_t,
+                          gss_buffer_t,
+                          int *);
+
+#define NEXT_SYMBOL(local, global)  ((local) = dlsym(RTLD_NEXT, (global)))
+
+OM_uint32
+gssEapReauthInitialize(OM_uint32 *minor)
+{
+    NEXT_SYMBOL(gssInitSecContextNext,                    "gss_init_sec_context");
+    NEXT_SYMBOL(gssAcceptSecContextNext,                  "gss_accept_sec_context");
+    NEXT_SYMBOL(gssReleaseCredNext,                       "gss_release_cred");
+    NEXT_SYMBOL(gssReleaseNameNext,                       "gss_release_name");
+    NEXT_SYMBOL(gssInquireSecContextByOidNext,            "gss_inquire_sec_context_by_oid");
+    NEXT_SYMBOL(gssDeleteSecContextNext,                  "gss_delete_sec_context");
+    NEXT_SYMBOL(gssDisplayNameNext,                       "gss_display_name");
+    NEXT_SYMBOL(gssImportNameNext,                        "gss_import_name");
+    NEXT_SYMBOL(gssStoreCredNext,                         "gss_store_cred");
+    NEXT_SYMBOL(gssGetNameAttributeNext,                  "gss_get_name_attribute");
+
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssInitSecContext(OM_uint32 *minor,
+                  gss_cred_id_t cred,
+                  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)
+{
+    if (gssInitSecContextNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssInitSecContextNext(minor, cred, 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
+gssAcceptSecContext(OM_uint32 *minor,
+                    gss_ctx_id_t *context_handle,
+                    gss_cred_id_t cred,
+                    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)
+{
+    if (gssAcceptSecContextNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssAcceptSecContextNext(minor, context_handle, cred,
+                                   input_token, input_chan_bindings,
+                                   src_name, mech_type, output_token,
+                                   ret_flags, time_rec, delegated_cred_handle);
+}
+
+OM_uint32
+gssReleaseCred(OM_uint32 *minor,
+               gss_cred_id_t *cred_handle)
+{
+    if (gssReleaseCredNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssReleaseCredNext(minor, cred_handle);
+}
+
+OM_uint32
+gssReleaseName(OM_uint32 *minor,
+               gss_name_t *name)
+{
+    if (gssReleaseName == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssReleaseNameNext(minor, name);
+}
+
+OM_uint32
+gssDeleteSecContext(OM_uint32 *minor,
+                    gss_ctx_id_t *context_handle,
+                    gss_buffer_t output_token)
+{
+    if (gssDeleteSecContextNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssDeleteSecContextNext(minor, context_handle, output_token);
+}
+
+static OM_uint32
+gssDisplayName(OM_uint32 *minor,
+               gss_name_t name,
+               gss_buffer_t buffer,
+               gss_OID *name_type)
+{
+    if (gssDisplayNameNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssDisplayNameNext(minor, name, buffer, name_type);
+}
+
+static OM_uint32
+gssImportName(OM_uint32 *minor,
+              gss_buffer_t buffer,
+              gss_OID name_type,
+              gss_name_t *name)
+{
+    if (gssImportNameNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssImportNameNext(minor, buffer, name_type, name);
+}
+
+OM_uint32
+gssInquireSecContextByOid(OM_uint32 *minor,
+                          const gss_ctx_id_t context_handle,
+                          const gss_OID desired_object,
+                          gss_buffer_set_t *data_set)
+{
+    if (gssInquireSecContextByOidNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssInquireSecContextByOidNext(minor, context_handle,
+                                         desired_object, data_set);
+}
+
+OM_uint32
+gssStoreCred(OM_uint32 *minor,
+             const gss_cred_id_t input_cred_handle,
+             gss_cred_usage_t input_usage,
+             const gss_OID desired_mech,
+             OM_uint32 overwrite_cred,
+             OM_uint32 default_cred,
+             gss_OID_set *elements_stored,
+             gss_cred_usage_t *cred_usage_stored)
+{
+    if (gssStoreCredNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssStoreCredNext(minor, input_cred_handle, input_usage,
+                            desired_mech, overwrite_cred, default_cred,
+                            elements_stored, cred_usage_stored);
+}
+
+OM_uint32
+gssGetNameAttribute(OM_uint32 *minor,
+                    gss_name_t name,
+                    gss_buffer_t attr,
+                    int *authenticated,
+                    int *complete,
+                    gss_buffer_t value,
+                    gss_buffer_t display_value,
+                    int *more)
+{
+    if (gssGetNameAttributeNext == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    return gssGetNameAttributeNext(minor, name, attr, authenticated, complete,
+                                   value, display_value, more);
+}
diff --git a/util_reauth.h b/util_reauth.h
new file mode 100644 (file)
index 0000000..dca9bf0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2010, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gssapiP_eap.h"
+
+#ifndef _UTIL_REAUTH_H_
+#define _UTIL_REAUTH_H_ 1
+
+#define KRB5_AUTHDATA_RADIUS_AVP        513
+
+OM_uint32
+gssInitSecContext(OM_uint32 *minor,
+                  gss_cred_id_t cred,
+                  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
+gssAcceptSecContext(OM_uint32 *minor,
+                    gss_ctx_id_t *context_handle,
+                    gss_cred_id_t cred,
+                    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
+gssReleaseCred(OM_uint32 *minor,
+               gss_cred_id_t *cred_handle);
+
+OM_uint32
+gssReleaseName(OM_uint32 *minor,
+               gss_name_t *name);
+
+OM_uint32
+gssDeleteSecContext(OM_uint32 *minor,
+                    gss_ctx_id_t *context_handle,
+                    gss_buffer_t output_token);
+
+OM_uint32
+gssInquireSecContextByOid(OM_uint32 *minor,
+                          const gss_ctx_id_t context_handle,
+                          const gss_OID desired_object,
+                          gss_buffer_set_t *data_set);
+
+OM_uint32
+gssStoreCred(OM_uint32 *minor,
+             const gss_cred_id_t input_cred_handle,
+             gss_cred_usage_t input_usage,
+             const gss_OID desired_mech,
+             OM_uint32 overwrite_cred,
+             OM_uint32 default_cred,
+             gss_OID_set *elements_stored,
+             gss_cred_usage_t *cred_usage_stored);
+
+OM_uint32
+gssGetNameAttribute(OM_uint32 *minor,
+                    gss_name_t name,
+                    gss_buffer_t attr,
+                    int *authenticated,
+                    int *complete,
+                    gss_buffer_t value,
+                    gss_buffer_t display_value,
+                    int *more);
+
+OM_uint32
+gssEapMakeReauthCreds(OM_uint32 *minor,
+                      gss_ctx_id_t ctx,
+                      gss_cred_id_t cred,
+                      gss_buffer_t credBuf);
+
+OM_uint32
+gssEapStoreReauthCreds(OM_uint32 *minor,
+                       gss_ctx_id_t ctx,
+                       gss_cred_id_t cred,
+                       gss_buffer_t credBuf);
+
+
+OM_uint32
+gssEapGlueToMechName(OM_uint32 *minor,
+                     gss_name_t glueName,
+                     gss_name_t *pMechName);
+
+OM_uint32
+gssEapMechToGlueName(OM_uint32 *minor,
+                     gss_name_t mechName,
+                     gss_name_t *pGlueName);
+
+OM_uint32
+gssEapReauthComplete(OM_uint32 *minor,
+                    gss_ctx_id_t ctx,
+                    gss_cred_id_t cred,
+                    const gss_OID mech,
+                    OM_uint32 timeRec);
+
+OM_uint32
+gssEapReauthInitialize(OM_uint32 *minor);
+
+#endif /* _UTIL_REAUTH_H_ */
index 7f3d3c2..02d6557 100644 (file)
@@ -202,7 +202,7 @@ verifyTokenHeader(OM_uint32 *minor,
                   size_t *body_size,
                   unsigned char **buf_in,
                   size_t toksize_in,
-                  enum gss_eap_token_type tok_type)
+                  enum gss_eap_token_type *ret_tok_type)
 {
     unsigned char *buf = *buf_in;
     ssize_t seqsize;
@@ -211,6 +211,9 @@ verifyTokenHeader(OM_uint32 *minor,
 
     *minor = 0;
 
+    if (ret_tok_type != NULL)
+        *ret_tok_type = TOK_TYPE_NONE;
+
     if ((toksize -= 1) < 0)
         return GSS_S_DEFECTIVE_TOKEN;
 
@@ -249,13 +252,12 @@ verifyTokenHeader(OM_uint32 *minor,
         return GSS_S_BAD_MECH;
     }
 
-    if (tok_type != TOK_TYPE_NONE) {
+    if (ret_tok_type != NULL) {
         if ((toksize -= 2) < 0)
             return GSS_S_DEFECTIVE_TOKEN;
 
-        if ((*buf++ != ((tok_type >> 8) & 0xff)) ||
-            (*buf++ != (tok_type & 0xff)))
-            return GSS_S_DEFECTIVE_TOKEN;
+        *ret_tok_type = load_uint16_be(buf);
+        buf += 2;
     }
     *buf_in = buf;
     *body_size = toksize;
diff --git a/wrap.c b/wrap.c
index 4208e91..f6542b7 100644 (file)
--- a/wrap.c
+++ b/wrap.c
@@ -41,13 +41,28 @@ gss_wrap(OM_uint32 *minor,
          int *conf_state,
          gss_buffer_t output_message_buffer)
 {
+    if (!CTX_IS_ESTABLISHED(ctx))
+        return GSS_S_NO_CONTEXT;
+
+    return gssEapWrap(minor, ctx, conf_req_flag, qop_req,
+                      input_message_buffer,
+                      conf_state, output_message_buffer);
+}
+
+OM_uint32
+gssEapWrap(OM_uint32 *minor,
+           gss_ctx_id_t ctx,
+           int conf_req_flag,
+           gss_qop_t qop_req,
+           gss_buffer_t input_message_buffer,
+           int *conf_state,
+           gss_buffer_t output_message_buffer)
+{
     OM_uint32 major, tmpMinor;
     gss_iov_buffer_desc iov[4];
     unsigned char *p;
     int i;
 
-    if (!CTX_IS_ESTABLISHED(ctx))
-        return GSS_S_NO_CONTEXT;
 
     iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
     iov[0].buffer.value = NULL;
index 9a10fe3..ae43ef9 100644 (file)
 
 #include "gssapiP_eap.h"
 
+unsigned char
+rfc4121Flags(gss_ctx_id_t ctx, int receiving)
+{
+    unsigned char flags;
+    int isAcceptor;
+
+    isAcceptor = !CTX_IS_INITIATOR(ctx);
+    if (receiving)
+        isAcceptor = !isAcceptor;
+
+    flags = 0;
+    if (isAcceptor)
+        flags |= TOK_FLAG_SENDER_IS_ACCEPTOR;
+
+    if ((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) &&
+        (ctx->gssFlags & GSS_C_MUTUAL_FLAG))
+        flags |= TOK_FLAG_ACCEPTOR_SUBKEY;
+
+    return flags;
+}
+
 OM_uint32
 gssEapWrapOrGetMIC(OM_uint32 *minor,
                    gss_ctx_id_t ctx,
@@ -68,7 +89,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
     gss_iov_buffer_t header;
     gss_iov_buffer_t padding;
     gss_iov_buffer_t trailer;
-    unsigned char acceptorFlag;
+    unsigned char flags;
     unsigned char *outbuf = NULL;
     unsigned char *tbuf = NULL;
     int keyUsage;
@@ -82,23 +103,16 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
 
     GSSEAP_KRB_INIT(&krbContext);
 
-    acceptorFlag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR;
+    flags = rfc4121Flags(ctx, FALSE);
 
-    switch (toktype) {
-    case TOK_TYPE_WRAP:
+    if (toktype == TOK_TYPE_WRAP) {
         keyUsage = CTX_IS_INITIATOR(ctx)
                    ? KEY_USAGE_INITIATOR_SEAL
                    : KEY_USAGE_ACCEPTOR_SEAL;
-        break;
-    case TOK_TYPE_GSS_CB:
-        keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
-        break;
-    case TOK_TYPE_MIC:
-    default:
+    } else {
         keyUsage = CTX_IS_INITIATOR(ctx)
                    ? KEY_USAGE_INITIATOR_SIGN
                    : KEY_USAGE_ACCEPTOR_SIGN;
-        break;
     }
 
     gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
@@ -177,9 +191,8 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         /* TOK_ID */
         store_uint16_be((uint16_t)toktype, outbuf);
         /* flags */
-        outbuf[2] = (acceptorFlag
-                     | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0)
-                     | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0));
+        outbuf[2] = flags
+                     | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0);
         /* filler */
         outbuf[3] = 0xFF;
         /* EC */
@@ -251,8 +264,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         /* TOK_ID */
         store_uint16_be((uint16_t)toktype, outbuf);
         /* flags */
-        outbuf[2] = (acceptorFlag
-                     | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0));
+        outbuf[2] = flags;
         /* filler */
         outbuf[3] = 0xFF;
         if (toktype == TOK_TYPE_WRAP) {
@@ -276,8 +288,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         if (code != 0)
             goto cleanup;
 
-        if (toktype != TOK_TYPE_GSS_CB)
-            ctx->sendSeq++;
+        ctx->sendSeq++;
 
         if (toktype == TOK_TYPE_WRAP) {
             /* Fix up EC field */
@@ -285,7 +296,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
             /* Fix up RRC field */
             store_uint16_be(rrc, outbuf + 6);
         }
-    } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
+    } else if (toktype == TOK_TYPE_MIC) {
         trailer = NULL;
         goto wrap_with_checksum;
     } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
index c451cb2..33a15ef 100644 (file)
@@ -80,8 +80,8 @@ gssEapWrapIovLength(OM_uint32 *minor,
     if (qop_req != GSS_C_QOP_DEFAULT)
         return GSS_S_FAILURE;
 
-    if (!CTX_IS_ESTABLISHED(ctx))
-        return GSS_S_NO_CONTEXT;
+    if (ctx->encryptionType == ENCTYPE_NULL)
+        return GSS_S_UNAVAILABLE;
 
     GSSEAP_KRB_INIT(&krbContext);