/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corporation // // SYNOPSIS // // Defines the class RadiusExtension // /////////////////////////////////////////////////////////////////////////////// #include "Precompiled.h" #include "ias.h" #include "new" #include "extension.h" RadiusExtension::RadiusExtension() throw () : name(0), module(0), initialized(false), RadiusExtensionInit(0), RadiusExtensionTerm(0), RadiusExtensionProcess(0), RadiusExtensionProcessEx(0), RadiusExtensionFreeAttributes(0), RadiusExtensionProcess2(0) { } RadiusExtension::~RadiusExtension() throw () { if (initialized && (RadiusExtensionTerm != 0)) { RadiusExtensionTerm(); } if (module != 0) { FreeLibrary(module); } delete[] name; } // Load the extension DLL. DWORD RadiusExtension::Load(const wchar_t* dllPath) throw () { IASTracePrintf("Loading extension %S", dllPath); // Save the name of the module. const wchar_t* fileName = ExtractFileNameFromPath(dllPath); name = new (std::nothrow) wchar_t[wcslen(fileName) + 1]; if (name == 0) { return ERROR_NOT_ENOUGH_MEMORY; } wcscpy(name, fileName); // Load the extension DLL. module = LoadLibraryW(dllPath); if (module == 0) { DWORD error = GetLastError(); IASTraceFailure("LoadLibraryW", error); return error; } // Look-up the entry points. RadiusExtensionInit = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_INIT ) ); RadiusExtensionTerm = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_TERM ) ); RadiusExtensionProcess = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_PROCESS ) ); RadiusExtensionProcessEx = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_PROCESS_EX ) ); RadiusExtensionFreeAttributes = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_FREE_ATTRIBUTES ) ); RadiusExtensionProcess2 = reinterpret_cast( GetProcAddress( module, RADIUS_EXTENSION_PROCESS2 ) ); // Validate the entry points. if ((RadiusExtensionProcess == 0) && (RadiusExtensionProcessEx == 0) && (RadiusExtensionProcess2 == 0)) { IASTraceString( "Either RadiusExtensionProcess, RadiusExtensionProcessEx, or " "RadiusExtensionProcess2 must be defined." ); return ERROR_PROC_NOT_FOUND; } if ((RadiusExtensionProcessEx != 0) && (RadiusExtensionFreeAttributes == 0)) { IASTraceString( "RadiusExtensionFreeAttributes must be defined if " "RadiusExtensionProcessEx is defined." ); return ERROR_PROC_NOT_FOUND; } // Initialize the DLL. if (RadiusExtensionInit != 0) { DWORD error = RadiusExtensionInit(); if (error != NO_ERROR) { IASTraceFailure("RadiusExtensionInit", error); return error; } } initialized = true; return NO_ERROR; } DWORD RadiusExtension::Process( RADIUS_EXTENSION_CONTROL_BLOCK* ecb ) const throw () { IASTracePrintf("Invoking extension %S", name); DWORD retval; if (RadiusExtensionProcess2 != 0) { retval = RadiusExtensionProcess2(ecb); IASTracePrintf("RadiusExtensionProcess2 returned %lu", retval); return retval; } // Determine the allowed actions and attributes for an old-style extension. unsigned allowedActions = 0; RADIUS_ATTRIBUTE* inAttrs = 0; switch (MAKELONG(ecb->repPoint, ecb->rcResponseType)) { case MAKELONG(repAuthentication, rcUnknown): { allowedActions = (acceptAllowed | rejectAllowed); inAttrs = CreateExtensionAttributes(ecb); break; } case MAKELONG(repAuthorization, rcAccessAccept): { allowedActions = rejectAllowed; inAttrs = CreateAuthorizationAttributes(ecb); break; } case MAKELONG(repAuthentication, rcAccountingResponse): { inAttrs = CreateExtensionAttributes(ecb); break; } case MAKELONG(repAuthorization, rcAccountingResponse): { inAttrs = CreateAuthorizationAttributes(ecb); break; } case MAKELONG(repAuthorization, rcUnknown): { ecb->SetResponseType(ecb, rcAccountingResponse); inAttrs = CreateAuthorizationAttributes(ecb); break; } default: { // This is one of the combinations that doesn't get sent to old-style // extensions. // old-style Authorization dlls are called only when the return // type is known return NO_ERROR; } } if (inAttrs == 0) { return ERROR_NOT_ENOUGH_MEMORY; } RADIUS_ATTRIBUTE* outAttrs = 0; RADIUS_ACTION action = raContinue; RADIUS_ACTION* pAction = (allowedActions != 0) ? &action : 0; if (RadiusExtensionProcessEx != 0) { retval = RadiusExtensionProcessEx(inAttrs, &outAttrs, pAction); IASTracePrintf("RadiusExtensionProcessEx returned %lu", retval); } else { retval = RadiusExtensionProcess(inAttrs, pAction); IASTracePrintf("RadiusExtensionProcess returned %lu", retval); } delete[] inAttrs; if (retval != NO_ERROR) { return retval; } // Process the action code. RADIUS_CODE outAttrDst; if ((action == raAccept) && ((allowedActions & acceptAllowed) != 0)) { ecb->SetResponseType(ecb, rcAccessAccept); outAttrDst = rcAccessAccept; } else if ((action == raReject) && ((allowedActions & rejectAllowed) != 0)) { ecb->SetResponseType(ecb, rcAccessReject); outAttrDst = rcAccessReject; } else { outAttrDst = rcAccessAccept; } // Insert the returned attributes. if (outAttrs != 0) { RADIUS_ATTRIBUTE_ARRAY* array = ecb->GetResponse(ecb, outAttrDst); for (RADIUS_ATTRIBUTE* outAttrsIter = outAttrs; outAttrsIter->dwAttrType != ratMinimum; ++outAttrsIter) { retval = array->Add(array, outAttrsIter); if (retval != NO_ERROR) { break; } } RadiusExtensionFreeAttributes(outAttrs); } return retval; } RADIUS_ATTRIBUTE* RadiusExtension::CreateExtensionAttributes( RADIUS_EXTENSION_CONTROL_BLOCK* ecb ) throw () { // ExtensionDLLs just get incoming attributes. RADIUS_ATTRIBUTE_ARRAY* request = ecb->GetRequest(ecb); size_t numRequestAttrs = request->GetSize(request); // Allocate extra space for ratCode and the array terminator. size_t numAttrs = numRequestAttrs + 2; RADIUS_ATTRIBUTE* attrs = new (std::nothrow) RADIUS_ATTRIBUTE[numAttrs]; if (attrs == 0) { return 0; } RADIUS_ATTRIBUTE* dst = attrs; // New style extensions don't use ratCode, so we have to add it ourself. dst->dwAttrType = ratCode; dst->fDataType = rdtInteger; dst->cbDataLength = sizeof(DWORD); dst->dwValue = ecb->rcRequestType; ++dst; // Now add the rest of the incoming attributes. for (size_t i = 0; i < numRequestAttrs; ++i) { *dst = *(request->AttributeAt(request, i)); ++dst; } // Finally, add the array terminator. dst->dwAttrType = ratMinimum; return attrs; } RADIUS_ATTRIBUTE* RadiusExtension::CreateAuthorizationAttributes( RADIUS_EXTENSION_CONTROL_BLOCK* ecb ) throw () { // AuthorizationDLLs get internal attributes from the request ... RADIUS_ATTRIBUTE_ARRAY* request = ecb->GetRequest(ecb); // We're not going to use all the request attributes, but it's easier to // just allocate the extra space, rather than looping through the attributes // to determine how many we will really use. size_t numRequestAttrs = request->GetSize(request); // ... and any outgoing attributes. RADIUS_ATTRIBUTE_ARRAY* response = ecb->GetResponse( ecb, ecb->rcResponseType ); // passed a valid type, shoude get an array back _ASSERT(response); size_t numResponseAttrs = response->GetSize(response); // Save space for ratCode and the array terminator. size_t numAttrs = numRequestAttrs + numResponseAttrs + 2; RADIUS_ATTRIBUTE* attrs = new (std::nothrow) RADIUS_ATTRIBUTE[numAttrs]; if (attrs == 0) { return 0; } RADIUS_ATTRIBUTE* dst = attrs; // New style extensions don't use ratCode, so we have to add it ourself. dst->dwAttrType = ratCode; dst->fDataType = rdtInteger; dst->cbDataLength = sizeof(DWORD); dst->dwValue = ecb->rcResponseType; ++dst; // Add internal attributes from the request. for (size_t i = 0; i < numRequestAttrs; ++i) { const RADIUS_ATTRIBUTE* attr = request->AttributeAt(request, i); if (attr->dwAttrType > 255) { *dst = *attr; ++dst; } } // Add response attributes. for (size_t i = 0; i < numResponseAttrs; ++i) { *dst = *(response->AttributeAt(response, i)); ++dst; } // Finally, add the array terminator. dst->dwAttrType = ratMinimum; return attrs; } const wchar_t* ExtractFileNameFromPath(const wchar_t* path) throw () { const wchar_t* lastSlash = wcsrchr(path, L'\\'); return (lastSlash == 0) ? path : (lastSlash + 1); }