Merge remote-tracking branch 'origin/eap-tls'
authorSam Hartman <hartmans@debian.org>
Fri, 20 Sep 2013 17:04:12 +0000 (13:04 -0400)
committerSam Hartman <hartmans@debian.org>
Fri, 20 Sep 2013 17:04:12 +0000 (13:04 -0400)
The eap-tls branch includes build dependencies on openssl which we
need for the sha2 hash support in IDP certs.  The eap-tls changes are
not widely exposed, but to the extent they are present are harmless.

Conflicts:
libeap/Makefile.am
mech_eap/Makefile.am
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c

1  2 
acinclude.m4
configure.ac
libeap/Makefile.am
mech_eap/Makefile.am
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c
mech_eap/util.h
mech_eap/util_cred.c

diff --combined acinclude.m4
@@@ -17,19 -17,17 +17,19 @@@ AC_ARG_WITH(krb5
         [Use krb5 (in specified installation directory)]),
      [check_krb5_dir="$withval"],
      [check_krb5_dir=])
 -for dir in $check_krb5_dir $prefix /usr /usr/local ; do
 +for dir in $check_krb5_dir $prefix /usr/local /usr ; do
     krb5dir="$dir"
     if test -x "$dir/bin/krb5-config"; then
       found_krb5="yes";
       if test "x$target_windows" = "xyes"; then
          KRB5_CFLAGS=-I"$check_krb5_dir/include";
 -        KRB5_LIBS="-L$check_krb5_dir/lib/ -lkrb5_32 -lgssapi32";
 +        KRB5_LDFLAGS="-L$check_krb5_dir/lib/";
 +        KRB5_LIBS="-lkrb5_32 -lgssapi32";
          COMPILE_ET="$check_krb5_dir/bin/compile_et";
        AC_MSG_RESULT([yes])
       else
          KRB5_CFLAGS=`$dir/bin/krb5-config gssapi --cflags`;
 +        KRB5_LDFLAGS="-L$dir/lib";
          KRB5_LIBS=`$dir/bin/krb5-config gssapi --libs`
  AC_MSG_RESULT([yes])
          AC_PATH_PROG(COMPILE_ET, [compile_et], [compile_et], [$dir/bin$PATH_SEPARATOr])
@@@ -50,7 -48,6 +50,7 @@@ if test x_$found_krb5 != x_yes; the
  else
        printf "Kerberos found in $krb5dir\n";
        AC_SUBST(KRB5_CFLAGS)
 +        AC_SUBST(KRB5_LDFLAGS)
        AC_SUBST(KRB5_LIBS)
        AC_SUBST(COMPILE_ET)
        AC_CHECK_LIB(krb5, GSS_C_NT_COMPOSITE_EXPORT, [AC_DEFINE_UNQUOTED([HAVE_GSS_C_NT_COMPOSITE_EXPORT], 1, [Define if GSS-API library supports recent naming extensions draft])], [], "$KRB5_LIBS")
@@@ -118,7 -115,7 +118,7 @@@ els
  -DEAP_SERVER_GPSK \
  -DEAP_SERVER_GPSK_SHA256 \
  -DIEEE8021X_EAPOL";
-       EAP_LIBS="-leap -lutils -lcrypto -ltls";
+       EAP_LIBS="-leap -lutils -lcrypto -ltls -lssl";
        EAP_LDFLAGS="-L$eapdir/eap_example -L$eapdir/src/utils -L$eapdir/src/crypto -L$eapdir/src/tls";
        AC_SUBST(EAP_CFLAGS)
        AC_SUBST(EAP_LDFLAGS)
  fi
  ])dnl
  
