Merge branch 'oldradius'
authorLuke Howard <lukeh@padl.com>
Tue, 21 Sep 2010 10:49:23 +0000 (12:49 +0200)
committerLuke Howard <lukeh@padl.com>
Tue, 21 Sep 2010 10:49:23 +0000 (12:49 +0200)
Conflicts:
mech_eap/accept_sec_context.c
mech_eap/init_sec_context.c

17 files changed:
Makefile.am
accept_sec_context.c
export_sec_context.c
gssapiP_eap.h
gssapi_eap.h
import_sec_context.c
init_sec_context.c
mech_eap.exports
set_cred_option.c
util_context.c
util_cred.c
util_krb.c
util_name.c
util_radius.cpp
util_radius.h
util_saml.cpp
util_shib.cpp

index 336354a..aa22204 100644 (file)
@@ -4,7 +4,7 @@ gssdir = $(libdir)/gss
 
 gss_LTLIBRARIES = libmech_eap.la
 
-libmech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB
+libmech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\"
 libmech_eap_la_CFLAGS   = -g -Wall -fno-strict-aliasing \
                          @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@
 libmech_eap_la_CXXFLAGS = -g -Wall \
@@ -13,7 +13,7 @@ libmech_eap_la_CXXFLAGS = -g -Wall \
 libmech_eap_la_LDFLAGS  = -export-symbols mech_eap.exports -version-info 0:0:0 \
                          -no-undefined \
                          @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@
-libmech_eap_la_LIBADD   = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ @SHIBRESOLVER_LIBS@
+libmech_eap_la_LIBADD   = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ @SHIBRESOLVER_LIBS@ -lfreeradius-client
 
 libmech_eap_la_SOURCES =                       \
        accept_sec_context.c                    \
index dcb8ce9..42c29c2 100644 (file)
 
 #include "gssapiP_eap.h"
 
-#ifdef BUILTIN_EAP
-#define EAP_KEY_AVAILABLE(ctx)  ((ctx)->acceptorCtx.eapPolInterface->eapKeyAvailable)
-#define EAP_KEY_DATA(ctx)       ((ctx)->acceptorCtx.eapPolInterface->eapKeyData)
-#define EAP_KEY_LENGTH(ctx)     ((ctx)->acceptorCtx.eapPolInterface->eapKeyDataLen)
-#else
-#define EAP_KEY_AVAILABLE(ctx)  0
-#define EAP_KEY_DATA(ctx)       NULL
-#define EAP_KEY_LENGTH(ctx)     0
-#endif /* BUILTIN_EAP */
-
+/*
+ * Mark a context as ready for cryptographic operations
+ */
 static OM_uint32
-acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx);
-
-#ifdef BUILTIN_EAP
-#define EAP_MAX_METHODS 8
-
-#define EAP_TTLS_AUTH_PAP 1
-#define EAP_TTLS_AUTH_CHAP 2
-#define EAP_TTLS_AUTH_MSCHAP 4
-#define EAP_TTLS_AUTH_MSCHAPV2 8
-
-struct eap_user {
-        struct {
-                int vendor;
-                u32 method;
-        } methods[EAP_MAX_METHODS];
-        u8 *password;
-        size_t password_len;
-        int password_hash; /* whether password is hashed with
-                            * nt_password_hash() */
-        int phase2;
-        int force_version;
-        int ttls_auth; /* bitfield of
-                        * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
-};
+acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
+{
+    OM_uint32 major;
+    VALUE_PAIR *vp;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
 
-struct eap_eapol_interface {
-        /* Lower layer to full authenticator variables */
-        Boolean eapResp; /* shared with EAPOL Backend Authentication */
-        struct wpabuf *eapRespData;
-        Boolean portEnabled;
-        int retransWhile;
-        Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
-        int eapSRTT;
-        int eapRTTVAR;
-
-        /* Full authenticator to lower layer variables */
-        Boolean eapReq; /* shared with EAPOL Backend Authentication */
-        Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
-        Boolean eapSuccess;
-        Boolean eapFail;
-        Boolean eapTimeout;
-        struct wpabuf *eapReqData;
-        u8 *eapKeyData;
-        size_t eapKeyDataLen;
-        Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
-
-        /* AAA interface to full authenticator variables */
-        Boolean aaaEapReq;
-        Boolean aaaEapNoReq;
-        Boolean aaaSuccess;
-        Boolean aaaFail;
-        struct wpabuf *aaaEapReqData;
-        u8 *aaaEapKeyData;
-        size_t aaaEapKeyDataLen;
-        Boolean aaaEapKeyAvailable;
-        int aaaMethodTimeout;
-
-        /* Full authenticator to AAA interface variables */
-        Boolean aaaEapResp;
-        struct wpabuf *aaaEapRespData;
-        /* aaaIdentity -> eap_get_identity() */
-        Boolean aaaTimeout;
-};
+    /* Cache encryption type derived from selected mechanism OID */
+    major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
+                               &ctx->encryptionType);
+    if (GSS_ERROR(major))
+        return major;
 
-#define eapol_callbacks     SERVER_eapol_callbacks
+    vp = rc_avpair_get(ctx->acceptorCtx.avps, PW_USER_NAME, 0);
+    if (vp != NULL) {
+        nameBuf.length = vp->lvalue;
+        nameBuf.value = vp->strvalue;
+    } else if (ctx->initiatorName == GSS_C_NO_NAME) {
+        ctx->gssFlags |= GSS_C_ANON_FLAG;
+    }
 
-struct eapol_callbacks {
-        int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
-                            int phase2, struct eap_user *user);
-        const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
-};
+    if (nameBuf.length != 0 || ctx->initiatorName == GSS_C_NO_NAME) {
+        major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
+                                 &ctx->initiatorName);
+        if (GSS_ERROR(major))
+            return major;
+    }
 
-#define eap_config          SERVER_eap_config
-
-struct eap_config {
-        void *ssl_ctx;
-        void *msg_ctx;
-        void *eap_sim_db_priv;
-        Boolean backend_auth;
-        int eap_server;
-        u8 *pac_opaque_encr_key;
-        u8 *eap_fast_a_id;
-        size_t eap_fast_a_id_len;
-        char *eap_fast_a_id_info;
-        int eap_fast_prov;
-        int pac_key_lifetime;
-        int pac_key_refresh_time;
-        int eap_sim_aka_result_ind;
-        int tnc;
-        struct wps_context *wps;
-        const struct wpabuf *assoc_wps_ie;
-        const u8 *peer_addr;
-        int fragment_size;
-};
+    ctx->initiatorName->attrCtx = gssEapCreateAttrContext(cred, ctx);
+
+    vp = rc_avpair_get(ctx->acceptorCtx.avps,
+                       RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+                       RADIUS_VENDOR_ID_MICROSOFT);
+    if (ctx->encryptionType != ENCTYPE_NULL && vp != NULL) {
+        major = gssEapDeriveRfc3961Key(minor,
+                                       (unsigned char *)vp->strvalue,
+                                       vp->lvalue,
+                                       ctx->encryptionType,
+                                       &ctx->rfc3961Key);
+        if (GSS_ERROR(major))
+            return major;
 
-struct eap_sm * eap_server_sm_init(void *eapol_ctx,
-                                   struct eapol_callbacks *eapol_cb,
-                                   struct eap_config *eap_conf);
-void eap_server_sm_deinit(struct eap_sm *sm);
-int eap_server_sm_step(struct eap_sm *sm);
-void eap_sm_notify_cached(struct eap_sm *sm);
-void eap_sm_pending_cb(struct eap_sm *sm);
-int eap_sm_method_pending(struct eap_sm *sm);
-const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
-struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
+        major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
+                                           &ctx->checksumType);
+        if (GSS_ERROR(major))
+            return major;
+    } else {
+        /*
+         * draft-howlett-eap-gss says that integrity/confidentialty should
+         * always be advertised as available, but if we have no keying
+         * material it seems confusing to the caller to advertise this.
+         */
+        ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
+        ctx->encryptionType = ENCTYPE_NULL;
+    }
+
+    major = sequenceInit(minor,
+                         &ctx->seqState, ctx->recvSeq,
+                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
+                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
+                         TRUE);
+    if (GSS_ERROR(major))
+        return major;
 
