Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1761 lines
50 KiB

/********************************************************************/
/** Copyright(c) 1989 Microsoft Corporation. **/
/********************************************************************/
//***
//
// Filename: auth.c
//
// Description: Contains FSM code to handle and authentication protocols.
//
// History:
// Nov 11,1993. NarenG Created original version.
// Jan 09,1995 RamC Save Lsa hToken in the PCB structure
// This will be closed
// in ProcessLineDownWorker() routine
// to release the RAS license.
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h> // needed for winbase.h
#include <windows.h> // Win32 base API's
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <lmcons.h>
#include <raserror.h>
#include <rasman.h>
#include <rtutils.h>
#include <mprlog.h>
#include <mprerror.h>
#include <rasppp.h>
#include <pppcp.h>
#include <ppp.h>
#include <auth.h>
#include <smevents.h>
#include <smaction.h>
#include <lcp.h>
#include <timer.h>
#include <util.h>
#include <worker.h>
#define INCL_RASAUTHATTRIBUTES
#define INCL_MISC
#include <ppputil.h>
DWORD
EapGetCredentials(
VOID *pWorkBuf,
VOID *ppCredentials);
//**
//
// Call: SetMsChapMppeKeys
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description: Set the MS-CHAP-MPPE-Keys with NDISWAN
//
DWORD
SetMsChapMppeKeys(
IN HPORT hPort,
IN RAS_AUTH_ATTRIBUTE * pAttribute,
IN BYTE * pChallenge,
IN BYTE * pResponse,
IN DWORD AP,
IN DWORD APData
)
{
RAS_COMPRESSION_INFO rciSend;
RAS_COMPRESSION_INFO rciReceive;
DWORD dwRetCode = NO_ERROR;
ASSERT( 8 == sizeof( rciSend.RCI_LMSessionKey ) );
ASSERT( 16 == sizeof( rciSend.RCI_UserSessionKey ) );
//
// Length of key is 8 (LM key) + 16 (NT key)
//
if ( pAttribute->dwLength < ( 6 + 8 + 16 ) )
{
return( ERROR_INVALID_PARAMETER );
}
ZeroMemory( &rciSend, sizeof( rciSend ) );
rciSend.RCI_MacCompressionType = 0xFF;
CopyMemory( rciSend.RCI_LMSessionKey,
((PBYTE)(pAttribute->Value))+6,
8 );
CopyMemory( rciSend.RCI_UserSessionKey,
((PBYTE)(pAttribute->Value))+6+8,
16 );
CopyMemory( rciSend.RCI_Challenge, pChallenge, 8 );
CopyMemory( rciSend.RCI_NTResponse, pResponse, 24 );
rciSend.RCI_Flags = CCP_SET_KEYS;
ZeroMemory( &rciReceive, sizeof( rciReceive ) );
rciReceive.RCI_MacCompressionType = 0xFF;
CopyMemory( rciReceive.RCI_LMSessionKey,
((PBYTE)(pAttribute->Value))+6,
8 );
CopyMemory( rciReceive.RCI_UserSessionKey,
((PBYTE)(pAttribute->Value))+6+8,
16 );
CopyMemory( rciReceive.RCI_Challenge, pChallenge, 8 );
CopyMemory( rciReceive.RCI_NTResponse, pResponse, 24 );
rciReceive.RCI_Flags = CCP_SET_KEYS;
rciSend.RCI_AuthType = AUTH_USE_MSCHAPV2;
rciReceive.RCI_AuthType = AUTH_USE_MSCHAPV2;
if ( ( AP == PPP_CHAP_PROTOCOL ) &&
( APData == PPP_CHAP_DIGEST_MSEXT ))
{
rciSend.RCI_AuthType = AUTH_USE_MSCHAPV1;
rciReceive.RCI_AuthType = AUTH_USE_MSCHAPV1;
}
dwRetCode = RasCompressionSetInfo(hPort,&rciSend,&rciReceive);
if ( dwRetCode != NO_ERROR )
{
PppLog( 1,"RasCompressionSetInfo failed, Error=%d", dwRetCode );
}
return( dwRetCode );
}
//**
//
// Call: SetMsMppeSendRecvKeys
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description: Set the MS-MPPE-Send-Key and MS-MPPE-Recv-Key with NDISWAN
//
DWORD
SetMsMppeSendRecvKeys(
IN HPORT hPort,
IN RAS_AUTH_ATTRIBUTE * pAttributeSendKey,
IN RAS_AUTH_ATTRIBUTE * pAttributeRecvKey
)
{
RAS_COMPRESSION_INFO rciSend;
RAS_COMPRESSION_INFO rciRecv;
DWORD dwRetCode = NO_ERROR;
//
// 4: for Vendor-Id.
//
// The Microsoft Vendor-specific RADIUS Attributes draft says that
// Vendor-Length should be > 4.
//
if ( pAttributeSendKey->dwLength <= ( 4 + 4 ) )
{
return( ERROR_INVALID_PARAMETER );
}
ZeroMemory( &rciSend, sizeof( rciSend ) );
rciSend.RCI_MacCompressionType = 0xFF;
rciSend.RCI_EapKeyLength = *(((BYTE*)(pAttributeSendKey->Value))+8);
CopyMemory( rciSend.RCI_EapKey,
((BYTE*)(pAttributeSendKey->Value))+9,
rciSend.RCI_EapKeyLength );
rciSend.RCI_Flags = CCP_SET_KEYS;
rciSend.RCI_AuthType = AUTH_USE_EAP;
if ( pAttributeRecvKey->dwLength <= ( 4 + 4 ) )
{
return( ERROR_INVALID_PARAMETER );
}
ZeroMemory( &rciRecv, sizeof( rciRecv ) );
rciRecv.RCI_MacCompressionType = 0xFF;
rciRecv.RCI_EapKeyLength = *(((BYTE*)(pAttributeRecvKey->Value))+8);
CopyMemory( rciRecv.RCI_EapKey,
((BYTE*)(pAttributeRecvKey->Value))+9,
rciRecv.RCI_EapKeyLength );
rciRecv.RCI_Flags = CCP_SET_KEYS;
rciRecv.RCI_AuthType = AUTH_USE_EAP;
dwRetCode = RasCompressionSetInfo(hPort,&rciSend,&rciRecv);
if ( dwRetCode != NO_ERROR )
{
PppLog( 1,"RasCompressionSetInfo failed, Error=%d", dwRetCode );
}
return( dwRetCode );
}
//**
//
// Call: SetUserAuthorizedAttributes
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
DWORD
SetUserAuthorizedAttributes(
IN PCB * pPcb,
IN RAS_AUTH_ATTRIBUTE * pUserAttributes,
IN BOOL fAuthenticator,
IN BYTE * pChallenge,
IN BYTE * pResponse
)
{
RAS_AUTH_ATTRIBUTE * pAttribute;
RAS_AUTH_ATTRIBUTE * pAttributeSendKey;
RAS_AUTH_ATTRIBUTE * pAttributeRecvKey;
DWORD dwRetCode;
DWORD dwEncryptionPolicy = 0;
DWORD dwEncryptionTypes = 0;
BOOL fL2tp = FALSE;
BOOL fPptp = FALSE;
CreateAccountingAttributes( pPcb );
if ( RAS_DEVICE_TYPE( pPcb->dwDeviceType ) == RDT_Tunnel_L2tp )
{
fL2tp = TRUE;
}
if ( RAS_DEVICE_TYPE( pPcb->dwDeviceType ) == RDT_Tunnel_Pptp )
{
fPptp = TRUE;
}
//
// Find out if we are to require encrypted data using MPPE
//
pAttribute = RasAuthAttributeGetVendorSpecific( 311, 7, pUserAttributes );
if ( pAttribute != NULL )
{
dwEncryptionPolicy
= WireToHostFormat32(((BYTE*)(pAttribute->Value))+6);
pAttribute = RasAuthAttributeGetVendorSpecific( 311,
8,
pUserAttributes );
if ( pAttribute != NULL )
{
dwEncryptionTypes
= WireToHostFormat32(((BYTE*)(pAttribute->Value))+6);
if ( dwEncryptionPolicy == 2 )
{
if (!fL2tp)
{
//
// Find out what types of encryption are to be required
//
if ( ( dwEncryptionTypes & 0x00000002 )
|| ( dwEncryptionTypes & 0x00000008 ) )
{
pPcb->ConfigInfo.dwConfigMask
|= PPPCFG_RequireEncryption;
PppLog( 1,"Encryption" );
}
if ( dwEncryptionTypes & 0x00000004 )
{
pPcb->ConfigInfo.dwConfigMask
|= PPPCFG_RequireStrongEncryption;
PppLog( 1,"Strong encryption" );
}
if ( dwEncryptionTypes == 0 )
{
pPcb->ConfigInfo.dwConfigMask
|= PPPCFG_DisableEncryption;
PppLog( 1,"Encryption is not allowed" );
}
}
}
else if ( dwEncryptionPolicy == 1 )
{
//
// Find out what types of encryption are to be allowed
//
if ( !fL2tp && !dwEncryptionTypes )
{
pPcb->ConfigInfo.dwConfigMask |= PPPCFG_DisableEncryption;
PppLog( 1,"Encryption is not allowed" );
}
}
}
}
//
// Set encryption keys if we got them, provided we have not already done so
//
if ( !( pPcb->fFlags & PCBFLAG_MPPE_KEYS_SET ) )
{
pAttribute = RasAuthAttributeGetVendorSpecific(
311, 12, pUserAttributes);
if ( pAttribute != NULL )
{
//
// Set the MS-CHAP-MPPE-Keys with NDISWAN
//
LCPCB * pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf);
DWORD AP;
DWORD APData = 0;
AP = ( fAuthenticator ? pLcpCb->Local.Work.AP :
pLcpCb->Remote.Work.AP );
if ( AP == PPP_CHAP_PROTOCOL )
{
APData = ( fAuthenticator ? *(pLcpCb->Local.Work.pAPData) :
*(pLcpCb->Remote.Work.pAPData) );
}
dwRetCode = SetMsChapMppeKeys( pPcb->hPort,
pAttribute,
pChallenge,
pResponse,
AP,
APData
);
if ( NO_ERROR != dwRetCode )
{
return( dwRetCode );
}
PppLog( 1,"MS-CHAP-MPPE-Keys set" );
pPcb->fFlags |= PCBFLAG_MPPE_KEYS_SET;
}
pAttributeSendKey = RasAuthAttributeGetVendorSpecific( 311, 16,
pUserAttributes );
pAttributeRecvKey = RasAuthAttributeGetVendorSpecific( 311, 17,
pUserAttributes );
if ( ( pAttributeSendKey != NULL )
&& ( pAttributeRecvKey != NULL ) )
{
//
// Set the MS-MPPE-Send-Key and MS-MPPE-Recv-Key with NDISWAN
//
dwRetCode = SetMsMppeSendRecvKeys( pPcb->hPort,
pAttributeSendKey,
pAttributeRecvKey
);
if ( NO_ERROR != dwRetCode )
{
return( dwRetCode );
}
PppLog( 1,"MPPE-Send/Recv-Keys set" );
pPcb->fFlags |= PCBFLAG_MPPE_KEYS_SET;
}
}
//
// Check if L2tp is being used
//
if ( fL2tp )
{
DWORD dwMask = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwConfigMask;
dwRetCode = RasGetPortUserData( pPcb->hPort, PORT_IPSEC_INFO_INDEX,
(BYTE*) &dwMask, &dwSize );
if ( NO_ERROR != dwRetCode )
{
PppLog( 1, "RasGetPortUserData failed: 0x%x", dwRetCode );
dwRetCode = NO_ERROR;
}
PppLog( 1, "Checking encryption. Policy=0x%x,Types=0x%x,Mask=0x%x",
dwEncryptionPolicy, dwEncryptionTypes, dwMask );
if ( dwMask == RASMAN_IPSEC_ESP_DES )
{
pPcb->pBcb->fFlags |= BCBFLAG_BASIC_ENCRYPTION;
}
else if ( dwMask == RASMAN_IPSEC_ESP_3_DES )
{
pPcb->pBcb->fFlags |= BCBFLAG_STRONGEST_ENCRYPTION;
}
if ( !fAuthenticator )
{
//
// If the user requires maximum encryption (3DES), but we
// negotiated weaker encryption (56-bit DES), then return an error.
//
dwConfigMask = pPcb->ConfigInfo.dwConfigMask;
if ( ( dwConfigMask & PPPCFG_RequireStrongEncryption )
&& !( dwConfigMask & PPPCFG_RequireEncryption )
&& !( dwConfigMask & PPPCFG_DisableEncryption )
&& ( dwMask != RASMAN_IPSEC_ESP_3_DES ) )
{
return( ERROR_NO_REMOTE_ENCRYPTION );
}
//
// We are done with the PPPCFG_Require*Encryption flags. Let us now
// turn them off because we don't care what kind of encryption CCP
// negotiates.
//
pPcb->ConfigInfo.dwConfigMask &= ~PPPCFG_RequireStrongEncryption;
pPcb->ConfigInfo.dwConfigMask &= ~PPPCFG_RequireEncryption;
}
else if ( dwEncryptionPolicy != 0 )
{
BOOL fPolicyError = FALSE;
//
// There is an encryption policy
//
switch ( dwMask )
{
case 0:
if ( ( dwEncryptionPolicy == 2 )
&& ( dwEncryptionTypes != 0 ) )
{
fPolicyError = TRUE;
break;
}
break;
case RASMAN_IPSEC_ESP_DES:
if ( !( dwEncryptionTypes & 0x00000002 )
&& !( dwEncryptionTypes & 0x00000008 ) )
{
fPolicyError = TRUE;
break;
}
break;
case RASMAN_IPSEC_ESP_3_DES:
if (!( dwEncryptionTypes & 0x00000004 ) )
{
fPolicyError = TRUE;
break;
}
break;
}
if ( fPolicyError )
{
//
// We need to send an Accounting Stop if RADIUS sends an Access
// Accept but we still drop the line.
//
pPcb->fFlags |= PCBFLAG_SERVICE_UNAVAILABLE;
return( ERROR_NO_REMOTE_ENCRYPTION );
}
}
}
//
// If we require encryption make sure we have the keys and that CCP is
// loaded.
//
if ( pPcb->ConfigInfo.dwConfigMask & ( PPPCFG_RequireEncryption |
PPPCFG_RequireStrongEncryption ) )
{
if ( !( pPcb->fFlags & PCBFLAG_MPPE_KEYS_SET )
|| ( GetCpIndexFromProtocol( PPP_CCP_PROTOCOL ) == -1 ) )
{
//
// We need to send an Accounting Stop if RADIUS sends an Access
// Accept but we still drop the line.
//
pPcb->fFlags |= PCBFLAG_SERVICE_UNAVAILABLE;
return( ERROR_NO_LOCAL_ENCRYPTION );
}
}
//
// If we are not the authenticator then there is nothing more to set
//
if ( !fAuthenticator )
{
return( NO_ERROR );
}
//
// Check framed protocol attribute. It must be PPP.
//
pAttribute = RasAuthAttributeGet( raatFramedProtocol, pUserAttributes );
if ( pAttribute != NULL )
{
if ( PtrToUlong(pAttribute->Value) != 1 )
{
//
// We need to send an Accounting Stop if RADIUS sends an Access
// Accept but we still drop the line.
//
pPcb->fFlags |= PCBFLAG_SERVICE_UNAVAILABLE;
return( ERROR_UNKNOWN_FRAMED_PROTOCOL );
}
}
//
// Check tunnel type attribute. It must be correct.
//
pAttribute = RasAuthAttributeGet( raatTunnelType, pUserAttributes );
if ( pAttribute != NULL )
{
DWORD dwTunnelType = PtrToUlong(pAttribute->Value);
if ( ( fL2tp && ( dwTunnelType != 3 ) )
|| ( fPptp && ( dwTunnelType != 1 ) ) )
{
//
// We need to send an Accounting Stop if RADIUS sends an Access
// Accept but we still drop the line.
//
pPcb->fFlags |= PCBFLAG_SERVICE_UNAVAILABLE;
return( ERROR_WRONG_TUNNEL_TYPE );
}
}
//
// Get the logon domain attribute
//
pAttribute = RasAuthAttributeGetVendorSpecific( 311, 10, pUserAttributes );
if ( pAttribute != NULL )
{
DWORD cbDomain = sizeof( pPcb->pBcb->szRemoteDomain ) - 1;
if ( ( pAttribute->dwLength - 7 ) < cbDomain )
{
cbDomain = pAttribute->dwLength - 7;
}
ZeroMemory( pPcb->pBcb->szRemoteDomain,
sizeof( pPcb->pBcb->szRemoteDomain ) );
CopyMemory( pPcb->pBcb->szRemoteDomain,
(LPSTR)((PBYTE)(pAttribute->Value)+7),
cbDomain );
PppLog( 2, "Auth Attribute Domain = %s", pPcb->pBcb->szRemoteDomain);
}
//
// Setup callback information, default is no callback
//
pPcb->fCallbackPrivilege = RASPRIV_NoCallback;
pPcb->szCallbackNumber[0] = (CHAR)NULL;
pAttribute = RasAuthAttributeGet( raatServiceType, pUserAttributes );
if ( pAttribute != NULL )
{
if ( PtrToUlong(pAttribute->Value) == 4 )
{
//
// If service type is callback framed
//
pAttribute=RasAuthAttributeGet(raatCallbackNumber,pUserAttributes);
if ( ( pAttribute == NULL ) || ( pAttribute->dwLength == 0 ) )
{
pPcb->fCallbackPrivilege = RASPRIV_NoCallback |
RASPRIV_CallerSetCallback;
pPcb->szCallbackNumber[0] = (CHAR)NULL;
PppLog(2,"Auth Attribute Caller Specifiable callback");
}
else
{
pPcb->fCallbackPrivilege = RASPRIV_AdminSetCallback;
ZeroMemory(pPcb->szCallbackNumber,
sizeof(pPcb->szCallbackNumber));
CopyMemory( pPcb->szCallbackNumber,
pAttribute->Value,
pAttribute->dwLength );
PppLog( 2, "Auth Attribute Forced callback to %s",
pPcb->szCallbackNumber );
//
// Don't accept BAP Call-Requests. Otherwise, when the client
// calls us, we will drop the line and callback. The first call
// would be a waste.
//
pPcb->pBcb->fFlags &= ~BCBFLAG_CAN_ACCEPT_CALLS;
}
}
else if ( PtrToUlong(pAttribute->Value) != 2 )
{
PppLog( 2, "Service Type %d is not of type Framed",
PtrToUlong(pAttribute->Value) );
//
// We need to send an Accounting Stop if RADIUS sends an Access
// Accept but we still drop the line.
//
pPcb->fFlags |= PCBFLAG_SERVICE_UNAVAILABLE;
return( ERROR_UNKNOWN_SERVICE_TYPE );
}
}
if ( ( pPcb->fCallbackPrivilege & RASPRIV_CallerSetCallback )
|| ( pPcb->fCallbackPrivilege & RASPRIV_AdminSetCallback ) )
{
pPcb->pBcb->fFlags |= BCBFLAG_CAN_CALL;
}
//
// Use idle-timeout value if we got one.
//
pAttribute = RasAuthAttributeGet( raatIdleTimeout, pUserAttributes );
if ( pAttribute != NULL )
{
pPcb->dwAutoDisconnectTime = PtrToUlong(pAttribute->Value);
}
else
{
pPcb->dwAutoDisconnectTime = PppConfigInfo.dwDefaulIdleTimeout;
}
PppLog( 2, "Auth Attribute Idle Timeout Seconds = %d",
pPcb->dwAutoDisconnectTime );
//
// Use MaxChannels value if we got one.
//
pAttribute = RasAuthAttributeGet( raatPortLimit, pUserAttributes );
if ( pAttribute != NULL )
{
if ( PtrToUlong(pAttribute->Value) > 0 )
{
pPcb->pBcb->dwMaxLinksAllowed = PtrToUlong(pAttribute->Value);
}
else
{
pPcb->pBcb->dwMaxLinksAllowed = PppConfigInfo.dwDefaultPortLimit;
}
}
else
{
pPcb->pBcb->dwMaxLinksAllowed = PppConfigInfo.dwDefaultPortLimit;
}
PppLog( 2, "AuthAttribute MaxChannelsAllowed = %d",
pPcb->pBcb->dwMaxLinksAllowed );
//
// See if BAP is required
//
pAttribute = RasAuthAttributeGetVendorSpecific( 311, 13, pUserAttributes );
if ( pAttribute != NULL )
{
if ( WireToHostFormat32( (PBYTE)(pAttribute->Value)+6 ) == 2 )
{
PppLog( 2, "AuthAttribute BAPRequired" );
pPcb->pBcb->fFlags |= BCBFLAG_BAP_REQUIRED;
}
}
//
// For the server never send a request bring up the line
//
pPcb->pBcb->BapParams.dwDialExtraPercent = 100;
pPcb->pBcb->BapParams.dwDialExtraSampleSeconds = 100;
pAttribute = RasAuthAttributeGetVendorSpecific( 311, 14, pUserAttributes );
if ( ( pAttribute != NULL ) && ( pAttribute->dwLength == 10 ) )
{
pPcb->pBcb->BapParams.dwHangUpExtraPercent =
WireToHostFormat32( ((BYTE *)(pAttribute->Value))+6 );
PppLog( 2, "AuthAttribute BAPLineDownLimit = %d",
pPcb->pBcb->BapParams.dwHangUpExtraPercent );
}
else
{
pPcb->pBcb->BapParams.dwHangUpExtraPercent =
PppConfigInfo.dwHangupExtraPercent;
}
pAttribute = RasAuthAttributeGetVendorSpecific( 311, 15, pUserAttributes );
if ( ( pAttribute != NULL ) && ( pAttribute->dwLength == 10 ) )
{
pPcb->pBcb->BapParams.dwHangUpExtraSampleSeconds =
WireToHostFormat32( ((BYTE *)(pAttribute->Value))+6 );
PppLog( 2, "AuthAttribute BAPLineDownTime = %d",
pPcb->pBcb->BapParams.dwHangUpExtraSampleSeconds );
}
else
{
pPcb->pBcb->BapParams.dwHangUpExtraSampleSeconds =
PppConfigInfo.dwHangUpExtraSampleSeconds;
}
return( NO_ERROR );
}
//**
//
// Call: RasAuthenticateUserWorker
//
// Returns: None.
//
// Description:
//
VOID
RasAuthenticateUserWorker(
PVOID pContext
)
{
PCB_WORK_ITEM * pWorkItem = (PCB_WORK_ITEM *)pContext;
pWorkItem->PppMsg.AuthInfo.dwError =
(*PppConfigInfo.RasAuthProviderAuthenticateUser)(
pWorkItem->PppMsg.AuthInfo.pInAttributes,
&(pWorkItem->PppMsg.AuthInfo.pOutAttributes),
&(pWorkItem->PppMsg.AuthInfo.dwResultCode) );
RasAuthAttributeDestroy( pWorkItem->PppMsg.AuthInfo.pInAttributes );
InsertWorkItemInQ( pWorkItem );
}
//**
//
// Call: RasAuthenticateClient
//
// Returns: NO_ERROR - Success
// Non-zero returns - Failure
//
// Description:
//
DWORD
RasAuthenticateClient(
IN HPORT hPort,
IN RAS_AUTH_ATTRIBUTE * pInAttributes
)
{
PCB * pPcb = GetPCBPointerFromhPort( hPort );
LCPCB * pLcpCb = NULL;
PCB_WORK_ITEM * pWorkItem = NULL;
DWORD dwRetCode = NO_ERROR;
if ( pPcb == NULL )
{
RasAuthAttributeDestroy( pInAttributes );
return( ERROR_INVALID_PORT_HANDLE );
}
pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf);
pWorkItem = (PCB_WORK_ITEM *)LOCAL_ALLOC( LPTR, sizeof(PCB_WORK_ITEM) );
if ( pWorkItem == (PCB_WORK_ITEM *)NULL )
{
LogPPPEvent( ROUTERLOG_NOT_ENOUGH_MEMORY, GetLastError() );
RasAuthAttributeDestroy( pInAttributes );
return( GetLastError() );
}
pWorkItem->hPort = hPort;
pWorkItem->dwPortId = pPcb->dwPortId;
pWorkItem->Protocol = pLcpCb->Local.Work.AP;
pWorkItem->PppMsg.AuthInfo.pInAttributes = pInAttributes;
pWorkItem->PppMsg.AuthInfo.dwId = GetUId( pPcb, LCP_INDEX );
pWorkItem->Process = ProcessAuthInfo;
pPcb->dwOutstandingAuthRequestId = pWorkItem->PppMsg.AuthInfo.dwId;
dwRetCode = RtlNtStatusToDosError( RtlQueueWorkItem( RasAuthenticateUserWorker,
pWorkItem,
WT_EXECUTEDEFAULT ) );
if ( dwRetCode != NO_ERROR )
{
RasAuthAttributeDestroy( pInAttributes );
LOCAL_FREE( pWorkItem );
}
return( dwRetCode );
}
//**
//
// Call: RemoteError
//
// Returns: DWORD - Remote version of this error
//
// Description: Called by a client authenticating the server.
//
DWORD
RemoteError(
IN DWORD dwError
)
{
switch( dwError )
{
case ERROR_NO_DIALIN_PERMISSION:
return( ERROR_REMOTE_NO_DIALIN_PERMISSION );
case ERROR_PASSWD_EXPIRED:
return( ERROR_REMOTE_PASSWD_EXPIRED );
case ERROR_ACCT_DISABLED:
return( ERROR_REMOTE_ACCT_DISABLED );
case ERROR_RESTRICTED_LOGON_HOURS:
return( ERROR_REMOTE_RESTRICTED_LOGON_HOURS );
case ERROR_AUTHENTICATION_FAILURE:
return( ERROR_REMOTE_AUTHENTICATION_FAILURE );
case ERROR_REQ_NOT_ACCEP:
return( ERROR_LICENSE_QUOTA_EXCEEDED );
default:
return( dwError );
}
}
//**
//
// Call: ApIsAuthenticatorPacket
//
// Returns: TRUE - Packet belongs to authenticator
// FALSE - Otherwise
//
// Description: Called to figure out whether to send the auth packet to the
// authenticator or authenticatee.
//
BOOL
ApIsAuthenticatorPacket(
IN DWORD CpIndex,
IN BYTE bConfigCode
)
{
switch( CpTable[CpIndex].CpInfo.Protocol )
{
case PPP_PAP_PROTOCOL:
switch( bConfigCode )
{
case 1:
return( TRUE );
default:
return( FALSE );
}
break;
case PPP_CHAP_PROTOCOL:
switch( bConfigCode )
{
case 2:
case 5:
case 6:
case 7:
return( TRUE );
default:
return( FALSE );
}
break;
case PPP_SPAP_NEW_PROTOCOL:
switch( bConfigCode )
{
case 1:
case 6:
return( TRUE );
default:
return( FALSE );
}
break;
case PPP_EAP_PROTOCOL:
switch( bConfigCode )
{
case 2:
return( TRUE );
default:
return( FALSE );
}
break;
default:
PPP_ASSERT( FALSE );
}
PPP_ASSERT( FALSE );
return( FALSE );
}
//**
//
// Call: ApStart
//
// Returns: TRUE - Success
// FALSE - Otherwise
//
// Description: Called to initiatialze the authetication protocol and to
// initiate to authentication.
//
BOOL
ApStart(
IN PCB * pPcb,
IN DWORD CpIndex,
IN BOOL fAuthenticator
)
{
DWORD dwRetCode;
PPPAP_INPUT PppApInput;
LCPCB * pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf);
CPCB * pCpCb = ( fAuthenticator )
? &(pPcb->AuthenticatorCb)
: &(pPcb->AuthenticateeCb);
pCpCb->fConfigurable = TRUE;
pCpCb->State = FSM_INITIAL;
pCpCb->Protocol = CpTable[CpIndex].CpInfo.Protocol;
pCpCb->LastId = (DWORD)-1;
InitRestartCounters( pPcb, pCpCb );
ZeroMemory( &PppApInput, sizeof( PppApInput ) );
PppApInput.hPort = pPcb->hPort;
PppApInput.fServer = fAuthenticator;
PppApInput.fRouter = ( ROUTER_IF_TYPE_FULL_ROUTER ==
pPcb->pBcb->InterfaceInfo.IfType );
PppApInput.Luid = pPcb->Luid;
PppApInput.dwEapTypeToBeUsed = pPcb->dwEapTypeToBeUsed;
PppApInput.hTokenImpersonateUser = pPcb->pBcb->hTokenImpersonateUser;
PppApInput.pCustomAuthConnData = pPcb->pBcb->pCustomAuthConnData;
PppApInput.pCustomAuthUserData = pPcb->pBcb->pCustomAuthUserData;
PppApInput.EapUIData = pPcb->pBcb->EapUIData;
PppApInput.fLogon = ( pPcb->pBcb->fFlags &
BCBFLAG_LOGON_USER_DATA );
PppApInput.fNonInteractive = ( pPcb->fFlags &
PCBFLAG_NON_INTERACTIVE );
PppApInput.fConfigInfo = pPcb->ConfigInfo.dwConfigMask;
if ( fAuthenticator )
{
PppApInput.dwRetries = pPcb->dwAuthRetries;
PppApInput.pAPData = pLcpCb->Local.Work.pAPData;
PppApInput.APDataSize = pLcpCb->Local.Work.APDataSize;
PppApInput.pUserAttributes = pPcb->pUserAttributes;
ZeroMemory( &pPcb->pBcb->szRemoteUserName,
sizeof( pPcb->pBcb->szRemoteUserName ) );
}
else
{
//
// If we are a server and we do not know who is dialing in and therefore
// do not have credentials to use for being authenticated by the
// remote peer, then we wait till we do.
//
if ( pPcb->fFlags & PCBFLAG_IS_SERVER )
{
if ( strlen( pPcb->pBcb->szRemoteUserName ) == 0 )
{
PppLog(1,"Remote user not identifiable at this time, waiting");
return( FALSE );
}
//
// Ok we know who is dialed in so get credentials to used for this
// connection.
//
dwRetCode = GetCredentialsFromInterface( pPcb );
if ( dwRetCode != NO_ERROR )
{
//
// We do not have credentials to use for this user so we
// renegotiate LCP and do not accept the authentication option
//
PppLog( 1, "No credentials available to use for user=%s",
pPcb->pBcb->szRemoteUserName );
FsmDown( pPcb, LCP_INDEX );
pLcpCb->Remote.WillNegotiate &= (~LCP_N_AUTHENT);
FsmUp( pPcb, LCP_INDEX );
return( FALSE );
}
}
//
// Decode the password
//
DecodePw( pPcb->pBcb->chSeed, pPcb->pBcb->szPassword );
DecodePw( pPcb->pBcb->chSeed, pPcb->pBcb->szOldPassword );
PppApInput.pszUserName = pPcb->pBcb->szLocalUserName;
PppApInput.pszPassword = pPcb->pBcb->szPassword;
PppApInput.pszDomain = pPcb->pBcb->szLocalDomain;
PppApInput.pszOldPassword = pPcb->pBcb->szOldPassword;
PppApInput.pAPData = pLcpCb->Remote.Work.pAPData;
PppApInput.APDataSize = pLcpCb->Remote.Work.APDataSize;
PppApInput.dwInitialPacketId = (DWORD)GetUId( pPcb, CpIndex );
if ( CpTable[CpIndex].CpInfo.Protocol == PPP_EAP_PROTOCOL )
{
PppApInput.fPortWillBeBundled = WillPortBeBundled( pPcb );
PppApInput.fThisIsACallback =
( pPcb->fFlags & PCBFLAG_THIS_IS_A_CALLBACK );
}
}
dwRetCode = (CpTable[CpIndex].CpInfo.RasCpBegin)(&(pCpCb->pWorkBuf),
&PppApInput );
if ( !fAuthenticator )
{
//
// Encode the password back
//
EncodePw( pPcb->pBcb->chSeed, pPcb->pBcb->szPassword );
EncodePw( pPcb->pBcb->chSeed, pPcb->pBcb->szOldPassword );
}
if ( dwRetCode != NO_ERROR )
{
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, dwRetCode );
return( FALSE );
}
PppLog(1,"Calling APWork in APStart");
ApWork( pPcb, CpIndex, NULL, NULL, fAuthenticator );
return( TRUE );
}
//**
//
// Call: ApStop
//
// Returns: none
//
// Description: Called to stop the authentication machine.
//
VOID
ApStop(
IN PCB * pPcb,
IN DWORD CpIndex,
IN BOOL fAuthenticator
)
{
CPCB * pCpCb = ( fAuthenticator )
? &(pPcb->AuthenticatorCb)
: &(pPcb->AuthenticateeCb);
if ( pCpCb->pWorkBuf == NULL )
{
return;
}
pCpCb->Protocol = 0;
pCpCb->fConfigurable = FALSE;
if ( pCpCb->LastId != (DWORD)-1 )
{
RemoveFromTimerQ(
pPcb->dwPortId,
pCpCb->LastId,
CpTable[CpIndex].CpInfo.Protocol,
fAuthenticator,
TIMER_EVENT_TIMEOUT );
}
(CpTable[CpIndex].CpInfo.RasCpEnd)( pCpCb->pWorkBuf );
pCpCb->pWorkBuf = NULL;
}
//**
//
// Call: ApWork
//
// Returns: none
//
// Description: Called when and authentication packet was received or
// a timeout occurred or to initiate authentication.
//
VOID
ApWork(
IN PCB * pPcb,
IN DWORD CpIndex,
IN PPP_CONFIG * pRecvConfig,
IN PPPAP_INPUT * pApInput,
IN BOOL fAuthenticator
)
{
DWORD dwRetCode;
DWORD dwLength;
PPPAP_RESULT ApResult;
PPP_CONFIG * pSendConfig = (PPP_CONFIG*)(pPcb->pSendBuf->Information);
LCPCB * pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf);
CPCB * pCpCb = ( fAuthenticator )
? &(pPcb->AuthenticatorCb)
: &(pPcb->AuthenticateeCb);
//
// If the protocol has not been started yet, call ApStart
//
if ( pCpCb->pWorkBuf == NULL )
{
if ( !ApStart( pPcb, CpIndex, fAuthenticator ) )
{
return;
}
}
ZeroMemory( &ApResult, sizeof(ApResult) );
dwRetCode = (CpTable[CpIndex].CpInfo.RasApMakeMessage)(
pCpCb->pWorkBuf,
pRecvConfig,
pSendConfig,
((pLcpCb->Remote.Work.MRU > LCP_DEFAULT_MRU)
? LCP_DEFAULT_MRU : pLcpCb->Remote.Work.MRU)
- PPP_PACKET_HDR_LEN,
&ApResult,
pApInput );
if ( NULL != ApResult.szReplyMessage )
{
LocalFree( pPcb->pBcb->szReplyMessage );
pPcb->pBcb->szReplyMessage = ApResult.szReplyMessage;
}
if ( dwRetCode != NO_ERROR )
{
switch( dwRetCode )
{
case ERROR_PPP_INVALID_PACKET:
PppLog( 1, "Silently discarding invalid auth packet on port %d",
pPcb->hPort );
break;
default:
pPcb->LcpCb.dwError = dwRetCode;
PppLog( 1, "Auth Protocol %x returned error %d",
CpTable[CpIndex].CpInfo.Protocol, dwRetCode );
if ( fAuthenticator )
{
//
// Get the username from the CP if it supplies one.
//
if ( strlen( ApResult.szUserName ) > 0 )
{
strcpy( pPcb->pBcb->szRemoteUserName, ApResult.szUserName );
}
}
NotifyCallerOfFailure( pPcb, dwRetCode );
break;
}
return;
}
//
// Check to see if we have to save any user data
//
if ( ( !fAuthenticator ) && ( ApResult.fSaveUserData ) )
{
dwRetCode = RasSetEapUserDataA(
pPcb->pBcb->hTokenImpersonateUser,
pPcb->pBcb->szPhonebookPath,
pPcb->pBcb->szEntryName,
ApResult.pUserData,
ApResult.dwSizeOfUserData );
PppLog( 2, "Saved EAP data for user, dwRetCode = %d", dwRetCode );
}
//
// Check to see if we have to save any connection data
//
if ( ( !fAuthenticator ) && ( ApResult.fSaveConnectionData ) &&
( 0 != ApResult.SetCustomAuthData.dwSizeOfConnectionData ) )
{
NotifyCaller( pPcb, PPPMSG_SetCustomAuthData,
&(ApResult.SetCustomAuthData) );
PppLog( 2, "Saved EAP data for connection" );
}
switch( ApResult.Action )
{
case APA_Send:
case APA_SendWithTimeout:
case APA_SendWithTimeout2:
case APA_SendAndDone:
HostToWireFormat16( (WORD)CpTable[CpIndex].CpInfo.Protocol,
(PBYTE)(pPcb->pSendBuf->Protocol) );
dwLength = WireToHostFormat16( pSendConfig->Length );
LogPPPPacket(FALSE,pPcb,pPcb->pSendBuf,dwLength+PPP_PACKET_HDR_LEN);
if ( ( dwRetCode = PortSendOrDisconnect( pPcb,
(dwLength + PPP_PACKET_HDR_LEN)))
!= NO_ERROR )
{
return;
}
pCpCb->LastId = (DWORD)-1;
if ( ( ApResult.Action == APA_SendWithTimeout ) ||
( ApResult.Action == APA_SendWithTimeout2 ) )
{
pCpCb->LastId = ApResult.bIdExpected;
InsertInTimerQ( pPcb->dwPortId,
pPcb->hPort,
pCpCb->LastId,
CpTable[CpIndex].CpInfo.Protocol,
fAuthenticator,
TIMER_EVENT_TIMEOUT,
pPcb->RestartTimer );
//
// For SendWithTimeout2 we increment the ConfigRetryCount. This
// means send with infinite retry count
//
if ( ApResult.Action == APA_SendWithTimeout2 )
{
(pCpCb->ConfigRetryCount)++;
}
}
if ( ApResult.Action != APA_SendAndDone )
{
break;
}
case APA_Done:
switch( ApResult.dwError )
{
case NO_ERROR:
//
// If authentication was successful
//
if ( CpTable[CpIndex].CpInfo.Protocol == PPP_EAP_PROTOCOL )
{
if ( fAuthenticator )
{
pPcb->dwServerEapTypeId = ApResult.dwEapTypeId;
}
else
{
VOID *pCredentials = NULL;
pPcb->dwClientEapTypeId = ApResult.dwEapTypeId;
//
// Call the eap dll to collect credentials here
// so that they can be passed to rasman to be
// saved in the cred manager.
//
if( (NO_ERROR == EapGetCredentials(pCpCb->pWorkBuf,
&pCredentials))
&& (NULL != pCredentials))
{
//
// Below call is not fatal.
//
(void) RasSetPortUserData(
pPcb->hPort,
PORT_CREDENTIALS_INDEX,
pCredentials,
sizeof(RASMAN_CREDENTIALS));
ZeroMemory(pCredentials,
sizeof(RASMAN_CREDENTIALS));
LocalFree(pCredentials);
}
}
}
if ( fAuthenticator )
{
RAS_AUTH_ATTRIBUTE * pAttribute;
RAS_AUTH_ATTRIBUTE * pUserAttributes = NULL;
if ( NULL != pPcb->pBcb->szRemoteIdentity )
{
LOCAL_FREE( pPcb->pBcb->szRemoteIdentity );
pPcb->pBcb->szRemoteIdentity = NULL;
}
pPcb->pBcb->szRemoteIdentity =
LOCAL_ALLOC( LPTR, strlen( ApResult.szUserName ) + 1 );
if ( NULL == pPcb->pBcb->szRemoteIdentity )
{
dwRetCode = GetLastError();
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, dwRetCode );
return;
}
strcpy( pPcb->pBcb->szRemoteIdentity, ApResult.szUserName );
dwRetCode = ExtractUsernameAndDomain(
ApResult.szUserName,
pPcb->pBcb->szRemoteUserName,
NULL );
if ( dwRetCode != NO_ERROR )
{
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, dwRetCode );
return;
}
if ( 0 == pPcb->pBcb->szLocalUserName[0] )
{
if ( NO_ERROR != GetCredentialsFromInterface( pPcb ) )
{
pPcb->pBcb->szLocalUserName[0] =
pPcb->pBcb->szPassword[0] =
pPcb->pBcb->szLocalDomain[0] = 0;
}
}
if ( ApResult.pUserAttributes != NULL )
{
pPcb->pAuthProtocolAttributes = ApResult.pUserAttributes;
pUserAttributes = ApResult.pUserAttributes;
}
else
{
pUserAttributes = pPcb->pAuthenticatorAttributes;
}
//
// Set all the user connection parameters authorized by the
// back-end authenticator
//
dwRetCode = SetUserAuthorizedAttributes(
pPcb,
pUserAttributes,
fAuthenticator,
(BYTE*)&(ApResult.abChallenge),
(BYTE*)&(ApResult.abResponse));
if ( dwRetCode != NO_ERROR )
{
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, dwRetCode );
return;
}
//
// If we are a server and we negotiated to be authenticated
// by the remote peer we can do so now that we know who
// is dialed in.
//
if ( ( pLcpCb->Remote.Work.AP != 0 ) &&
( pPcb->AuthenticateeCb.pWorkBuf == NULL ) &&
( pPcb->AuthenticateeCb.fConfigurable ) &&
( pPcb->fFlags & PCBFLAG_IS_SERVER ) )
{
CpIndex = GetCpIndexFromProtocol( pLcpCb->Remote.Work.AP );
PPP_ASSERT(( CpIndex != (DWORD)-1 ));
if ( !ApStart( pPcb, CpIndex, FALSE ) )
{
return;
}
}
}
else
{
//
// Get the username from the CP if it supplies one.
//
if ( ( strlen( pPcb->pBcb->szLocalUserName ) == 0 )
&& ( strlen( ApResult.szUserName ) > 0 ) )
{
strcpy( pPcb->pBcb->szLocalUserName, ApResult.szUserName );
}
dwRetCode = SetUserAuthorizedAttributes(
pPcb,
ApResult.pUserAttributes,
fAuthenticator,
(BYTE*)&(ApResult.abChallenge),
(BYTE*)&(ApResult.abResponse));
if ( dwRetCode != NO_ERROR )
{
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, dwRetCode );
return;
}
pPcb->pAuthProtocolAttributes = ApResult.pUserAttributes;
}
pCpCb->State = FSM_OPENED;
FsmThisLayerUp( pPcb, CpIndex );
break;
case ERROR_PASSWD_EXPIRED:
if ( pPcb->fFlags & PCBFLAG_IS_SERVER )
{
//
// We are a server and hence in non-interactive mode and
// hence we cannot do this.
//
pPcb->LcpCb.dwError = ApResult.dwError;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
else
{
//
// Password has expired so the user has to change his/her
// password.
//
NotifyCaller( pPcb, PPPMSG_ChangePwRequest, NULL );
}
break;
default:
//
// If we can retry with a new password then tell the client to
// get a new one from the user.
//
if ( (!fAuthenticator) && ( ApResult.fRetry ))
{
if ( pPcb->fFlags & PCBFLAG_IS_SERVER )
{
//
// We are a server and hence in non-interactive mode and
// hence we cannot do this.
//
pPcb->LcpCb.dwError = ApResult.dwError;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
else
{
PppLog( 2, "Sending auth retry message to UI" );
NotifyCaller( pPcb, PPPMSG_AuthRetry, &(ApResult.dwError) );
}
}
else
{
PppLog( 1, "Auth Protocol %x terminated with error %d",
CpTable[CpIndex].CpInfo.Protocol, ApResult.dwError );
if ( ApResult.szUserName[0] != (CHAR)NULL )
{
strcpy( pPcb->pBcb->szRemoteUserName, ApResult.szUserName );
}
if ( !( pPcb->fFlags & PCBFLAG_IS_SERVER ) &&
( fAuthenticator) )
{
pPcb->LcpCb.dwError = RemoteError( ApResult.dwError );
}
else
{
pPcb->LcpCb.dwError = ApResult.dwError;
}
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
break;
}
break;
case APA_Authenticate:
if ( fAuthenticator )
{
DWORD dwIndex = 0;
DWORD dwExtraAttributes = 0;
DWORD dwNumUserAttributes = 0;
RAS_AUTH_ATTRIBUTE * pUserAttributes = NULL;
for ( dwNumUserAttributes = 0;
pPcb->pUserAttributes[dwNumUserAttributes].raaType
!= raatMinimum;
dwNumUserAttributes++ );
if ( CpTable[CpIndex].CpInfo.Protocol == PPP_EAP_PROTOCOL )
{
//
// One more for Framed-MTU
//
dwExtraAttributes = 1;
}
pUserAttributes = RasAuthAttributeCopyWithAlloc(
ApResult.pUserAttributes,
dwNumUserAttributes + dwExtraAttributes );
if ( pUserAttributes == NULL )
{
dwRetCode = GetLastError();
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
if ( dwExtraAttributes )
{
ULONG mru = (pLcpCb->Remote.Work.MRU > LCP_DEFAULT_MRU) ?
LCP_DEFAULT_MRU : pLcpCb->Remote.Work.MRU;
//
// Insert the Framed-MTU attribute at the start.
//
dwRetCode = RasAuthAttributeInsert(
0,
pUserAttributes,
raatFramedMTU,
FALSE,
4,
(LPVOID)
( UlongToPtr(mru)) );
if ( dwRetCode != NO_ERROR )
{
RasAuthAttributeDestroy( pUserAttributes );
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
}
//
// Insert the extra (user) attributes at the start. Attributes
// returned by the auth protocol follow.
//
for ( dwIndex = 0; dwIndex < dwNumUserAttributes; dwIndex++ )
{
dwRetCode = RasAuthAttributeInsert(
dwIndex + dwExtraAttributes,
pUserAttributes,
pPcb->pUserAttributes[dwIndex].raaType,
FALSE,
pPcb->pUserAttributes[dwIndex].dwLength,
pPcb->pUserAttributes[dwIndex].Value );
if ( dwRetCode != NO_ERROR )
{
RasAuthAttributeDestroy( pUserAttributes );
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
}
dwRetCode = RasAuthenticateClient( pPcb->hPort, pUserAttributes );
if ( dwRetCode != NO_ERROR )
{
pPcb->LcpCb.dwError = dwRetCode;
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError );
return;
}
}
break;
case APA_NoAction:
break;
default:
break;
}
//
// Check to see if we have to bring up the UI for EAP
//
if ( ( !fAuthenticator ) && ( ApResult.fInvokeEapUI ) )
{
NotifyCaller(pPcb, PPPMSG_InvokeEapUI, &(ApResult.InvokeEapUIData));
}
}