+ AC_DEFUN([AX_CHECK_OPENSSL],
+ [AC_MSG_CHECKING(for OpenSSL)
+ OPENSSL_DIR=
+ found_openssl="no"
+ AC_ARG_WITH(openssl,
+     AC_HELP_STRING([--with-openssl],
+        [Use OpenSSL (in specified installation directory)]),
+     [check_openssl_dir="$withval"],
+     [check_openssl_dir=])
+ for dir in $check_openssl_dir $prefix /usr /usr/local ; do
+    openssldir="$dir"
+    if test -f "$dir/include/openssl/opensslv.h"; then
+      found_openssl="yes";
+      OPENSSL_DIR="${openssldir}"
+      OPENSSL_CFLAGS="-I$openssldir/include";
+      break;
+    fi
+ done
+ AC_MSG_RESULT($found_openssl)
+ if test x_$found_openssl != x_yes; then
+    AC_MSG_ERROR([
+ ----------------------------------------------------------------------
+   Cannot find OpenSSL libraries.
+   Please install libssl or specify installation directory with
+   --with-openssl=(dir).
+ ----------------------------------------------------------------------
+ ])
+ else
+       printf "OpenSSL found in $openssldir\n";
+       OPENSSL_LIBS="-lssl -lcrypto";
+       OPENSSL_LDFLAGS="-L$openssldir/lib";
+       AC_SUBST(OPENSSL_CFLAGS)
+       AC_SUBST(OPENSSL_LDFLAGS)
+       AC_SUBST(OPENSSL_LIBS)
+ fi
+ ])dnl
  AC_DEFUN([AX_CHECK_RADSEC],
  [AC_MSG_CHECKING(for radsec)
  RADSEC_DIR=
diff --combined configure.ac
@@@ -1,5 -1,5 +1,5 @@@
 -AC_PREREQ([2.61])
 -AC_INIT([mech_eap], [0.1], [bugs@project-moonshot.org])
 +/AC_PREREQ([2.61])
 +AC_INIT([mech_eap], [0.9], [bugs@project-moonshot.org])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_AUX_DIR([build-aux])
  
@@@ -81,6 -81,8 +81,8 @@@ if test x_$found_shibresolver = x_yes; 
    AX_CHECK_SHIBSP
  fi
  
+ AX_CHECK_OPENSSL
  if test "x$acceptor" = "xyes" ; then
    AX_CHECK_RADSEC
    AX_CHECK_JANSSON
diff --combined libeap/Makefile.am
@@@ -1,6 -1,6 +1,6 @@@
  AUTOMAKE_OPTIONS = foreign
  
- AM_CPPFLAGS = -I$(srcdir)/src -I$(srcdir)/eap_example -I$(srcdir)/src/utils 
+ AM_CPPFLAGS = -I$(srcdir)/src -I$(srcdir)/eap_example -I$(srcdir)/src/utils @OPENSSL_CFLAGS@
  noinst_HEADERS = \
        src/common/defs.h \
        src/common/eapol_common.h \
@@@ -19,7 -19,7 +19,7 @@@ SOURCES_BOTH += src/eap_common/eap_pax_
  SOURCES_BOTH += src/eap_common/eap_sake_common.c
  SOURCES_BOTH += src/eap_common/eap_gpsk_common.c
  SOURCES_BOTH += src/eap_common/chap.c \
- src/eap_common/chap.h \
      src/eap_common/chap.h \
        src/eap_common/eap_common.h \
        src/eap_common/eap_defs.h \
        src/eap_common/eap_fast_common.h \
@@@ -64,37 -64,38 +64,38 @@@ SOURCES_peer += src/eap_peer/eap_tls_co
        src/eap_peer/mschapv2.h \
        src/eap_peer/tncc.h
  
 -CFLAGS += -DEAP_TLS
 -CFLAGS += -DEAP_PEAP
 -CFLAGS += -DEAP_TTLS
 -CFLAGS += -DEAP_MD5
 -CFLAGS += -DEAP_MSCHAPv2
 -CFLAGS += -DEAP_GTC
 -CFLAGS += -DEAP_OTP
 -CFLAGS += -DEAP_LEAP
 -CFLAGS += -DEAP_PSK
 -CFLAGS += -DEAP_PAX
 -CFLAGS += -DEAP_SAKE
 -CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
 +AM_CFLAGS = -DEAP_TLS
 +AM_CFLAGS += -DEAP_PEAP
 +AM_CFLAGS += -DEAP_TTLS
 +AM_CFLAGS += -DEAP_MD5
 +AM_CFLAGS += -DEAP_MSCHAPv2
 +AM_CFLAGS += -DEAP_GTC
 +AM_CFLAGS += -DEAP_OTP
 +AM_CFLAGS += -DEAP_LEAP
 +AM_CFLAGS += -DEAP_PSK
 +AM_CFLAGS += -DEAP_PAX
 +AM_CFLAGS += -DEAP_SAKE
 +AM_CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
  
 -CFLAGS += -DEAP_SERVER_IDENTITY
 -CFLAGS += -DEAP_SERVER_TLS
 -CFLAGS += -DEAP_SERVER_PEAP
 -CFLAGS += -DEAP_SERVER_TTLS
 -CFLAGS += -DEAP_SERVER_MD5
 -CFLAGS += -DEAP_SERVER_MSCHAPV2
 -CFLAGS += -DEAP_SERVER_GTC
 -CFLAGS += -DEAP_SERVER_PSK
 -CFLAGS += -DEAP_SERVER_PAX
 -CFLAGS += -DEAP_SERVER_SAKE
 -CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
 +AM_CFLAGS += -DEAP_SERVER_IDENTITY
 +AM_CFLAGS += -DEAP_SERVER_TLS
 +AM_CFLAGS += -DEAP_SERVER_PEAP
 +AM_CFLAGS += -DEAP_SERVER_TTLS
 +AM_CFLAGS += -DEAP_SERVER_MD5
 +AM_CFLAGS += -DEAP_SERVER_MSCHAPV2
 +AM_CFLAGS += -DEAP_SERVER_GTC
 +AM_CFLAGS += -DEAP_SERVER_PSK
 +AM_CFLAGS += -DEAP_SERVER_PAX
 +AM_CFLAGS += -DEAP_SERVER_SAKE
 +AM_CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
  
 -CFLAGS += -DIEEE8021X_EAPOL
 -CFLAGS += -DCONFIG_IPV6
 +AM_CFLAGS += -DIEEE8021X_EAPOL
 +AM_CFLAGS += -DCONFIG_IPV6
  
 -CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
 -CFLAGS += -DCONFIG_INTERNAL_SHA1
 -CFLAGS += -DEAP_TLS_OPENSSL
 -CFLAGS += -DPKCS12_FUNCS
 +AM_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
- AM_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
- AM_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++AM_CFLAGS += -DCONFIG_INTERNAL_SHA1
++AM_CFLAGS += -DEAP_TLS_OPENSSL
++AM_CFLAGS += -DPKCS12_FUNCS
  
  UTILS_SRCS = src/utils/base64.c \
        src/utils/common.c \
        src/utils/wpa_debug.c \
        src/utils/wpabuf.c \
        src/utils/os_unix.c \
 +      src/utils/radius_utils.c \
 +src/utils/radius_utils.h \
        src/utils/base64.h \
        src/utils/build_config.h \
        src/utils/common.h \
@@@ -130,51 -129,30 +131,30 @@@ CRYPTO_SRCS = 
      src/crypto/aes-ctr.c \
      src/crypto/aes-eax.c \
      src/crypto/aes-encblock.c \
-     src/crypto/aes-internal.c \
-     src/crypto/aes-internal-dec.c \
-     src/crypto/aes-internal-enc.c \
      src/crypto/aes-omac1.c \
      src/crypto/aes-unwrap.c \
      src/crypto/aes-wrap.c \
-     src/crypto/des-internal.c \
-     src/crypto/dh_group5.c \
-     src/crypto/dh_groups.c \
-     src/crypto/md4-internal.c \
      src/crypto/md5.c \
-     src/crypto/md5-internal.c \
      src/crypto/md5-non-fips.c \
      src/crypto/milenage.c \
      src/crypto/ms_funcs.c \
-     src/crypto/rc4.c \
      src/crypto/sha1.c \
-     src/crypto/sha1-internal.c \
      src/crypto/sha1-pbkdf2.c \
      src/crypto/sha1-tlsprf.c \
      src/crypto/sha1-tprf.c \
      src/crypto/sha256.c \
-     src/crypto/sha256-internal.c \
-     src/crypto/crypto_internal.c \
-     src/crypto/crypto_internal-cipher.c \
-     src/crypto/crypto_internal-modexp.c \
-     src/crypto/crypto_internal-rsa.c \
-     src/crypto/tls_internal.c \
-     src/crypto/fips_prf_internal.c \
-       src/crypto/aes.h \
-       src/crypto/aes_i.h \
-       src/crypto/aes_wrap.h \
-       src/crypto/crypto.h \
-       src/crypto/des_i.h \
-       src/crypto/dh_group5.h \
-       src/crypto/dh_groups.h \
-       src/crypto/md5.h \
-       src/crypto/md5_i.h \
-       src/crypto/milenage.h \
-       src/crypto/ms_funcs.h \
-       src/crypto/sha1.h \
-       src/crypto/sha1_i.h \
-       src/crypto/sha256.h \
-       src/crypto/tls.h
+     src/crypto/crypto_openssl.c \
+     src/crypto/tls_openssl.c \
+     src/crypto/aes.h \
+     src/crypto/aes_i.h \
+     src/crypto/aes_wrap.h \
+     src/crypto/crypto.h \
+     src/crypto/md5.h \
+     src/crypto/milenage.h \
+     src/crypto/ms_funcs.h \
+     src/crypto/sha1.h \
+     src/crypto/sha256.h \
+     src/crypto/tls.h
  
  TLS_SRCS = \
      src/tls/asn1.c \
      src/tls/tlsv1_server_read.c \
      src/tls/tlsv1_server_write.c \
      src/tls/x509v3.c \
-       src/tls/asn1.h \
-       src/tls/bignum.h \
-       src/tls/pkcs1.h \
-       src/tls/pkcs5.h \
-       src/tls/pkcs8.h \
-       src/tls/rsa.h \
-       src/tls/tlsv1_client.h \
-       src/tls/tlsv1_client_i.h \
-       src/tls/tlsv1_common.h \
-       src/tls/tlsv1_cred.h \
-       src/tls/tlsv1_record.h \
-       src/tls/tlsv1_server.h \
-       src/tls/tlsv1_server_i.h \
-       src/tls/x509v3.h 
- libeap_la_SOURCES = $(SOURCES_BOTH) $(SOURCES_peer) $(UTILS_SRCS) $(CRYPTO_SRCS) $(TLS_SRCS)
+     src/tls/asn1.h \
+     src/tls/bignum.h \
+     src/tls/pkcs1.h \
+     src/tls/pkcs5.h \
+     src/tls/pkcs8.h \
+     src/tls/rsa.h \
+     src/tls/tlsv1_client.h \
+     src/tls/tlsv1_client_i.h \
+     src/tls/tlsv1_common.h \
+     src/tls/tlsv1_cred.h \
+     src/tls/tlsv1_record.h \
+     src/tls/tlsv1_server.h \
+     src/tls/tlsv1_server_i.h \
+     src/tls/x509v3.h 
+ libeap_la_SOURCES = $(SOURCES_BOTH) $(SOURCES_peer) $(UTILS_SRCS) $(CRYPTO_SRCS)
  
  noinst_LTLIBRARIES = libeap.la
diff --combined mech_eap/Makefile.am
@@@ -42,13 -42,14 +42,15 @@@ mech_eap_la_CXXFLAGS += 
                        @TARGET_CFLAGS@ $(EAP_CFLAGS)
  mech_eap_la_LDFLAGS  = -avoid-version -module \
                        -export-symbols $(GSSEAP_EXPORTS) -no-undefined \
-                       @KRB5_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@
 -                      @RADSEC_LDFLAGS@ @OPENSSL_LDFLAGS@ @TARGET_LDFLAGS@
++                      @KRB5_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@ @OPENSSL_LDFLAGS@
++
  if TARGET_WINDOWS
  mech_eap_la_LDFLAGS += -debug
  endif
  
  mech_eap_la_LIBADD   = @KRB5_LIBS@ ../libeap/libeap.la @RADSEC_LIBS@ \
-                      @OPENSAML_LIBS@ @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@
+                      @OPENSAML_LIBS@ @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@ \
+                      @OPENSSL_LIBS@
  mech_eap_la_SOURCES =                         \
        acquire_cred.c                          \
        acquire_cred_with_password.c            \
        util_name.c                             \
        util_oid.c                              \
        util_ordering.c                         \
 +      util_radius.cpp                         \
        util_sm.c                               \
        util_tld.c                              \
        util_token.c                            \
@@@ -147,7 -147,8 +149,7 @@@ mech_eap_la_SOURCES +=                             
        set_name_attribute.c                    \
        util_attr.cpp                           \
        util_base64.c                           \
 -      util_json.cpp                           \
 -      util_radius.cpp
 +      util_json.cpp
  
  if OPENSAML
  mech_eap_la_SOURCES += util_saml.cpp
diff --combined mech_eap/gssapiP_eap.h
@@@ -90,11 -90,28 +90,11 @@@ typedef const gss_OID_desc *gss_const_O
  #include <wpabuf.h>
  
  #ifdef GSSEAP_ENABLE_ACCEPTOR
 -/* FreeRADIUS headers */
 -#ifdef __cplusplus
 -extern "C" {
 -#ifndef WIN32
 -#define operator fr_operator
 -#endif
 -#endif
 -#include <freeradius/libradius.h>
 -#include <freeradius/radius.h>
 -
 -#undef pid_t
 -
  /* libradsec headers */
  #include <radsec/radsec.h>
  #include <radsec/request.h>
 -#ifdef __cplusplus
 -#ifndef WIN32
 -#undef operator
 +#include <radsec/radius.h>
  #endif
 -}
 -#endif
 -#endif /* GSSEAP_ENABLE_ACCEPTOR */
  
  #include "gsseap_err.h"
  #include "radsec_err.h"
@@@ -133,6 -150,8 +133,8 @@@ struct gss_name_struc
  #define CRED_FLAG_DEFAULT_CCACHE            0x00080000
  #define CRED_FLAG_RESOLVED                  0x00100000
  #define CRED_FLAG_TARGET                    0x00200000
+ #define CRED_FLAG_CERTIFICATE               0x00400000
+ #define CRED_FLAG_CONFIG_BLOB               0x00800000
  #define CRED_FLAG_PUBLIC_MASK               0x0000FFFF
  
  #ifdef HAVE_HEIMDAL_VERSION
@@@ -153,6 -172,8 +155,8 @@@ struct gss_cred_id_struc
      gss_buffer_desc caCertificate;
      gss_buffer_desc subjectNameConstraint;
      gss_buffer_desc subjectAltNameConstraint;
+     gss_buffer_desc clientCertificate;
+     gss_buffer_desc privateKey;
  #ifdef GSSEAP_ENABLE_REAUTH
      krb5_ccache krbCredCache;
      gss_cred_id_t reauthCred;
  
  #define CTX_FLAG_INITIATOR                  0x00000001
  #define CTX_FLAG_KRB_REAUTH                 0x00000002
 +#define CTX_FLAG_CHANNEL_BINDINGS_VERIFIED  0x00000004
  
  #define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
  
  #define CTX_FLAG_EAP_PORT_ENABLED           0x00400000
  #define CTX_FLAG_EAP_ALT_ACCEPT             0x00800000
  #define CTX_FLAG_EAP_ALT_REJECT             0x01000000
 +#define CTX_FLAG_EAP_CHBIND_ACCEPT          0x02000000
  #define CTX_FLAG_EAP_MASK                   0xFFFF0000
  
+ #define CONFIG_BLOB_CLIENT_CERT             0
+ #define CONFIG_BLOB_PRIVATE_KEY             1
+ #define CONFIG_BLOB_MAX                     2
  struct gss_eap_initiator_ctx {
      unsigned int idleWhile;
      struct eap_peer_config eapPeerConfig;
      struct eap_sm *eap;
      struct wpabuf reqData;
 +    struct wpabuf *chbindData;
 +    unsigned int chbindReqFlags;
+     struct wpa_config_blob configBlobs[CONFIG_BLOB_MAX];
  };
  
  #ifdef GSSEAP_ENABLE_ACCEPTOR
@@@ -195,7 -217,7 +204,7 @@@ struct gss_eap_acceptor_ctx 
      struct rs_connection *radConn;
      char *radServer;
      gss_buffer_desc state;
 -    VALUE_PAIR *vps;
 +    rs_avp *vps;
  };
  #endif
  
@@@ -244,10 -266,6 +253,10 @@@ struct gss_ctx_id_struc
  #define KEY_USAGE_INITIATOR_SEAL            24
  #define KEY_USAGE_INITIATOR_SIGN            25
  
 +#define KEY_USAGE_GSSEAP_CHBIND_MIC         60
 +#define KEY_USAGE_GSSEAP_ACCTOKEN_MIC       61
 +#define KEY_USAGE_GSSEAP_INITOKEN_MIC       62
 +
  /* accept_sec_context.c */
  OM_uint32
  gssEapAcceptSecContext(OM_uint32 *minor,
@@@ -329,12 -347,9 +338,12 @@@ gssEapDisplayStatus(OM_uint32 *minor
  #define IS_WIRE_ERROR(err)              ((err) > GSSEAP_RESERVED && \
                                           (err) <= GSSEAP_RADIUS_PROT_FAILURE)
  
 -/* upper bound of RADIUS error range must be kept in sync with radsec.h */
 +#ifdef GSSEAP_ENABLE_ACCEPTOR
  #define IS_RADIUS_ERROR(err)            ((err) >= ERROR_TABLE_BASE_rse && \
 -                                         (err) <= ERROR_TABLE_BASE_rse + 20)
 +                                         (err) <= ERROR_TABLE_BASE_rse + RSE_MAX)
 +#else
 +#define IS_RADIUS_ERROR(err)            (0)
 +#endif
  
  /* exchange_meta_data.c */
  OM_uint32 GSSAPI_CALLCONV
