From: Phil Mayers Date: Fri, 8 Oct 2010 10:01:40 +0000 (+0200) Subject: Added SoH functionality to the PEAP module X-Git-Tag: release_3_0_0_beta0~1205 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=freeradius.git;a=commitdiff_plain;h=2023c9f3d4df8c7160485b36dcfd23faf7cfca80 Added SoH functionality to the PEAP module --- diff --git a/raddb/eap.conf b/raddb/eap.conf index b34acbe..998b1b5 100644 --- a/raddb/eap.conf +++ b/raddb/eap.conf @@ -538,6 +538,18 @@ # outer requests. # virtual_server = "inner-tunnel" + + # This option enables support for MS-SoH + # see doc/SoH.txt for more info. + # It is disabled by default. + # + # soh = yes + + # + # The SoH reply will be turned into a request which + # can be sent to a specific virtual server: + # + # soh_virtual_server = "soh-server" } # diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/eap_peap.h b/src/modules/rlm_eap/types/rlm_eap_peap/eap_peap.h index d09fb24..3ad3a33 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/eap_peap.h +++ b/src/modules/rlm_eap/types/rlm_eap_peap/eap_peap.h @@ -27,6 +27,7 @@ RCSIDH(eap_peap_h, "$Id$") #include "eap_tls.h" +#include typedef struct peap_tunnel_t { VALUE_PAIR *username; @@ -39,6 +40,9 @@ typedef struct peap_tunnel_t { int use_tunneled_reply; int proxy_tunneled_request_as_eap; const char *virtual_server; + int soh; + const char *soh_virtual_server; + VALUE_PAIR *soh_reply_vps; int session_resumption_state; } peap_tunnel_t; @@ -49,6 +53,7 @@ typedef struct peap_tunnel_t { #define PEAP_STATUS_INNER_IDENTITY_REQ_SENT 4 #define PEAP_STATUS_PHASE2_INIT 5 #define PEAP_STATUS_PHASE2 6 +#define PEAP_STATUS_WAIT_FOR_SOH_RESPONSE 7 #define PEAP_RESUMPTION_NO (0) #define PEAP_RESUMPTION_YES (1) diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/peap.c b/src/modules/rlm_eap/types/rlm_eap_peap/peap.c index ff13ef3..9d4d81c 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/peap.c +++ b/src/modules/rlm_eap/types/rlm_eap_peap/peap.c @@ -117,6 +117,92 @@ static int eappeap_identity(EAP_HANDLER *handler, tls_session_t *tls_session) return 1; } +/* + * Send an MS SoH request + */ +static int eappeap_soh(EAP_HANDLER *handler, tls_session_t *tls_session) +{ + uint8_t tlv_packet[20]; + + tlv_packet[0] = 254; /* extended type */ + + tlv_packet[1] = 0; + tlv_packet[2] = 0x01; /* ms vendor */ + tlv_packet[3] = 0x37; + + tlv_packet[4] = 0; /* ms soh eap */ + tlv_packet[5] = 0; + tlv_packet[6] = 0; + tlv_packet[7] = 0x21; + + tlv_packet[8] = 0; /* vendor-spec tlv */ + tlv_packet[9] = 7; + + tlv_packet[10] = 0; + tlv_packet[11] = 8; /* payload len */ + + tlv_packet[12] = 0; /* ms vendor */ + tlv_packet[13] = 0; + tlv_packet[14] = 0x01; + tlv_packet[15] = 0x37; + + tlv_packet[16] = 0; + tlv_packet[17] = 2; + tlv_packet[18] = 0; + tlv_packet[19] = 0; + + (tls_session->record_plus)(&tls_session->clean_in, tlv_packet, 20); + tls_handshake_send(handler->request, tls_session); + return 1; +} + +static VALUE_PAIR* eapsoh_verify(REQUEST *request, const uint8_t *data, unsigned int data_len) { + + VALUE_PAIR *vp; + uint8_t eap_type_base; + uint32_t eap_vendor; + uint32_t eap_type; + int rv; + + vp = pairmake("SoH-Supported", "no", T_OP_EQ); + if (data && data[0] == PW_EAP_NAK) { + RDEBUG("SoH - client NAKed"); + goto done; + } + + if (!data || data_len < 8) { + RDEBUG("SoH - eap payload too short"); + goto done; + } + + eap_type_base = *data++; + if (eap_type_base != 254) { + RDEBUG("SoH - response is not extended EAP: %i", eap_type_base); + goto done; + } + + eap_vendor = soh_pull_be_24(data); data += 3; + if (eap_vendor != 0x137) { + RDEBUG("SoH - extended eap vendor %08x is not Microsoft", eap_vendor); + goto done; + } + + eap_type = soh_pull_be_32(data); data += 4; + if (eap_type != 0x21) { + RDEBUG("SoH - response eap type %08x is not EAP-SoH", eap_type); + goto done; + } + + + rv = soh_verify(request, vp, data, data_len - 8); + if (rv<0) { + RDEBUG("SoH - error decoding payload"); + } else { + vp->vp_integer = 1; + } +done: + return vp; +} /* * Verify the tunneled EAP message. @@ -601,6 +687,8 @@ static const char *peap_state(peap_tunnel_t *t) switch (t->status) { case PEAP_STATUS_TUNNEL_ESTABLISHED: return "TUNNEL ESTABLISHED"; + case PEAP_STATUS_WAIT_FOR_SOH_RESPONSE: + return "WAITING FOR SOH RESPONSE"; case PEAP_STATUS_INNER_IDENTITY_REQ_SENT: return "WAITING FOR INNER IDENTITY"; case PEAP_STATUS_SENT_TLV_SUCCESS: @@ -675,7 +763,12 @@ int eappeap_process(EAP_HANDLER *handler, tls_session_t *tls_session) if (SSL_session_reused(tls_session->ssl)) { RDEBUG2("Skipping Phase2 because of session resumption"); t->session_resumption_state = PEAP_RESUMPTION_YES; - + if (t->soh) { + t->status = PEAP_STATUS_WAIT_FOR_SOH_RESPONSE; + RDEBUG2("Requesting SoH from client"); + eappeap_soh(handler, tls_session); + return RLM_MODULE_HANDLED; + } /* we're good, send success TLV */ t->status = PEAP_STATUS_SENT_TLV_SUCCESS; eappeap_success(handler, tls_session); @@ -710,10 +803,54 @@ int eappeap_process(EAP_HANDLER *handler, tls_session_t *tls_session) t->username->length = data_len - 1; t->username->vp_strvalue[t->username->length] = 0; RDEBUG("Got inner identity '%s'", t->username->vp_strvalue); - + if (t->soh) { + t->status = PEAP_STATUS_WAIT_FOR_SOH_RESPONSE; + RDEBUG2("Requesting SoH from client"); + eappeap_soh(handler, tls_session); + return RLM_MODULE_HANDLED; + } + t->status = PEAP_STATUS_PHASE2_INIT; + break; + + case PEAP_STATUS_WAIT_FOR_SOH_RESPONSE: + fake = request_alloc_fake(request); + rad_assert(fake->packet->vps == NULL); + fake->packet->vps = eapsoh_verify(request, data, data_len); + setup_fake_request(request, fake, t); + + if (t->soh_virtual_server) { + fake->server = t->soh_virtual_server; + } + RDEBUG("Sending SoH request to server %s", fake->server ? fake->server : "NULL"); + debug_pair_list(fake->packet->vps); + rad_authenticate(fake); + RDEBUG("Got SoH reply"); + debug_pair_list(fake->reply->vps); + + if (fake->reply->code != PW_AUTHENTICATION_ACK) { + RDEBUG2("SoH was rejected"); + request_free(&fake); + t->status = PEAP_STATUS_SENT_TLV_FAILURE; + eappeap_failure(handler, tls_session); + return RLM_MODULE_HANDLED; + } + + /* save the SoH VPs */ + t->soh_reply_vps = fake->reply->vps; + fake->reply->vps = NULL; + request_free(&fake); + + if (t->session_resumption_state == PEAP_RESUMPTION_YES) { + /* we're good, send success TLV */ + t->status = PEAP_STATUS_SENT_TLV_SUCCESS; + eappeap_success(handler, tls_session); + return RLM_MODULE_HANDLED; + } + t->status = PEAP_STATUS_PHASE2_INIT; break; + /* * If we authenticated the user, then it's OK. */ diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c index 9b4ed41..cb460ac 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c +++ b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c @@ -58,6 +58,12 @@ typedef struct rlm_eap_peap_t { * Virtual server for inner tunnel session. */ char *virtual_server; + + /* + * Do we do SoH request? + */ + int soh; + char *soh_virtual_server; } rlm_eap_peap_t; @@ -79,6 +85,12 @@ static CONF_PARSER module_config[] = { { "virtual_server", PW_TYPE_STRING_PTR, offsetof(rlm_eap_peap_t, virtual_server), NULL, NULL }, + { "soh", PW_TYPE_BOOLEAN, + offsetof(rlm_eap_peap_t, soh), NULL, "no" }, + + { "soh_virtual_server", PW_TYPE_STRING_PTR, + offsetof(rlm_eap_peap_t, soh_virtual_server), NULL, NULL }, + { NULL, -1, 0, NULL, NULL } /* end the list */ }; @@ -146,6 +158,7 @@ static void peap_free(void *p) pairfree(&t->username); pairfree(&t->state); pairfree(&t->accept_vps); + pairfree(&t->soh_reply_vps); free(t); } @@ -168,6 +181,8 @@ static peap_tunnel_t *peap_alloc(rlm_eap_peap_t *inst) t->proxy_tunneled_request_as_eap = inst->proxy_tunneled_request_as_eap; #endif t->virtual_server = inst->virtual_server; + t->soh = inst->soh; + t->soh_virtual_server = inst->soh_virtual_server; t->session_resumption_state = PEAP_RESUMPTION_MAYBE; return t; @@ -273,6 +288,12 @@ static int eappeap_authenticate(void *arg, EAP_HANDLER *handler) * our Access-Accept. */ peap = tls_session->opaque; + if (peap->soh_reply_vps) { + RDEBUG2("Using saved attributes from the SoH reply"); + debug_pair_list(peap->soh_reply_vps); + pairadd(&handler->request->reply->vps, peap->soh_reply_vps); + peap->soh_reply_vps = NULL; + } if (peap->accept_vps) { RDEBUG2("Using saved attributes from the original Access-Accept"); debug_pair_list(peap->accept_vps);