-#include <eap_server/eap_i.h>
+    return GSS_S_COMPLETE;
+}
 
 static OM_uint32
-initTls(OM_uint32 *minor,
-        gss_ctx_id_t ctx)
+eapGssSmAcceptIdentity(OM_uint32 *minor,
+                       gss_ctx_id_t ctx,
+                       gss_cred_id_t cred,
+                       gss_buffer_t inputToken,
+                       gss_channel_bindings_t chanBindings,
+                       gss_buffer_t outputToken)
 {
-    struct tls_config tconf;
-    struct tls_connection_params tparams;
+    OM_uint32 major;
+    union {
+        struct eap_hdr pdu;
+        unsigned char data[5];
+    } pkt;
+    gss_buffer_desc pktBuffer;
 
-    memset(&tconf, 0, sizeof(tconf));
-    ctx->acceptorCtx.tlsContext = tls_init(&tconf);
-    if (ctx->acceptorCtx.tlsContext == NULL)
-        return GSS_S_FAILURE;
+    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0)
+        return GSS_S_DEFECTIVE_TOKEN;
 
-    memset(&tparams, 0, sizeof(tparams));
-    tparams.ca_cert = "ca.pem";
-    tparams.client_cert = "server.pem";
-    tparams.private_key = "server-key.pem";
+    assert(ctx->acceptorCtx.radHandle == NULL);
 
-    if (tls_global_set_params(ctx->acceptorCtx.tlsContext, &tparams)) {
-        return GSS_S_FAILURE;
-    }
+    major = gssEapRadiusAllocHandle(minor, cred, &ctx->acceptorCtx.radHandle);
+    if (GSS_ERROR(major))
+        return major;
 