@@@ -371,6 -386,7 +380,6 @@@ gssEapPseudoRandom(OM_uint32 *minor
                     gss_ctx_id_t ctx,
                     int prf_key,
                     const gss_buffer_t prf_in,
 -                   ssize_t desired_output_len,
                     gss_buffer_t prf_out);
  
  /* query_mechanism_info.c */
@@@ -36,9 -36,6 +36,9 @@@
   */
  
  #include "gssapiP_eap.h"
 +#include "radius/radius.h"
 +#include "util_radius.h"
 +#include "utils/radius_utils.h"
  
  static OM_uint32
  policyVariableToFlag(enum eapol_bool_var variable)
@@@ -170,10 -167,20 +170,20 @@@ peerSetConfigBlob(void *ctx GSSEAP_UNUS
  }
  
  static const struct wpa_config_blob *
- peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
-                   const char *name GSSEAP_UNUSED)
+ peerGetConfigBlob(void *ctx,
+                   const char *name)
  {
-     return NULL;
+     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
+     size_t index;
+     if (strcmp(name, "client-cert") == 0)
+         index = CONFIG_BLOB_CLIENT_CERT;
+     else if (strcmp(name, "private-key") == 0)
+         index = CONFIG_BLOB_PRIVATE_KEY;
+     else
+         return NULL;
+     return &gssCtx->initiatorCtx.configBlobs[index];
  }
  
  static void
