Use AD-KDCIssued to protect RADIUS authdata. Cleanup.
authorLuke Howard <lukeh@padl.com>
Thu, 23 Sep 2010 14:54:10 +0000 (16:54 +0200)
committerLuke Howard <lukeh@padl.com>
Thu, 23 Sep 2010 14:54:10 +0000 (16:54 +0200)
16 files changed:
Makefile.am
accept_sec_context.c
authdata_plugin.h [new file with mode: 0644]
gssapiP_eap.h
init_sec_context.c
mech
radius_ad.exports [new file with mode: 0644]
unwrap.c
unwrap_iov.c
util.h
util_adshim.c [new file with mode: 0644]
util_reauth.c
util_reauth.h
wrap.c
wrap_iov.c
wrap_iov_length.c

index 5347b06..b59b57c 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            \
@@ -82,3 +82,12 @@ libmech_eap_la_SOURCES =                     \
        wrap_iov_length.c                       \
        wrap_size_limit.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
index f223cc5..eb1a07c 100644 (file)
@@ -326,7 +326,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         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,54 +339,41 @@ 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;
-
-    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
-        if (inputToken->length < 14) {
-            return GSS_S_DEFECTIVE_TOKEN;
-        }
-
-        iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
-        iov[0].buffer.length = 0;
-        iov[0].buffer.value = NULL;
-
-        if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
-            iov[0].buffer = chanBindings->application_data;
+    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+    iov[0].buffer.length = 0;
+    iov[0].buffer.value = NULL;
 
-        iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
-        iov[1].buffer.length = 16;
-        iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
+    iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
+    iov[1].buffer = *inputToken;
 
-        assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
-
-        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 = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+                                    iov, 2, TOK_TYPE_WRAP);
+    if (GSS_ERROR(major))
+        return major;
 
-        major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
-                                        iov, 3, TOK_TYPE_GSS_CB);
-        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;
     }
 
-    ctx->state = EAP_STATE_KRB_REAUTH_CRED;
+    gss_release_buffer(&tmpMinor, &iov[0].buffer);
 
-    return GSS_S_CONTINUE_NEEDED;
+    return major;
 }
 
 static OM_uint32
-eapGssSmAcceptKrbReauthCred(OM_uint32 *minor,
+eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
                             gss_ctx_id_t ctx,
                             gss_cred_id_t cred,
                             gss_buffer_t inputToken,
@@ -395,12 +382,44 @@ eapGssSmAcceptKrbReauthCred(OM_uint32 *minor,
 {
     OM_uint32 major;
 
-    major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
+    outputToken->length = 0;
+    outputToken->value = NULL;
+
+    major = acceptGssChannelBindings(minor, ctx, cred, inputToken,
+                                     chanBindings);
+    if (GSS_ERROR(major))
+        return major;
+
+    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;
+
+    major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken);
     if (GSS_ERROR(major))
         return major;
 
     ctx->state = EAP_STATE_ESTABLISHED;
 
+    major = duplicateBuffer(minor, &credsToken, outputToken);
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &credsToken);
+        return major;
+    }
+
+    gss_release_buffer(&tmpMinor, &credsToken);
+
     return GSS_S_COMPLETE;
 }
 
@@ -425,39 +444,25 @@ acceptReadyKrb(OM_uint32 *minor,
                const gss_OID mech,
                OM_uint32 timeRec)
 {
-    OM_uint32 major, tmpMinor;
-    gss_buffer_desc authData = GSS_C_EMPTY_BUFFER;
+    OM_uint32 major;
 
     major = gssEapGlueToMechName(minor, initiator, &ctx->initiatorName);
     if (GSS_ERROR(major))
-        goto cleanup;
-
-    major = gssKrbExtractAuthzDataFromSecContext(minor, ctx->kerberosCtx,
-                                                 KRB5_AUTHDATA_RADIUS_AVP,
-                                                 &authData);
-    if (GSS_ERROR(major))
-        goto cleanup;
-
-    major = gssEapImportAttrContext(minor, &authData, ctx->initiatorName);
-    if (GSS_ERROR(major))
-        goto cleanup;
+        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))
-            goto cleanup;
+            return major;
     }
 
     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
     if (GSS_ERROR(major))