-    if (tls_global_set_verify(ctx->acceptorCtx.tlsContext, 0)) {
-        return GSS_S_FAILURE;
+    if (ctx->acceptorName == GSS_C_NO_NAME &&
+        cred != GSS_C_NO_CREDENTIAL &&
+        cred->name != GSS_C_NO_NAME) {
+        major = gss_duplicate_name(minor, cred->name, &ctx->acceptorName);
+        if (GSS_ERROR(major))
+            return major;
     }
 
-    return GSS_S_COMPLETE;
+    pkt.pdu.code = EAP_CODE_REQUEST;
+    pkt.pdu.identifier = 0;
+    pkt.pdu.length = htons(sizeof(pkt.data));
+    pkt.data[4] = EAP_TYPE_IDENTITY;
+
+    pktBuffer.length = sizeof(pkt.data);
+    pktBuffer.value = pkt.data;
+
+    major = duplicateBuffer(minor, &pktBuffer, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    ctx->state = EAP_STATE_AUTHENTICATE;
+
+    return GSS_S_CONTINUE_NEEDED;
 }
 
-static int
-serverGetEapUser(void *ctx,
-                 const unsigned char *identity,
-                 size_t identityLength,
-                 int phase2,
-                 struct eap_user *user)
+static OM_uint32
+importInitiatorIdentity(OM_uint32 *minor,
+                        gss_ctx_id_t ctx,
+                        gss_buffer_t inputToken,
+                        gss_buffer_t nameBuf)
 {
-    gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
-    OM_uint32 major, minor;
-    gss_buffer_desc buf;
-
-    memset(user, 0, sizeof(*user));
+    OM_uint32 major, tmpMinor;
+    struct eap_hdr *pdu = (struct eap_hdr *)inputToken->value;
+    unsigned char *pos = (unsigned char *)(pdu + 1);
+    gss_name_t name;
 
-    buf.length = identityLength;
-    buf.value = (void *)identity;
+    assert(pdu->code == EAP_CODE_RESPONSE);
+    assert(pos[0] == EAP_TYPE_IDENTITY);
 
-    if (phase2 == 0) {
-        user->methods[0].vendor = EAP_VENDOR_IETF;
-        user->methods[0].method = EAP_TYPE_PEAP;
-        return 0;
-    }
+    nameBuf->value = pos + 1;
+    nameBuf->length = inputToken->length - sizeof(*pdu) - 1;
 
-    major = gssEapImportName(&minor, &buf, GSS_C_NT_USER_NAME,
-                             &gssCtx->initiatorName);
+    major = gssEapImportName(minor, nameBuf, GSS_C_NT_USER_NAME, &name);
     if (GSS_ERROR(major))
-        return -1;
-
-    /*
-     * OK, obviously there is no real security here, this is simply
-     * for testing the token exchange; this code will be completely
-     * replaced with libradius once that library is available.
-     */
-    user->methods[0].vendor = EAP_VENDOR_IETF;
-    user->methods[0].method = EAP_TYPE_MSCHAPV2;
-    user->password = (unsigned char *)strdup("foo");
-    user->password_len = 3;
-
-    gssCtx->initiatorName->attrCtx = gssEapCreateAttrContext(NULL, gssCtx);
-    if (gssCtx->initiatorName->attrCtx != NULL)
-        gssCtx->initiatorName->flags |= NAME_FLAG_COMPOSITE;
-
-    return 0;
-}
+        return major;
 
-static const char *
-serverGetEapReqIdText(void *ctx,
-                      size_t *len)
-{
-    *len = 0;
-    return NULL;
+    gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
+    ctx->initiatorName = name;
+
+    return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -239,101 +186,96 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
                            gss_channel_bindings_t chanBindings,
                            gss_buffer_t outputToken)
 {
-    OM_uint32 major;
-    OM_uint32 tmpMinor, tmpMajor;
+    OM_uint32 major, tmpMinor;
     int code;
-    struct wpabuf respData;
-    static struct eapol_callbacks cb = { serverGetEapUser, serverGetEapReqIdText };
-    if (ctx->acceptorCtx.eap == NULL) {
-        struct eap_config eapConfig;
+    VALUE_PAIR *send = NULL;
+    VALUE_PAIR *received = NULL;
+    rc_handle *rh = ctx->acceptorCtx.radHandle;
+    char msgBuffer[4096];
+    struct eap_hdr *pdu;
+    unsigned char *pos;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+
+    pdu = (struct eap_hdr *)inputToken->value;
+    pos = (unsigned char *)(pdu + 1);
+
+    if (inputToken->length > sizeof(*pdu) &&
+        pdu->code == EAP_CODE_RESPONSE &&
+        pos[0] == EAP_TYPE_IDENTITY) {
+        major = importInitiatorIdentity(minor, ctx, inputToken, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
 
-        major = initTls(minor, ctx);
+        major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, &nameBuf);
         if (GSS_ERROR(major))
             goto cleanup;
+    }
 
-        memset(&eapConfig, 0, sizeof(eapConfig));
-        eapConfig.eap_server = 1;
-        eapConfig.ssl_ctx = ctx->acceptorCtx.tlsContext;
+    major = addAvpFromBuffer(minor, rh, &send, PW_EAP_MESSAGE, inputToken);
+    if (GSS_ERROR(major))
+        goto cleanup;
 
-        ctx->acceptorCtx.eap = eap_server_sm_init(ctx, &cb, &eapConfig);
-        if (ctx->acceptorCtx.eap == NULL) {
-            major = GSS_S_FAILURE;
+    if (ctx->acceptorCtx.lastStatus == CHALLENGE_RC) {
+        major = addAvpFromBuffer(minor, rh, &send, PW_STATE,
+                                 &ctx->acceptorCtx.state);
+        if (GSS_ERROR(major))
             goto cleanup;
-        }
 
-        ctx->acceptorCtx.eapPolInterface = eap_get_interface(ctx->acceptorCtx.eap);
-        ctx->acceptorCtx.eapPolInterface->portEnabled = TRUE;
-        ctx->acceptorCtx.eapPolInterface->eapRestart = TRUE;
+        gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
     }
 
-    if (ctx->acceptorName == GSS_C_NO_NAME &&
-        cred != GSS_C_NO_CREDENTIAL &&
-        cred->name != GSS_C_NO_NAME) {
-        major = gss_duplicate_name(minor, cred->name, &ctx->acceptorName);
-        if (GSS_ERROR(major))
-            goto cleanup;
+    code = rc_auth(rh, 0, send, &received, msgBuffer);
+    switch (code) {
+    case OK_RC:
+    case CHALLENGE_RC:
+        major = GSS_S_CONTINUE_NEEDED;
+        break;
+    case TIMEOUT_RC:
+        major = GSS_S_UNAVAILABLE;
+        break;
+    case REJECT_RC:
+        major = GSS_S_DEFECTIVE_CREDENTIAL;
+        break;
+    default:
+        major = GSS_S_FAILURE;
+        goto cleanup;
     }
 
-    wpabuf_set(&respData, inputToken->value, inputToken->length);
-    ctx->acceptorCtx.eapPolInterface->eapRespData = &respData;
-    ctx->acceptorCtx.eapPolInterface->eapResp = TRUE;
+    if (GSS_ERROR(major))
+        goto cleanup;
 
-    code = eap_server_sm_step(ctx->acceptorCtx.eap);
+    ctx->acceptorCtx.lastStatus = code;
 
-    if (ctx->acceptorCtx.eapPolInterface->eapReq) {
-        ctx->acceptorCtx.eapPolInterface->eapReq = 0;
-        major = GSS_S_CONTINUE_NEEDED;
-    }
+    major = getBufferFromAvps(minor, received, PW_EAP_MESSAGE,
+                              outputToken, TRUE);
+    if ((major == GSS_S_UNAVAILABLE && code != OK_RC) ||
+        GSS_ERROR(major))
+        goto cleanup;
 
-    if (ctx->acceptorCtx.eapPolInterface->eapSuccess) {
-        ctx->acceptorCtx.eapPolInterface->eapSuccess = 0;
-        major = acceptReady(minor, ctx);
+    if (code == CHALLENGE_RC) {
+        major = getBufferFromAvps(minor, received, PW_STATE,
+                                  &ctx->acceptorCtx.state, TRUE);
+        if (major != GSS_S_UNAVAILABLE && GSS_ERROR(major))
+            goto cleanup;
+    } else {
+        ctx->acceptorCtx.avps = received;
+        received = NULL;
+
+        major = acceptReady(minor, ctx, cred);
         if (GSS_ERROR(major))
             goto cleanup;
 
         ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
-        major = GSS_S_CONTINUE_NEEDED;
-    } else if (ctx->acceptorCtx.eapPolInterface->eapFail) {
-        ctx->acceptorCtx.eapPolInterface->eapFail = 0;
-        major = GSS_S_FAILURE;
-    } else if (code == 0) {
-        major = GSS_S_FAILURE;
     }
 
-    if (ctx->acceptorCtx.eapPolInterface->eapReqData != NULL) {
-        gss_buffer_desc buf;
-
-        buf.length = wpabuf_len(ctx->acceptorCtx.eapPolInterface->eapReqData);
-        buf.value = (void *)wpabuf_head(ctx->acceptorCtx.eapPolInterface->eapReqData);
-
-        tmpMajor = duplicateBuffer(&tmpMinor, &buf, outputToken);
-        if (GSS_ERROR(tmpMajor)) {
-            major = tmpMajor;
-            *minor = tmpMinor;
-            goto cleanup;
-        }
-    }
+    major = GSS_S_CONTINUE_NEEDED;
 
 cleanup:
-    ctx->acceptorCtx.eapPolInterface->eapRespData = NULL;
+    if (received != NULL)
+        rc_avpair_free(received);
 
     return major;
 }
-#else
-static OM_uint32
-eapGssSmAcceptAuthenticate(OM_uint32 *minor,
-                           gss_ctx_id_t ctx,
-                           gss_cred_id_t cred,
-                           gss_buffer_t inputToken,
-                           gss_channel_bindings_t chanBindings,
-                           gss_buffer_t outputToken)
-{
-    OM_uint32 major, tmpMinor;
-
-cleanup:
-    return major;
-}
-#endif /* BUILTIN_EAP */
 
 static OM_uint32
 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
@@ -343,7 +285,7 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
                                  gss_channel_bindings_t chanBindings,
                                  gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
     gss_iov_buffer_desc iov[2];
 
     outputToken->length = 0;
@@ -411,11 +353,8 @@ static struct gss_eap_acceptor_sm {
                               gss_channel_bindings_t,
                               gss_buffer_t);
 } eapGssAcceptorSm[] = {
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptIdentity           },
     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate       },
-#if 0
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                             },
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                             },
-#endif
     { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     eapGssSmAcceptGssChannelBindings },
     { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished        },
 };
@@ -530,49 +469,3 @@ cleanup:
     return major;
 }
 
