More work on initial implementation
authorLuke Howard <lukeh@padl.com>
Tue, 7 Sep 2010 21:41:05 +0000 (23:41 +0200)
committerLuke Howard <lukeh@padl.com>
Tue, 7 Sep 2010 21:41:05 +0000 (23:41 +0200)
.gitignore
Makefile.am
acinclude.m4
configure.ac
gssapiP_eap.h
unwrap_iov.c
util_context.c
wrap_iov.c

index fbcaac4..5be0c7e 100644 (file)
@@ -24,3 +24,5 @@ Makefile
 
 .deps
 .libs
+.a.out.dSYM
+.dSYM
index 479c57a..3a86451 100644 (file)
@@ -4,9 +4,9 @@ gssdir = $(libdir)/gss
 
 gss_LTLIBRARIES = libmech_eap.la
 
-libmech_eap_la_CFLAGS  = -g -Wall -fno-strict-aliasing @KRB5_CFLAGS@ @TARGET_CFLAGS@
-libmech_eap_la_LDFLAGS = -export-symbols mech_eap.exports -version-info 0:0:0 @KRB5_LDFLAGS@ @TARGET_LDFLAGS@
-libmech_eap_la_LIBADD  = @KRB5_LIBS@
+libmech_eap_la_CFLAGS  = -DBUILD_GSSEAP_LIB -g -Wall -fno-strict-aliasing @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@
+libmech_eap_la_LDFLAGS = -export-symbols mech_eap.exports -version-info 0:0:0 @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@
+libmech_eap_la_LIBADD  = @EAP_LIBS@ @KRB5_LIBS@
 
 libmech_eap_la_SOURCES =                       \
        accept_sec_context.c                    \
@@ -32,8 +32,10 @@ libmech_eap_la_SOURCES =                     \
        init_sec_context.c                      \
        inquire_context.c                       \
        inquire_cred.c                          \
+       inquire_cred_by_oid.c                   \
        inquire_mechs_for_name.c                \
        inquire_names_for_mech.c                \
+       inquire_sec_context_by_oid.c            \
        map_name_to_any.c                       \
        process_context_token.c                 \
        pseudo_random.c                         \
@@ -41,9 +43,16 @@ libmech_eap_la_SOURCES =                     \
        release_cred.c                          \
        release_name.c                          \
        set_name_attribute.c                    \
+       set_cred_option.c                       \
+       set_sec_context_option.c                \
        store_cred.c                            \
        unwrap.c                                \
        unwrap_iov.c                            \
+       util_context.c                          \
+       util_cksum.c                            \
+       util_cred.c                             \
+       util_crypt.c                            \
+       util_name.c                             \
        verify_mic.c                            \
        wrap.c                                  \
        wrap_iov.c                              \