-        goto cleanup;
+        return major;
 
     ctx->state = EAP_STATE_ESTABLISHED;
 
-cleanup:
-    gss_release_buffer(&tmpMinor, &authData);
-
-    return major;
+    return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -514,8 +519,8 @@ static struct gss_eap_acceptor_sm {
 } 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_KRB_CRED,   eapGssSmAcceptKrbReauthCred      },
+    { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,       eapGssSmAcceptExtensionsReq      },
+    { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,   eapGssSmAcceptExtensionsResp     },
     { TOK_TYPE_NONE,        TOK_TYPE_NONE,       eapGssSmAcceptEstablished        },
     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth          },
 };
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 c9c785c..846c3ed 100644 (file)
@@ -104,8 +104,8 @@ struct gss_cred_id_struct {
 enum gss_eap_state {
     EAP_STATE_IDENTITY = 0,
     EAP_STATE_AUTHENTICATE,
-    EAP_STATE_GSS_CHANNEL_BINDINGS,
-    EAP_STATE_KRB_REAUTH_CRED,
+    EAP_STATE_EXTENSIONS_REQ,
+    EAP_STATE_EXTENSIONS_RESP,
     EAP_STATE_ESTABLISHED,
     EAP_STATE_KRB_REAUTH_GSS
 };
@@ -201,6 +201,14 @@ 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);
index 0709b1f..f1f260a 100644 (file)
@@ -428,7 +428,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) {
@@ -462,59 +462,28 @@ 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;
+    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
 
-    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
-    iov[0].buffer.length = 0;
-    iov[0].buffer.value = NULL;
-
-    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;
-
-    /* Skip past token ID */
-    assert(iov[1].buffer.length > 2);
-    assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
-
-    buf.length = iov[1].buffer.length - 2;
-    buf.value = (unsigned char *)iov[1].buffer.value + 2;
-
-    major = duplicateBuffer(minor, &buf, outputToken);
-    if (GSS_ERROR(major))
-        goto cleanup;
+        return major;                       
 
-    major = GSS_S_CONTINUE_NEEDED;
-    ctx->state = EAP_STATE_KRB_REAUTH_CRED;
-
-cleanup:
-    gssEapReleaseIov(iov, 2);
-
-    return major;
+    return GSS_S_CONTINUE_NEEDED;
 }
 
 static OM_uint32