-/*
- * Mark a context as ready for cryptographic operations
- */
-static OM_uint32
-acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx)
-{
-    OM_uint32 major;
-
-    /* Cache encryption type derived from selected mechanism OID */
-    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
-    if (GSS_ERROR(major))
-        return major;
-
-    if (ctx->encryptionType != ENCTYPE_NULL &&
-        EAP_KEY_AVAILABLE(ctx)) {
-        major = gssEapDeriveRfc3961Key(minor,
-                                       EAP_KEY_DATA(ctx),
-                                       EAP_KEY_LENGTH(ctx),
-                                       ctx->encryptionType,
-                                       &ctx->rfc3961Key);
-        if (GSS_ERROR(major))
-            return major;
-
-        major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
-                                           &ctx->checksumType);
-        if (GSS_ERROR(major))
-            return major;
-    } else {
-        /*
-         * draft-howlett-eap-gss says that integrity/confidentialty should
-         * always be advertised as available, but if we have no keying
-         * material it seems confusing to the caller to advertise this.
-         */
-        ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
-    }
-
-    major = sequenceInit(minor,
-                         &ctx->seqState, ctx->recvSeq,
-                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
-                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
-                         TRUE);
-    if (GSS_ERROR(major))
-        return major;
-
-    return GSS_S_COMPLETE;
-}
index 50d4f5c..2387dd6 100644 (file)
@@ -37,13 +37,7 @@ gssEapExportPartialContext(OM_uint32 *minor,
                            gss_ctx_id_t ctx,
                            gss_buffer_t token)
 {
-    token->length = 0;
-    token->value = NULL;
-
-    /*
-     * The format of this token awaits definition by libradius.
-     */
-    return GSS_S_COMPLETE;
+    return duplicateBuffer(minor, &ctx->acceptorCtx.state, token);
 }
 
 static OM_uint32
index bb61a01..7830e2e 100644 (file)
@@ -33,8 +33,6 @@
 #ifndef _GSSAPIP_EAP_H_
 #define _GSSAPIP_EAP_H_ 1
 
-#define BUILTIN_EAP 1
-
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
@@ -46,7 +44,6 @@
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_ext.h>
 #include "gssapi_eap.h"
-#include "util.h"
 
 /* Kerberos includes */
 #include <krb5.h>
 #include <wpabuf.h>
 #endif
 
+#include <freeradius-client.h>
+#include <freeradius/radius.h>
+
+#include "util.h"
+
 /* These name flags are informative and not actually used by anything yet */
 #define NAME_FLAG_NAI                       0x00000001
 #define NAME_FLAG_SERVICE                   0x00000002
@@ -87,6 +89,7 @@ struct gss_cred_id_struct {
     gss_buffer_desc password;
     gss_OID_set mechanisms;
     time_t expiryTime;
+    char *radiusConfigFile;
 };
 
 #define CTX_FLAG_INITIATOR                  0x00000001
