|
|
/////////////////////////////////////////////////////////////////////////////// //
// FILE
//
// EAP.cpp
//
// SYNOPSIS
//
// This file implements the class EAP.
//
// MODIFICATION HISTORY
//
// 02/12/1998 Original version.
// 05/08/1998 Do not delete sessions when they're done. Let everything
// age out of the session table.
// 05/15/1998 Pass received packet to EAPSession constructor.
// 06/12/1998 Changed put_Response to SetResponse.
// 07/06/1998 Only RAS is allowed to use EAP-TLS.
// 02/09/1998 Allow EAP-TLS over RADIUS.
// 05/20/1999 Identity is now a Unicode string.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <iaslsa.h>
#include <iastlutl.h>
#include <eap.h>
#include <eapdnary.h>
#include <eapstate.h>
#include <eaptypes.h>
//////////
// Define static members.
//////////
EAPTypes EAP::theTypes;
STDMETHODIMP EAP::Initialize() { HRESULT hr;
// Initialize the LSA API.
DWORD error = IASLsaInitialize(); if (error) { hr = HRESULT_FROM_WIN32(error); goto lsa_failed; }
// Initialize the sessions.
hr = EAPSession::initialize(); if (FAILED(hr)) { goto sessions_failed; }
// Initialize the IAS <--> RAS translator.
hr = EAPTranslator::initialize(); if (FAILED(hr)) { goto translator_failed; }
// The rest can't fail.
EAPState::initialize(); theTypes.initialize();
// Everything succeeded, so we're done.
return S_OK;
translator_failed: EAPSession::finalize();
sessions_failed: IASLsaUninitialize();
lsa_failed: return hr; }
STDMETHODIMP EAP::Shutdown() { // Clear out any remaining sessions.
sessions.clear();
// Shutdown our sub-systems.
theTypes.finalize(); EAPTranslator::finalize(); EAPSession::finalize(); IASLsaUninitialize();
return S_OK; }
STDMETHODIMP EAP::PutProperty(LONG Id, VARIANT* pValue) { if (pValue == NULL) { return E_INVALIDARG; }
switch (Id) { case PROPERTY_EAP_SESSION_TIMEOUT: { if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; } if (V_I4(pValue) <= 0) { return E_INVALIDARG; }
IASTracePrintf("Setting EAP session timeout to %ld msec.", V_I4(pValue));
sessions.setSessionTimeout(V_I4(pValue)); break; }
case PROPERTY_EAP_MAX_SESSIONS: { if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; } if (V_I4(pValue) <= 0) { return E_INVALIDARG; }
IASTracePrintf("Setting max. EAP sessions to %ld.", V_I4(pValue));
sessions.setMaxSessions(V_I4(pValue)); break; }
default: { return DISP_E_MEMBERNOTFOUND; } }
return S_OK; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// EAP::onSyncRequest
//
// DESCRIPTION
//
// Processes a request. Note that this method does only enough work to
// retrieve or create a session object. Once this has been accomplished
// the main processing logic takes place inside EAPSession (q.v.).
//
///////////////////////////////////////////////////////////////////////////////
IASREQUESTSTATUS EAP::onSyncRequest(IRequest* pRequest) throw () { EAPSession* session = NULL;
try { IASRequest request(pRequest);
//////////
// Does the request contain an EAP-Message?
//////////
DWORD attrID = RADIUS_ATTRIBUTE_EAP_MESSAGE; IASAttributeVectorWithBuffer<16> eapMessage; if (!eapMessage.load(request, 1, &attrID)) { // If not, we're not interested.
return IAS_REQUEST_STATUS_CONTINUE; }
IASTraceString("NT-SAM EAP handler received request.");
//////////
// Concatenate the RADIUS EAP-Message attributes into a single packet.
//////////
IASAttributeVector::iterator it; DWORD pktlen = 0; for (it = eapMessage.begin(); it != eapMessage.end(); ++it) { pktlen += it->pAttribute->Value.OctetString.dwLength; }
PBYTE p = (PBYTE)_alloca(pktlen); PPPP_EAP_PACKET recvPkt = (PPPP_EAP_PACKET)p; for (it = eapMessage.begin(); it != eapMessage.end(); ++it) { memcpy(p, it->pAttribute->Value.OctetString.lpValue, it->pAttribute->Value.OctetString.dwLength); p += it->pAttribute->Value.OctetString.dwLength; }
//////////
// Ensure that the packet is valid.
//////////
if (pktlen < 5 || IASExtractWORD(recvPkt->Length) != pktlen) { IASTraceString("Assembled EAP-Message has invalid length.");
request.SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_MALFORMED_REQUEST); return IAS_REQUEST_STATUS_ABORT; }
//////////
// Get a session object to handle this request.
//////////
IASREQUESTSTATUS retval; IASAttribute state; if (state.load(request, RADIUS_ATTRIBUTE_STATE, IASTYPE_OCTET_STRING)) { //////////
// If the State attribute exists, this is an ongoing session.
//////////
EAPState& s = (EAPState&)(state->Value.OctetString);
if (!s.isValid()) { IASTraceString("State attribute is present, but unrecognized.");
// We don't recognize this state attribute, so it must belong
// to someone else.
return IAS_REQUEST_STATUS_CONTINUE; }
// Retrieve the object for this session ID.
session = sessions.remove(s.getSessionID());
if (!session) { IASTraceString("Session timed-out. Discarding packet.");
// The session is already complete.
request.SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_SESSION_TIMEOUT); return IAS_REQUEST_STATUS_ABORT; }
IASTracePrintf("Successfully retrieved session state for user %S.", session->getAccountName());
retval = session->process(request, recvPkt); } else { IASTraceString("No State attribute present. Creating new session.");
//////////
// No state attribute, so this is a new session.
// Does the request contain an NT4-Account-Name ?
//////////
IASAttribute identity; if (!identity.load(request, IAS_ATTRIBUTE_NT4_ACCOUNT_NAME, IASTYPE_STRING)) { IASTraceString("SAM account name not found.");
// We only handle SAM users.
return IAS_REQUEST_STATUS_CONTINUE; }
//////////
// Find out which EAP provider to use.
//////////
IASAttribute eapType; if (!eapType.load(request, IAS_ATTRIBUTE_NP_ALLOWED_EAP_TYPE)) { IASTraceString("EAP not authorized for this user.");
// Since we don't have an EAP-Type attribute, the user is not
// allowed to use EAP.
request.SetResponse(IAS_RESPONSE_ACCESS_REJECT, IAS_INVALID_AUTH_TYPE); return IAS_REQUEST_STATUS_HANDLED; }
//////////
// Retrieve the provider for this EAP type.
//////////
EAPType* provider = theTypes[(BYTE)(eapType->Value.Enumerator)];
if (!provider) { // We can't handle this EAP type, so reject.
request.SetResponse(IAS_RESPONSE_ACCESS_REJECT, IAS_UNSUPPORTED_AUTH_TYPE); return IAS_REQUEST_STATUS_HANDLED; }
session = new EAPSession(identity, *provider);
IASTracePrintf("Successfully created new session for user %S.", session->getAccountName());
retval = session->begin(request, recvPkt); }
// Save it for later.
sessions.insert(session);
return retval; } catch (const _com_error& ce) { IASTraceExcept();
pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, ce.Error()); } catch (const std::bad_alloc&) { IASTraceExcept();
pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, E_OUTOFMEMORY); }
// If we have any errors, we'll delete the session.
delete session;
return IAS_REQUEST_STATUS_ABORT; }
|