-eapGssSmInitKrbReauthCred(OM_uint32 *minor,
+eapGssSmInitExtensionsReq(OM_uint32 *minor,
                           gss_cred_id_t cred,
                           gss_ctx_id_t ctx,
                           gss_name_t target,
@@ -525,6 +494,38 @@ eapGssSmInitKrbReauthCred(OM_uint32 *minor,
                           gss_buffer_t inputToken,
                           gss_buffer_t outputToken)
 {
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc cbToken = GSS_C_EMPTY_BUFFER;
+
+    major = initGssChannelBindings(minor, ctx, chanBindings, &cbToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    ctx->state = EAP_STATE_EXTENSIONS_RESP;
+
+    major = duplicateBuffer(minor, &cbToken, outputToken);
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &cbToken);
+        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)
+{
     OM_uint32 major;
 
     major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
@@ -642,8 +643,8 @@ static struct gss_eap_initiator_sm {
 } 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_KRB_CRED,TOK_TYPE_NONE,          eapGssSmInitKrbReauthCred       },
+    { TOK_TYPE_NONE,    TOK_TYPE_EXT_REQ,       eapGssSmInitExtensionsReq       },
+    { TOK_TYPE_EXT_RESP,TOK_TYPE_NONE,          eapGssSmInitExtensionsResp      },
     { TOK_TYPE_NONE,    TOK_TYPE_NONE,          eapGssSmInitEstablished         },
     { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth           },
 };
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 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 b178da8..ee2790d 100644 (file)
@@ -101,21 +101,14 @@ unwrapToken(OM_uint32 *minor,
 
     flags = rfc4121Flags(ctx, TRUE);
 
-    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);
@@ -213,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;
 
@@ -229,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;
@@ -471,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;
 
@@ -496,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 6b647e3..f667440 100644 (file)
--- a/util.h
+++ b/util.h
@@ -88,8 +88,8 @@ 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_KRB_CRED                = 0x0604,  /* to be specified */
+    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 */
 };
 
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 cd73da6..63b12d9 100644 (file)
@@ -66,7 +66,7 @@ getAcceptorKey(krb5_context krbContext,
 
     if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
         code = krb5_kt_get_entry(krbContext, keytab,
-                                 cred->name->krbPrincipal, 0, 
+                                 cred->name->krbPrincipal, 0,
                                  ctx->encryptionType, &ktent);
         if (code != 0)
             goto cleanup;
@@ -104,7 +104,7 @@ cleanup:
         memset(key, 0, sizeof(key));
     }
 
-    return code; 
+    return code;
 }
 
 OM_uint32
@@ -124,10 +124,10 @@ gssEapMakeReauthCreds(OM_uint32 *minor,
     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,
@@ -170,7 +170,12 @@ gssEapMakeReauthCreds(OM_uint32 *minor,
     authDatum.contents = attrBuf.value;
     authData[0] = &authDatum;
     authData[1] = NULL;
-    enc_part.authorization_data = authData;
+
+    code = krb5_make_authdata_kdc_issued(krbContext, &session,
+                                         ticket.server, authData,
+                                         &enc_part.authorization_data);
+    if (code != 0)
+        goto cleanup;
 
     ticket.enc_part2 = &enc_part;
 
@@ -217,6 +222,7 @@ cleanup:
     gss_release_buffer(minor, &attrBuf);
     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);
 
@@ -408,6 +414,16 @@ static OM_uint32 (*gssStoreCredNext)(
     gss_OID_set *elements_stored,
     gss_cred_usage_t *cred_usage_stored);
 
+static OM_uint32 (*gssGetNameAttributeNext)(
+    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);
+
 #define NEXT_SYMBOL(local, global)  ((local) = dlsym(RTLD_NEXT, (global)))
 
 OM_uint32
@@ -423,6 +439,7 @@ gssEapReauthInitialize(OM_uint32 *minor)
     NEXT_SYMBOL(gssImportNameNext,                        "gss_import_name");
     NEXT_SYMBOL(gssKrbExtractAuthzDataFromSecContextNext, "gsskrb5_extract_authz_data_from_sec_context");
     NEXT_SYMBOL(gssStoreCredNext,                         "gss_store_cred");
+    NEXT_SYMBOL(gssGetNameAttributeNext,                  "gss_get_name_attribute");
 
     return GSS_S_COMPLETE;
 }
@@ -574,6 +591,68 @@ gssStoreCred(OM_uint32 *minor,
 }
 
 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);
+}
+
+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.
+ */
+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;
+}
+
+OM_uint32
 gssEapGlueToMechName(OM_uint32 *minor,
                      gss_name_t glueName,
                      gss_name_t *pMechName)
@@ -592,7 +671,16 @@ gssEapGlueToMechName(OM_uint32 *minor,
     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;
@@ -701,3 +789,4 @@ cleanup:
 
     return major;
 }
+
index e37dd49..241edce 100644 (file)
@@ -101,6 +101,16 @@ gssStoreCred(OM_uint32 *minor,
              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,
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 994dded..ae43ef9 100644 (file)
@@ -105,21 +105,14 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
 
     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);
@@ -295,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 */
@@ -304,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);