[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])
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")
-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=
-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])
AX_CHECK_SHIBSP
fi
+ AX_CHECK_OPENSSL
+
if test "x$acceptor" = "xyes" ; then
AX_CHECK_RADSEC
AX_CHECK_JANSSON
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 \
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 \
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 \
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
@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 \
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
#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"
#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
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
struct rs_connection *radConn;
char *radServer;
gss_buffer_desc state;
- VALUE_PAIR *vps;
+ rs_avp *vps;
};
#endif
#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,
#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
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 */
*/
#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)
}
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
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;
* 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))
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;
}
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;
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;
}
return GSS_S_CONTINUE_NEEDED;
}
-
+
#ifdef GSSEAP_ENABLE_REAUTH
static OM_uint32
eapGssSmInitReauthCreds(OM_uint32 *minor,
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;
{
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
},
{
goto cleanup;
}
}
+
if (ret_flags != NULL)
*ret_flags = ctx->gssFlags;
+
if (time_rec != NULL)
gssEapContextTime(&tmpMinor, ctx, time_rec);
#include <krb5.h>
#ifdef WIN32
-#define inline __inline
+# ifndef __cplusplus
+# define inline __inline
+# endif
#define snprintf _snprintf
#endif
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);
#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 { \
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 */
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
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
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) {
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
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,
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 */
isDefaultIdentity = TRUE;
} else {
major = gssEapCompareName(minor, cred->name,
- defaultIdentityName, &isDefaultIdentity);
+ defaultIdentityName, 0,
+ &isDefaultIdentity);
if (GSS_ERROR(major))
goto cleanup;
}
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;