@@@ -197,149 -204,13 +207,150 @@@ static struct eapol_callbacks gssEapPol
  extern int wpa_debug_level;
  #endif
  
- tatic OM_uint32
 +#define CHBIND_SERVICE_NAME_FLAG        0x01
 +#define CHBIND_HOST_NAME_FLAG           0x02
 +#define CHBIND_SERVICE_SPECIFIC_FLAG    0x04
 +#define CHBIND_REALM_NAME_FLAG          0x08
 +
++static OM_uint32
 +peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
 +{
 +    struct wpabuf *buf = NULL;
 +    unsigned int chbindReqFlags = 0;
 +    krb5_principal princ = NULL;
 +    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
 +    OM_uint32 major = GSS_S_COMPLETE;
 +    krb5_context krbContext = NULL;
 +
 +    /* XXX is this check redundant? */
 +    if (ctx->acceptorName == GSS_C_NO_NAME) {
 +        major = GSS_S_BAD_NAME;
 +        *minor = GSSEAP_NO_ACCEPTOR_NAME;
 +        goto cleanup;
 +    }
 +
 +    princ = ctx->acceptorName->krbPrincipal;
 +
 +    krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
 +    if (nameBuf.length > 0) {
 +        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
 +                                    0, &nameBuf);
 +        if (GSS_ERROR(major))
 +            goto cleanup;
 +
 +        chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
 +    }
 +
 +    krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
 +    if (nameBuf.length > 0) {
 +        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
 +                                    0, &nameBuf);
 +        if (GSS_ERROR(major))
 +            goto cleanup;
 +
 +        chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
 +    }
 +
 +    GSSEAP_KRB_INIT(&krbContext);
 +
 +    *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
 +    if (*minor != 0)
 +        goto cleanup;
 +
 +    if (nameBuf.length > 0) {
 +        major = gssEapRadiusAddAttr(minor, &buf,
 +                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
 +                                    0, &nameBuf);
 +        if (GSS_ERROR(major))
 +            goto cleanup;
 +
 +        chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
 +    }
 +
 +    krbFreeUnparsedName(krbContext, &nameBuf);
 +    krbPrincRealmToGssBuffer(princ, &nameBuf);
 +
 +    if (nameBuf.length > 0) {
 +        major = gssEapRadiusAddAttr(minor, &buf,
 +                                    PW_GSS_ACCEPTOR_REALM_NAME,
 +                                    0, &nameBuf);
 +        chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
 +    }
 +
 +    if (chbindReqFlags == 0) {
 +        major = GSS_S_BAD_NAME;
 +        *minor = GSSEAP_BAD_ACCEPTOR_NAME;
 +        goto cleanup;
 +    }
 +
 +    ctx->initiatorCtx.chbindData = buf;
 +    ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
 +
 +    buf = NULL;
 +
 +    major = GSS_S_COMPLETE;
 +    *minor = 0;
 +
 +cleanup:
 +    krbFreeUnparsedName(krbContext, &nameBuf);
 +    wpabuf_free(buf);
 +
 +    return major;
 +}
 +
 +static void
 +peerProcessChbindResponse(void *context, int code, int nsid,
 +                          u8 *data, size_t len)
 +{
 +    radius_parser msg;
 +    gss_ctx_id_t ctx = (gss_ctx_id_t )context;
 +    void *vsadata;
 +    u8 type;
 +    u32 vendor_id;
 +    u32 chbindRetFlags = 0;
 +    size_t vsadata_len;
 +
 +    if (nsid != CHBIND_NSID_RADIUS)
 +        return;
 +
 +    msg = radius_parser_start(data, len);
 +    if (msg == NULL)
 +        return;
 +
 +    while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
 +                                   &vsadata_len) == 0) {
 +        switch (type) {
 +        case PW_GSS_ACCEPTOR_SERVICE_NAME:
 +            chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
 +            break;
 +        case PW_GSS_ACCEPTOR_HOST_NAME:
 +            chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
 +            break;
 +        case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
 +            chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
 +            break;
 +        case PW_GSS_ACCEPTOR_REALM_NAME:
 +            chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
 +            break;
 +        }
 +    }
 +
 +    radius_parser_finish(msg);
 +
 +    if (code == CHBIND_CODE_SUCCESS &&
 +        ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
 +        ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
 +        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
 +    } /* else log failures? */
 +}
 +
  static OM_uint32
  peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
  {
      OM_uint32 major;
      krb5_context krbContext;
      struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
+     struct wpa_config_blob *configBlobs = ctx->initiatorCtx.configBlobs;
      gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
      gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
      gss_cred_id_t cred = ctx->cred;
      eapPeerConfig->anonymous_identity_len = 1 + realm.length;
  
      /* password */
-     eapPeerConfig->password = (unsigned char *)cred->password.value;
-     eapPeerConfig->password_len = cred->password.length;
+     if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
+         eapPeerConfig->password = (unsigned char *)cred->password.value;
+         eapPeerConfig->password_len = cred->password.length;
+     }
  
      /* certs */
      eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
      eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
      eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
  
 +    /* eap channel binding */
 +    if (ctx->initiatorCtx.chbindData != NULL) {
 +        struct eap_peer_chbind_config *chbind_config =
 +            (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
 +        if (chbind_config == NULL) {
 +            *minor = ENOMEM;
 +            return GSS_S_FAILURE;
 +        }
 +
 +        chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
 +        chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
 +        chbind_config->nsid = CHBIND_NSID_RADIUS;
 +        chbind_config->response_cb = &peerProcessChbindResponse;
 +        chbind_config->ctx = ctx;
 +        eapPeerConfig->chbind_config = chbind_config;
 +        eapPeerConfig->chbind_config_len = 1;
 +    } else {
 +        eapPeerConfig->chbind_config = NULL;
 +        eapPeerConfig->chbind_config_len = 0;
 +    }
+     if (cred->flags & CRED_FLAG_CERTIFICATE) {
+         /*
+          * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
+          * future to directly pass certificate and private key data to the
+          * EAP implementation, rather than an indirected string pointer.
+          */
+         if (cred->flags & CRED_FLAG_CONFIG_BLOB) {
+             eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert";
+             configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value;
+             configBlobs[CONFIG_BLOB_CLIENT_CERT].len  = cred->clientCertificate.length;
+             eapPeerConfig->client_cert = (unsigned char *)"blob://private-key";
+             configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value;
+             configBlobs[CONFIG_BLOB_PRIVATE_KEY].len  = cred->privateKey.length;
+         } else {
+             eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
+             eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
+         }
+         eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
+     }
  
      *minor = 0;
      return GSS_S_COMPLETE;
@@@ -449,12 -322,18 +482,12 @@@ peerConfigFree(OM_uint32 *minor
   * Mark an initiator context as ready for cryptographic operations
   */
  static OM_uint32
 -initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
 +initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
  {
      OM_uint32 major;
      const unsigned char *key;
      size_t keyLength;
  
 -#if 1
 -    /* XXX actually check for mutual auth */
 -    if (reqFlags & GSS_C_MUTUAL_FLAG)
 -        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
 -#endif
 -
      /* Cache encryption type derived from selected mechanism OID */
      major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
      if (GSS_ERROR(major))
@@@ -716,45 -595,17 +749,45 @@@ eapGssSmInitAcceptorName(OM_uint32 *min
                                    outputToken, NULL);
          if (GSS_ERROR(major))
              return major;
 -    } else if (inputToken != GSS_C_NO_BUFFER &&
 -               ctx->acceptorName == GSS_C_NO_NAME) {
 -        /* Accept target name hint from acceptor */
 +    } else if (inputToken != GSS_C_NO_BUFFER) {
 +        OM_uint32 tmpMinor;
 +        gss_name_t nameHint;
 +        int equal;
 +
 +        /* Accept target name hint from acceptor or verify acceptor */
          major = gssEapImportName(minor, inputToken,
                                   GSS_C_NT_USER_NAME,
                                   ctx->mechanismUsed,
 -                                 &ctx->acceptorName);
 +                                 &nameHint);
          if (GSS_ERROR(major))
              return major;
 +
 +        if (ctx->acceptorName != GSS_C_NO_NAME) {
 +            /* verify name hint matched asserted acceptor name  */
 +            major = gssEapCompareName(minor,
 +                                      nameHint,
 +                                      ctx->acceptorName,
 +                                      COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS,
 +                                      &equal);
 +            if (GSS_ERROR(major)) {
 +                gssEapReleaseName(&tmpMinor, &nameHint);
 +                return major;
 +            }
 +
 +            gssEapReleaseName(&tmpMinor, &nameHint);
 +
 +            if (!equal) {
 +                *minor = GSSEAP_WRONG_ACCEPTOR_NAME;
 +                return GSS_S_DEFECTIVE_TOKEN;
 +            }
 +        } else { /* acceptor name is no_name */
 +            /* accept acceptor name hint */
 +            ctx->acceptorName = nameHint;
 +            nameHint = GSS_C_NO_NAME;
 +        }
      }
  
 +
      /*
       * Currently, other parts of the code assume that the acceptor name
       * is available, hence this check.
          return GSS_S_FAILURE;
      }
  
 +    /*
 +     * Generate channel binding data
 +     */
 +    if (ctx->initiatorCtx.chbindData == NULL) {
 +        major = peerInitEapChannelBinding(minor, ctx);
 +        if (GSS_ERROR(major))
 +            return major;
 +    }
 +
      return GSS_S_CONTINUE_NEEDED;
  }
  
