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