/********************************************************************/ /** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INCL_RASAUTHATTRIBUTES #define INCL_HOSTWIRE #include #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 ); }