@@@ -873,7 -715,7 +906,7 @@@ eapGssSmInitAuthenticate(OM_uint32 *min
  
          resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
      } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
 -        major = initReady(minor, ctx, reqFlags);
 +        major = initReady(minor, ctx);
          if (GSS_ERROR(major))
              goto cleanup;
  
@@@ -951,45 -793,21 +984,45 @@@ eapGssSmInitGssChannelBindings(OM_uint3
                                 OM_uint32 *smFlags)
  {
      OM_uint32 major;
 -    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
 +    krb5_error_code code;
 +    krb5_context krbContext;
 +    krb5_data data;
 +    krb5_checksum cksum;
 +    gss_buffer_desc cksumBuffer;
  
 -    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
 -        buffer = chanBindings->application_data;
 +    if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
 +        chanBindings->application_data.length == 0)
 +        return GSS_S_CONTINUE_NEEDED;
  
 -    major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
 -                       &buffer, NULL, outputToken);
 -    if (GSS_ERROR(major))
 -        return major;
 +    GSSEAP_KRB_INIT(&krbContext);
  
 -    GSSEAP_ASSERT(outputToken->value != NULL);
 +    KRB_DATA_INIT(&data);
 +
 +    gssBufferToKrbData(&chanBindings->application_data, &data);
 +
 +    code = krb5_c_make_checksum(krbContext, ctx->checksumType,
 +                                &ctx->rfc3961Key,
 +                                KEY_USAGE_GSSEAP_CHBIND_MIC,
 +                                &data, &cksum);
 +    if (code != 0) {
 +        *minor = code;
 +        return GSS_S_FAILURE;
 +    }
 +
 +    cksumBuffer.length = KRB_CHECKSUM_LENGTH(&cksum);
 +    cksumBuffer.value  = KRB_CHECKSUM_DATA(&cksum);
 +
 +    major = duplicateBuffer(minor, &cksumBuffer, outputToken);
 +    if (GSS_ERROR(major)) {
 +        krb5_free_checksum_contents(krbContext, &cksum);
 +        return major;
 +    }
  
      *minor = 0;
      *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
  
 +    krb5_free_checksum_contents(krbContext, &cksum);
 +
      return GSS_S_CONTINUE_NEEDED;
  }
  