index c376194..cd51aab 100644 (file)
@@ -1,7 +1,7 @@
 dnl Based on the one from the Boinc project by Reinhard
 
 AC_DEFUN([AX_CHECK_KRB5],
-[AC_MSG_CHECKING(for Kerberos)
+[AC_MSG_CHECKING(for GSS-API and Kerberos implementation)
 KRB5_DIR=
 found_krb5="no"
 AC_ARG_WITH(krb5,
@@ -13,22 +13,16 @@ for dir in $check_krb5_dir /usr /usr/local ; do
    krb5dir="$dir"
    if test -f "$dir/include/krb5.h"; then
      found_krb5="yes";
-     krb5_DIR="${krb5dir}"
-     krb5_CFLAGS="-I$krb5dir/include";
+     KRB5_DIR="${krb5dir}"
+     KRB5_CFLAGS="-I$krb5dir/include";
      break;
    fi
-   if test -f "$dir/include/krb5.h"; then
-     found_krb5="yes";
-     krb5_DIR="${krb5dir}"
-     krb5_CFLAGS="-I$krb5dir/include/";
-     break
-   fi
 done
 AC_MSG_RESULT($found_krb5)
 if test x_$found_krb5 != x_yes; then
    AC_MSG_ERROR([
 ----------------------------------------------------------------------
-  Cannot find krb5 libraries.
+  Cannot find GSS-API/Kerberos libraries.
 
   Please install MIT or Heimdal or specify installation directory with
   --with-krb5=(dir).
@@ -36,10 +30,49 @@ if test x_$found_krb5 != x_yes; then
 ])
 else
         printf "Kerberos found in $krb5dir\n";
-       krb5_LIBS="-lgssapi_krb5 -lkrb5";
-        krb5_LDFLAGS="-L$krb5dir/lib";
+       KRB5_LIBS="-lgssapi_krb5 -lkrb5";
+        KRB5_LDFLAGS="-L$krb5dir/lib";
        AC_SUBST(KRB5_CFLAGS)
        AC_SUBST(KRB5_LDFLAGS)
        AC_SUBST(KRB5_LIBS)
 fi
 ])dnl
+
+AC_DEFUN([AX_CHECK_EAP],
+[AC_MSG_CHECKING(for EAP implementation)
+EAP_DIR=
+found_eap="no"
+AC_ARG_WITH(eap,
+    AC_HELP_STRING([--with-eap],
+       [Use eap (in specified installation directory)]),
+    [check_eap_dir="$withval"],
+    [check_eap_dir=])
+for dir in $check_eap_dir /usr /usr/local ; do
+   eapdir="$dir"
+   if test -f "$dir/src/eap_peer/eap.h"; then
+     found_eap="yes";
+     EAP_DIR="${eapdir}"
+     EAP_CFLAGS="-I$eapdir/src/common -I$eapdir/src -I$eapdir/src/utils";
+     break;
+   fi
+done
+AC_MSG_RESULT($found_eap)
+if test x_$found_eap != x_yes; then
+   AC_MSG_ERROR([
+----------------------------------------------------------------------
+  Cannot find EAP libraries.
+
+  Please install wpa_supplicant or specify installation directory with
+  --with-eap=(dir).
+----------------------------------------------------------------------
+])
+else
+        printf "EAP found in $eapdir\n";
+       EAP_LIBS="-leap";
+        EAP_LDFLAGS="-L$eapdir/eap_example";
+       AC_SUBST(EAP_CFLAGS)
+       AC_SUBST(EAP_LDFLAGS)
+       AC_SUBST(EAP_LIBS)
+fi
+])dnl
+
index 79826d3..ee0ae6b 100644 (file)
@@ -27,5 +27,6 @@ dnl   esac
 AC_SUBST(TARGET_CFLAGS)
 AC_SUBST(TARGET_LDFLAGS)
 AX_CHECK_KRB5
+AX_CHECK_EAP
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
index 6415371..128b0fe 100644 (file)
 #ifndef _GSSAPIP_EAP_H_
 #define _GSSAPIP_EAP_H_ 1
 
-#include <gssapi/gssapi.h>
-#include <krb5.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
 
+/* GSS includes */
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
 #include "gssapi_eap.h"
 
+/* EAP includes */
+#define IEEE8021X_EAPOL 1
+
+#include <common.h>
+#include <eap_peer/eap.h>
+#include <eap_peer/eap_config.h>
+#include <wpabuf.h>
+
+/* Kerberos includes */
+#include <krb5.h>
+
 struct gss_name_struct {
     OM_uint32 flags;
-    krb5_principal principal;
+    krb5_principal kerberosName;
     void *aaa;
     void *assertion;
 };
@@ -52,13 +68,15 @@ struct gss_name_struct {
 
 struct gss_cred_id_struct {
     OM_uint32 flags;
-    gss_name_t initiatorName;
-    gss_name_t acceptorName;
+    gss_name_t name;
     gss_buffer_desc password;
+    time_t expiryTime;
 };
 
 #define CTX_FLAG_INITIATOR                  0x00000001
 
+#define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
+
 enum eap_gss_state {
     EAP_STATE_AUTHENTICATE = 1,
     EAP_STATE_KEY_TRANSPORT,
@@ -67,18 +85,168 @@ enum eap_gss_state {
     EAP_STATE_ESTABLISHED
 };
 
+#define CTX_IS_ESTABLISHED(ctx)             ((ctx)->state == EAP_STATE_ESTABLISHED)
+
+/* Initiator context flags */
+#define CTX_FLAG_EAP_SUCCESS                0x00010000
+#define CTX_FLAG_EAP_RESTART                0x00020000
+#define CTX_FLAG_EAP_FAIL                   0x00040000
+#define CTX_FLAG_EAP_RESP                   0x00080000
+#define CTX_FLAG_EAP_NO_RESP                0x00100000
+#define CTX_FLAG_EAP_REQ                    0x00200000
+#define CTX_FLAG_EAP_PORT_ENABLED           0x00400000
+#define CTX_FLAG_EAP_ALT_ACCEPT             0x00800000
+#define CTX_FLAG_EAP_ALT_REJECT             0x01000000
+
+struct eap_gss_initiator_ctx {
+    struct wpabuf *eapReqData;
+    unsigned int idleWhile;
+    struct eap_peer_config eapConfig;
+    struct eap_sm *eap;
+};
+
+/* Acceptor context flags */
+struct eap_gss_acceptor_ctx {
+};
+
 struct gss_ctx_id_struct {
     enum eap_gss_state state;
     OM_uint32 flags;
     OM_uint32 gssFlags;
     krb5_context kerberosCtx;
     gss_OID mechanismUsed;
+    krb5_enctype encryptionType;
     krb5_cksumtype checksumType;
     krb5_keyblock *encryptionKey;
     gss_name_t initiatorName;
     gss_name_t acceptorName;
-    OM_uint32 lifetime;
+    time_t expiryTime;
+    union {
+        struct eap_gss_initiator_ctx initiator;
+        #define initiatorCtx         ctxU.initiator
+        struct eap_gss_acceptor_ctx  acceptor;
+        #define acceptorCtx          ctxU.acceptor
+    } ctxU;
+    uint64_t sendSeq, recvSeq;
+    void *seqState;
+};
+
+#define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
+#define TOK_FLAG_WRAP_CONFIDENTIAL          0x02
+#define TOK_FLAG_ACCEPTOR_SUBKEY            0x04
+
+enum gss_eap_token_type {
+    TOK_TYPE_MIC     = 0x0404,
+    TOK_TYPE_WRAP    = 0x0504,
+    TOK_TYPE_DELETE  = 0x0405
 };
 
+/* Helper APIs */
+OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
+OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
+
+OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName);
+OM_uint32 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName);
+
+OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred);
+OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred);
+
+/* Kerberos token services */
+#define KRB_USAGE_ACCEPTOR_SEAL             22
+#define KRB_USAGE_ACCEPTOR_SIGN             23
+#define KRB_USAGE_INITIATOR_SEAL            24
+#define KRB_USAGE_INITIATOR_SIGN            25
+
+#if 0
+#define KRB_KEYTYPE(key)                    ((key)->keytype)
+#else
+#define KRB_KEYTYPE(key)                    ((key)->enctype)
+#endif
+
+/* util_crypt.c */
+int
+gssEapEncrypt(krb5_context context, int dce_style, size_t ec,
+              size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv,
+              gss_iov_buffer_desc *iov, int iov_count);
+
+int
+gssEapDecrypt(krb5_context context, int dce_style, size_t ec,
+              size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv,
+              gss_iov_buffer_desc *iov, int iov_count);
+
+krb5_cryptotype
+gssEapTranslateCryptoFlag(OM_uint32 type);
+
+gss_iov_buffer_t
+gssEapLocateIov(gss_iov_buffer_desc *iov,
+                int iov_count,
+                OM_uint32 type);
+
+void
+gssEapIovMessageLength(gss_iov_buffer_desc *iov,
+                       int iov_count,
+                       size_t *data_length,
+                       size_t *assoc_data_length);
+
+void
+gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count);
+
+int
+gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count);
+
+int
+gssEapAllocIov(gss_iov_buffer_t iov, size_t size);
+
+/* util_cksum.c */
+int
+gssEapSign(krb5_context context,
+           krb5_cksumtype type,
+           size_t rrc,
+           krb5_keyblock *key,
+           krb5_keyusage sign_usage,
+           gss_iov_buffer_desc *iov,
+           int iov_count);
+
+int
+gssEapVerify(krb5_context context,
+             krb5_cksumtype type,
+             size_t rrc,  
+             krb5_keyblock *key,
+             krb5_keyusage sign_usage,
+             gss_iov_buffer_desc *iov,
+             int iov_count,
+             int *valid);
+
+/* wrap_iov.c */
+OM_uint32
+gssEapWrapOrGetMIC(OM_uint32 *minor,
+                   gss_ctx_id_t ctx,
+                   int conf_req_flag,
+                   int *conf_state,
+                   gss_iov_buffer_desc *iov,
+                   int iov_count,
+                   enum gss_eap_token_type toktype);
+
+OM_uint32
+gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status,
+                        gss_ctx_id_t ctx,
+                        int *conf_state,
+                        gss_qop_t *qop_state,
+                        gss_iov_buffer_desc *iov,
+                        int iov_count,
+                        enum gss_eap_token_type toktype);
+
+/* Helper macros */
+#define GSSEAP_CALLOC(count, size)      (calloc((count), (size)))
+#define GSSEAP_FREE(ptr)                (free((ptr)))
+#define GSSEAP_MALLOC(size)             (malloc((size)))
+#define GSSEAP_REALLOC(ptr, size)       (realloc((ptr), (size)))
+
+#define GSSEAP_NOT_IMPLEMENTED          do {            \
+        assert(0 && "not implemented");                 \
+        *minor = ENOSYS;                                \
+        return GSS_S_FAILURE;                           \
+    } while (0)
+
 #endif /* _GSSAPIP_EAP_H_ */
 
