Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1494 lines
40 KiB

/********************************************************************/
/** 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 );
}