@@@ -1019,7 -837,7 +1052,7 @@@ eapGssSmInitInitiatorMIC(OM_uint32 *min
  
      return GSS_S_CONTINUE_NEEDED;
  }
 - 
 +
  #ifdef GSSEAP_ENABLE_REAUTH
  static OM_uint32
  eapGssSmInitReauthCreds(OM_uint32 *minor,
@@@ -1066,11 -884,6 +1099,11 @@@ eapGssSmInitAcceptorMIC(OM_uint32 *mino
      if (GSS_ERROR(major))
          return major;
  
 +    /*
 +     * As a temporary measure, force mutual authentication until channel binding is
 +     * more widely deployed.
 +     */
 +    ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
      GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
  
      *minor = 0;
@@@ -1089,8 -902,7 +1122,8 @@@ static struct gss_eap_sm eapGssInitiato
      {
          ITOK_TYPE_ACCEPTOR_NAME_RESP,
          ITOK_TYPE_ACCEPTOR_NAME_REQ,
 -        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
 +        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE |
 +        GSSEAP_STATE_ACCEPTOR_EXTS,
          0,
          eapGssSmInitAcceptorName
      },
          ITOK_TYPE_NONE,
          ITOK_TYPE_GSS_CHANNEL_BINDINGS,
          GSSEAP_STATE_INITIATOR_EXTS,
 -        SM_ITOK_FLAG_REQUIRED,
 +        0,
          eapGssSmInitGssChannelBindings
      },
      {
@@@ -1240,10 -1052,8 +1273,10 @@@ gssEapInitSecContext(OM_uint32 *minor
              goto cleanup;
          }
      }
 +
      if (ret_flags != NULL)
          *ret_flags = ctx->gssFlags;
 +
      if (time_rec != NULL)
          gssEapContextTime(&tmpMinor, ctx, time_rec);
  
diff --combined mech_eap/util.h
@@@ -73,9 -73,7 +73,9 @@@
  #include <krb5.h>
  
  #ifdef WIN32
 -#define inline __inline
 +# ifndef __cplusplus
 +# define inline __inline
 +# endif
  #define snprintf _snprintf
  #endif
  
@@@ -272,6 -270,12 +272,12 @@@ gssEapSetCredPassword(OM_uint32 *minor
                        const gss_buffer_t password);
  
  OM_uint32
+ gssEapSetCredClientCertificate(OM_uint32 *minor,
+                                gss_cred_id_t cred,
+                                const gss_buffer_t clientCert,
+                                const gss_buffer_t privateKey);
+ OM_uint32
  gssEapSetCredService(OM_uint32 *minor,
                       gss_cred_id_t cred,
                       const gss_name_t target);
@@@ -381,16 -385,6 +387,16 @@@ gssEapDeriveRfc3961Key(OM_uint32 *minor
  
  #define KRB_DATA_INIT(d)        krb5_data_zero((d))
  
 +#define KRB_CHECKSUM_TYPE(c)    ((c)->cksumtype)
 +#define KRB_CHECKSUM_LENGTH(c)  ((c)->checksum.length)
 +#define KRB_CHECKSUM_DATA(c)    ((c)->checksum.data)
 +
 +#define KRB_CHECKSUM_INIT(cksum, type, d)      do { \
 +        (cksum)->cksumtype = (type);                \
 +        (cksum)->checksum.length = (d)->length;     \
 +        (cksum)->checksum.data = (d)->value;        \
 +    } while (0)
 +
  #else
  
  #define KRB_TIME_FOREVER        KRB5_INT32_MAX
  #define KRB_PRINC_TYPE(princ)   (krb5_princ_type(NULL, (princ)))
  #define KRB_PRINC_NAME(princ)   (krb5_princ_name(NULL, (princ)))
  #define KRB_PRINC_REALM(princ)  (krb5_princ_realm(NULL, (princ)))
 +#define KRB_PRINC_COMPONENT(princ, component) \
 +        (krb5_princ_component(NULL, (princ), (component)))
  
  #define KRB_KT_ENT_KEYBLOCK(e)  (&(e)->key)
  #define KRB_KT_ENT_FREE(c, e)   krb5_free_keytab_entry_contents((c), (e))
          (d)->data = NULL;                   \
      } while (0)
  
 +#define KRB_CHECKSUM_TYPE(c)    ((c)->checksum_type)
 +#define KRB_CHECKSUM_LENGTH(c)  ((c)->length)
 +#define KRB_CHECKSUM_DATA(c)    ((c)->contents)
 +
 +#define KRB_CHECKSUM_INIT(cksum, type, d)      do { \
 +        (cksum)->checksum_type = (type);            \
 +        (cksum)->length = (d)->length;              \
 +        (cksum)->contents = (d)->value;             \
 +    } while (0)
 +
  #endif /* HAVE_HEIMDAL_VERSION */
  
  #define KRB_KEY_INIT(key)       do {        \