@@ -94,11 +97,8 @@ struct gss_cred_id_struct {
 #define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
 
 enum gss_eap_state {
-    EAP_STATE_AUTHENTICATE = 0,
-#if 0
-    EAP_STATE_KEY_TRANSPORT,
-    EAP_STATE_SECURE_ASSOCIATION,
-#endif
+    EAP_STATE_IDENTITY = 0,
+    EAP_STATE_AUTHENTICATE,
     EAP_STATE_GSS_CHANNEL_BINDINGS,
     EAP_STATE_ESTABLISHED
 };
@@ -127,11 +127,10 @@ struct gss_eap_initiator_ctx {
 };
 
 struct gss_eap_acceptor_ctx {
-#if defined(BUILTIN_EAP) && !defined(__cplusplus)
-    struct eap_eapol_interface *eapPolInterface;
-    void *tlsContext;
-    struct eap_sm *eap;
-#endif
+    rc_handle *radHandle;
+    int lastStatus;
+    VALUE_PAIR *avps;
+    gss_buffer_desc state;
 };
 
 struct gss_ctx_id_struct {
index 2c8cee0..5e121f5 100644 (file)
@@ -46,6 +46,9 @@ extern gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM;
 /* name type */
 extern gss_OID GSS_EAP_NT_PRINCIPAL_NAME;
 
+/* set credential option for acceptor configuration file */
+extern gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 28962fb..3960b92 100644 (file)
@@ -38,6 +38,7 @@ gssEapImportPartialContext(OM_uint32 *minor,
                            size_t *pRemain,
                            gss_ctx_id_t ctx)
 {
+    OM_uint32 major;
     unsigned char *p = *pBuf;
     size_t remain = *pRemain;
     gss_buffer_desc buf;
@@ -46,15 +47,22 @@ gssEapImportPartialContext(OM_uint32 *minor,
         *minor = ERANGE;
         return GSS_S_DEFECTIVE_TOKEN;
     }
-
     buf.length = load_uint32_be(p);
 
-    if (buf.length != 0) {
-        *minor = EINVAL;
+    if (remain < buf.length) {
+        *minor = ERANGE;
         return GSS_S_DEFECTIVE_TOKEN;
+
     }
+    buf.value = &p[4];
+
+    major = duplicateBuffer(minor, &buf, &ctx->acceptorCtx.state);
+    if (GSS_ERROR(major))
+        return major;
+
+    *pBuf += 4 + buf.length;
+    *pRemain -= 4 + buf.length;
 
-    *minor = 0;
     return GSS_S_COMPLETE;
 }
 
@@ -206,7 +214,7 @@ gssEapImportContext(OM_uint32 *minor,
     remain -= 16;
 
     /* Validate state */
-    if (ctx->state < EAP_STATE_AUTHENTICATE ||
+    if (ctx->state < EAP_STATE_IDENTITY ||
         ctx->state > EAP_STATE_ESTABLISHED)
         return GSS_S_DEFECTIVE_TOKEN;
 
index 9ec5e66..050f335 100644 (file)
@@ -188,8 +188,7 @@ extern int wpa_debug_level;
 static OM_uint32
 peerConfigInit(OM_uint32 *minor,
                gss_cred_id_t cred,
-               gss_ctx_id_t ctx,
-               int loadConfig)
+               gss_ctx_id_t ctx)
 {
     krb5_context krbContext;
     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
@@ -198,10 +197,8 @@ peerConfigInit(OM_uint32 *minor,
 
     GSSEAP_KRB_INIT(&krbContext);
 
-    if (loadConfig) {
-        eapPeerConfig->fragment_size = 1024;
-        wpa_debug_level = 0;
-    }
+    eapPeerConfig->fragment_size = 1024;
+    wpa_debug_level = 0;
 
     code = krb5_unparse_name(krbContext, cred->name->krbPrincipal, &identity);
     if (code != 0) {
@@ -237,25 +234,37 @@ initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
     OM_uint32 major;
     const unsigned char *key;
     size_t keyLength;
+    krb5_enctype encryptionType;
+    int gotKey = 0;
 
     /* Cache encryption type derived from selected mechanism OID */
-    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
+    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &encryptionType);
     if (GSS_ERROR(major))
         return major;
 
-    if (ctx->encryptionType != ENCTYPE_NULL &&
+    if (encryptionType != ENCTYPE_NULL &&
         eap_key_available(ctx->initiatorCtx.eap)) {
         key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
 
-        major = gssEapDeriveRfc3961Key(minor, key, keyLength,
-                                       ctx->encryptionType, &ctx->rfc3961Key);
-        if (GSS_ERROR(major))
-            return major;
+        if (keyLength >= EAP_EMSK_LEN) {
+            major = gssEapDeriveRfc3961Key(minor,
+                                           &key[EAP_EMSK_LEN / 2],
+                                           EAP_EMSK_LEN / 2,
+                                           encryptionType,
+                                           &ctx->rfc3961Key);
+               if (GSS_ERROR(major))
+                   return major;
+
+            major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
+                                              &ctx->checksumType);
+            if (GSS_ERROR(major))
+                return major;
+            gotKey++;
+        }
+    }
 
-        major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
-                                           &ctx->checksumType);
-        if (GSS_ERROR(major))
-            return major;
+    if (gotKey) {
+        ctx->encryptionType = encryptionType;
     } else {
         /*
          * draft-howlett-eap-gss says that integrity/confidentialty should
@@ -278,6 +287,63 @@ initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
 }
 
 static OM_uint32
+eapGssSmInitIdentity(OM_uint32 *minor,
+                     gss_cred_id_t cred,
+                     gss_ctx_id_t ctx,
+                     gss_name_t target,
+                     gss_OID mech,
+                     OM_uint32 reqFlags,
+                     OM_uint32 timeReq,
+                     gss_channel_bindings_t chanBindings,
+                     gss_buffer_t inputToken,
+                     gss_buffer_t outputToken)
+{
+    time_t now;
+    OM_uint32 major;
+    int initialContextToken;
+
+    initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
+                           inputToken->length == 0);
+    if (!initialContextToken)
+        return GSS_S_DEFECTIVE_TOKEN;
+
+    time(&now);
+    if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
+        ctx->expiryTime = 0;
+    else
+        ctx->expiryTime = now + timeReq;
+
+    major = gss_duplicate_name(minor, cred->name, &ctx->initiatorName);
+    if (GSS_ERROR(major))
+        return major;
+
+    major = gss_duplicate_name(minor, target, &ctx->acceptorName);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (mech == GSS_C_NULL_OID || oidEqual(mech, GSS_EAP_MECHANISM)) {
+        major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
+    } else if (gssEapIsConcreteMechanismOid(mech)) {
+        if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed))
+            major = duplicateOid(minor, mech, &ctx->mechanismUsed);
+    } else {
+        major = GSS_S_BAD_MECH;
+    }
+    if (GSS_ERROR(major))
+        return major;
+
+    /* If credentials were provided, check they're usable with this mech */
+    if (!gssEapCredAvailable(cred, ctx->mechanismUsed))
+        return GSS_S_BAD_MECH;
+
+    ctx->state = EAP_STATE_AUTHENTICATE;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static struct wpabuf emptyWpaBuffer;
+
+static OM_uint32
 eapGssSmInitAuthenticate(OM_uint32 *minor,
                          gss_cred_id_t cred,
                          gss_ctx_id_t ctx,
@@ -290,67 +356,37 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
                          gss_buffer_t outputToken)
 {
     OM_uint32 major;
-    OM_uint32 tmpMajor, tmpMinor;
-    time_t now;
-    int initialContextToken = 0, code;
-    gss_buffer_desc respBuf = GSS_C_EMPTY_BUFFER;
+    OM_uint32 tmpMinor;
+    int code;
+    struct wpabuf *resp = NULL;
+    int initialContextToken;
 
     initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
                            inputToken->length == 0);
 
-    major = peerConfigInit(minor, cred, ctx, initialContextToken);
+    major = peerConfigInit(minor, cred, ctx);
     if (GSS_ERROR(major))
         goto cleanup;
 
-    if (initialContextToken) {
+    if (ctx->initiatorCtx.eap == NULL) {
         struct eap_config eapConfig;
 
         memset(&eapConfig, 0, sizeof(eapConfig));
-        ctx->flags |= CTX_FLAG_EAP_PORT_ENABLED;
 
         ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
                                                  &gssEapPolicyCallbacks,
                                                  ctx,
                                                  &eapConfig);
-
-        time(&now);
-        if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
-            ctx->expiryTime = 0;
-        else
-            ctx->expiryTime = now + timeReq;
-
-        major = gss_duplicate_name(minor, cred->name, &ctx->initiatorName);
-        if (GSS_ERROR(major))
-            goto cleanup;
-
-        major = gss_duplicate_name(minor, target, &ctx->acceptorName);
-        if (GSS_ERROR(major))
-            goto cleanup;
-
-        if (mech == GSS_C_NULL_OID || oidEqual(mech, GSS_EAP_MECHANISM)) {
-            major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
-        } else if (gssEapIsConcreteMechanismOid(mech)) {
-            if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed))
-                major = duplicateOid(minor, mech, &ctx->mechanismUsed);
-        } else {
-            major = GSS_S_BAD_MECH;
-        }
-        if (GSS_ERROR(major))
-            goto cleanup;
-
-        /* If credentials were provided, check they're usable with this mech */
-        if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
-            major = GSS_S_BAD_MECH;
+        if (ctx->initiatorCtx.eap == NULL) {
+            major = GSS_S_FAILURE;
             goto cleanup;
         }
 
-        respBuf.value = ""; /* emit empty inner token */
-        major = GSS_S_CONTINUE_NEEDED;
-        goto cleanup;
-    } else {
-        ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
+        ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
     }
 
+    ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
+
     wpabuf_set(&ctx->initiatorCtx.reqData,
                inputToken->value, inputToken->length);
 
@@ -358,15 +394,9 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
 
     code = eap_peer_sm_step(ctx->initiatorCtx.eap);
     if (ctx->flags & CTX_FLAG_EAP_RESP) {
-        struct wpabuf *resp;
-
         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
 
         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
-        if (resp != NULL) {
-            respBuf.length = wpabuf_len(resp);
-            respBuf.value = (void *)wpabuf_head(resp);
-        }
     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
         major = initReady(minor, ctx);
         if (GSS_ERROR(major))
@@ -377,16 +407,23 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
         ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
         major = GSS_S_DEFECTIVE_CREDENTIAL;
-    } else if (code == 0) {
-        major = GSS_S_FAILURE;
+    } else if (code == 0 && initialContextToken) {
+        resp = &emptyWpaBuffer;
+        major = GSS_S_CONTINUE_NEEDED;
+    } else {
+        major = GSS_S_DEFECTIVE_TOKEN;
     }
 
 cleanup:
-    if (respBuf.value != NULL) {
+    if (resp != NULL) {
         OM_uint32 tmpMajor;
+        gss_buffer_desc respBuf;
 
         assert(major == GSS_S_CONTINUE_NEEDED);
 
+        respBuf.length = wpabuf_len(resp);
+        respBuf.value = (void *)wpabuf_head(resp);
+
         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
         if (GSS_ERROR(tmpMajor)) {
             major = tmpMajor;
@@ -400,38 +437,6 @@ cleanup:
     return major;
 }
 
-#if 0
-static OM_uint32
-eapGssSmInitKeyTransport(OM_uint32 *minor,
-                         gss_cred_id_t cred,
-                         gss_ctx_id_t ctx,
-                         gss_name_t target,
-                         gss_OID mech,
-                         OM_uint32 reqFlags,
-                         OM_uint32 timeReq,
-                         gss_channel_bindings_t chanBindings,
-                         gss_buffer_t inputToken,
-                         gss_buffer_t outputToken)
-{
-    GSSEAP_NOT_IMPLEMENTED;
-}
-
-static OM_uint32
-eapGssSmInitSecureAssoc(OM_uint32 *minor,
-                        gss_cred_id_t cred,
-                        gss_ctx_id_t ctx,
-                        gss_name_t target,
-                        gss_OID mech,
-                        OM_uint32 reqFlags,
-                        OM_uint32 timeReq,
-                        gss_channel_bindings_t chanBindings,
-                        gss_buffer_t inputToken,
-                        gss_buffer_t outputToken)
-{
-    GSSEAP_NOT_IMPLEMENTED;
-}
-#endif
-
 static OM_uint32
 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                gss_cred_id_t cred,
@@ -444,7 +449,7 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                gss_buffer_t inputToken,
                                gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
     gss_iov_buffer_desc iov[2];
     gss_buffer_desc buf;
 
@@ -515,11 +520,8 @@ static struct gss_eap_initiator_sm {
                               gss_buffer_t,
                               gss_buffer_t);
 } eapGssInitiatorSm[] = {
+    { TOK_TYPE_NONE,    TOK_TYPE_EAP_RESP,  eapGssSmInitIdentity            },
     { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP,  eapGssSmInitAuthenticate        },
-#if 0
-    { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP,  eapGssSmInitKeyTransport        },
-    { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP,  eapGssSmInitSecureAssoc         },
-#endif
     { TOK_TYPE_NONE,    TOK_TYPE_GSS_CB,    eapGssSmInitGssChannelBindings  },
     { TOK_TYPE_NONE,    TOK_TYPE_NONE,      eapGssSmInitEstablished         },
 };
index ad4dc24..ddd2826 100644 (file)
@@ -47,4 +47,5 @@ GSS_EAP_MECHANISM
 GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM
 GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM
 GSS_EAP_NT_PRINCIPAL_NAME
+GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE
 gssspi_acquire_cred_with_password
index d5c5709..0affb32 100644 (file)
 
 #include "gssapiP_eap.h"
 
+static OM_uint32
+setCredRadiusConfigFile(OM_uint32 *minor,
+                        gss_cred_id_t cred,
+                        const gss_OID oid,
+                        const gss_buffer_t buffer)
+{
+    OM_uint32 major;
+    gss_buffer_desc configFileBuffer = GSS_C_EMPTY_BUFFER;
+
+    if (buffer != GSS_C_NO_BUFFER && buffer->length != 0) {
+        major = duplicateBuffer(minor, buffer, &configFileBuffer);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    if (cred->radiusConfigFile != NULL)
+        free(cred->radiusConfigFile);
+
+    cred->radiusConfigFile = (char *)configFileBuffer.value;
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
 static struct {
     gss_OID_desc oid;
-    OM_uint32 (*setOption)(OM_uint32 *, gss_cred_id_t *pCred,
+    OM_uint32 (*setOption)(OM_uint32 *, gss_cred_id_t cred,
                            const gss_OID, const gss_buffer_t);
 } setCredOps[] = {
+    /* 1.3.6.1.4.1.5322.21.3.3.1 */
+    {
+        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x15\x03\x03\x01" },
+        setCredRadiusConfigFile,
+    },
 };
 
+gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE = &setCredOps[0].oid;
+
 OM_uint32
 gssspi_set_cred_option(OM_uint32 *minor,
                        gss_cred_id_t cred,
@@ -50,7 +81,7 @@ gssspi_set_cred_option(OM_uint32 *minor,
 
     for (i = 0; i < sizeof(setCredOps) / sizeof(setCredOps[0]); i++) {
         if (oidEqual(&setCredOps[i].oid, desired_object)) {
-            major = (*setCredOps[i].setOption)(minor, &cred,
+            major = (*setCredOps[i].setOption)(minor, cred,
                                               desired_object, value);
             break;
         }
index 13372ea..6d3b937 100644 (file)
@@ -53,7 +53,7 @@ gssEapAllocContext(OM_uint32 *minor,
         return GSS_S_FAILURE;
     }
 
-    ctx->state = EAP_STATE_AUTHENTICATE;
+    ctx->state = EAP_STATE_IDENTITY;
 
     /*
      * Integrity, confidentiality, sequencing and replay detection are
@@ -82,11 +82,14 @@ releaseInitiatorContext(struct gss_eap_initiator_ctx *ctx)
 static void
 releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx)
 {
-#ifdef BUILTIN_EAP
-    eap_server_sm_deinit(ctx->eap);
-    if (ctx->tlsContext != NULL)
-        tls_deinit(ctx->tlsContext);
-#endif
+    OM_uint32 tmpMinor;
+
+    if (ctx->avps != NULL)
+        rc_avpair_free(ctx->avps);
+    if (ctx->radHandle != NULL)
+        rc_config_free(ctx->radHandle);
+
+    gss_release_buffer(&tmpMinor, &ctx->state);
 }
 
 OM_uint32
index d1f31ee..bec6120 100644 (file)
@@ -75,6 +75,9 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
         GSSEAP_FREE(cred->password.value);
     }
 
+    if (cred->radiusConfigFile != NULL)
+        free(cred->radiusConfigFile);
+
     GSSEAP_MUTEX_DESTROY(&cred->mutex);
     memset(cred, 0, sizeof(*cred));
     GSSEAP_FREE(cred);
@@ -98,6 +101,7 @@ gssEapAcquireCred(OM_uint32 *minor,
     OM_uint32 major, tmpMinor;
     gss_cred_id_t cred;
 
+    /* XXX TODO validate with changed set_cred_option API */
     *pCred = GSS_C_NO_CREDENTIAL;
 
     major = gssEapAllocCred(minor, &cred);
index 3607ec5..48c79a4 100644 (file)
@@ -95,6 +95,8 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor,
     unsigned char constant[4 + sizeof("rfc4121-gss-eap") - 1], *p;
     ssize_t i, remain;
 
+    assert(encryptionType != ENCTYPE_NULL);
+
     memset(pKey, 0, sizeof(*pKey));
 
     GSSEAP_KRB_INIT(&krbContext);
@@ -105,6 +107,9 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor,
     t.data = NULL;
     t.length = 0;
 
+    prfOut.data = NULL;
+    prfOut.length = 0;
+
     code = krb5_c_keylengths(krbContext, encryptionType,
                              &randomLength, &keyLength);
     if (code != 0)
index d45b60f..e64ebdd 100644 (file)
@@ -194,14 +194,21 @@ importUserName(OM_uint32 *minor,
 
     GSSEAP_KRB_INIT(&krbContext);
 
-    major = bufferToString(minor, nameBuffer, &nameString);
-    if (GSS_ERROR(major))
-        return major;
+    if (nameBuffer == GSS_C_NO_BUFFER) {
+        *minor = krb5_copy_principal(krbContext,
+                                     krb5_anonymous_principal(), &krbPrinc);
+        if (*minor != 0)
+            return GSS_S_FAILURE;
+    } else {
+        major = bufferToString(minor, nameBuffer, &nameString);
+        if (GSS_ERROR(major))
+            return major;
 
-    *minor = krb5_parse_name(krbContext, nameString, &krbPrinc);
-    if (*minor != 0) {
-        GSSEAP_FREE(nameString);
-        return GSS_S_FAILURE;
+        *minor = krb5_parse_name(krbContext, nameString, &krbPrinc);
+        if (*minor != 0) {
+            GSSEAP_FREE(nameString);
+            return GSS_S_FAILURE;
+        }
     }
 
     major = krbPrincipalToName(minor, &krbPrinc, pName);
index 97dee6e..2c55bff 100644 (file)
 
 #include "gssapiP_eap.h"
 
+VALUE_PAIR *
+gss_eap_radius_attr_provider::copyAvps(const VALUE_PAIR *src)
+{
+    const VALUE_PAIR *vp;
+    VALUE_PAIR *dst = NULL, **pDst = &dst;
+
+    for (vp = src; vp != NULL; vp = vp->next) {
+        VALUE_PAIR *vp2;
+
+        vp2 = (VALUE_PAIR *)GSSEAP_CALLOC(1, sizeof(*vp2));
+        if (vp2 == NULL) {
+            rc_avpair_free(dst);
+            return NULL;
+        }
+        memcpy(vp2, vp, sizeof(*vp));
+        vp2->next = NULL;
+        *pDst = vp2;
+        pDst = &vp2->next;
+    }
+
+    return dst;
+}
+
 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
 {
+    m_rh = NULL;
+    m_avps = NULL;
     m_authenticated = false;
 }
 
 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
 {
+    if (m_rh != NULL)
+        rc_config_free(m_rh);
+    if (m_avps != NULL)
+        rc_avpair_free(m_avps);
+}
+
+bool
+gss_eap_radius_attr_provider::initFromGssCred(const gss_cred_id_t cred)
+{
+    OM_uint32 minor;
+
+    return !GSS_ERROR(gssEapRadiusAllocHandle(&minor, cred, &m_rh));
 }
 
 bool
 gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager,
                                                       const gss_eap_attr_provider *ctx)
 {
+    const gss_eap_radius_attr_provider *radius;
+
     if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx))
         return false;
 
+    if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
+        return false;
+
+    radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
+    if (radius->m_avps != NULL) {
+        m_avps = copyAvps(radius->getAvps());
+    }
+
     return true;
 }
 
@@ -59,12 +106,79 @@ gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager
     if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx))
         return false;
 
+    if (!initFromGssCred(gssCred))
+        return false;
+
+    if (gssCtx != GSS_C_NO_CONTEXT) {
+        if (gssCtx->acceptorCtx.avps != NULL) {
+            m_avps = copyAvps(gssCtx->acceptorCtx.avps);
+            if (m_avps == NULL)
+                return false;
+        }
+    }
+
     return true;
 }
 
+static bool
+alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
+{
+    for (std::vector<std::string>::const_iterator a = attrs.begin();
+         a != attrs.end();
+         ++a) {
+        if (strcmp(vp->name, (*a).c_str()) == 0)
+            return true;
+    }
+
+    return false;
+}
+
+static bool
+isSecretAttributeP(int attrid, int vendor)
+{
+    bool ret = false;
+
+    switch (vendor) {
+    case RADIUS_VENDOR_ID_MICROSOFT:
+        switch (attrid) {
+        case RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY:
+        case RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY:
+            ret = true;
+            break;
+        default:
+            break;
+        }
+    default:
+        break;
+    }
+
+    return ret;
+}
+
 bool
 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, void *data) const
 {
+    VALUE_PAIR *vp;
+    std::vector <std::string> seen;
+
+    for (vp = m_avps; vp != NULL; vp = vp->next) {
+        gss_buffer_desc attribute;
+
+        if (isSecretAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute)))
+            continue;
+
+        if (alreadyAddedAttributeP(seen, vp))
+            continue;
+
+        attribute.value = (void *)vp->name;
+        attribute.length = strlen(vp->name);
+
+        if (!addAttribute(this, &attribute, data))
+            return false;
+
+        seen.push_back(std::string(vp->name));
+    }
+
     return true;
 }
 
@@ -88,31 +202,152 @@ gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
                                            gss_buffer_t display_value,
                                            int *more) const
 {
-    return false;
+    OM_uint32 tmpMinor;
+    gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
+    DICT_ATTR *d;
+    int attrid;
+    char *s;
+
+    /* XXX vendor */
+
+    duplicateBuffer(*attr, &strAttr);
+    s = (char *)strAttr.value;
+
+    if (isdigit(((char *)strAttr.value)[0])) {
+        attrid = strtoul(s, NULL, 10);
+    } else {
+        d = rc_dict_findattr(m_rh, (char *)s);
+        if (d == NULL) {
+            gss_release_buffer(&tmpMinor, &strAttr);
+            return false;
+        }
+        attrid = d->value;
+    }
+
+    gss_release_buffer(&tmpMinor, &strAttr);
+
+    return getAttribute(attrid, authenticated, complete,
+                        value, display_value, more);
+}
+
+static bool
+isPrintableAttributeP(VALUE_PAIR *vp)
+{
+    size_t i;
+
+    for (i = 0; i < sizeof(vp->strvalue); i++) {
+        if (!isprint(vp->strvalue[i]))
+            return false;
+    }
+
+    return true;
 }
 
 bool
-gss_eap_radius_attr_provider::getAttribute(unsigned int attr,
+gss_eap_radius_attr_provider::getAttribute(int attrid,
+                                           int vendor,
                                            int *authenticated,
                                            int *complete,
                                            gss_buffer_t value,
                                            gss_buffer_t display_value,
                                            int *more) const
 {
-    return false;
+    OM_uint32 tmpMinor;
+    VALUE_PAIR *vp;
+    int i = *more;
+    int max = 0;
+    char name[NAME_LENGTH + 1];
+    char displayString[AUTH_STRING_LEN + 1];
+    gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc displayBuf = GSS_C_EMPTY_BUFFER;
+
+    *more = 0;
+
+    if (isSecretAttributeP(attrid, vendor))
+        return false;
+
+    vp = rc_avpair_get(m_avps, attrid, vendor);
+    if (vp == NULL)
+        return false;
+
+    if (i == -1)
+        i = 0;
+
+    do {
+        if (i == max)
+            break;
+
+        max++;
+    } while ((vp = rc_avpair_get(vp->next, attrid, vendor)) != NULL);
+
+    if (i > max)
+        return false;
+
+    if (vp->type == PW_TYPE_STRING) {
+        valueBuf.value = (void *)vp->strvalue;
+        valueBuf.length = vp->lvalue;
+    } else {
+        valueBuf.value = (void *)&vp->lvalue;
+        valueBuf.length = 4;
+    }
+
+    if (value != GSS_C_NO_BUFFER)
+        duplicateBuffer(valueBuf, value);
+
+    if (display_value != GSS_C_NO_BUFFER &&
+        isPrintableAttributeP(vp)) {
+        if (rc_avpair_tostr(m_rh, vp, name, NAME_LENGTH,
+                            displayString, AUTH_STRING_LEN) != 0) {
+            gss_release_buffer(&tmpMinor, value);
+            return false;
+        }
+
+        displayBuf.value = (void *)displayString;
+        displayBuf.length = strlen(displayString);
+
+        duplicateBuffer(displayBuf, display_value);
+    }
+
+    if (authenticated != NULL)
+        *authenticated = m_authenticated;
+    if (complete != NULL)
+        *complete = true;
+
+    if (max > i)
+        *more = i;
+
+    return true;
+}
+
+bool
+gss_eap_radius_attr_provider::getAttribute(int attrid,
+                                           int *authenticated,
+                                           int *complete,
+                                           gss_buffer_t value,
+                                           gss_buffer_t display_value,
+                                           int *more) const
+{
+
+    return getAttribute(ATTRID(attrid), VENDOR(attrid),
+                        authenticated, complete,
+                        value, display_value, more);
 }
 
 gss_any_t
 gss_eap_radius_attr_provider::mapToAny(int authenticated,
                                        gss_buffer_t type_id) const
 {
-    return (gss_any_t)NULL;
+    if (authenticated && !m_authenticated)
+        return (gss_any_t)NULL;
+
+    return (gss_any_t)copyAvps(m_avps);
 }
 
 void
 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
                                                     gss_any_t input) const
 {
+    rc_avpair_free((VALUE_PAIR *)input);
 }
 
 void
@@ -129,6 +364,9 @@ gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
         return false;
 
+    if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
+        return false;
+
     return true;
 }
 
@@ -154,6 +392,60 @@ gss_eap_radius_attr_provider::createAttrContext(void)
 }
 
 OM_uint32
+addAvpFromBuffer(OM_uint32 *minor,
+                 rc_handle *rh,
+                 VALUE_PAIR **vp,
+                 int type,
+                 gss_buffer_t buffer)
+{
+    if (rc_avpair_add(rh, vp, type, buffer->value, buffer->length, 0) == NULL) {
+        return GSS_S_FAILURE;
+    }
+
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+getBufferFromAvps(OM_uint32 *minor,
+                  VALUE_PAIR *vps,
+                  int type,
+                  gss_buffer_t buffer,
+                  int concat)
+{
+    VALUE_PAIR *vp;
+    unsigned char *p;
+
+    buffer->length = 0;
+    buffer->value = NULL;
+
+    vp = rc_avpair_get(vps, type, 0);
+    if (vp == NULL)
+        return GSS_S_UNAVAILABLE;
+
+    do {
+        buffer->length += vp->lvalue;
+    } while (concat && (vp = rc_avpair_get(vp->next, type, 0)) != NULL);
+
+    buffer->value = GSSEAP_MALLOC(buffer->length);
+    if (buffer->value == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    p = (unsigned char *)buffer->value;
+
+    for (vp = rc_avpair_get(vps, type, 0);
+         concat && vp != NULL;
+         vp = rc_avpair_get(vp->next, type, 0)) {
+        memcpy(p, vp->strvalue, vp->lvalue);
+        p += vp->lvalue;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
 {
     return gss_eap_radius_attr_provider::init()
@@ -166,3 +458,32 @@ gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
     gss_eap_radius_attr_provider::finalize();
     return GSS_S_COMPLETE;
 }
+
+OM_uint32
+gssEapRadiusAllocHandle(OM_uint32 *minor,
+                        const gss_cred_id_t cred,
+                        rc_handle **pHandle)
+{
+    rc_handle *rh;
+    const char *config = RC_CONFIG_FILE;
+
+    *pHandle = NULL;
+
+    if (cred != GSS_C_NO_CREDENTIAL && cred->radiusConfigFile != NULL)
+        config = cred->radiusConfigFile;
+
+    rh = rc_read_config((char *)config);
+    if (rh == NULL) {
+        *minor = errno;
+        rc_config_free(rh);
+        return GSS_S_FAILURE;
+    }
+
+    if (rc_read_dictionary(rh, rc_conf_str(rh, (char *)"dictionary")) != 0) {
+        *minor = errno;
+        return GSS_S_FAILURE;
+    }
+
+    *pHandle = rh;
+    return GSS_S_COMPLETE;
+}
index 75984c6..acc2283 100644 (file)
@@ -66,7 +66,14 @@ public:
     bool initFromBuffer(const gss_eap_attr_ctx *ctx,
                         const gss_buffer_t buffer);
 
-    bool getAttribute(unsigned int attribute,
+    bool getAttribute(int attribute,
+                      int *authenticated,
+                      int *complete,
+                      gss_buffer_t value,
+                      gss_buffer_t display_value,
+                      int *more) const;
+    bool getAttribute(int attribute,
+                      int vendor,
                       int *authenticated,
                       int *complete,
                       gss_buffer_t value,
@@ -81,6 +88,14 @@ public:
     static gss_eap_attr_provider *createAttrContext(void);
 
 private:
+    bool initFromGssCred(const gss_cred_id_t cred);
+    static VALUE_PAIR *copyAvps(const VALUE_PAIR *in);
+    const VALUE_PAIR *getAvps(void) const {
+        return m_avps;
+    }
+
+    rc_handle *m_rh;
+    VALUE_PAIR *m_avps;
     bool m_authenticated;
 };
 
@@ -90,9 +105,37 @@ private:
 extern "C" {
 #endif
 
+OM_uint32
+addAvpFromBuffer(OM_uint32 *minor,
+                 rc_handle *rh,
+                 VALUE_PAIR **vp,
+                 int type,
+                 gss_buffer_t buffer);
+
+OM_uint32
+getBufferFromAvps(OM_uint32 *minor,
+                  VALUE_PAIR *vps,
+                  int type,
+                  gss_buffer_t buffer,
+                  int concat);
+
 OM_uint32 gssEapRadiusAttrProviderInit(OM_uint32 *minor);
 OM_uint32 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor);
 
+OM_uint32
+gssEapRadiusAllocHandle(OM_uint32 *minor,
+                        const gss_cred_id_t cred,
+                        rc_handle **pHandle);
+
+#define RC_CONFIG_FILE      SYSCONFDIR "/radiusclient/radiusclient.conf"
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+       RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
 #ifdef __cplusplus
 }
 #endif
index 115824f..0d4d893 100644 (file)
@@ -236,6 +236,9 @@ gss_any_t
 gss_eap_saml_assertion_provider::mapToAny(int authenticated,
                                           gss_buffer_t type_id) const
 {
+    if (authenticated && !m_authenticated)
+        return (gss_any_t)NULL;
+
     return (gss_any_t)m_assertion;
 }
 
index 8da0bbc..d0c1a79 100644 (file)
  * limitations under the License.
  */
 
-#include "gssapiP_eap.h"
-
 #include <shibsp/exceptions.h>
 #include <shibsp/attribute/SimpleAttribute.h>
 #include <shibsp/handler/AssertionConsumerService.h>
 
 #include <shibresolver/resolver.h>
 
+#include "gssapiP_eap.h"
+
 using namespace shibsp;
 using namespace shibresolver;
 using namespace opensaml::saml2md;
@@ -352,6 +352,9 @@ gss_eap_shib_attr_provider::mapToAny(int authenticated,
 {
     gss_any_t output;
 
+    if (authenticated && !m_authenticated)
+        return (gss_any_t)NULL;
+
     vector <Attribute *>v = duplicateAttributes(m_attributes);
 
     output = (gss_any_t)new vector <Attribute *>(v);