index 264e137..3cec4f0 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*
+ * lib/gssapi/krb5/k5sealv3iov.c
+ *
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ */
 
 #include "gssapiP_eap.h"
 
 OM_uint32
+gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status,
+                        gss_ctx_id_t ctx,
+                        int *conf_state,
+                        gss_qop_t *qop_state,
+                        gss_iov_buffer_desc *iov,
+                        int iov_count,
+                        enum gss_eap_token_type toktype)
+{
+    OM_uint32 code;
+    gss_iov_buffer_t header;
+    gss_iov_buffer_t padding;
+    gss_iov_buffer_t trailer;
+    unsigned char acceptor_flag;
+    unsigned char *ptr = NULL;
+    int key_usage;
+    size_t rrc, ec;
+    size_t data_length, assoc_data_length;
+    uint64_t seqnum;
+    krb5_boolean valid;
+    krb5_cksumtype cksumtype;
+    int conf_flag = 0;
+
+    *minor_status = 0;
+
+    if (qop_state != NULL)
+        *qop_state = GSS_C_QOP_DEFAULT;
+
+    if (!CTX_IS_ESTABLISHED(ctx))
+        return GSS_S_NO_CONTEXT;
+
+    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
+    assert(header != NULL);
+
+    padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
+    if (padding != NULL && padding->buffer.length != 0)
+        return GSS_S_DEFECTIVE_TOKEN;
+
+    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
+
+    acceptor_flag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0;
+    key_usage = (toktype == TOK_TYPE_WRAP
+                 ? (!CTX_IS_INITIATOR(ctx)
+                    ? KRB_USAGE_INITIATOR_SEAL
+                    : KRB_USAGE_ACCEPTOR_SEAL)
+                 : (!CTX_IS_INITIATOR(ctx)
+                    ? KRB_USAGE_INITIATOR_SIGN
+                    : KRB_USAGE_ACCEPTOR_SIGN));
+
+    gssEapIovMessageLength(iov, iov_count, &data_length, &assoc_data_length);
+
+    ptr = (unsigned char *)header->buffer.value;
+
+    if (header->buffer.length < 16) {
+        *minor_status = 0;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    if ((ptr[2] & TOK_FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+        return GSS_S_BAD_SIG;
+    }
+
+    if (ptr[2] & TOK_FLAG_ACCEPTOR_SUBKEY) {
+        return GSS_S_BAD_SIG;
+    }
+
+    if (toktype == TOK_TYPE_WRAP) {
+        unsigned int k5_trailerlen;
+
+        if (load_16_be(ptr) != TOK_TYPE_WRAP)
+            goto defective;
+        conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
+        if (ptr[3] != 0xFF)
+            goto defective;
+        ec = load_16_be(ptr + 4);
+        rrc = load_16_be(ptr + 6);
+        seqnum = load_64_be(ptr + 8);
+
+        code = krb5_c_crypto_length(ctx->kerberosCtx,
+                                    KRB_KEYTYPE(ctx->encryptionKey),
+                                    conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
+                                    KRB5_CRYPTO_TYPE_CHECKSUM,
+                                    &k5_trailerlen);
+        if (code != 0) {
+            *minor_status = code;
+            return GSS_S_FAILURE;
+        }
+
+        /* Deal with RRC */
+        if (trailer == NULL) {
+            size_t desired_rrc = k5_trailerlen;
+
+            if (conf_flag) {
+                desired_rrc += 16; /* E(Header) */
+
+                if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
+                    desired_rrc += ec;
+            }
+
+            /* According to MS, we only need to deal with a fixed RRC for DCE */
+            if (rrc != desired_rrc)
+                goto defective;
+        } else if (rrc != 0) {
+            goto defective;
+        }
+
+        if (conf_flag) {
+            unsigned char *althdr;
+
+            /* Decrypt */
+            code = gssEapDecrypt(ctx->kerberosCtx,
+                                 ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
+                                 ec, rrc, ctx->encryptionKey,
+                                 key_usage, 0, iov, iov_count);
+            if (code != 0) {
+                *minor_status = code;
+                return GSS_S_BAD_SIG;
+            }
+
+            /* Validate header integrity */
+            if (trailer == NULL)
+                althdr = (unsigned char *)header->buffer.value + 16 + ec;
+            else
+                althdr = (unsigned char *)trailer->buffer.value + ec;
+
+            if (load_16_be(althdr) != TOK_TYPE_WRAP
+                || althdr[2] != ptr[2]
+                || althdr[3] != ptr[3]
+                || memcmp(althdr + 8, ptr + 8, 8) != 0) {
+                *minor_status = 0;
+                return GSS_S_BAD_SIG;
+            }
+        } else {
+            /* Verify checksum: note EC is checksum size here, not padding */
+            if (ec != k5_trailerlen)
+                goto defective;
+
+            /* Zero EC, RRC before computing checksum */
+            store_16_be(0, ptr + 4);
+            store_16_be(0, ptr + 6);
+
+            code = gssEapVerify(ctx->kerberosCtx, cksumtype, rrc,
+                                ctx->encryptionKey, key_usage,
+                                iov, iov_count, &valid);
+            if (code != 0 || valid == FALSE) {
+                *minor_status = code;
+                return GSS_S_BAD_SIG;
+            }
+        }
+
+        code = g_order_check(&ctx->seqState, seqnum);
+    } else if (toktype == TOK_TYPE_MIC) {
+        if (load_16_be(ptr) != TOK_TYPE_MIC)
+            goto defective;
+
+    verify_mic_1:
+        if (ptr[3] != 0xFF)
+            goto defective;
+        seqnum = load_64_be(ptr + 8);
+
+        code = gssEapVerify(ctx->kerberosCtx, cksumtype, 0,
+                            ctx->encryptionKey, key_usage,
+                            iov, iov_count, &valid);
+        if (code != 0 || valid == FALSE) {
+            *minor_status = code;
+            return GSS_S_BAD_SIG;
+        }
+        code = g_order_check(&ctx->seqState, seqnum);
+    } else if (toktype == TOK_TYPE_DELETE) {
+        if (load_16_be(ptr) != TOK_TYPE_DELETE)
+            goto defective;
+        goto verify_mic_1;
+    } else {
+        goto defective;
+    }
+
+    *minor_status = 0;
+
+    if (conf_state != NULL)
+        *conf_state = conf_flag;
+
+    return code;
+
+defective:
+    *minor_status = 0;
+
+    return GSS_S_DEFECTIVE_TOKEN;
+}
+
+OM_uint32
 gss_unwrap_iov(OM_uint32 *minor,
                gss_ctx_id_t ctx,
                int *conf_state,
@@ -40,5 +256,8 @@ gss_unwrap_iov(OM_uint32 *minor,
                gss_iov_buffer_desc *iov,
                int iov_count)
 {
-    GSSEAP_NOT_IMPLEMENTED;
+    return gssEapUnwrapOrVerifyMIC(minor, ctx,
+                                   iov, iov_count, conf_state,
+                                   qop_state, TOK_TYPE_WRAP);
+
 }
index daf65d6..7121d7f 100644 (file)
@@ -36,6 +36,7 @@ OM_uint32
 gssEapAllocContext(OM_uint32 *minor,
                    gss_ctx_id_t *pCtx)
 {
+    OM_uint32 tmpMinor;
     gss_ctx_id_t ctx;
 
     assert(*pCtx == GSS_C_NO_CONTEXT);
@@ -46,6 +47,12 @@ gssEapAllocContext(OM_uint32 *minor,
         return GSS_S_FAILURE;
     }
 
+    *minor = krb5_init_context(&ctx->kerberosCtx);
+    if (*minor != 0) {
+        gssEapReleaseContext(&tmpMinor, &ctx);
+        return GSS_S_FAILURE;
+    }
+
     *pCtx = ctx;
 
     return GSS_S_COMPLETE;
index ccc9b16..7bf7f94 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
 
 #include "gssapiP_eap.h"
 
 OM_uint32
+gssEapWrapOrGetMIC(OM_uint32 *minor,
+                   gss_ctx_id_t ctx,
+                   int conf_req_flag,
+                   int *conf_state,
+                   gss_iov_buffer_desc *iov,
+                   int iov_count,
+                   enum gss_eap_token_type toktype)
+{
+    krb5_error_code code = 0;
+    gss_iov_buffer_t header;
+    gss_iov_buffer_t padding;
+    gss_iov_buffer_t trailer;
+    unsigned char acceptor_flag;
+    unsigned char *outbuf = NULL;
+    unsigned char *tbuf = NULL;
+    int key_usage;
+    size_t rrc = 0;
+    unsigned int gss_headerlen, gss_trailerlen;
+    size_t data_length, assoc_data_length;
+
+    if (!CTX_IS_ESTABLISHED(ctx)) {
+        return GSS_S_NO_CONTEXT;
+    }
+
+    acceptor_flag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR;
+    key_usage = ((toktype == TOK_TYPE_WRAP)
+                 ? (CTX_IS_INITIATOR(ctx)
+                    ? KRB_USAGE_INITIATOR_SEAL
+                    : KRB_USAGE_ACCEPTOR_SEAL)
+                 : (CTX_IS_INITIATOR(ctx)
+                    ? KRB_USAGE_INITIATOR_SIGN
+                    : KRB_USAGE_ACCEPTOR_SIGN));
+
+    gssEapIovMessageLength(iov, iov_count, &data_length, &assoc_data_length);
+
+    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
+    if (header == NULL) {
+        *minor = EINVAL;
+        return GSS_S_FAILURE;
+    }
+
+    padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
+    if (padding != NULL)
+        padding->buffer.length = 0;
+
+    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
+
+    if (toktype == TOK_TYPE_WRAP && conf_req_flag) {
+        unsigned int k5_headerlen, k5_trailerlen, k5_padlen;
+        size_t ec = 0;
+        size_t conf_data_length = data_length - assoc_data_length;
+
+        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+                                    KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
+        if (code != 0)
+            goto cleanup;
+
+        code = krb5_c_padding_length(ctx->kerberosCtx, ctx->encryptionType,
+                                     conf_data_length + 16 /* E(Header) */,
+                                     &k5_padlen);
+        if (code != 0)
+            goto cleanup;
+
+        if (k5_padlen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) {
+            /* Windows rejects AEAD tokens with non-zero EC */
+            code = krb5_c_block_size(ctx->kerberosCtx, ctx->encryptionType, &ec);
+            if (code != 0)
+                goto cleanup;
+        } else
+            ec = k5_padlen;
+
+        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+                                    KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
+        if (code != 0)
+            goto cleanup;
+
+        gss_headerlen = 16 /* Header */ + k5_headerlen;
+        gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen;
+
+        if (trailer == NULL) {
+            rrc = gss_trailerlen;
+            /* Workaround for Windows bug where it rotates by EC + RRC */
+            if (ctx->gssFlags & GSS_C_DCE_STYLE)
+                rrc -= ec;
+            gss_headerlen += gss_trailerlen;
+        }
+
+        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
+            code = gssEapAllocIov(header, (size_t) gss_headerlen);
+        } else if (header->buffer.length < gss_headerlen)
+            code = KRB5_BAD_MSIZE;
+        if (code != 0)
+            goto cleanup;
+        outbuf = (unsigned char *)header->buffer.value;
+        header->buffer.length = (size_t) gss_headerlen;
+
+        if (trailer != NULL) {
+            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
+                code = gssEapAllocIov(trailer, (size_t) gss_trailerlen);
+            else if (trailer->buffer.length < gss_trailerlen)
+                code = KRB5_BAD_MSIZE;
+            if (code != 0)
+                goto cleanup;
+            trailer->buffer.length = (size_t) gss_trailerlen;
+        }
+
+        /* TOK_ID */
+        store_16_be((uint16_t)toktype, outbuf);
+        /* flags */
+        outbuf[2] = (acceptor_flag
+                     | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0)
+                     | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0));
+        /* filler */
+        outbuf[3] = 0xFF;
+        /* EC */
+        store_16_be(ec, outbuf + 4);
+        /* RRC */
+        store_16_be(0, outbuf + 6);
+        store_64_be(ctx->sendSeq, outbuf + 8);
+
+        /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */
+        if (trailer == NULL)
+            tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
+        else
+            tbuf = (unsigned char *)trailer->buffer.value;
+
+        memset(tbuf, 0xFF, ec);
+        memcpy(tbuf + ec, header->buffer.value, 16);
+
+        code = gssEapEncrypt(ctx->kerberosCtx,
+                             ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
+                             ec, rrc, ctx->encryptionKey,
+                             key_usage, 0, iov, iov_count);
+        if (code != 0)
+            goto cleanup;
+
+        /* RRC */
+        store_16_be(rrc, outbuf + 6);
+
+        ctx->sendSeq++;
+    } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) {
+    wrap_with_checksum:
+
+        gss_headerlen = 16;
+
+        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+                                    KRB5_CRYPTO_TYPE_CHECKSUM,
+                                    &gss_trailerlen);
+        if (code != 0)
+            goto cleanup;
+
+        assert(gss_trailerlen <= 0xFFFF);
+
+        if (trailer == NULL) {
+            rrc = gss_trailerlen;
+            gss_headerlen += gss_trailerlen;
+        }
+
+        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
+            code = gssEapAllocIov(header, (size_t) gss_headerlen);
+        else if (header->buffer.length < gss_headerlen)
+            code = KRB5_BAD_MSIZE;
+        if (code != 0)
+            goto cleanup;
+        outbuf = (unsigned char *)header->buffer.value;
+        header->buffer.length = (size_t) gss_headerlen;
+
+        if (trailer != NULL) {
+            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
+                code = gssEapAllocIov(trailer, (size_t) gss_trailerlen);
+            else if (trailer->buffer.length < gss_trailerlen)
+                code = KRB5_BAD_MSIZE;
+            if (code != 0)
+                goto cleanup;
+            trailer->buffer.length = (size_t) gss_trailerlen;
+        }
+
+        /* TOK_ID */
+        store_16_be((uint16_t)toktype, outbuf);
+        /* flags */
+        outbuf[2] = (acceptor_flag
+                     | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0));
+        /* filler */
+        outbuf[3] = 0xFF;
+        if (toktype == TOK_TYPE_WRAP) {
+            /* Use 0 for checksum calculation, substitute
+             * checksum length later.
+             */
+            /* EC */
+            store_16_be(0, outbuf + 4);
+            /* RRC */
+            store_16_be(0, outbuf + 6);
+        } else {
+            /* MIC and DEL store 0xFF in EC and RRC */
+            store_16_be(0xFFFF, outbuf + 4);
+            store_16_be(0xFFFF, outbuf + 6);
+        }
+        store_64_be(ctx->sendSeq, outbuf + 8);
+
+        code = gssEapSign(ctx->kerberosCtx, ctx->checksumType,
+                          rrc, ctx->encryptionKey, key_usage,
+                          iov, iov_count);
+        if (code != 0)
+            goto cleanup;
+
+        ctx->sendSeq++;
+
+        if (toktype == TOK_TYPE_WRAP) {
+            /* Fix up EC field */
+            store_16_be(gss_trailerlen, outbuf + 4);
+            /* Fix up RRC field */
+            store_16_be(rrc, outbuf + 6);
+        }
+    } else if (toktype == TOK_TYPE_MIC) {
+        trailer = NULL;
+        goto wrap_with_checksum;
+    } else if (toktype == TOK_TYPE_DELETE) {
+        goto wrap_with_checksum;
+    } else {
+        abort();
+    }
+
+    code = 0;
+
+cleanup:
+    if (code != 0)
+        gssEapReleaseIov(iov, iov_count);
+
+    *minor = code;
+
+    if (code == 0)
+        return GSS_S_FAILURE;
+    else
+        return GSS_S_COMPLETE;
+}
+
+OM_uint32
 gss_wrap_iov(OM_uint32 *minor,
              gss_ctx_id_t ctx,
              int conf_req_flag,
@@ -41,5 +301,6 @@ gss_wrap_iov(OM_uint32 *minor,
              gss_iov_buffer_desc *iov,
              int iov_count)
 {
-    GSSEAP_NOT_IMPLEMENTED;
+    return gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state,
+                             iov, iov_count, TOK_TYPE_WRAP);
 }