@@@ -615,13 -597,10 +621,13 @@@ gssEapDisplayName(OM_uint32 *minor
                    gss_buffer_t output_name_buffer,
                    gss_OID *output_name_type);
  
 +#define COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS   0x1
 +
  OM_uint32
  gssEapCompareName(OM_uint32 *minor,
                    gss_name_t name1,
                    gss_name_t name2,
 +                  OM_uint32 flags,
                    int *name_equal);
  
  /* util_oid.c */
@@@ -801,20 -780,13 +807,20 @@@ verifyTokenHeader(OM_uint32 *minor
                    enum gss_eap_token_type *ret_tok_type);
  
  /* Helper macros */
 -
  #ifndef GSSEAP_MALLOC
 +#if _WIN32
 +#include <gssapi/gssapi_alloc.h>
 +#define GSSEAP_MALLOC                   gssalloc_malloc
 +#define GSSEAP_CALLOC                   gssalloc_calloc
 +#define GSSEAP_FREE                     gssalloc_free
 +#define GSSEAP_REALLOC                  gssalloc_realloc
 +#else
  #define GSSEAP_CALLOC                   calloc
  #define GSSEAP_MALLOC                   malloc
  #define GSSEAP_FREE                     free
  #define GSSEAP_REALLOC                  realloc
 -#endif
 +#endif /* _WIN32 */
 +#endif /* !GSSEAP_MALLOC */
  
  #ifndef GSSAPI_CALLCONV
  #define GSSAPI_CALLCONV                 KRB5_CALLCONV
