/********************************************************************/ /** Copyright(c) 1985-1998 Microsoft Corporation. **/ /********************************************************************/ //*** // // Filename: packconv.c // // Description: // // History: Feb 11,1998 NarenG Created original version. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INCL_RASAUTHATTRIBUTES #define INCL_HOSTWIRE #include #include "radclnt.h" #include "hmacmd5.h" #include "md5.h" #include "radclnt.h" //** // // Call: Router2Radius // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: Converts attribute array from RAS_AUTH_ATTRIBUTE to // RADIUS_ATTRIBUTE // INPUT: // prgRouter - array of attributes passed in from // the application // pRadiusServer - RADIUS server // descriptor(ip address, secret) // pHeader - RADIUS packet header // bSubCode - accounting sub codes. // OUTPUT: // prgRadius - array of attribtes that will be sent // to the RADIUS server. // pAttrLength - Length of the Radius packet. // DWORD Router2Radius( IN RAS_AUTH_ATTRIBUTE * prgRouter, OUT RADIUS_ATTRIBUTE UNALIGNED * prgRadius, IN RADIUSSERVER UNALIGNED * pRadiusServer, IN RADIUS_PACKETHEADER UNALIGNED * pHeader, IN BYTE bSubCode, IN DWORD dwRetryCount, OUT PBYTE * ppSignature, OUT DWORD * pAttrLength ) { DWORD dwError = NO_ERROR; BOOL fEAPMessage = FALSE; *pAttrLength = 0; *ppSignature = NULL; do { // // add the attribute for accounting records // switch( bSubCode ) { case atStart: case atStop: case atAccountingOn: case atAccountingOff: case atInterimUpdate: // // Add the accounting status type attribute // prgRadius->bType = ptAcctStatusType; prgRadius->bLength = sizeof(RADIUS_ATTRIBUTE) + sizeof(DWORD); (*pAttrLength) += prgRadius->bLength; prgRadius++; *((DWORD UNALIGNED *) prgRadius) = htonl(bSubCode); prgRadius = (RADIUS_ATTRIBUTE *)(((PBYTE) prgRadius)+sizeof(DWORD)); // // Add the accounting delay time attribute // prgRadius->bType = raatAcctDelayTime; prgRadius->bLength = sizeof(RADIUS_ATTRIBUTE) + sizeof(DWORD); (*pAttrLength) += prgRadius->bLength; prgRadius++; HostToWireFormat32( dwRetryCount * pRadiusServer->Timeout.tv_sec, (LPBYTE)(prgRadius) ); prgRadius = (RADIUS_ATTRIBUTE *)(((PBYTE) prgRadius)+sizeof(DWORD)); break; default: break; } while( prgRouter->raaType != raatMinimum ) { // // Copy attribute type & length // prgRadius->bType = (BYTE)(prgRouter->raaType); prgRadius->bLength = (BYTE)(prgRouter->dwLength); switch( prgRouter->raaType ) { case raatUserPassword: (*pAttrLength) += EncryptPassword( prgRouter, prgRadius, pRadiusServer, pHeader, bSubCode); break; case raatUserName: case raatMD5CHAPPassword: case raatFilterId: case raatReplyMessage: case raatCallbackNumber: case raatCallbackId: case raatFramedRoute: case raatState: case raatClass: case raatVendorSpecific: case raatCalledStationId: case raatCallingStationId: case raatNASIdentifier: case raatProxyState: case raatLoginLATService: case raatLoginLATNode: case raatLoginLATGroup: case raatFramedAppleTalkZone: case raatAcctSessionId: case raatAcctMultiSessionId: case raatMD5CHAPChallenge: case raatLoginLATPort: case raatTunnelClientEndpoint: case raatTunnelServerEndpoint: case raatARAPPassword: case raatARAPFeatures: case raatARAPSecurityData: case raatConnectInfo: case raatConfigurationToken: case raatSignature: case raatCertificateOID: CopyMemory( prgRadius+1, (PBYTE)prgRouter->Value, prgRadius->bLength); prgRadius->bLength += sizeof(RADIUS_ATTRIBUTE); (*pAttrLength) += prgRadius->bLength; break; case raatEAPMessage: { DWORD dwLength = prgRouter->dwLength; PBYTE pRouterEapMessage = (PBYTE)(prgRouter->Value); while( dwLength > 0 ) { if ( dwLength > 253 ) { CopyMemory( (PBYTE)(prgRadius+1), pRouterEapMessage, 253 ); prgRadius->bLength = 253; pRouterEapMessage += 253; dwLength -= 253; } else { CopyMemory( prgRadius+1, (PBYTE)pRouterEapMessage, dwLength ); prgRadius->bLength = (BYTE)dwLength; dwLength = 0; } prgRadius->bType = (BYTE)raatEAPMessage; prgRadius->bLength += sizeof(RADIUS_ATTRIBUTE); (*pAttrLength) += prgRadius->bLength; if ( dwLength > 0 ) { prgRadius = (PRADIUS_ATTRIBUTE) ((PBYTE) prgRadius + prgRadius->bLength); } } } fEAPMessage = TRUE; break; case raatNASPort: case raatServiceType: case raatFramedProtocol: case raatFramedRouting: case raatFramedMTU: case raatFramedCompression: case raatLoginIPHost: case raatLoginService: case raatLoginTCPPort: case raatFramedIPXNetwork: case raatSessionTimeout: case raatIdleTimeout: case raatTerminationAction: case raatFramedAppleTalkLink: case raatFramedAppleTalkNetwork: case raatNASPortType: case raatPortLimit: case raatTunnelType: case raatTunnelMediumType: case raatAcctStatusType: case raatAcctDelayTime: case raatAcctInputOctets: case raatAcctOutputOctets: case raatAcctAuthentic: case raatAcctSessionTime: case raatAcctInputPackets: case raatAcctOutputPackets: case raatAcctTerminateCause: case raatAcctLinkCount: case raatFramedIPAddress: case raatFramedIPNetmask: case raatPrompt: case raatPasswordRetry: case raatARAPZoneAccess: case raatARAPSecurity: case raatAcctInterimInterval: case raatAcctEventTimeStamp: case raatPEAPFastRoamedSession: case raatPEAPEmbeddedEAPTypeId: switch( prgRouter->dwLength ) { case 1: *((LPBYTE)(prgRadius+1)) = (BYTE)prgRouter->Value; break; case 2: HostToWireFormat16U( (WORD)prgRouter->Value, (LPBYTE)(prgRadius+1) ); break; case 4: HostToWireFormat32( PtrToUlong(prgRouter->Value), (LPBYTE)(prgRadius+1) ); break; default: break; } prgRadius->bLength += sizeof(RADIUS_ATTRIBUTE); (*pAttrLength) += prgRadius->bLength; break; case raatNASIPAddress: RTASSERT( 4 == prgRouter->dwLength ); if ( pRadiusServer->nboNASIPAddress != INADDR_NONE ) { CopyMemory( (LPBYTE)(prgRadius+1), (LPBYTE)&(pRadiusServer->nboNASIPAddress), 4 ); } else if( pRadiusServer->nboBestIf != INADDR_NONE) { CopyMemory( (LPBYTE)(prgRadius+1), (LPBYTE)&(pRadiusServer->nboBestIf), 4 ); } else { HostToWireFormat32( PtrToUlong(prgRouter->Value), (LPBYTE)(prgRadius+1) ); } prgRadius->bLength += sizeof(RADIUS_ATTRIBUTE); (*pAttrLength) += prgRadius->bLength; break; } prgRadius = (PRADIUS_ATTRIBUTE) ((PBYTE) prgRadius + prgRadius->bLength); prgRouter++; } }while( FALSE ); if ( dwError != NO_ERROR ) { (*pAttrLength) = 0; } else { if ( ( ( bSubCode == atInvalid ) ? pRadiusServer->fSendSignature : FALSE ) || ( fEAPMessage ) ) { // // Add a signature attribute as well. Zero this out for now. // *ppSignature = (BYTE *)prgRadius; prgRadius->bType = (BYTE)raatSignature; prgRadius->bLength = (BYTE)18; ZeroMemory( prgRadius+1, 16 ); (*pAttrLength) += prgRadius->bLength; } } return( dwError ); } //** // // Call: Radius2Router // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: Converts RADIUS attribute array to RAS_AUTH_ATTRIBUTE array // DWORD Radius2Router( IN RADIUS_PACKETHEADER UNALIGNED * pRecvHeader, IN RADIUSSERVER UNALIGNED * pRadiusServer, IN PBYTE pRequestAuthenticator, IN DWORD dwNumAttributes, OUT DWORD * pdwExtError, OUT PRAS_AUTH_ATTRIBUTE * pprgRouter, OUT BOOL * fEapMessageReceived ) { LONG cbLength; DWORD dwRetCode = NO_ERROR; BOOL fEAPMessage = FALSE; BOOL fSignature = FALSE; PBYTE pEAPMessage = NULL; DWORD cbEAPMessage = 0; RADIUS_ATTRIBUTE UNALIGNED * prgRadius = (PRADIUS_ATTRIBUTE)(pRecvHeader + 1); *pdwExtError = 0; *pprgRouter = NULL; *fEapMessageReceived = FALSE; *pprgRouter = RasAuthAttributeCreate( dwNumAttributes ); if ( *pprgRouter == NULL ) { return( GetLastError() ); } dwNumAttributes = 0; cbLength = ntohs( pRecvHeader->wLength ) - sizeof(RADIUS_PACKETHEADER); while( cbLength > 0 ) { switch( (RAS_AUTH_ATTRIBUTE_TYPE)prgRadius->bType ) { case raatNASPort: case raatServiceType: case raatFramedProtocol: case raatFramedRouting: case raatFramedMTU: case raatFramedCompression: case raatLoginIPHost: case raatLoginService: case raatLoginTCPPort: case raatFramedIPXNetwork: case raatSessionTimeout: case raatIdleTimeout: case raatTerminationAction: case raatFramedAppleTalkLink: case raatFramedAppleTalkNetwork: case raatNASPortType: case raatPortLimit: case raatTunnelType: case raatTunnelMediumType: case raatAcctStatusType: case raatAcctDelayTime: case raatAcctInputOctets: case raatAcctOutputOctets: case raatAcctAuthentic: case raatAcctSessionTime: case raatAcctInputPackets: case raatAcctOutputPackets: case raatAcctTerminateCause: case raatAcctLinkCount: case raatFramedIPAddress: case raatFramedIPNetmask: case raatNASIPAddress: case raatPrompt: case raatPasswordRetry: case raatARAPZoneAccess: case raatARAPSecurity: case raatAcctInterimInterval: case raatAcctEventTimeStamp: case raatPEAPFastRoamedSession: case raatPEAPEmbeddedEAPTypeId: { DWORD dwIntegralValue; DWORD dwLength = prgRadius->bLength - sizeof(RADIUS_ATTRIBUTE); if ( dwLength == 1 ) { dwIntegralValue = (DWORD)(*(LPBYTE)(prgRadius+1)); } else if ( dwLength == 2 ) { dwIntegralValue=(DWORD)WireToHostFormat16U( (PBYTE)(prgRadius+1)); } else if ( dwLength == 4 ) { dwIntegralValue=(DWORD)WireToHostFormat32( (PBYTE)(prgRadius+1)); } else { // // Drop bad attribute // break; } dwRetCode = RasAuthAttributeInsert( dwNumAttributes++, *pprgRouter, (RAS_AUTH_ATTRIBUTE_TYPE)prgRadius->bType, FALSE, prgRadius->bLength-sizeof(RADIUS_ATTRIBUTE), (LPVOID)ULongToPtr(dwIntegralValue) ); } break; case raatSignature: // // Check the signature // { BYTE MD5d[MD5_LEN]; HmacContext HmacMD5c; BYTE Signature[16]; HmacMD5Init( &HmacMD5c, (PBYTE)(pRadiusServer->szSecret), pRadiusServer->cbSecret); // // Zero out the signature attribute before calculating it // if ( prgRadius->bLength != 18 ) { RADIUS_TRACE("Received invalid signature length in packet"); *pdwExtError = ERROR_INVALID_SIGNATURE_LENGTH; dwRetCode = ERROR_INVALID_RADIUS_RESPONSE; break; } CopyMemory( Signature, (prgRadius+1), 16 ); ZeroMemory( (PBYTE)(prgRadius+1), 16 ); CopyMemory( (PBYTE)(pRecvHeader->rgAuthenticator), pRequestAuthenticator, 16 ); HmacMD5Update( &HmacMD5c, (PBYTE)pRecvHeader, ntohs(pRecvHeader->wLength) ); HmacMD5Final( MD5d, &HmacMD5c ); if ( memcmp( Signature, MD5d, 16 ) != 0 ) { RADIUS_TRACE("Received invalid signature in packet"); *pdwExtError = ERROR_INVALID_SIGNATURE; dwRetCode = ERROR_INVALID_RADIUS_RESPONSE; break; } fSignature = TRUE; } // // Fall thru // case raatUserName: case raatUserPassword: case raatMD5CHAPPassword: case raatFilterId: case raatReplyMessage: case raatCallbackNumber: case raatCallbackId: case raatFramedRoute: case raatState: case raatClass: case raatCalledStationId: case raatCallingStationId: case raatNASIdentifier: case raatProxyState: case raatLoginLATService: case raatLoginLATNode: case raatLoginLATGroup: case raatFramedAppleTalkZone: case raatAcctSessionId: case raatAcctMultiSessionId: case raatMD5CHAPChallenge: case raatLoginLATPort: case raatTunnelClientEndpoint: case raatTunnelServerEndpoint: case raatARAPPassword: case raatARAPFeatures: case raatARAPSecurityData: case raatConnectInfo: case raatConfigurationToken: case raatARAPChallengeResponse: case raatCertificateOID: dwRetCode = RasAuthAttributeInsert( dwNumAttributes++, *pprgRouter, (RAS_AUTH_ATTRIBUTE_TYPE)prgRadius->bType, FALSE, prgRadius->bLength-sizeof(RADIUS_ATTRIBUTE), (LPVOID)(prgRadius+1) ); break; case raatVendorSpecific: if ( WireToHostFormat32( (PBYTE)(prgRadius+1) ) == 311 ) { BYTE abTemp[34]; PBYTE pVSAWalker = (PBYTE)(prgRadius+1)+4; DWORD cbVSALength = prgRadius->bLength - sizeof( RADIUS_ATTRIBUTE ) - 4; while( cbVSALength > 1 ) { if ( *pVSAWalker == 12 ) { if ( *(pVSAWalker+1) != 34 ) { RADIUS_TRACE("Recvd invalid MPPE key packet"); } else { // // We don't want to modify whatever data we got // from RADIUS (to keep the signature valid). // CopyMemory( abTemp, pVSAWalker, 34 ); // // Decrypt the MPPE session keys. // dwRetCode = DecryptMPPEKeys( pRadiusServer, pRequestAuthenticator, abTemp+2 ); if ( dwRetCode != NO_ERROR ) { break; } dwRetCode = RasAuthAttributeInsertVSA( dwNumAttributes++, *pprgRouter, 311, *(pVSAWalker+1), abTemp ); } } else if ( ( *pVSAWalker == 16 ) || ( *pVSAWalker == 17 ) ) { DWORD dwLength; BYTE* pbTemp; dwLength = *(pVSAWalker+1); if ( ( dwLength <= 4 ) || ( ( dwLength - 4 ) % 16 != 0 ) ) { RADIUS_TRACE("Recvd invalid MPPE key packet"); } else { pbTemp = LocalAlloc( LPTR, dwLength ); if ( NULL == pbTemp ) { dwRetCode = GetLastError(); RADIUS_TRACE("Out of memory"); break; } // // We don't want to modify whatever data we got // from RADIUS (to keep the signature valid). // CopyMemory( pbTemp, pVSAWalker, dwLength ); // // Decrypt the MPPE Send/Recv keys. // dwRetCode = DecryptMPPESendRecvKeys( pRadiusServer, pRequestAuthenticator, dwLength, pbTemp+2 ); if ( dwRetCode != NO_ERROR ) { LocalFree( pbTemp ); break; } dwRetCode = RasAuthAttributeInsertVSA( dwNumAttributes++, *pprgRouter, 311, *(pVSAWalker+1), pbTemp ); LocalFree( pbTemp ); } } else { dwRetCode = RasAuthAttributeInsertVSA( dwNumAttributes++, *pprgRouter, 311, *(pVSAWalker+1), pVSAWalker ); } if ( dwRetCode != NO_ERROR ) { break; } cbVSALength -= *(pVSAWalker+1); pVSAWalker += *(pVSAWalker+1); } } break; case raatEAPMessage: fEAPMessage = TRUE; { if ( pEAPMessage == NULL ) { // // Nothing has been allocated for EAP yet. // pEAPMessage = (PBYTE)LocalAlloc( LPTR, prgRadius->bLength - sizeof( RADIUS_ATTRIBUTE ) ); } else { // // Need to increase the size of the buffer to hold this // message // PBYTE pReallocEAPMessage = (PBYTE)LocalReAlloc( pEAPMessage, cbEAPMessage + prgRadius->bLength - sizeof( RADIUS_ATTRIBUTE ), LMEM_MOVEABLE ); if ( pReallocEAPMessage == NULL ) { LocalFree( pEAPMessage ); pEAPMessage = NULL; } else { pEAPMessage = pReallocEAPMessage; } } if ( pEAPMessage == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } // // Copy existing buffer to new buffer // CopyMemory( pEAPMessage+cbEAPMessage, (PBYTE)(prgRadius+1), prgRadius->bLength - sizeof(RADIUS_ATTRIBUTE) ); // // Increment the cbEAPMessage so that the size is updated // properly // cbEAPMessage += (prgRadius->bLength - sizeof(RADIUS_ATTRIBUTE)); } break; default: // // Drop attributes we do not know about. // break; } if ( dwRetCode != NO_ERROR ) { RasAuthAttributeDestroy( *pprgRouter ); *pprgRouter = NULL; if ( pEAPMessage != NULL ) { LocalFree( pEAPMessage ); } return( dwRetCode ); } RADIUS_TRACE1( "Returning attribute type %d", prgRadius->bType ); cbLength -= prgRadius->bLength; prgRadius = (PRADIUS_ATTRIBUTE)((PBYTE)prgRadius+prgRadius->bLength); } if ( dwRetCode == NO_ERROR ) { if ( fEAPMessage ) { // // If we have received an EAP message, make sure we received a valid // signature as well // if ( !fSignature ) { RADIUS_TRACE("Did not receive signature along auth EAPMessage"); *pdwExtError = ERROR_NO_SIGNATURE; dwRetCode = ERROR_INVALID_RADIUS_RESPONSE; } else { dwRetCode = RasAuthAttributeInsert( dwNumAttributes++, *pprgRouter, (RAS_AUTH_ATTRIBUTE_TYPE)raatEAPMessage, FALSE, cbEAPMessage, (LPVOID)pEAPMessage ); *fEapMessageReceived = TRUE; } LocalFree( pEAPMessage ); } } return( dwRetCode ); } DWORD EncryptPassword( IN RAS_AUTH_ATTRIBUTE * prgRouter, IN RADIUS_ATTRIBUTE UNALIGNED * prgRadius, IN RADIUSSERVER UNALIGNED * pRadiusServer, IN RADIUS_PACKETHEADER UNALIGNED * pHeader, IN BYTE bSubCode ) { MD5_CTX MD5c; DWORD iIndex, iBlock, cBlocks; DWORD bLength, AttrLength; BYTE UNALIGNED *pbValue; // // make the password into a 16 octet multiple // bLength = ((prgRadius->bLength + 15) / 16) * 16; if ( bLength == 0 ) { bLength = 16; } prgRadius->bLength = (BYTE)(sizeof(RADIUS_ATTRIBUTE) + bLength); pbValue = (PBYTE) (prgRadius + 1); AttrLength = sizeof(RADIUS_ATTRIBUTE); // // Zero pad the password // ZeroMemory( pbValue, bLength ); // // Copy the original password // CopyMemory( pbValue, (PBYTE)prgRouter->Value, (BYTE)prgRouter->dwLength); cBlocks = bLength / 16; for ( iBlock = 0; iBlock < cBlocks; iBlock++ ) { MD5Init( &MD5c ); MD5Update(&MD5c,(PBYTE)pRadiusServer->szSecret,pRadiusServer->cbSecret); if (iBlock == 0) { MD5Update( &MD5c, pHeader->rgAuthenticator, sizeof(pHeader->rgAuthenticator)); } else { MD5Update(&MD5c, (pbValue - 16), 16); } MD5Final( &MD5c ); for ( iIndex = 0; iIndex < 16; iIndex++ ) { *pbValue ^= MD5c.digest[iIndex]; pbValue++; } } return( AttrLength + bLength ); } DWORD DecryptMPPEKeys( IN RADIUSSERVER UNALIGNED * pRadiusServer, IN PBYTE pRequestAuthenticator, IN OUT PBYTE pEncryptionKeys ) { BYTE * pbValue = (BYTE *)pEncryptionKeys; MD5_CTX MD5c; DWORD dwIndex; DWORD dwBlock; BYTE abCipherText[16]; // // Save the cipherText from the first block. // CopyMemory(abCipherText, pbValue, sizeof(abCipherText)); // // Walk thru the 2 blocks // for ( dwBlock = 0; dwBlock < 2; dwBlock++ ) { MD5Init( &MD5c ); MD5Update( &MD5c, (PBYTE)(pRadiusServer->szSecret), pRadiusServer->cbSecret); if ( dwBlock == 0 ) { // // Use the Request Authenticator for the first block // MD5Update( &MD5c, pRequestAuthenticator, 16 ); } else { // // Use the first block of cipherText for the second block // MD5Update( &MD5c, abCipherText, 16 ); } MD5Final( &MD5c ); for ( dwIndex = 0; dwIndex < 16; dwIndex ++ ) { *pbValue ^= MD5c.digest[dwIndex]; pbValue++; } } return( NO_ERROR ); } DWORD DecryptMPPESendRecvKeys( IN RADIUSSERVER UNALIGNED * pRadiusServer, IN PBYTE pRequestAuthenticator, IN DWORD dwLength, IN OUT PBYTE pEncryptionKeys ) { BYTE * pbValue = (BYTE *)pEncryptionKeys + 2; BYTE abCipherText[16]; MD5_CTX MD5c; DWORD dwIndex; DWORD dwBlock; DWORD dwNumBlocks; dwNumBlocks = ( dwLength - 2 ) / 16; // // Walk thru the blocks // for ( dwBlock = 0; dwBlock < dwNumBlocks; dwBlock++ ) { MD5Init( &MD5c ); MD5Update( &MD5c, (PBYTE)(pRadiusServer->szSecret), pRadiusServer->cbSecret); if ( dwBlock == 0 ) { // // Use the Request Authenticator and salt for the first block // MD5Update( &MD5c, pRequestAuthenticator, 16 ); MD5Update( &MD5c, pEncryptionKeys, 2 ); } else { // // Use the previous block of cipherText // MD5Update( &MD5c, abCipherText, 16 ); } MD5Final( &MD5c ); // // Save the cipherText from this block. // CopyMemory(abCipherText, pbValue, sizeof(abCipherText)); for ( dwIndex = 0; dwIndex < 16; dwIndex++ ) { *pbValue ^= MD5c.digest[dwIndex]; pbValue++; } } return( NO_ERROR ); }