/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corp. All rights reserved. // // SYNOPSIS // // Defines the class Accountant. // /////////////////////////////////////////////////////////////////////////////// #include "ias.h" #include "account.h" #include #include "sdoias.h" #define STACK_ALLOC(type, num) (type*)_alloca(sizeof(type) * (num)) Accountant::Accountant() throw () : logAuth(false), logAcct(false), logInterim(false), logAuthInterim(false) { } Accountant::~Accountant() throw () { } STDMETHODIMP Accountant::Initialize() { return schema.initialize(); } STDMETHODIMP Accountant::Shutdown() { schema.shutdown(); return S_OK; } HRESULT Accountant::PutProperty(LONG id, VARIANT* value) throw () { if (value == 0) { return E_INVALIDARG; } HRESULT hr = S_OK; switch (id) { case PROPERTY_ACCOUNTING_LOG_ACCOUNTING: { if (V_VT(value) == VT_BOOL) { logAcct = (V_BOOL(value) != 0); } else { hr = DISP_E_TYPEMISMATCH; } break; } case PROPERTY_ACCOUNTING_LOG_ACCOUNTING_INTERIM: { if (V_VT(value) == VT_BOOL) { logInterim = (V_BOOL(value) != 0); } else { hr = DISP_E_TYPEMISMATCH; } break; } case PROPERTY_ACCOUNTING_LOG_AUTHENTICATION: { if (V_VT(value) == VT_BOOL) { logAuth = (V_BOOL(value) != 0); } else { hr = DISP_E_TYPEMISMATCH; } break; } case PROPERTY_ACCOUNTING_LOG_AUTHENTICATION_INTERIM: { if (V_VT(value) == VT_BOOL) { logAuthInterim = (V_BOOL(value) != 0); } else { hr = DISP_E_TYPEMISMATCH; } break; } default: { // We just ignore properties that we don't understand. break; } } return hr; } void Accountant::RecordEvent(void* context, IASTL::IASRequest& request) { // Array of PacketTypes to be inserted. PKT_UNKNOWN indicates no record. PacketType types[3] = { PKT_UNKNOWN, PKT_UNKNOWN, PKT_UNKNOWN }; // Determine packet types based on request type and the configuration. switch (request.get_Request()) { case IAS_REQUEST_ACCOUNTING: { if (IsInterimRecord(request) ? logInterim : logAcct) { types[0] = PKT_ACCOUNTING_REQUEST; } break; } case IAS_REQUEST_ACCESS_REQUEST: { switch (request.get_Response()) { case IAS_RESPONSE_ACCESS_ACCEPT: { if (logAuth) { types[0] = PKT_ACCESS_REQUEST; types[1] = PKT_ACCESS_ACCEPT; } break; } case IAS_RESPONSE_ACCESS_REJECT: { if (logAuth) { types[0] = PKT_ACCESS_REQUEST; types[1] = PKT_ACCESS_REJECT; } break; } case IAS_RESPONSE_ACCESS_CHALLENGE: { if (logAuthInterim) { types[0] = PKT_ACCESS_REQUEST; types[1] = PKT_ACCESS_CHALLENGE; } break; } default: { break; } } break; } default: { break; } } // Get the local SYSTEMTIME. SYSTEMTIME localTime; GetLocalTime(&localTime); // Insert the appropriate records. for (const PacketType* type = types; *type != PKT_UNKNOWN; ++type) { InsertRecord(context, request, localTime, *type); } // Flush the accounting stream. Flush(context, request, localTime); } void Accountant::InsertRecord( void* context, IASTL::IASRequest& request, const SYSTEMTIME& localTime, PacketType packetType ) { ////////// // Retrieve all the attributes from the request. Save room for three extra // attributes: Packet-Type, Reason-Code, and a null-terminator. ////////// PATTRIBUTEPOSITION firstPos, curPos, lastPos; DWORD nattr = request.GetAttributeCount(); firstPos = STACK_ALLOC(ATTRIBUTEPOSITION, nattr + 3); nattr = request.GetAttributes(nattr, firstPos, 0, NULL); lastPos = firstPos + nattr; ////////// // Compute the attribute filter and reason code. ////////// DWORD always, never, reason = 0; switch (packetType) { case PKT_ACCESS_REQUEST: always = IAS_RECVD_FROM_CLIENT | IAS_RECVD_FROM_PROTOCOL; never = IAS_INCLUDE_IN_RESPONSE; break; case PKT_ACCESS_ACCEPT: always = IAS_INCLUDE_IN_ACCEPT; never = IAS_RECVD_FROM_CLIENT | IAS_INCLUDE_IN_REJECT | IAS_INCLUDE_IN_CHALLENGE; break; case PKT_ACCESS_REJECT: always = IAS_INCLUDE_IN_REJECT; never = IAS_RECVD_FROM_CLIENT | IAS_INCLUDE_IN_ACCEPT | IAS_INCLUDE_IN_CHALLENGE; reason = request.get_Reason(); break; case PKT_ACCESS_CHALLENGE: always = IAS_INCLUDE_IN_CHALLENGE; never = IAS_RECVD_FROM_CLIENT | IAS_INCLUDE_IN_ACCEPT | IAS_INCLUDE_IN_REJECT; break; case PKT_ACCOUNTING_REQUEST: always = IAS_INCLUDE_IN_ACCEPT | IAS_RECVD_FROM_CLIENT | IAS_RECVD_FROM_PROTOCOL; never = IAS_INCLUDE_IN_RESPONSE; reason = request.get_Reason(); break; } ////////// // Filter the attributes based on flags. ////////// for (curPos = firstPos; curPos != lastPos; ) { // We can release here since the request still holds a reference. IASAttributeRelease(curPos->pAttribute); if (!(curPos->pAttribute->dwFlags & always) && (curPos->pAttribute->dwFlags & never ) && (curPos->pAttribute->dwId != RADIUS_ATTRIBUTE_CLASS)) { --lastPos; std::swap(lastPos->pAttribute, curPos->pAttribute); } else { ++curPos; } } ////////// // Add the Packet-Type pseudo-attribute. ////////// IASATTRIBUTE packetTypeAttr; packetTypeAttr.dwId = IAS_ATTRIBUTE_PACKET_TYPE; packetTypeAttr.dwFlags = (DWORD)-1; packetTypeAttr.Value.itType = IASTYPE_ENUM; packetTypeAttr.Value.Enumerator = packetType; lastPos->pAttribute = &packetTypeAttr; ++lastPos; ////////// // Add the Reason-Code pseudo-attribute. ////////// IASATTRIBUTE reasonCodeAttr; reasonCodeAttr.dwId = IAS_ATTRIBUTE_REASON_CODE; reasonCodeAttr.dwFlags = (DWORD)-1; reasonCodeAttr.Value.itType = IASTYPE_INTEGER; reasonCodeAttr.Value.Integer = reason; lastPos->pAttribute = &reasonCodeAttr; ++lastPos; ////////// // Invoke the derived class. ////////// InsertRecord(context, request, localTime, firstPos, lastPos); } IASREQUESTSTATUS Accountant::onSyncRequest(IRequest* pRequest) throw () { try { Process(IASTL::IASRequest(pRequest)); } catch (...) { pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_NO_RECORD); return IAS_REQUEST_STATUS_ABORT; } return IAS_REQUEST_STATUS_CONTINUE; } bool Accountant::IsInterimRecord(IAttributesRaw* attrs) throw () { const DWORD accountingInterim = 3; IASATTRIBUTE* attr = IASPeekAttribute( attrs, RADIUS_ATTRIBUTE_ACCT_STATUS_TYPE, IASTYPE_ENUM ); return (attr != 0) && (attr->Value.Enumerator == accountingInterim); }