blob: 72885ea53c63fd5239a38b427b5edda9f14a6f60 [file] [log] [blame]
/*****************************************************************************************************
* Software License Agreement (BSD License)
* Author : Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>
*
* Copyright (c) 2009-2010, Souheil Ben Ayed, Teraoka Laboratory of Keio University, and the WIDE Project
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>.
*
* 4. Neither the name of Souheil Ben Ayed, Teraoka Laboratory of Keio University or the WIDE Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************************************/
#include "diameap_common.h"
static void diameap_ba_nextid(struct eap_state_machine * sm, int * id)
{
TRACE_ENTRY("%p %p",sm,id);
if (sm->currentId < 0)
{
*id = (u8) (255 * rand() / RAND_MAX) & 0xFFU;
}
else
{
*id = (sm->currentId++) & 0xFFU;
}
if (*id == sm->lastId)
{
*id=*id+1;
}
}
static void diameap_ba_policyupdate(struct eap_state_machine * eap_sm,
struct eap_packet *eapPacket)
{
TRACE_ENTRY("%p %p",eap_sm, eapPacket);
if ((eap_sm->respMethod == TYPE_NAK))
{
int id;
eap_sm->user.pmethods = 0;
u32 vendor;
eap_type type;
u8 *data = (u8 *) eapPacket->data;
data += 5;
id = 5;
while (id < eapPacket->length)
{
vendor = VENDOR_IETF;
type = G8(data);
if (diameap_plugin_exist(vendor, type) == TRUE)
{
eap_sm->user.proposedmethods[id - 5].method = type;
eap_sm->user.proposedmethods[id - 5].vendor = vendor;
eap_sm->user.pmethods++;
}
data++;
id++;
}
eap_sm->user.methodId = -1;
}
}
static int diameap_ba_policygetnextmethod(struct eap_state_machine * eap_sm,
eap_type * eaptype, u32 * vendor)
{
TRACE_ENTRY("%p %p %p",eap_sm,eaptype,vendor);
*vendor = 0;
*eaptype = TYPE_NONE;
if (eap_sm == NULL)
{
return EINVAL;
}
eap_sm->selectedMethod = NULL;
if (eap_sm->user.userid == NULL)
{
if ((eap_sm->currentMethod == TYPE_NONE))
{
*vendor = VENDOR_IETF;
*eaptype = TYPE_IDENTITY;
if (eap_sm->selectedMethod != NULL)
{
(*eap_sm->selectedMethod->eap_method_free)(eap_sm->methodData);
eap_sm->methodData = NULL;
}
CHECK_FCT(diameap_plugin_get(VENDOR_IETF,TYPE_IDENTITY,&eap_sm->selectedMethod));
return 0;
}
eap_sm->selectedMethod = NULL;
*vendor = 0;
*eaptype = TYPE_NONE;
return 0;
}
if (eap_sm->user.methodId == -1)
{
if (eap_sm->user.proposed_eap_method >= TYPE_EAP_MD5)
{
*vendor = eap_sm->user.proposed_eap_method_vendor;
if (*vendor == VENDOR_IETF)
{
*eaptype = eap_sm->user.proposed_eap_method;
}
else
{
*eaptype = TYPE_EXPANDED_TYPES;
}
if (eap_sm->selectedMethod != NULL)
{
(*eap_sm->selectedMethod->eap_method_free)(eap_sm->methodData);
eap_sm->methodData = NULL;
}
CHECK_FCT_DO(diameap_plugin_get(*vendor,*eaptype,&eap_sm->selectedMethod),
{ TRACE_DEBUG(INFO,"%s [EAP Protocol] Invalid EAP-TYPE %d (vendor %d)",DIAMEAP_EXTENSION,*eaptype,*vendor);return 1;});
}
eap_sm->user.proposed_eap_method = TYPE_NONE;
}
else
{
*vendor = eap_sm->user.proposedmethods[eap_sm->user.methodId].vendor;
if (eap_sm->user.proposedmethods[eap_sm->user.methodId].vendor
== VENDOR_IETF)
{
*eaptype
= eap_sm->user.proposedmethods[eap_sm->user.methodId].method;
}
else
{
*eaptype = TYPE_EXPANDED_TYPES;
}
if (eap_sm->selectedMethod != NULL)
{
(*eap_sm->selectedMethod->eap_method_free)(eap_sm->methodData);
eap_sm->methodData=NULL;
}
CHECK_FCT(diameap_plugin_get(eap_sm->user.proposedmethods[eap_sm->user.methodId].vendor,eap_sm->user.proposedmethods[eap_sm->user.methodId].method,&eap_sm->selectedMethod));
eap_sm->user.methodId++;
}
return 0;
}
static int diameap_ba_policygetdecision(struct eap_state_machine * eap_sm,
struct diameap_eap_interface * eap_i, decision * gdecision)
{
TRACE_ENTRY("%p %p %p",eap_sm,eap_i,gdecision);
if (eap_sm->user.userid != NULL)
{
if (eap_sm->methodState == EAP_M_END)
{
if (eap_sm->respMethod == TYPE_IDENTITY)
{
*gdecision = DECISION_CONTINUE;
return 0;
}
if ((eap_sm->respMethod == TYPE_NAK) || ((eap_sm->respMethod
== TYPE_EXPANDED_TYPES) && (eap_sm->respVendor
== VENDOR_IETF) && (eap_sm->respVendorMethod == TYPE_NAK)))
{
goto SelectNextMethod;
}
if (eap_sm->user.success == TRUE)
{
*gdecision = DECISION_SUCCESS;
}
else
{
*gdecision = DECISION_FAILURE;
}
}
else
{
goto SelectNextMethod;
}
return 0;
SelectNextMethod: if ((eap_sm->user.methodId
== (MAXPROPOSEDMETHODS - 1))
|| ((eap_sm->user.proposedmethods[eap_sm->user.methodId + 1].method
== TYPE_NONE)
&& (eap_sm->user.proposedmethods[eap_sm->user.methodId
+ 1].vendor == VENDOR_IETF)))
{
TRACE_DEBUG(FULL+1,
"%s [EAP protocol] None of proposed EAP Methods authenticated the user.(FAILURE)",DIAMEAP_EXTENSION);
*gdecision = DECISION_FAILURE;
return 0;
}
eap_sm->user.methodId = 0;
*gdecision = DECISION_CONTINUE;
return 0;
}
if (eap_sm->currentMethod == TYPE_IDENTITY)
{
*gdecision = DECISION_FAILURE;
return 0;
}
*gdecision = DECISION_CONTINUE;
return 0;
}
static boolean diameap_ba_policydopickup(eap_type type)
{
TRACE_ENTRY("%d",type);
if (type == TYPE_IDENTITY)
{
return TRUE;
}
return FALSE;
}
int diameap_eap_statemachine(struct eap_state_machine * eap_sm,
struct diameap_eap_interface * eap_i, boolean * non_fatal_error)
{
TRACE_ENTRY("%p %p %p", eap_sm, eap_i, non_fatal_error);
int ret;
if ((eap_sm->eap_state == EAP_IDLE) && (eap_i->aaaEapResp == TRUE))
{
eap_sm->eap_state = EAP_RECEIVED;
}
while (!((eap_sm->eap_state == EAP_IDLE) || (eap_sm->eap_state == EAP_END)))
{
switch (eap_sm->eap_state)
{
case EAP_INITIALIZE:
if (eap_sm->rxResp == TRUE)
{
eap_sm->lastId = eap_sm->currentId;
eap_sm->currentId = eap_sm->respId;
}
else
{
eap_sm->lastId = -1;
eap_sm->currentId = -1;
}
if (eap_sm->rxResp == FALSE)
{
eap_sm->eap_state = EAP_SELECT_ACTION;
}
else if ((eap_sm->respMethod == TYPE_NAK) || (eap_sm->respMethod
== TYPE_EXPANDED_TYPES && eap_sm->respVendor == VENDOR_IETF
&& eap_sm->respVendorMethod == TYPE_NAK))
{
eap_sm->eap_state = EAP_NAK;
}
else
{
eap_sm->eap_state = EAP_PICK_UP_METHOD;
}
break;
case EAP_PICK_UP_METHOD:
if (diameap_ba_policydopickup(eap_sm->respMethod) == TRUE)
{
eap_sm->currentMethod = eap_sm->respMethod;
if (diameap_plugin_get(eap_sm->currentVendor,
eap_sm->currentMethod, &eap_sm->selectedMethod))
{
TRACE_DEBUG(INFO,"%sNo EAP Method plugin available for EAP Method {Type=%d, Vendor=%d}.",DIAMEAP_EXTENSION,eap_sm->currentMethod, eap_sm->currentVendor);
}
else
{
TRACE_DEBUG(FULL,"%sCurrent EAP Method {Type=%d, Vendor=%d} (EAP Method plugin selected).",DIAMEAP_EXTENSION,eap_sm->currentMethod, eap_sm->currentVendor);
}
eap_sm->currentVendor = VENDOR_IETF;
if (eap_sm->selectedMethod != NULL)
{
ret = (*eap_sm->selectedMethod->eap_method_initPickUp)(
eap_sm);
if (ret)
{
TRACE_DEBUG(INFO, "%sEAP Method InitPickUp returned error.",DIAMEAP_EXTENSION);
eap_sm->selectedMethod = NULL;
eap_sm->currentMethod = TYPE_NONE;
}
}
}
if (eap_sm->currentMethod == TYPE_NONE)
{
eap_sm->eap_state = EAP_SELECT_ACTION;
}
else
{
eap_sm->eap_state = EAP_METHOD_RESPONSE;
}
break;
case EAP_RECEIVED:
TRACE_DEBUG(FULL+1,"%s[EAP Protocol] New EAP Response received",DIAMEAP_EXTENSION)
;
diameap_eap_dump(FULL + 1, &eap_i->aaaEapRespData);
if ((eap_sm->rxResp == TRUE) && (eap_sm->respId
== eap_sm->currentId) && ((eap_sm->respMethod
== eap_sm->currentMethod) || ((eap_sm->respMethod
== TYPE_EXPANDED_TYPES) && (eap_sm->respVendor
== VENDOR_IETF) && (eap_sm->respVendorMethod
== eap_sm->currentMethod))))
{
eap_sm->eap_state = EAP_INTEGRITY_CHECK;
}
else if ((eap_sm->rxResp == TRUE) && (eap_sm->respId
== eap_sm->currentId) && ((eap_sm->respMethod == TYPE_NAK)
|| (eap_sm->respMethod == TYPE_EXPANDED_TYPES
&& eap_sm->respVendor == VENDOR_IETF
&& eap_sm->respVendorMethod == TYPE_NAK))
&& (eap_sm->methodState == EAP_M_PROPOSED))
{
eap_sm->eap_state = EAP_NAK;
}
else
{
eap_sm->eap_state = EAP_DISCARD;
}
break;
case EAP_DISCARD:
TRACE_DEBUG(INFO,"%s[EAP Protocol] Invalid EAP Packet received (Non fatal error).",DIAMEAP_EXTENSION)
;
*non_fatal_error = TRUE;
eap_sm->eap_state = EAP_IDLE;
break;
case EAP_SEND_REQUEST:
TRACE_DEBUG(FULL+1,"%s[EAP Protocol] New EAP packet request created.",DIAMEAP_EXTENSION)
;
diameap_eap_dump(FULL, &eap_i->aaaEapReqData);
eap_i->aaaEapResp = FALSE;
eap_i->aaaEapReq = TRUE;
eap_sm->eap_state = EAP_IDLE;
break;
case EAP_INTEGRITY_CHECK:
if ((*eap_sm->selectedMethod->eap_method_check)(eap_sm,
&eap_i->aaaEapRespData) == FALSE)
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] Invalid EAP packet received {Type=%d, Vendor=%d}. Integrity check failed (non fatal error).",DIAMEAP_EXTENSION,eap_sm->currentMethod,eap_sm->currentVendor);
//non_fata_error
*non_fatal_error = TRUE;
eap_sm->eap_state = EAP_IDLE;
}
else
{
eap_sm->eap_state = EAP_METHOD_RESPONSE;
}
break;
case EAP_METHOD_REQUEST:
eap_sm->lastId = eap_sm->currentId;
diameap_ba_nextid(eap_sm, &eap_sm->currentId);
CHECK_FCT((*eap_sm->selectedMethod->eap_method_buildReq)(
eap_sm, eap_sm->currentId,&eap_i->aaaEapReqData))
;
if (eap_sm->selectedMethod->eap_method_getTimeout)
{
if ((*eap_sm->selectedMethod->eap_method_getTimeout)(eap_sm,
&eap_i->aaaMethodTimeout))
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] [%s plugin] getTimeout failed.",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname);
eap_i->aaaMethodTimeout = 0;
}
}
else
{
eap_i->aaaMethodTimeout = 0;
}
eap_sm->eap_state = EAP_SEND_REQUEST;
break;
case EAP_METHOD_RESPONSE:
if (eap_sm->respMethod >= TYPE_EAP_MD5)
{
if (eap_sm->user.methodId < 0)
{
eap_sm->user.methodId = 0;
eap_sm->user.methods[eap_sm->user.methodId].vendor
= eap_sm->respVendor;
eap_sm->user.methods[eap_sm->user.methodId].method
= eap_sm->respMethod;
eap_sm->user.methodId++;
}
else if (!((eap_sm->user.methods[eap_sm->user.methodId - 1].vendor
== eap_sm->respVendor)
&& (eap_sm->user.methods[eap_sm->user.methodId - 1].method
== eap_sm->respMethod)))
{
eap_sm->user.methods[eap_sm->user.methodId].vendor
= eap_sm->respVendor;
eap_sm->user.methods[eap_sm->user.methodId].method
= eap_sm->respMethod;
eap_sm->user.methodId++;
}
}
if ((*eap_sm->selectedMethod->eap_method_process)(eap_sm,
&eap_i->aaaEapRespData))
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] [%s plugin] Authentication process failed.",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname);
*non_fatal_error = TRUE;
eap_sm->eap_state = EAP_IDLE;
}else{
if ((*eap_sm->selectedMethod->eap_method_isDone)(eap_sm) == TRUE)
{
/*diameap_ba_PolicyUpdate();*/
eap_i->aaaEapMSKLength = 0;
eap_i->aaaEapEMSKLength = 0;
if (eap_sm->selectedMethod->eap_method_getKey)
{
if ((*eap_sm->selectedMethod->eap_method_getKey)(eap_sm,
&eap_i->aaaEapMSKData, &eap_i->aaaEapMSKLength,
&eap_i->aaaEapEMSKData, &eap_i->aaaEapEMSKLength))
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] [%s plugin] Generating EAP Master Key failed.",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname)
eap_i->aaaEapMSKLength = 0;
eap_i->aaaEapEMSKLength = 0;
eap_i->aaaEapKeyAvailable = FALSE;
}
else
{
eap_i->aaaEapKeyAvailable = TRUE;
}
}
eap_sm->methodState = EAP_M_END;
eap_sm->eap_state = EAP_SELECT_ACTION;
}
else
{
eap_sm->methodState = EAP_M_CONTINUE;
eap_sm->eap_state = EAP_METHOD_REQUEST;
}
}
break;
case EAP_PROPOSE_METHOD:
if (diameap_ba_policygetnextmethod(eap_sm, &eap_sm->currentMethod,
&eap_sm->currentVendor))
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] [%s plugin] Selecting EAP Method plugin failed.",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname);
*non_fatal_error = TRUE;
eap_sm->eap_state = EAP_END;
}
TRACE_DEBUG(FULL,"%s[EAP Protocol] Selecting EAP Method plugin: %s(%d). [user: %s]",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname,eap_sm->selectedMethod->methodtype,eap_sm->user.userid)
;
if ((*eap_sm->selectedMethod->eap_method_init)(eap_sm))
{
TRACE_DEBUG(INFO,"%s[EAP Protocol] [%s plugin] Initializing EAP plugin failed.",DIAMEAP_EXTENSION,eap_sm->selectedMethod->methodname);
}
if ((eap_sm->currentMethod == TYPE_IDENTITY)
|| (eap_sm->currentMethod == TYPE_NOTIFICATION))
{
eap_sm->methodState = EAP_M_CONTINUE;
}
else
{
eap_sm->methodState = EAP_M_PROPOSED;
}
eap_sm->eap_state = EAP_METHOD_REQUEST;
break;
case EAP_NAK:
TRACE_DEBUG(FULL+1,"%s[EAP Protocol] EAP NAK Packet received.",DIAMEAP_EXTENSION)
;
if (eap_sm->selectedMethod->eap_method_free)
{
(*eap_sm->selectedMethod->eap_method_free)(eap_sm->methodData);
eap_sm->methodData = NULL;
}
else
{
if (eap_sm->methodData)
{
free(eap_sm->methodData);
eap_sm->methodData = NULL;
}
}
diameap_ba_policyupdate(eap_sm, &eap_i->aaaEapRespData);
eap_sm->eap_state = EAP_SELECT_ACTION;
break;
case EAP_SELECT_ACTION:
CHECK_FCT(diameap_ba_policygetdecision(eap_sm, eap_i,&eap_sm->sm_decision))
;
switch (eap_sm->sm_decision)
{
case DECISION_CONTINUE:
eap_sm->eap_state = EAP_PROPOSE_METHOD;
break;
case DECISION_FAILURE:
TRACE_DEBUG(FULL,"%s[EAP Protocol] Authentication Failure [User: %s].",DIAMEAP_EXTENSION,eap_sm->user.userid)
;
eap_sm->lastId = eap_sm->currentId;
diameap_ba_nextid(eap_sm, &eap_sm->currentId);
CHECK_FCT(diameap_eap_new(EAP_FAILURE, (u8) eap_sm->currentId, TYPE_NONE, NULL, 0,&eap_i->aaaEapReqData))
;
eap_i->aaaFail = TRUE;
*non_fatal_error = FALSE;
eap_sm->eap_state = EAP_END;
if (eap_sm->methodData)
{
if (eap_sm->selectedMethod->eap_method_free)
{
(*eap_sm->selectedMethod->eap_method_free)(
eap_sm->methodData);
eap_sm->methodData = NULL;
}
else
{
free(eap_sm->methodData);
eap_sm->methodData = NULL;
}
}
break;
case DECISION_SUCCESS:
TRACE_DEBUG(FULL,"%s[EAP Protocol] Authentication Success [User: %s].",DIAMEAP_EXTENSION,eap_sm->user.userid)
;
eap_sm->lastId = eap_sm->currentId;
diameap_ba_nextid(eap_sm, &eap_sm->currentId);
CHECK_FCT(diameap_eap_new(EAP_SUCCESS, (u8) eap_sm->currentId, TYPE_NONE, NULL, 0,&eap_i->aaaEapReqData))
;
if (eap_i->aaaEapMSKData != NULL)
{
TRACE_DEBUG(FULL+1,"%s[EAP Protocol] EAP Key available [User: %s].",DIAMEAP_EXTENSION,eap_sm->user.userid);
eap_i->aaaEapKeyAvailable = TRUE;
}
else
{
TRACE_DEBUG(FULL+1,"%s[EAP Protocol] No EAP Key available [User: %s].",DIAMEAP_EXTENSION,eap_sm->user.userid);
}
eap_i->aaaSuccess = TRUE;
*non_fatal_error = FALSE;
eap_sm->eap_state = EAP_END;
if (eap_sm->selectedMethod->eap_method_free)
{
(*eap_sm->selectedMethod->eap_method_free)(
eap_sm->methodData);
eap_sm->methodData = NULL;
}
else
{
if (eap_sm->methodData)
{
free(eap_sm->methodData);
eap_sm->methodData = NULL;
}
}
break;
default:
TRACE_DEBUG(INFO,"%sIncorrect EAP Decision.(Please report this problem.)",DIAMEAP_EXTENSION)
;
}
break;
case EAP_END:
break;
case EAP_IDLE:
break;
}
}
return 0;
}