|
|
/********************************************************************/ /** Copyright(c) 1985-1998 Microsoft Corporation. **/ /********************************************************************/
//***
//
// Filename: radsrvrs.c
//
// Description: Routines to manipulate the radius server list
//
// History: Feb 11,1998 NarenG Created original version.
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <ntmsv1_0.h>
#include <windows.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <lmaccess.h>
#include <raserror.h>
#include <string.h>
#include <rasauth.h>
#include <stdlib.h>
#include <stdio.h>
#include <rtutils.h>
#include <mprlog.h>
#include <mprerror.h>
#define INCL_RASAUTHATTRIBUTES
#define INCL_HOSTWIRE
#include <ppputil.h>
#include "md5.h"
#include "radclnt.h"
#include "rasman.h"
#define STRSAFE_NO_DEPRECATE
#include "strsafe.h"
//**
//
// Call: InitializeRadiusServerList
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
VOID InitializeRadiusServerList( IN BOOL fAuthentication ) { if ( fAuthentication ) { if ( g_AuthServerListHead.Flink == NULL ) { InitializeListHead( &g_AuthServerListHead );
InitializeCriticalSection( &g_csAuth ); } } else { if ( g_AcctServerListHead.Flink == NULL ) { InitializeListHead( &g_AcctServerListHead );
InitializeCriticalSection( &g_csAcct ); } }
g_pszCurrentServer = NULL; g_pszCurrentAcctServer = NULL; }
//**
//
// Call: FreeRadiusServerList
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
VOID FreeRadiusServerList( IN BOOL fAuthentication ) { RADIUSSERVER * pServer; CRITICAL_SECTION * pcs; LIST_ENTRY * pListHead;
if ( fAuthentication ) { pcs = &g_csAuth; pListHead = &g_AuthServerListHead; } else { pcs = &g_csAcct; pListHead = &g_AcctServerListHead; }
EnterCriticalSection( pcs );
if ( pListHead->Flink != NULL ) { //
// free all items in linked list
//
while( !IsListEmpty( pListHead ) ) { pServer = (RADIUSSERVER *)RemoveHeadList( pListHead );
if ( !fAuthentication ) { //
// Notify Accounting server of NAS going down
//
NotifyServer( FALSE, pServer ); }
LocalFree( pServer ); } }
LeaveCriticalSection( pcs ); DeleteCriticalSection( pcs );
if ( fAuthentication ) { g_AuthServerListHead.Flink = NULL; g_AuthServerListHead.Blink = NULL; } else { g_AcctServerListHead.Flink = NULL; g_AcctServerListHead.Blink = NULL; }
if(NULL != g_pszCurrentServer) { LocalFree(g_pszCurrentServer); g_pszCurrentServer = NULL; }
if(NULL != g_pszCurrentAcctServer) { LocalFree(g_pszCurrentAcctServer); g_pszCurrentAcctServer = NULL; }
}
//**
//
// Call: RetrievePrivateData
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
DWORD RetrievePrivateData( IN WCHAR *pwszServerName, OUT WCHAR *pwszSecret, IN DWORD cbSecretSize ) { LSA_HANDLE hLSA = NULL; NTSTATUS ntStatus; LSA_OBJECT_ATTRIBUTES objectAttributes; LSA_UNICODE_STRING *pLSAPrivData; LSA_UNICODE_STRING LSAPrivDataDesc; WCHAR wszPrivData[MAX_PATH+1]; WCHAR wszPrivDataDesc[MAX_PATH+1]; WCHAR *pwszPrefix = L"RADIUSServer."; DWORD dwPrefixLen = wcslen(pwszPrefix);
InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL);
ntStatus = LsaOpenPolicy(NULL, &objectAttributes, POLICY_ALL_ACCESS, &hLSA);
if ( !NT_SUCCESS( ntStatus) ) { return( RtlNtStatusToDosError( ntStatus ) ); }
wcscpy(wszPrivDataDesc, pwszPrefix); wcsncat(wszPrivDataDesc, pwszServerName, MAX_PATH-dwPrefixLen);
LSAPrivDataDesc.Length = (wcslen(wszPrivDataDesc) + 1) * sizeof(WCHAR); LSAPrivDataDesc.MaximumLength = sizeof(wszPrivDataDesc); LSAPrivDataDesc.Buffer = wszPrivDataDesc;
ntStatus = LsaRetrievePrivateData(hLSA, &LSAPrivDataDesc, &pLSAPrivData);
if ( !NT_SUCCESS( ntStatus ) ) { LsaClose(hLSA); return( RtlNtStatusToDosError( ntStatus ) ); } else { if ( pLSAPrivData ) { ZeroMemory(pwszSecret, cbSecretSize); CopyMemory(pwszSecret, pLSAPrivData->Buffer, (pLSAPrivData->Length > cbSecretSize) ? cbSecretSize : pLSAPrivData->Length);
LsaFreeMemory(pLSAPrivData);
LsaClose(hLSA);
} else {
LsaClose(hLSA);
//
// API succeeded but did not return any private data
//
if ( ntStatus ) { return ( RtlNtStatusToDosError(ntStatus) ); } else return ERROR_INVALID_DATA; } }
return( NO_ERROR ); }
//**
//
// Call: LoadRadiusServers
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
DWORD LoadRadiusServers( IN BOOL fAuthenticationServers ) { HKEY hKeyServers = NULL; HKEY hKeyServer = NULL; DWORD dwErrorCode; BOOL fValidServerFound = FALSE; do { DWORD dwKeyIndex, cbKeyServer, cbValue, dwType; CHAR szNASIPAddress[20]; SHORT sPort; WCHAR wszKeyServer[MAX_PATH+1]; CHAR szName[MAX_PATH+1]; RADIUSSERVER RadiusServer;
dwErrorCode = RegOpenKeyEx( HKEY_LOCAL_MACHINE, ( fAuthenticationServers ) ? PSZAUTHRADIUSSERVERS : PSZACCTRADIUSSERVERS, 0, KEY_READ, &hKeyServers );
if ( dwErrorCode != NO_ERROR ) { break; }
//
// Get the retry value
//
cbValue = sizeof( DWORD );
dwErrorCode = RegQueryValueEx( hKeyServers, PSZRETRIES, NULL, NULL, ( fAuthenticationServers ) ? (PBYTE)&g_cAuthRetries : (PBYTE)&g_cAcctRetries, &cbValue );
if ( dwErrorCode != NO_ERROR ) { dwErrorCode = NO_ERROR;
if ( fAuthenticationServers ) { g_cAuthRetries = 2; } else { g_cAcctRetries = 2; } }
dwKeyIndex = 0; cbKeyServer = sizeof(wszKeyServer)/sizeof(TCHAR);
while( RegEnumKeyEx( hKeyServers, dwKeyIndex, wszKeyServer, &cbKeyServer, NULL, NULL, NULL, NULL ) == NO_ERROR ) { dwErrorCode = RegOpenKeyEx( hKeyServers, wszKeyServer, 0, KEY_READ, &hKeyServer );
if ( dwErrorCode != NO_ERROR ) { break; }
ZeroMemory( &RadiusServer, sizeof( RadiusServer ) ); wcscpy( RadiusServer.wszName, wszKeyServer );
RadiusServer.Timeout.tv_usec = 0;
cbValue = sizeof( RadiusServer.Timeout.tv_sec );
dwErrorCode = RegQueryValueEx( hKeyServer, PSZTIMEOUT, NULL, NULL, (PBYTE)&RadiusServer.Timeout.tv_sec, &cbValue );
if ( dwErrorCode != NO_ERROR ) { RadiusServer.Timeout.tv_sec = DEFTIMEOUT; }
//
// Secret Value is required
//
dwErrorCode = RetrievePrivateData( RadiusServer.wszName, RadiusServer.wszSecret, sizeof(WCHAR) * (MAX_PATH + 1));
if ( dwErrorCode != NO_ERROR ) { break; }
RadiusServer.szSecret[0] = 0;
WideCharToMultiByte( CP_ACP, 0, RadiusServer.wszSecret, -1, RadiusServer.szSecret, MAX_PATH, NULL, NULL );
RadiusServer.cbSecret = lstrlenA(RadiusServer.szSecret);
if ( fAuthenticationServers ) { //
// Get the SendSignature value
//
cbValue = sizeof( BOOL );
dwErrorCode = RegQueryValueEx( hKeyServer, PSZSENDSIGNATURE, NULL, NULL, (PBYTE)&RadiusServer.fSendSignature, &cbValue );
if ( dwErrorCode != NO_ERROR ) { RadiusServer.fSendSignature = FALSE; }
//
// read in port numbers
//
cbValue = sizeof( RadiusServer.AuthPort );
dwErrorCode = RegQueryValueEx( hKeyServer, PSZAUTHPORT, NULL, NULL, (PBYTE)&RadiusServer.AuthPort, &cbValue ); if ( dwErrorCode != NO_ERROR ) { RadiusServer.AuthPort = DEFAUTHPORT; }
sPort = (SHORT)RadiusServer.AuthPort; } else { cbValue = sizeof(RadiusServer.AcctPort);
dwErrorCode = RegQueryValueEx( hKeyServer, PSZACCTPORT, NULL, NULL, (PBYTE)&RadiusServer.AcctPort, &cbValue );
if ( dwErrorCode != NO_ERROR ) { RadiusServer.AcctPort = DEFACCTPORT; }
sPort = (SHORT)RadiusServer.AcctPort;
cbValue = sizeof( RadiusServer.fAccountingOnOff );
dwErrorCode = RegQueryValueEx( hKeyServer, PSZENABLEACCTONOFF, NULL, NULL, (PBYTE)&RadiusServer.fAccountingOnOff, &cbValue );
if ( dwErrorCode != NO_ERROR ) { RadiusServer.fAccountingOnOff = TRUE; } }
cbValue = sizeof( RadiusServer.cScore );
dwErrorCode = RegQueryValueEx( hKeyServer, PSZSCORE, NULL, NULL, (PBYTE)&RadiusServer.cScore, &cbValue );
if ( dwErrorCode != NO_ERROR ) { RadiusServer.cScore = MAXSCORE; }
//
// See if we need to bind to a particular IP address. This is
// useful if there are multiple NICs on the RAS server.
//
cbValue = sizeof( szNASIPAddress );
dwErrorCode = RegQueryValueExA( hKeyServer, PSZNASIPADDRESS, NULL, &dwType, (PBYTE)szNASIPAddress, &cbValue );
if ( ( dwErrorCode != NO_ERROR ) || ( dwType != REG_SZ ) ) { RadiusServer.nboNASIPAddress = INADDR_NONE;
dwErrorCode = NO_ERROR; } else { RadiusServer.nboNASIPAddress = inet_addr(szNASIPAddress); RadiusServer.NASIPAddress.sin_family = AF_INET; RadiusServer.NASIPAddress.sin_port = 0; RadiusServer.NASIPAddress.sin_addr.S_un.S_addr = RadiusServer.nboNASIPAddress; }
RadiusServer.nboBestIf = INADDR_NONE;
//
// Convert name to ip address.
//
szName[0] = 0;
WideCharToMultiByte( CP_ACP, 0, RadiusServer.wszName, -1, szName, MAX_PATH, NULL, NULL ); if ( inet_addr( szName ) == INADDR_NONE ) { //
// resolve name
//
struct hostent * phe = gethostbyname( szName );
if ( phe != NULL ) { //
// host could have multiple addresses
//
DWORD iAddress = 0; while( phe->h_addr_list[iAddress] != NULL ) { RadiusServer.IPAddress.sin_family = AF_INET; RadiusServer.IPAddress.sin_port = htons(sPort); RadiusServer.IPAddress.sin_addr.S_un.S_addr = *((PDWORD) phe->h_addr_list[iAddress]);
if ( AddRadiusServerToList( &RadiusServer , fAuthenticationServers ) == NO_ERROR ) { fValidServerFound = TRUE; } iAddress++; } } else { LPWSTR lpwsRadiusServerName = RadiusServer.wszName;
RadiusLogWarning( ROUTERLOG_RADIUS_SERVER_NAME, 1, &lpwsRadiusServerName ); } } else { //
// use specified ip address
//
RadiusServer.IPAddress.sin_family = AF_INET; RadiusServer.IPAddress.sin_port = htons(sPort); RadiusServer.IPAddress.sin_addr.S_un.S_addr = inet_addr(szName);
if ( AddRadiusServerToList(&RadiusServer, fAuthenticationServers) == NO_ERROR) { fValidServerFound = TRUE; } }
RegCloseKey( hKeyServer ); hKeyServer = NULL; dwKeyIndex ++; cbKeyServer = sizeof(wszKeyServer); }
} while( FALSE );
RegCloseKey( hKeyServers ); RegCloseKey( hKeyServer );
//
// if no servers entries are found in registry return error code.
//
if ( ( !fValidServerFound ) && ( dwErrorCode == NO_ERROR ) ) { dwErrorCode = ERROR_NO_RADIUS_SERVERS; }
return( dwErrorCode ); }
//**
//
// Call: AddRadiusServerToList
//
// Returns: NO_ERROR - Success, Server Node added successfully
// Non-zero returns - Failure,unsuccessfully in adding server node.
//
// Description: Adds a RADIUS server node into the linked list of avialable
// servers.
//
// INPUT:
// pRadiusServer - struct defining attributes for RADIUS server
//
DWORD AddRadiusServerToList( IN RADIUSSERVER * pRadiusServer, IN BOOL fAuthentication ) { RADIUSSERVER * pNewServer; DWORD dwRetCode = NO_ERROR; CRITICAL_SECTION * pcs; LIST_ENTRY * pListHead; BOOL fServerFound = FALSE; if ( fAuthentication ) { pcs = &g_csAuth; } else { pcs = &g_csAcct; }
EnterCriticalSection( pcs );
if ( fAuthentication ) { pListHead = &g_AuthServerListHead; } else { pListHead = &g_AcctServerListHead; }
//
// First check to see if this server already exists in the list
//
if ( !IsListEmpty( pListHead ) ) { RADIUSSERVER * pServer;
for ( pServer = (RADIUSSERVER *)pListHead->Flink; pServer != (RADIUSSERVER *)pListHead; pServer = (RADIUSSERVER *)(pServer->ListEntry.Flink) ) { if ( _wcsicmp( pServer->wszName, pRadiusServer->wszName ) == 0 ) { pServer->fDelete = FALSE;
fServerFound = TRUE;
break; } } }
//
// If the server doesn't exist in the list, add it.
//
if ( !fServerFound ) { //
// Allocate space for node
//
pNewServer = (RADIUSSERVER *)LocalAlloc( LPTR, sizeof( RADIUSSERVER ) );
if ( pNewServer == NULL ) { dwRetCode = GetLastError(); } else { //
// Copy server data
//
*pNewServer = *pRadiusServer;
//
// Add node to linked list
//
InsertHeadList( pListHead, (LIST_ENTRY*)pNewServer );
pNewServer->fDelete = FALSE; } } else { pNewServer = pRadiusServer; }
//
// Notify it if this is an accounting server and accounting is turned on.
//
if ( dwRetCode == NO_ERROR ) { if ( !fAuthentication ) { if ( !NotifyServer( TRUE, pNewServer ) ) { dwRetCode = ERROR_NO_RADIUS_SERVERS; } } }
LeaveCriticalSection( pcs );
return( dwRetCode ); }
//**
//
// Call: ChooseRadiusServer
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description: Selects a RADIUS server to send requests to based on the
// servers with the highest score. If multiple server have the
// same score they are selected in a roundrobin fashion.
//
// OUTPUT:
// RADIUSSERVER *pServer - pointer to a struct defining
// the server.
//
RADIUSSERVER * ChooseRadiusServer( IN RADIUSSERVER * pRadiusServer, IN BOOL fAccounting, IN LONG lPacketID ) { RADIUSSERVER * pServer = NULL; CRITICAL_SECTION * pcs; LIST_ENTRY * pListHead; RADIUSSERVER * pCurrentServer; BOOL fAllScoresEqual = TRUE; DWORD dwNumServers = 0; LIST_ENTRY * ple; WCHAR ** ppszCurrentServer; RADIUSSERVER * pTempServer = NULL; if ( !fAccounting ) { pcs = &g_csAuth; ppszCurrentServer = &g_pszCurrentServer; } else { pcs = &g_csAcct; ppszCurrentServer = &g_pszCurrentAcctServer; }
EnterCriticalSection( pcs );
if ( !fAccounting ) { pListHead = &g_AuthServerListHead; } else { pListHead = &g_AcctServerListHead; }
if ( IsListEmpty( pListHead ) ) { LeaveCriticalSection( pcs );
return( NULL ); }
pCurrentServer = (RADIUSSERVER *)(pListHead->Flink);
//
// Find server with highest score
//
for ( ple = pListHead->Flink; ple != pListHead; ple = ple->Flink) { pServer = CONTAINING_RECORD(ple, RADIUSSERVER, ListEntry); if( pCurrentServer->cScore != pServer->cScore) { fAllScoresEqual = FALSE; } if ( pCurrentServer->cScore < pServer->cScore ) { pCurrentServer = pServer; }
if( (NULL == pTempServer) && (NULL != *ppszCurrentServer) && (0 == _wcsicmp(*ppszCurrentServer, pServer->wszName))) { pTempServer = pServer; }
dwNumServers += 1; }
if( (fAllScoresEqual) && (dwNumServers > 1)) { //
// If all servers have the same score round-robin. We ignore when
// list has only one server.
//
if(NULL != pTempServer) { pCurrentServer = (RADIUSSERVER *) pTempServer->ListEntry.Flink; }
if(pCurrentServer == (RADIUSSERVER *)pListHead) { pCurrentServer = (RADIUSSERVER *) pCurrentServer->ListEntry.Flink; }
RADIUS_TRACE("AllScoresEqual."); }
pServer = pCurrentServer;
//
// Make a copy of the values & pass them back to the caller.
// Increment unique packet id counter only if its an Accounting packet
// or not a retry packet. If its an Accounting packet and a retry packet,
// then we update AcctDelayTime; so Identifier must change.
//
if ( fAccounting || ( pServer->lPacketID != lPacketID ) ) { pServer->bIdentifier++; } pServer->lPacketID = lPacketID;
//
// Retrieve the secret from lsa - it might have changed. In the absence
// of a good notification mechanism from mmc, this is the best we can
// do to not require a reboot of ras service when the secret is
// changed.
//
if(NO_ERROR == RetrievePrivateData( pServer->wszName, pServer->wszSecret, sizeof(WCHAR) * (MAX_PATH + 1))) {
pServer->szSecret[0] = 0;
WideCharToMultiByte( CP_ACP, 0, pServer->wszSecret, -1, pServer->szSecret, MAX_PATH, NULL, NULL );
pServer->cbSecret = lstrlenA(pServer->szSecret);
RADIUS_TRACE("ChooseRadiusServer: updated secret"); } *pRadiusServer = *pServer;
pServer = pRadiusServer;
if(pServer->nboNASIPAddress == INADDR_NONE) { DWORD retcode; DWORD dwMask; //
// Get the best interface if there is no nboNASIPAddress
// configured for this server
//
retcode = RasGetBestInterface( pServer->IPAddress.sin_addr.S_un.S_addr, &pServer->nboBestIf, &dwMask);
RADIUS_TRACE2("ChooseRadiusServer: rc = 0x%x, BestIf=0x%x", retcode, pServer->nboBestIf); }
if( (NULL == *ppszCurrentServer) || (0 != _wcsicmp(*ppszCurrentServer,pServer->wszName))) {
LPWSTR auditstrp[1]; if(NULL == *ppszCurrentServer) { *ppszCurrentServer = LocalAlloc( LPTR, (MAX_PATH+1) * sizeof(WCHAR)); }
if(NULL != *ppszCurrentServer) { if(!fAccounting) { //
// This means radius server changed or we are choosing the
// server for the first time. In both these cases log an event.
//
auditstrp[0] = pServer->wszName;
RadiusLogInformation( ROUTERLOG_RADIUS_SERVER_CHANGED, 1, auditstrp); }
wcscpy(*ppszCurrentServer,pServer->wszName);
} }
LeaveCriticalSection( pcs );
RADIUS_TRACE2("Choosing RADIUS server %ws with score %d", pServer->wszName, pServer->cScore);
return( pServer ); }
//**
//
// Call: GetPointerToServer
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
RADIUSSERVER * GetPointerToServer( IN BOOL fAuthentication, IN LPWSTR lpwsName ) { RADIUSSERVER * pServer = NULL; CRITICAL_SECTION * pcs; LIST_ENTRY * pListHead; BOOL fServerFound = FALSE;
if ( fAuthentication ) { pcs = &g_csAuth; } else { pcs = &g_csAcct; }
EnterCriticalSection( pcs );
if ( fAuthentication ) { pListHead = &g_AuthServerListHead; } else { pListHead = &g_AcctServerListHead; }
if ( IsListEmpty( pListHead ) ) { LeaveCriticalSection( pcs );
return( NULL ); }
for ( pServer = (RADIUSSERVER *)pListHead->Flink; pServer != (RADIUSSERVER *)pListHead; pServer = (RADIUSSERVER *)(pServer->ListEntry.Flink) ) { if ( _wcsicmp( pServer->wszName, lpwsName ) == 0 ) { fServerFound = TRUE; break; } }
LeaveCriticalSection( pcs );
if ( fServerFound ) { return( pServer ); } else { return( NULL ); } }
//**
//
// Call: ValidateRadiusServer
//
// Returns: None
//
// Description: Used to update the status of the RADIUS servers.
// All servers start with a score of MAXSCORE
// Every time a server responding the score is increased by
// INCSCORE to a max of MAXSCORE. Every time a server fails to
// respond the score is decreased by DECSCORE to a min of MINSCORE
// Servers with the highest score are selected in a roundrobin
// method for servers with equal score
//
// INPUT:
// fResponding - Indicates if the server is responding or not
//
VOID ValidateRadiusServer( IN RADIUSSERVER * pServer, IN BOOL fResponding, IN BOOL fAuthentication ) { RADIUSSERVER * pRadiusServer; CRITICAL_SECTION * pcs;
if ( fAuthentication ) { pcs = &g_csAuth; } else { pcs = &g_csAcct; }
EnterCriticalSection( pcs );
pRadiusServer = GetPointerToServer( fAuthentication, pServer->wszName );
if ( pRadiusServer != NULL ) { if ( fResponding ) { pRadiusServer->cScore=min(MAXSCORE,pRadiusServer->cScore+INCSCORE);
RADIUS_TRACE1("Incrementing score for RADIUS server %ws", pRadiusServer->wszName ); } else { pRadiusServer->cScore=max(MINSCORE,pRadiusServer->cScore-DECSCORE);
RADIUS_TRACE1("Decrementing score for RADIUS server %ws", pRadiusServer->wszName ); } }
LeaveCriticalSection( pcs ); }
//**
//
// Call: ReloadConfig
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description: Used to dynamically reload configuration information of the
// server lists.
DWORD ReloadConfig( IN BOOL fAuthentication ) { DWORD dwError = NO_ERROR; RADIUSSERVER * pServer = NULL; LIST_ENTRY * pListHead; CRITICAL_SECTION * pcs;
if ( fAuthentication ) { pcs = &g_csAuth; pListHead = &g_AuthServerListHead; } else { pcs = &g_csAcct; pListHead = &g_AcctServerListHead; }
EnterCriticalSection( pcs );
//
// First mark all servers as to be deleted
//
for ( pServer = (RADIUSSERVER *)pListHead->Flink; pServer != (RADIUSSERVER *)pListHead; pServer = (RADIUSSERVER *)(pServer->ListEntry.Flink) ) { pServer->fDelete = TRUE; }
//
// Now reload server list, don't return on error since we have to
// cleanup the list of deleted servers first.
//
dwError = LoadRadiusServers( fAuthentication );
//
// Now delete the ones that are to be removed
//
pServer = (RADIUSSERVER *)pListHead->Flink;
while( pServer != (RADIUSSERVER *)pListHead ) { if ( pServer->fDelete ) { RADIUSSERVER * pServerToBeDeleted = pServer;
pServer = (RADIUSSERVER *)(pServer->ListEntry.Flink);
RemoveEntryList( (LIST_ENTRY *)pServerToBeDeleted );
if ( !fAuthentication ) { NotifyServer( FALSE, pServerToBeDeleted ); }
LocalFree( pServerToBeDeleted ); } else { pServer = (RADIUSSERVER *)(pServer->ListEntry.Flink); } }
LeaveCriticalSection( pcs );
return( dwError ); }
//**
//
// Call: NotifyServer
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description: Notifies the specified RADIUS server of this device is starting
// up or shuting down by sending Accounting Start/Stop records.
// INPUT:
// fStart - TRUE - Accounting Start
// - FALSE - Accounting Stop
//
BOOL NotifyServer( IN BOOL fStart, IN RADIUSSERVER * pServer ) { SOCKET SockServer = INVALID_SOCKET; DWORD dwError = NO_ERROR; BOOL fRadiusServerResponded = FALSE;
do { RADIUS_PACKETHEADER UNALIGNED *pSendHeader; RADIUS_PACKETHEADER UNALIGNED *pRecvHeader; BYTE szSendBuffer[MAXBUFFERSIZE]; BYTE szRecvBuffer[MAXBUFFERSIZE]; BYTE UNALIGNED *prgBuffer; RADIUS_ATTRIBUTE UNALIGNED *pAttribute; fd_set fdsSocketRead; DWORD cRetries; INT AttrLength; RAS_AUTH_ATTRIBUTE * pServerAttribute; //
// send start/stop records only to servers that have
// accounting On/Off set.
//
if ( !pServer->fAccountingOnOff ) { fRadiusServerResponded = TRUE; break; }
pSendHeader = (PRADIUS_PACKETHEADER) szSendBuffer; pSendHeader->bCode = ptAccountingRequest; pSendHeader->bIdentifier = pServer->bIdentifier; pSendHeader->wLength = sizeof(RADIUS_PACKETHEADER); ZeroMemory( pSendHeader->rgAuthenticator, sizeof(pSendHeader->rgAuthenticator));
//
// set attribute for accounting On/Off
//
pAttribute = (RADIUS_ATTRIBUTE *) (pSendHeader + 1); pAttribute->bType = ptAcctStatusType; pAttribute->bLength = sizeof(RADIUS_ATTRIBUTE) + sizeof(DWORD); *((DWORD UNALIGNED *) (pAttribute + 1)) = htonl(fStart == TRUE ? atAccountingOn : atAccountingOff);
pSendHeader->wLength += pAttribute->bLength;
//
// Set NAS IP address or Identifier attribute
//
pAttribute = (RADIUS_ATTRIBUTE *)( (PBYTE)pAttribute + pAttribute->bLength );
pServerAttribute = RasAuthAttributeGet( raatNASIPAddress, g_pServerAttributes ); if ( pServerAttribute != NULL ) { pAttribute->bType = (BYTE)(pServerAttribute->raaType);
if ( pServer->nboNASIPAddress == INADDR_NONE ) { HostToWireFormat32( PtrToUlong(pServerAttribute->Value), (BYTE *)(pAttribute + 1) ); } else { CopyMemory( (BYTE*)(pAttribute + 1), (BYTE*)&(pServer->nboNASIPAddress), sizeof( DWORD ) ); }
pAttribute->bLength = (BYTE) (sizeof( RADIUS_ATTRIBUTE ) + pServerAttribute->dwLength);
pSendHeader->wLength += pAttribute->bLength;
pAttribute = (RADIUS_ATTRIBUTE *)( (PBYTE)pAttribute + pAttribute->bLength ); } else { pServerAttribute = RasAuthAttributeGet( raatNASIdentifier, g_pServerAttributes ); if ( pServerAttribute != NULL ) { pAttribute->bType = (BYTE)(pServerAttribute->raaType);
CopyMemory( (BYTE *)(pAttribute + 1), (BYTE *)(pServerAttribute->Value), pServerAttribute->dwLength );
pAttribute->bLength = (BYTE) (sizeof( RADIUS_ATTRIBUTE ) + pServerAttribute->dwLength);
pSendHeader->wLength += pAttribute->bLength;
pAttribute = (RADIUS_ATTRIBUTE *)( (PBYTE)pAttribute + pAttribute->bLength ); } }
//
// Set Account session Id
//
pServerAttribute = RasAuthAttributeGet( raatAcctSessionId, g_pServerAttributes ); if ( pServerAttribute != NULL ) { pAttribute->bType = (BYTE)(pServerAttribute->raaType);
CopyMemory( (BYTE *)(pAttribute + 1), (BYTE *)(pServerAttribute->Value), pServerAttribute->dwLength );
pAttribute->bLength = (BYTE)(sizeof( RADIUS_ATTRIBUTE ) + pServerAttribute->dwLength);
pSendHeader->wLength += pAttribute->bLength; }
//
// convert to network order
//
pSendHeader->wLength = htons(pSendHeader->wLength);
//
// Set encryption block
//
{ MD5_CTX MD5c;
pServer->IPAddress.sin_port = htons((SHORT)(pServer->AcctPort)); ZeroMemory( pSendHeader->rgAuthenticator, sizeof(pSendHeader->rgAuthenticator));
MD5Init(&MD5c); MD5Update(&MD5c, szSendBuffer, ntohs(pSendHeader->wLength)); MD5Update(&MD5c, (PBYTE)pServer->szSecret, pServer->cbSecret); MD5Final(&MD5c); CopyMemory( pSendHeader->rgAuthenticator, MD5c.digest, sizeof(pSendHeader->rgAuthenticator)); } //
// Create a Datagram socket
//
SockServer = socket(AF_INET, SOCK_DGRAM, 0);
if (SockServer == INVALID_SOCKET) { break; }
if ( pServer->nboNASIPAddress != INADDR_NONE ) { if ( bind( SockServer, (PSOCKADDR)&pServer->NASIPAddress, sizeof(pServer->NASIPAddress) ) == SOCKET_ERROR ) { break; } }
if ( connect( SockServer, (PSOCKADDR) &(pServer->IPAddress), sizeof(pServer->IPAddress)) == SOCKET_ERROR) { break; }
//
// Send packet if server doesn't respond within a give amount of
// time.
//
cRetries = g_cAcctRetries+1;
while( cRetries-- > 0 ) { if ( send( SockServer, (PCSTR) szSendBuffer, ntohs(pSendHeader->wLength), 0) == SOCKET_ERROR) { break; }
RADIUS_TRACE1("Sending Accounting request packet to server %ws", pServer->wszName ); TraceSendPacket(szSendBuffer, ntohs(pSendHeader->wLength));
FD_ZERO(&fdsSocketRead); FD_SET(SockServer, &fdsSocketRead);
if ( select( 0, &fdsSocketRead, NULL, NULL, ( pServer->Timeout.tv_sec == 0 ) ? NULL : &(pServer->Timeout) ) < 1 ) { if ( cRetries == 0 ) { LPWSTR lpwsRadiusServerName = pServer->wszName;
//
// Server didn't respond to any of the requests.
// time to quit asking
//
RADIUS_TRACE1( "Timeout:Radius server %ws did not respond", lpwsRadiusServerName );
if ( fStart ) { RadiusLogWarning( ROUTERLOG_RADIUS_SERVER_NO_RESPONSE, 1, &lpwsRadiusServerName ); }
dwError = ERROR_AUTH_SERVER_TIMEOUT;
break; } } else { //
// Response received
//
break; } }
if ( dwError != NO_ERROR ) { break; } AttrLength = recv( SockServer, (PSTR) szRecvBuffer, MAXBUFFERSIZE, 0 );
if ( AttrLength == SOCKET_ERROR ) { LPWSTR lpwsRadiusServerName = pServer->wszName;
//
// Server didn't respond to any of the requests.
// time to quit asking
//
RADIUS_TRACE1( "Timeout:Radius server %ws did not respond", lpwsRadiusServerName );
if ( fStart ) { RadiusLogWarning( ROUTERLOG_RADIUS_SERVER_NO_RESPONSE, 1, &lpwsRadiusServerName ); }
dwError = ERROR_AUTH_SERVER_TIMEOUT;
break; }
//
// Got a response from a RADIUS server.
//
fRadiusServerResponded = TRUE; pRecvHeader = (PRADIUS_PACKETHEADER) szRecvBuffer;
//
// Convert length from network order
//
pRecvHeader->wLength = ntohs(pRecvHeader->wLength);
//
// Ignore return packet
//
RADIUS_TRACE1("Response received from server %ws", pServer->wszName); TraceRecvPacket(szRecvBuffer, pRecvHeader->wLength );
} while( FALSE );
if ( SockServer != INVALID_SOCKET ) { closesocket( SockServer );
SockServer = INVALID_SOCKET; }
return( fRadiusServerResponded ); }
|