322 lines
8.3 KiB
C++
322 lines
8.3 KiB
C++
|
/////////////////////////////////////////////////////////////////////////////// //
|
||
|
// 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;
|
||
|
}
|