@@@ -1006,58 -978,13 +1012,58 @@@ static inline voi
  krbPrincComponentToGssBuffer(krb5_principal krbPrinc,
                               int index, gss_buffer_t buffer)
  {
 +    if (KRB_PRINC_LENGTH(krbPrinc) < index) {
 +        buffer->value = NULL;
 +        buffer->length = 0;
 +    } else {
  #ifdef HAVE_HEIMDAL_VERSION
 -    buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index];
 -    buffer->length = strlen((char *)buffer->value);
 +        buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index];
 +        buffer->length = strlen((char *)buffer->value);
  #else
 -    buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data;
 -    buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length;
 +        buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data;
 +        buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length;
  #endif /* HAVE_HEIMDAL_VERSION */
 +    }
 +}
 +
 +static inline krb5_error_code
 +krbPrincUnparseServiceSpecifics(krb5_context krbContext, krb5_principal krbPrinc,
 +                                gss_buffer_t nameBuf)
 +{
 +    krb5_error_code result = 0;
 +    if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
 +        /* Acceptor-Service-Specific */
 +        krb5_principal_data ssiPrinc = *krbPrinc;
 +        char *ssi;
 +
 +        KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
 +        KRB_PRINC_NAME(&ssiPrinc) += 2;
 +
 +        result = krb5_unparse_name_flags(krbContext, &ssiPrinc,
 +                                         KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
 +        if (result != 0)
 +            return result;
 +
 +        nameBuf->value = ssi;
 +        nameBuf->length = strlen(ssi);
 +    } else {
 +        nameBuf->value = NULL;
 +        nameBuf->length = 0;
 +    }
 +
 +    return result;
 +}
 +
 +static inline void
 +krbFreeUnparsedName(krb5_context krbContext, gss_buffer_t nameBuf)
 +{
 +#ifdef HAVE_HEIMDAL_VERSION
 +    krb5_xfree((char *) nameBuf->value);
 +#else
 +    krb5_free_unparsed_name(krbContext, (char *)(nameBuf->value));
 +#endif
 +    nameBuf->value = NULL;
 +    nameBuf->length = 0;
  }
  
  static inline void
diff --combined mech_eap/util_cred.c
@@@ -104,6 -104,8 +104,8 @@@ gssEapReleaseCred(OM_uint32 *minor, gss
      gss_release_buffer(&tmpMinor, &cred->caCertificate);
      gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
      gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
+     gss_release_buffer(&tmpMinor, &cred->clientCertificate);
+     gss_release_buffer(&tmpMinor, &cred->privateKey);
  
  #ifdef GSSEAP_ENABLE_REAUTH
      if (cred->krbCredCache != NULL) {
@@@ -241,14 -243,13 +243,14 @@@ cleanup
  gss_OID
  gssEapPrimaryMechForCred(gss_cred_id_t cred)
  {
 -    gss_OID nameMech = GSS_C_NO_OID;
 +    gss_OID credMech = GSS_C_NO_OID;
  
 -    if (cred->mechanisms != GSS_C_NO_OID_SET &&
 +    if (cred != GSS_C_NO_CREDENTIAL &&
 +        cred->mechanisms != GSS_C_NO_OID_SET &&
          cred->mechanisms->count == 1)
 -        nameMech = &cred->mechanisms->elements[0];
 +        credMech = &cred->mechanisms->elements[0];
  
 -    return nameMech;
 +    return credMech;
  }
  
  OM_uint32
@@@ -536,6 -537,68 +538,68 @@@ cleanup
      return major;
  }
  
+ /*
+  * Currently only the privateKey path is exposed to the application
+  * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
+  * At some point in the future we may add support for setting the
+  * client certificate separately.
+  */
+ OM_uint32
+ gssEapSetCredClientCertificate(OM_uint32 *minor,
+                               gss_cred_id_t cred,
+                               const gss_buffer_t clientCert,
+                               const gss_buffer_t privateKey)
+ {
+     OM_uint32 major, tmpMinor;
+     gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
+     gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
+     if (cred->flags & CRED_FLAG_RESOLVED) {
+         major = GSS_S_FAILURE;
+         *minor = GSSEAP_CRED_RESOLVED;
+         goto cleanup;
+     }
+     if (clientCert == GSS_C_NO_BUFFER &&
+         privateKey == GSS_C_NO_BUFFER) {
+         cred->flags &= ~(CRED_FLAG_CERTIFICATE);
+         major = GSS_S_COMPLETE;
+         *minor = 0;
+         goto cleanup;
+     }
+     if (clientCert != GSS_C_NO_BUFFER) {
+         major = duplicateBuffer(minor, clientCert, &newClientCert);
+         if (GSS_ERROR(major))
+             goto cleanup;
+     }
+     if (privateKey != GSS_C_NO_BUFFER) {
+         major = duplicateBuffer(minor, privateKey, &newPrivateKey);
+         if (GSS_ERROR(major))
+             goto cleanup;
+     }
+     cred->flags |= CRED_FLAG_CERTIFICATE;
+     gss_release_buffer(&tmpMinor, &cred->clientCertificate);
+     cred->clientCertificate = newClientCert;
+     gss_release_buffer(&tmpMinor, &cred->privateKey);
+     cred->privateKey = newPrivateKey;
+     major = GSS_S_COMPLETE;
+     *minor = 0;
+ cleanup:
+     if (GSS_ERROR(major)) {
+         gss_release_buffer(&tmpMinor, &newClientCert);
+         gss_release_buffer(&tmpMinor, &newPrivateKey);
+     }
+     return major;
+ }
  OM_uint32
  gssEapSetCredService(OM_uint32 *minor,
                       gss_cred_id_t cred,
@@@ -620,6 -683,10 +684,10 @@@ gssEapDuplicateCred(OM_uint32 *minor
          duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
      if (src->subjectAltNameConstraint.value != NULL)
          duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
+     if (src->clientCertificate.value != NULL)
+         duplicateBufferOrCleanup(&src->clientCertificate, &dst->clientCertificate);
+     if (src->privateKey.value != NULL)
+         duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
  
  #ifdef GSSEAP_ENABLE_REAUTH
      /* XXX krbCredCache, reauthCred */
@@@ -668,8 -735,7 +736,8 @@@ staticIdentityFileResolveInitiatorCred(
              isDefaultIdentity = TRUE;
          } else {
              major = gssEapCompareName(minor, cred->name,
 -                                      defaultIdentityName, &isDefaultIdentity);
 +                                      defaultIdentityName, 0,
 +                                      &isDefaultIdentity);
              if (GSS_ERROR(major))
                  goto cleanup;
          }
@@@ -736,7 -802,8 +804,8 @@@ gssEapResolveInitiatorCred(OM_uint32 *m
              goto cleanup;
  
          /* If we have a caller-supplied password, the credential is resolved. */
-         if ((resolvedCred->flags & CRED_FLAG_PASSWORD) == 0) {
+         if ((resolvedCred->flags &
+              (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
              major = GSS_S_CRED_UNAVAIL;
              *minor = GSSEAP_NO_DEFAULT_CRED;
              goto cleanup;