/* Copyright (c) 1997, Microsoft Corporation, all rights reserved Description: PPP EAP TLS Authentication Protocol. Based on RFC xxxx. History: Oct 9, 1997: Vijay Baliga created original version. Notes: Server Client [Initial] [Initial] TLS Start [SentStart] ------------------> TLS client_hello <------------------ [SentHello] TLS server_hello TLS certificate TLS server_key_exchange TLS server_hello_done [SentHello] ------------------> TLS certificate TLS client_key_exchange TLS change_cipher_spec TLS finished <------------------ [SentFinished] TLS change_cipher_spec TLS finished [SentFinished] ------------------> NULL/TLS Alert <------------------ [RecdFinished] Success/Failure [SentResult] ------------------> [RecdResult] */ #include // Required by windows.h #include // Required by windows.h #include // Required by windows.h #include // Win32 base API's #include #include #include // For RTASSERT, TraceVprintfEx #include // For SEC_E_OK #include // For WINTRUST_DATA #include // For UNLEN #include // Required by raseapif.h #include // For EAPCODE_Request #include // For ERROR_PPP_INVALID_PACKET #include // For ROUTERLOG_CANT_GET_SERVER_CRED #include // For sprintf #include // For IDS_CANT_VALIDATE_SERVER_TEXT #include #define SECURITY_WIN32 #include // For GetUserNameExA, CredHandle #define INCL_HOSTWIRE #define INCL_RASAUTHATTRIBUTES #include // For HostToWireFormat16, RasAuthAttributeInsert #define ALLOC_EAPTLS_GLOBALS #include // // Internal functions // void FreeCachedCredentials(EAPTLSCB * pEapTlsCb); void SetCachedCredentials (EAPTLSCB * pEapTlsCb); DWORD PeapCheckCookie ( PPEAPCB pPeapCb, PEAP_COOKIE *pCookie, DWORD cbCookie ); DWORD PeapCreateCookie ( PPEAPCB pPeapCb, PBYTE * ppbCookie, DWORD * pcbCookie ); DWORD PeapGetCredentials( IN VOID * pWorkBuf, OUT VOID ** ppCredentials); DWORD GetCredentialsFromUserProperties( EAPTLSCB *pUserProp, VOID **ppCredentials); DWORD CreatePEAPTLVStatusMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, DWORD cbPacket, BOOL fRequest, WORD wValue //Success or Failure ); DWORD GetPEAPTLVStatusMessageValue ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, WORD * pwValue ); DWORD CreatePEAPTLVNAKMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, DWORD cbPacket ); DWORD CreateOIDAttributes ( IN EAPTLSCB * pEapTlsCb, PCERT_ENHKEY_USAGE pUsage, PCCERT_CHAIN_CONTEXT pCCertChainContext); BOOL fIsPEAPTLVMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket ); extern const DWORD g_adwHelp[]; VOID ContextHelp( IN const DWORD* padwMap, IN HWND hWndDlg, IN UINT unMsg, IN WPARAM wParam, IN LPARAM lParam ); /* Notes: g_szEapTlsCodeName[0..MAXEAPCODE] contains the names of the various EAP codes. */ static CHAR* g_szEapTlsCodeName[] = { "Unknown", "Request", "Response", "Success", "Failure", }; /* Notes: To protect us from denial of service attacks. */ #define DEFAULT_MAX_BLOB_SIzE 0x4000 DWORD g_dwMaxBlobSize = DEFAULT_MAX_BLOB_SIzE; /* Notes: Configurable via registry values. */ BOOL g_fIgnoreNoRevocationCheck = FALSE; BOOL g_fIgnoreRevocationOffline = FALSE; BOOL g_fNoRootRevocationCheck = TRUE; BOOL g_fNoRevocationCheck = FALSE; // // Globals for cached credentials // #define VPN_CACHED_CREDS_INDEX 0 #define WIRELESS_CACHED_CREDS_INDEX 1 #define VPN_PEAP_CACHED_CREDS_INDEX 2 #define WIRELESS_PEAP_CACHED_CREDS_INDEX 3 BOOL g_fCriticalSectionInitialized = FALSE; CRITICAL_SECTION g_csProtectCachedCredentials; typedef struct _EAPTLS_CACHED_CREDS { BOOL fCachedCredentialInitialized; CredHandle hCachedCredential; BYTE * pbHash; DWORD cbHash; PCCERT_CONTEXT pcCachedCertContext; LUID AuthenticatedSessionLUID; //Session Id LUID } EAPTLS_CACHED_CREDS; EAPTLS_CACHED_CREDS g_CachedCreds[4]; #if 0 /* CredHandle g_hCachedCredential; //Cached Server and last client Credential BOOL g_fCachedCredentialInitialized = FALSE; BYTE * g_pbHash = NULL; //cached hash and DWORD g_cbHash = 0; //size of the hash PCCERT_CONTEXT g_pcCachedCertContext = NULL; //cached Certificate Context // // Cached credentials for PEAP // CredHandle g_hPEAPCachedCredential; //Cached Server and last client Credential BOOL g_fPEAPCachedCredentialInitialized = FALSE; BYTE * g_pbPEAPHash = NULL; //cached hash and DWORD g_cbPEAPHash = 0; //size of the hash PCCERT_CONTEXT g_pcPEAPCachedCertContext = NULL; //cached Certificate Context */ #endif //Peap Globals PPEAP_EAP_INFO g_pEapInfo = NULL; /* * This function encrypts the PIN * */ DWORD EncryptData ( IN PBYTE pbPlainData, IN DWORD cbPlainData, OUT PBYTE * ppEncData, OUT DWORD * pcbEncData ) { DWORD dwRetCode = NO_ERROR; DATA_BLOB DataIn; DATA_BLOB DataOut; EapTlsTrace("EncryptData"); if ( !pbPlainData || !cbPlainData ) { dwRetCode = ERROR_INVALID_DATA; goto done; } ZeroMemory(&DataIn, sizeof(DataIn) ); ZeroMemory(&DataOut, sizeof(DataOut) ); *ppEncData = NULL; *pcbEncData = 0; DataIn.pbData = pbPlainData; DataIn.cbData = cbPlainData; if ( ! CryptProtectData ( &DataIn, EAPTLS_8021x_PIN_DATA_DESCR, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN|CRYPTPROTECT_LOCAL_MACHINE, &DataOut ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptProtectData failed. Error: 0x%x", dwRetCode); goto done; } *ppEncData = DataOut.pbData; *pcbEncData = DataOut.cbData; done: EapTlsTrace("EncryptData done."); return dwRetCode; } /* * Decrypt the PIN here. */ DWORD DecryptData ( IN PBYTE pbEncData, IN DWORD cbEncData, OUT PBYTE * ppbPlainData, OUT DWORD * pcbPlainData ) { DWORD dwRetCode = NO_ERROR; DATA_BLOB DataIn; DATA_BLOB DataOut; LPWSTR pDescrOut = NULL; EapTlsTrace("DecryptData"); if ( !pbEncData || !cbEncData ) { dwRetCode = ERROR_INVALID_DATA; goto done; } *ppbPlainData = 0; *pcbPlainData = 0; ZeroMemory(&DataIn, sizeof(DataIn)); ZeroMemory(&DataOut, sizeof(DataOut)); DataIn.pbData = pbEncData; DataIn.cbData = cbEncData; if ( !CryptUnprotectData( &DataIn, &pDescrOut, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &DataOut ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptUnprotectData failed. Error: 0x%x", dwRetCode ); goto done; } if ( lstrcmp( pDescrOut, EAPTLS_8021x_PIN_DATA_DESCR ) ) { EapTlsTrace("Description of this data does not match expected value. Discarding data"); dwRetCode = ERROR_INVALID_DATA; goto done; } *ppbPlainData = DataOut.pbData; *pcbPlainData = DataOut.cbData; done: EapTlsTrace("DecryptData done."); return dwRetCode; } /* Returns: TRUE: Packet is valid FALSE: Packet is badly formed Notes: Returns TRUE iff the EapTls packet *pPacket is correctly formed or pPacket is NULL. */ BOOL FValidPacket( IN EAPTLS_PACKET* pPacket ) { WORD wLength; BYTE bCode; if (NULL == pPacket) { return(TRUE); } wLength = WireToHostFormat16(pPacket->pbLength); bCode = pPacket->bCode; switch (bCode) { case EAPCODE_Request: case EAPCODE_Response: if (PPP_EAP_PACKET_HDR_LEN + 1 > wLength) { EapTlsTrace("EAP %s packet does not have Type octet", g_szEapTlsCodeName[bCode]); return(FALSE); } if (PPP_EAP_TLS != pPacket->bType) { // We are not concerned with this packet. It is not TLS. return(TRUE); } if (EAPTLS_PACKET_HDR_LEN > wLength) { EapTlsTrace("EAP TLS %s packet does not have Flags octet", g_szEapTlsCodeName[bCode]); return(FALSE); } if ((pPacket->bFlags & EAPTLS_PACKET_FLAG_LENGTH_INCL) && EAPTLS_PACKET_HDR_LEN_MAX > wLength) { EapTlsTrace("EAP TLS %s packet with First fragment flag does " "not have TLS blob size octects", g_szEapTlsCodeName[bCode]); } break; case EAPCODE_Success: case EAPCODE_Failure: if (PPP_EAP_PACKET_HDR_LEN != wLength) { EapTlsTrace("EAP TLS %s packet has length %d", g_szEapTlsCodeName[bCode], wLength); return(FALSE); } break; default: EapTlsTrace("Invalid code in EAP packet: %d", bCode); return(FALSE); break; } return(TRUE); } /* Returns: VOID Notes: Print the contents of the EapTls packet *pPacket. pPacket can be NULL. fInput: TRUE iff we are receiving the packet */ VOID PrintEapTlsPacket( IN EAPTLS_PACKET* pPacket, IN BOOL fInput ) { BYTE bCode; WORD wLength; BOOL fLengthIncluded = FALSE; BOOL fMoreFragments = FALSE; BOOL fTlsStart = FALSE; DWORD dwType = 0; DWORD dwBlobLength = 0; if (NULL == pPacket) { return; } bCode = pPacket->bCode; if (bCode > MAXEAPCODE) { bCode = 0; } wLength = WireToHostFormat16(pPacket->pbLength); if ( FValidPacket(pPacket) && PPP_EAP_TLS == pPacket->bType && ( EAPCODE_Request == pPacket->bCode || EAPCODE_Response == pPacket->bCode)) { fLengthIncluded = pPacket->bFlags & EAPTLS_PACKET_FLAG_LENGTH_INCL; fMoreFragments = pPacket->bFlags & EAPTLS_PACKET_FLAG_MORE_FRAGMENTS; fTlsStart = pPacket->bFlags & EAPTLS_PACKET_FLAG_TLS_START; dwType = pPacket->bType; if (fLengthIncluded) { dwBlobLength = WireToHostFormat32(pPacket->pbData); } } EapTlsTrace("%s %s (Code: %d) packet: Id: %d, Length: %d, Type: %d, " "TLS blob length: %d. Flags: %s%s%s", fInput ? ">> Received" : "<< Sending", g_szEapTlsCodeName[bCode], pPacket->bCode, pPacket->bId, wLength, dwType, dwBlobLength, fLengthIncluded ? "L" : "", fMoreFragments ? "M" : "", fTlsStart ? "S" : ""); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: RasEapGetInfo entry point called by the EAP-PPP engine. */ DWORD RasEapGetInfo( IN DWORD dwEapTypeId, OUT PPP_EAP_INFO* pInfo ) { DWORD dwErr = NO_ERROR; EapTlsTrace("RasEapGetInfo"); //ZeroMemory ( g_CachedCreds, sizeof(EAPTLS_CACHED_CREDS) * 4 ); RTASSERT(NULL != pInfo); if (PPP_EAP_TLS != dwEapTypeId #ifdef IMPL_PEAP && PPP_EAP_PEAP != dwEapTypeId #endif ) { EapTlsTrace("EAP Type %d is not supported", dwEapTypeId); dwErr = ERROR_NOT_SUPPORTED; goto LDone; } ZeroMemory(pInfo, sizeof(PPP_EAP_INFO)); if ( PPP_EAP_TLS == dwEapTypeId ) { pInfo->dwEapTypeId = PPP_EAP_TLS; pInfo->RasEapInitialize = EapTlsInitialize; pInfo->RasEapBegin = EapTlsBegin; pInfo->RasEapEnd = EapTlsEnd; pInfo->RasEapMakeMessage = EapTlsMakeMessage; } else { pInfo->dwEapTypeId = PPP_EAP_PEAP; pInfo->RasEapInitialize = EapPeapInitialize; pInfo->RasEapBegin = EapPeapBegin; pInfo->RasEapEnd = EapPeapEnd; pInfo->RasEapMakeMessage = EapPeapMakeMessage; } LDone: return(dwErr); } ////////////////////////////////////////// ////// Dialog procs for interactive UI ////////////////////////////////////////// //// Server Configuration // BOOL ValidateServerInitDialog( IN HWND hWnd, IN LPARAM lParam ) { EAPTLS_VALIDATE_SERVER * pValidateServer; SetWindowLongPtr(hWnd, DWLP_USER, lParam); pValidateServer = (EAPTLS_VALIDATE_SERVER *)lParam; //Set the Dialog Title SetWindowText( hWnd, pValidateServer->awszTitle ); SetWindowText ( GetDlgItem(hWnd, IDC_MESSAGE), pValidateServer->awszWarning ); if ( !pValidateServer->fShowCertDetails ) { EnableWindow ( GetDlgItem(hWnd, IDC_BTN_VIEW_CERTIFICATE), FALSE ); ShowWindow( GetDlgItem(hWnd, IDC_BTN_VIEW_CERTIFICATE), SW_HIDE ); } return FALSE; } BOOL ValidateServerCommand( IN EAPTLS_VALIDATE_SERVER *pValidateServer, IN WORD wNotifyCode, IN WORD wId, IN HWND hWndDlg, IN HWND hWndCtrl ) { BOOL fRetVal = FALSE; switch(wId) { case IDC_BTN_VIEW_CERTIFICATE: { HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; CRYPT_HASH_BLOB chb; WCHAR szError[256]; LoadString( GetResouceDLLHInstance(), IDS_NO_CERT_DETAILS, szError, 255); hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_READONLY_FLAG |CERT_SYSTEM_STORE_CURRENT_USER, L"CA" ); if ( !hCertStore ) { MessageBox ( hWndDlg, szError, pValidateServer->awszTitle, MB_OK|MB_ICONSTOP ); break; } chb.cbData = pValidateServer->Hash.cbHash; chb.pbData = pValidateServer->Hash.pbHash; pCertContext = CertFindCertificateInStore( hCertStore, 0, 0, CERT_FIND_HASH, &chb, 0); if ( NULL == pCertContext ) { MessageBox ( hWndDlg, szError, pValidateServer->awszTitle, MB_OK|MB_ICONSTOP ); if ( hCertStore ) CertCloseStore( hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ); break; } // // Show Cert detail // ShowCertDetails ( hWndDlg, hCertStore, pCertContext ); if ( pCertContext ) CertFreeCertificateContext(pCertContext); if ( hCertStore ) CertCloseStore( hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ); fRetVal = TRUE; } break; case IDOK: case IDCANCEL: // // Delete context from store // { HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; CRYPT_HASH_BLOB chb; hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"CA" ); if ( hCertStore ) { chb.cbData = pValidateServer->Hash.cbHash; chb.pbData = pValidateServer->Hash.pbHash; pCertContext = CertFindCertificateInStore( hCertStore, 0, 0, CERT_FIND_HASH, &chb, 0); if ( pCertContext ) CertDeleteCertificateFromStore(pCertContext); CertCloseStore( hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ); } } EndDialog(hWndDlg, wId); fRetVal = TRUE; break; default: break; } return fRetVal; } INT_PTR CALLBACK ValidateServerDialogProc( IN HWND hWnd, IN UINT unMsg, IN WPARAM wParam, IN LPARAM lParam ) { EAPTLS_VALIDATE_SERVER * pValidateServer; switch (unMsg) { case WM_INITDIALOG: return(ValidateServerInitDialog(hWnd, lParam)); case WM_HELP: case WM_CONTEXTMENU: { ContextHelp(g_adwHelp, hWnd, unMsg, wParam, lParam); break; } case WM_COMMAND: pValidateServer = (EAPTLS_VALIDATE_SERVER *)GetWindowLongPtr(hWnd, DWLP_USER); return(ValidateServerCommand(pValidateServer, HIWORD(wParam), LOWORD(wParam), hWnd, (HWND)lParam) ); } return(FALSE); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: */ DWORD InvokeValidateServerDialog( IN HWND hWndParent, IN BYTE* pUIContextData, IN DWORD dwSizeofUIContextData, OUT BYTE** ppDataFromInteractiveUI, OUT DWORD* pdwSizeOfDataFromInteractiveUI ) { INT_PTR nRet; EAPTLS_VALIDATE_SERVER* pEapTlsValidateServer; BYTE* pbResult = NULL; DWORD dwSizeOfResult; DWORD dwErr = NO_ERROR; *ppDataFromInteractiveUI = NULL; *pdwSizeOfDataFromInteractiveUI = 0; pbResult = LocalAlloc(LPTR, sizeof(BYTE)); if (NULL == pbResult) { dwErr = GetLastError(); goto LDone; } dwSizeOfResult = sizeof(BYTE); pEapTlsValidateServer = (EAPTLS_VALIDATE_SERVER*) pUIContextData; nRet = DialogBoxParam( GetResouceDLLHInstance(), MAKEINTRESOURCE(IDD_VALIDATE_SERVER), hWndParent, ValidateServerDialogProc, (LPARAM)pEapTlsValidateServer); if (-1 == nRet) { dwErr = GetLastError(); goto LDone; } else if (IDOK == nRet) { *pbResult = IDYES; } else { *pbResult = IDNO; } *ppDataFromInteractiveUI = pbResult; *pdwSizeOfDataFromInteractiveUI = dwSizeOfResult; pbResult = NULL; LDone: LocalFree(pbResult); return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: RasEapInvokeInteractiveUI entry point called by the EAP-PPP engine by name. */ DWORD RasEapInvokeInteractiveUI( IN DWORD dwEapTypeId, IN HWND hWndParent, IN BYTE* pUIContextData, IN DWORD dwSizeofUIContextData, OUT BYTE** ppDataFromInteractiveUI, OUT DWORD* pdwSizeOfDataFromInteractiveUI ) { DWORD dwRetCode = NO_ERROR; PPEAP_EAP_INFO pEapList = NULL; PPEAP_EAP_INFO pEapInfo = NULL; PPEAP_INTERACTIVE_UI pPeapInteractiveUI = NULL; RASEAPINVOKEINTERACTIVEUI pfnInvoke = NULL; RASEAPFREE pfnFree = NULL; if ( PPP_EAP_TLS == dwEapTypeId ) { dwRetCode = InvokeValidateServerDialog( hWndParent, pUIContextData, dwSizeofUIContextData, ppDataFromInteractiveUI, pdwSizeOfDataFromInteractiveUI); } else if ( PPP_EAP_PEAP == dwEapTypeId ) { dwRetCode = PeapEapInfoGetList ( NULL, &pEapList ); if ( NO_ERROR != dwRetCode || NULL == pEapList ) { EapTlsTrace("Unable to load list of EAP Types on this machine."); goto LDone; } pPeapInteractiveUI = (PPEAP_INTERACTIVE_UI)pUIContextData; // // Load relevant IdentityUI DLL and then invoke // the dwRetCode = PeapEapInfoFindListNode ( pPeapInteractiveUI->dwEapTypeId, pEapList, &pEapInfo ); if ( NO_ERROR != dwRetCode || NULL == pEapInfo ) { EapTlsTrace("Cannot find configured PEAP in the list of EAP Types on this machine."); goto LDone; } if ( !((pfnInvoke) = (RASEAPINVOKEINTERACTIVEUI) GetProcAddress( pEapInfo->hEAPModule, "RasEapInvokeInteractiveUI")) || !((pfnFree ) = (RASEAPFREE) GetProcAddress( pEapInfo->hEAPModule, "RasEapFreeMemory"))) { dwRetCode = GetLastError(); EapTlsTrace("failed to get entrypoint. rc=%d", dwRetCode); goto LDone; } // // Invoke the entry point here // dwRetCode = pfnInvoke ( pPeapInteractiveUI->dwEapTypeId, hWndParent, pPeapInteractiveUI->bUIContextData, pPeapInteractiveUI->dwSizeofUIContextData, ppDataFromInteractiveUI, pdwSizeOfDataFromInteractiveUI ); } else { dwRetCode = ERROR_INVALID_PARAMETER; } LDone: PeapEapInfoFreeList( pEapList ); return dwRetCode; } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: Called to get a context buffer for this EAP session and pass initialization information. This will be called before any other call is made. */ DWORD EapTlsBegin( OUT VOID** ppWorkBuffer, IN PPP_EAP_INPUT* pPppEapInput ) { DWORD dwErr = NO_ERROR; LONG lRet; HKEY hKey = NULL; DWORD dwType; DWORD dwValue; DWORD dwSize; BOOL fServer = FALSE; BOOL fWinLogonData = FALSE; EAPTLSCB* pEapTlsCb = NULL; EAPTLS_CONN_PROPERTIES * pConnProp = NULL; PBYTE pbDecPIN = NULL; DWORD cbDecPIN = 0; EAPTLS_USER_PROPERTIES * pUserProp = NULL; RTASSERT(NULL != ppWorkBuffer); RTASSERT(NULL != pPppEapInput); EapTlsTrace(""); // Blank line EapTlsTrace("EapTlsBegin(%ws)", pPppEapInput->pwszIdentity ? pPppEapInput->pwszIdentity : L""); #if WINVER > 0x0500 //This piece of code causes boot time perf hit //So it is removed until XPSP2 #if 0 dwErr = VerifyCallerTrust(_ReturnAddress()); if ( NO_ERROR != dwErr ) { EapTlsTrace("Unauthorized use of TLS attempted"); goto LDone; } #endif #endif // Allocate the context buffer pEapTlsCb = LocalAlloc(LPTR, sizeof(EAPTLSCB)); if (NULL == pEapTlsCb) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pEapTlsCb->EapTlsState = EAPTLS_STATE_INITIAL; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); pEapTlsCb->fFlags = (pPppEapInput->fAuthenticator) ? EAPTLSCB_FLAG_SERVER : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_ROUTER) ? EAPTLSCB_FLAG_ROUTER : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_LOGON) ? EAPTLSCB_FLAG_LOGON : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_NON_INTERACTIVE) ? EAPTLSCB_FLAG_NON_INTERACTIVE : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_FIRST_LINK) ? EAPTLSCB_FLAG_FIRST_LINK : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_MACHINE_AUTH) ? EAPTLSCB_FLAG_MACHINE_AUTH : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_GUEST_ACCESS) ? EAPTLSCB_FLAG_GUEST_ACCESS : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & RAS_EAP_FLAG_8021X_AUTH) ? EAPTLSCB_FLAG_8021X_AUTH : 0; pEapTlsCb->fFlags |= (pPppEapInput->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP )? EAPTLSCB_FLAG_EXECUTING_PEAP : 0; if (pPppEapInput->fFlags & RAS_EAP_FLAG_8021X_AUTH) { EapTlsTrace("EapTlsBegin: Detected 8021X authentication"); } if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { EapTlsTrace("EapTlsBegin: Detected PEAP authentication"); } pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCRED_INVALID; pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCTXT_INVALID; if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER ) { fServer = TRUE; pEapTlsCb->hEventLog = pPppEapInput->hReserved; pEapTlsCb->fContextReq = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY | ASC_REQ_MUTUAL_AUTH | ASC_RET_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM; // //Server will always allow guest access. // if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS || pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) pEapTlsCb->fContextReq |= ASC_REQ_MUTUAL_AUTH; if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { // // We are executing PEap. So take the user props from // PppEapInput rather than serverconfigdataio. // dwErr = ReadUserData(pPppEapInput->pUserData, pPppEapInput->dwSizeOfUserData, &(pEapTlsCb->pUserProp)); if (NO_ERROR != dwErr) { goto LDone; } } else { dwErr = ServerConfigDataIO(TRUE /* fRead */, NULL /* pwszMachineName */, (BYTE**)&(pEapTlsCb->pUserProp), 0); if (NO_ERROR != dwErr) { goto LDone; } } pEapTlsCb->bId = pPppEapInput->bInitialId; } else { // // Client side config for TLS // pEapTlsCb->fContextReq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_STREAM; pEapTlsCb->hTokenImpersonateUser = pPppEapInput->hTokenImpersonateUser; #if 0 if (NULL == pPppEapInput->pUserData) { dwErr = ERROR_INVALID_DATA; EapTlsTrace("No user data!"); goto LDone; } #endif if (pPppEapInput->pUserData != NULL) { if (0 != *(DWORD*)(pPppEapInput->pUserData)) { fWinLogonData = TRUE; } } if (fWinLogonData) { // Data from Winlogon pEapTlsCb->fFlags |= EAPTLSCB_FLAG_WINLOGON_DATA; pEapTlsCb->pUserData = LocalAlloc(LPTR, pPppEapInput->dwSizeOfUserData); if (NULL == pEapTlsCb->pUserData) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pEapTlsCb->pUserData, pPppEapInput->pUserData, pPppEapInput->dwSizeOfUserData); pEapTlsCb->dwSizeOfUserData = pPppEapInput->dwSizeOfUserData; } else { dwErr = ReadUserData(pPppEapInput->pUserData, pPppEapInput->dwSizeOfUserData, &(pEapTlsCb->pUserProp)); if (NO_ERROR != dwErr) { goto LDone; } } } dwErr = ReadConnectionData( ( pPppEapInput->fFlags & RAS_EAP_FLAG_8021X_AUTH ), pPppEapInput->pConnectionData, pPppEapInput->dwSizeOfConnectionData, &pConnProp); if (NO_ERROR != dwErr) { goto LDone; } dwErr = ConnPropGetV1Struct ( pConnProp, &(pEapTlsCb->pConnProp) ); if ( NO_ERROR != dwErr ) { goto LDone; } // // Check to see if it is a 8021x and smart card user. // If so, decrypt the pin if one is present // if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) && pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH && pEapTlsCb->pConnProp && !(pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_REGISTRY) ) { if ( NO_ERROR != DecryptData ( (PBYTE)pEapTlsCb->pUserProp->pwszPin, pEapTlsCb->pUserProp->dwSize - sizeof(EAPTLS_USER_PROPERTIES) - ( ( wcslen(pEapTlsCb->pUserProp->pwszDiffUser) + 1 ) * sizeof (WCHAR) ), &pbDecPIN, &cbDecPIN ) ) { // //Dummy pin allocation // pbDecPIN = LocalAlloc(LPTR, 5); cbDecPIN = lstrlen((LPWSTR)pbDecPIN); } AllocUserDataWithNewPin(pEapTlsCb->pUserProp, pbDecPIN, cbDecPIN, &pUserProp); LocalFree(pEapTlsCb->pUserProp); pEapTlsCb->pUserProp = pUserProp; } // Save the identity. On the authenticatee side, this was obtained by // calling RasEapGetIdentity; on the authenticator side this was // obtained by the Identity request message. wcsncpy(pEapTlsCb->awszIdentity, pPppEapInput->pwszIdentity ? pPppEapInput->pwszIdentity : L"", UNLEN); _wcslwr(pEapTlsCb->awszIdentity); lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, EAPTLS_KEY_13, 0, KEY_READ, &hKey); if (ERROR_SUCCESS == lRet) { dwSize = sizeof(dwValue); lRet = RegQueryValueEx(hKey, EAPTLS_VAL_MAX_TLS_MESSAGE_LENGTH, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if ( (ERROR_SUCCESS == lRet) && (REG_DWORD == dwType)) { g_dwMaxBlobSize = dwValue; } else { g_dwMaxBlobSize = DEFAULT_MAX_BLOB_SIzE; } EapTlsTrace("MaxTLSMessageLength is now %d", g_dwMaxBlobSize); if (fServer) { dwSize = sizeof(dwValue); lRet = RegQueryValueEx(hKey, EAPTLS_VAL_IGNORE_NO_REVOCATION_CHECK, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if ( (ERROR_SUCCESS == lRet) && (REG_DWORD == dwType)) { g_fIgnoreNoRevocationCheck = dwValue; } else { g_fIgnoreNoRevocationCheck = FALSE; } EapTlsTrace("CRYPT_E_NO_REVOCATION_CHECK will %sbe ignored", g_fIgnoreNoRevocationCheck ? "" : "not "); dwSize = sizeof(dwValue); lRet = RegQueryValueEx(hKey, EAPTLS_VAL_IGNORE_REVOCATION_OFFLINE, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if ( (ERROR_SUCCESS == lRet) && (REG_DWORD == dwType)) { g_fIgnoreRevocationOffline = dwValue; } else { g_fIgnoreRevocationOffline = FALSE; } EapTlsTrace("CRYPT_E_REVOCATION_OFFLINE will %sbe ignored", g_fIgnoreRevocationOffline ? "" : "not "); dwSize = sizeof(dwValue); lRet = RegQueryValueEx(hKey, EAPTLS_VAL_NO_ROOT_REVOCATION_CHECK, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if ( (ERROR_SUCCESS == lRet) && (REG_DWORD == dwType)) { g_fNoRootRevocationCheck = dwValue; } else { g_fNoRootRevocationCheck = TRUE; } EapTlsTrace("The root cert will %sbe checked for revocation", g_fNoRootRevocationCheck ? "not " : ""); dwSize = sizeof(dwValue); lRet = RegQueryValueEx(hKey, EAPTLS_VAL_NO_REVOCATION_CHECK, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if ( (ERROR_SUCCESS == lRet) && (REG_DWORD == dwType)) { g_fNoRevocationCheck = dwValue; } else { g_fNoRevocationCheck = FALSE; } EapTlsTrace("The cert will %sbe checked for revocation", g_fNoRevocationCheck ? "not " : ""); } RegCloseKey(hKey); } // pbBlobIn, pbBlobOut, and pAttribues are initially NULL and cbBlobIn, // cbBlobInBuffer, cbBlobOut, cbBlobOutBuffer, dwBlobOutOffset, // dwBlobOutOffsetNew, and dwBlobInRemining are all 0. // bCode, dwErr are also 0. LDone: LocalFree(pConnProp); LocalFree(pbDecPIN); if ( (NO_ERROR != dwErr) && (NULL != pEapTlsCb)) { LocalFree(pEapTlsCb->pUserProp); LocalFree(pEapTlsCb->pConnProp); LocalFree(pEapTlsCb->pUserData); LocalFree(pEapTlsCb); pEapTlsCb = NULL; } *ppWorkBuffer = pEapTlsCb; return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: Called to free the context buffer for this EAP session. Called after this session is completed successfully or not. */ DWORD EapTlsEnd( IN EAPTLSCB* pEapTlsCb ) { SECURITY_STATUS Status; EapTlsTrace("EapTlsEnd"); // Blank line if (NULL != pEapTlsCb) { EapTlsTrace("EapTlsEnd(%ws)", pEapTlsCb->awszIdentity ? pEapTlsCb->awszIdentity : L""); if (NULL != pEapTlsCb->pCertContext) { // // Since we are using cached creds dont free the // cert context. // #if 0 if ( !(EAPTLSCB_FLAG_SERVER & pEapTlsCb->fFlags ) ) CertFreeCertificateContext(pEapTlsCb->pCertContext); #endif // Always returns TRUE; pEapTlsCb->pCertContext = NULL; } if (!(EAPTLSCB_FLAG_HCTXT_INVALID & pEapTlsCb->fFlags)) { Status = DeleteSecurityContext(&pEapTlsCb->hContext); if (SEC_E_OK != Status) { EapTlsTrace("DeleteSecurityContext failed and returned 0x%x", Status); } } if (!(EAPTLSCB_FLAG_HCRED_INVALID & pEapTlsCb->fFlags)) { // // Dont free the credentials handle. We are using // cached credentials. // #if 0 if ( !(EAPTLSCB_FLAG_SERVER & pEapTlsCb->fFlags ) ) { EapTlsTrace("Freeing Credentials handle: flags are 0x%x", pEapTlsCb->fFlags); Status = FreeCredentialsHandle(&pEapTlsCb->hCredential); if (SEC_E_OK != Status) { EapTlsTrace("FreeCredentialsHandle failed and returned 0x%x", Status); } } #endif ZeroMemory( &pEapTlsCb->hCredential, sizeof(CredHandle)); } pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCRED_INVALID; pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCTXT_INVALID; if (NULL != pEapTlsCb->pAttributes) { RasAuthAttributeDestroy(pEapTlsCb->pAttributes); } LocalFree(pEapTlsCb->pUserProp); LocalFree(pEapTlsCb->pConnProp); LocalFree(pEapTlsCb->pNewConnProp); LocalFree(pEapTlsCb->pUserData); LocalFree(pEapTlsCb->pbBlobIn); LocalFree(pEapTlsCb->pbBlobOut); LocalFree(pEapTlsCb->pUIContextData); if(NULL != pEapTlsCb->pSavedPin) { if(NULL != pEapTlsCb->pSavedPin->pwszPin) { ZeroMemory(pEapTlsCb->pSavedPin->pwszPin, wcslen(pEapTlsCb->pSavedPin->pwszPin) * sizeof(WCHAR)); LocalFree(pEapTlsCb->pSavedPin->pwszPin); } LocalFree(pEapTlsCb->pSavedPin); } // // auth result is not good or we are waiting for user ok //and we are a client. // if ( ( NO_ERROR != pEapTlsCb->dwAuthResultCode || EAPTLS_STATE_WAIT_FOR_USER_OK == pEapTlsCb->EapTlsState )&& !( pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER ) ) { EapTlsTrace("Auth failed so freeing cached creds."); EnterCriticalSection ( &g_csProtectCachedCredentials ); FreeCachedCredentials(pEapTlsCb); LeaveCriticalSection ( &g_csProtectCachedCredentials ); } ZeroMemory(pEapTlsCb, sizeof(EAPTLSCB)); LocalFree(pEapTlsCb); } return(NO_ERROR); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: Called to reset everything in pEapTlsCb (except awszIdentity, the flag indicating whether we are the server/router, fContextReq, pConnProp, UserProp and hTokenImpersonateUser) to the initial state, for example, when a client gets a TLS Start packet. */ DWORD EapTlsReset( IN EAPTLSCB* pEapTlsCb ) { SECURITY_STATUS Status; DWORD dwErr = NO_ERROR; DWORD fFlags; EapTlsTrace("EapTlsReset"); RTASSERT(NULL != pEapTlsCb); pEapTlsCb->EapTlsState = EAPTLS_STATE_INITIAL; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); // Forget all the flags, except whether we are a server/client, router fFlags = pEapTlsCb->fFlags; pEapTlsCb->fFlags = fFlags & EAPTLSCB_FLAG_SERVER; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_ROUTER; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_LOGON; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_WINLOGON_DATA; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_NON_INTERACTIVE; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_FIRST_LINK; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_MACHINE_AUTH; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_GUEST_ACCESS; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_8021X_AUTH; pEapTlsCb->fFlags |= fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP; // awszIdentity, hTokenImpersonateUser, pConnProp, UserProp remain the same if (NULL != pEapTlsCb->pCertContext) { // // We are using cached creds now. So no need to // release the context // #if 0 if ( !(EAPTLSCB_FLAG_SERVER & pEapTlsCb->fFlags ) ) CertFreeCertificateContext(pEapTlsCb->pCertContext); #endif // Always returns TRUE; pEapTlsCb->pCertContext = NULL; } if (!(EAPTLSCB_FLAG_HCTXT_INVALID & fFlags)) { Status = DeleteSecurityContext(&pEapTlsCb->hContext); if (SEC_E_OK != Status) { EapTlsTrace("DeleteSecurityContext failed and returned 0x%x", Status); } ZeroMemory(&pEapTlsCb->hContext, sizeof(CtxtHandle)); } if (!(EAPTLSCB_FLAG_HCRED_INVALID & fFlags)) { // // Since we cache client ans server creds, we dont // free the credentials handle anymore. // #if 0 if ( !(EAPTLSCB_FLAG_SERVER & pEapTlsCb->fFlags ) ) { EapTlsTrace("Freeing Credentials handle: flags are 0x%x", pEapTlsCb->fFlags); //if this is a server we are using cached creds Status = FreeCredentialsHandle(&pEapTlsCb->hCredential); if (SEC_E_OK != Status) { EapTlsTrace("FreeCredentialsHandle failed and returned 0x%x", Status); } } #endif ZeroMemory(&pEapTlsCb->hCredential, sizeof(CredHandle)); } pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCRED_INVALID; pEapTlsCb->fFlags |= EAPTLSCB_FLAG_HCTXT_INVALID; if (NULL != pEapTlsCb->pAttributes) { RasAuthAttributeDestroy(pEapTlsCb->pAttributes); pEapTlsCb->pAttributes = NULL; } // fContextReq remains the same LocalFree(pEapTlsCb->pbBlobIn); pEapTlsCb->pbBlobIn = NULL; pEapTlsCb->cbBlobIn = pEapTlsCb->cbBlobInBuffer = 0; pEapTlsCb->dwBlobInRemining = 0; LocalFree(pEapTlsCb->pbBlobOut); pEapTlsCb->pbBlobOut = NULL; pEapTlsCb->cbBlobOut = pEapTlsCb->cbBlobOutBuffer = 0; pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew = 0; LocalFree(pEapTlsCb->pUIContextData); pEapTlsCb->pUIContextData = NULL; pEapTlsCb->dwAuthResultCode = NO_ERROR; dwErr = GetCredentials(pEapTlsCb); if (NO_ERROR == dwErr) { pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_HCRED_INVALID; } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Called to process an incoming packet and/or send a packet. cbSendPacket is the size in bytes of the buffer pointed to by pSendPacket. */ DWORD EapTlsMakeMessage( IN EAPTLSCB* pEapTlsCb, IN PPP_EAP_PACKET* pInput, OUT PPP_EAP_PACKET* pOutput, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { EAPTLS_PACKET* pReceivePacket = (EAPTLS_PACKET*) pInput; EAPTLS_PACKET* pSendPacket = (EAPTLS_PACKET*) pOutput; DWORD dwErr = NO_ERROR; BOOL fServer = FALSE; BOOL fRouter = FALSE; RTASSERT(NULL != pEapTlsCb); RTASSERT(NULL != pEapOutput); EapTlsTrace(""); // Blank line EapTlsTrace("EapTlsMakeMessage(%ws)", pEapTlsCb->awszIdentity ? pEapTlsCb->awszIdentity : L""); PrintEapTlsPacket(pReceivePacket, TRUE /* fInput */); if (!FValidPacket(pReceivePacket)) { pEapOutput->Action = EAPACTION_NoAction; return(ERROR_PPP_INVALID_PACKET); } fServer = pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER; fRouter = pEapTlsCb->fFlags & EAPTLSCB_FLAG_ROUTER; if ( !fServer && !fRouter && !(EAPTLSCB_FLAG_LOGON & pEapTlsCb->fFlags) && !(EAPTLSCB_FLAG_MACHINE_AUTH & pEapTlsCb->fFlags) && !(EAPTLSCB_FLAG_GUEST_ACCESS & pEapTlsCb->fFlags) && !(EAPTLSCB_FLAG_EXECUTING_PEAP & pEapTlsCb->fFlags) && !ImpersonateLoggedOnUser(pEapTlsCb->hTokenImpersonateUser)) { dwErr = GetLastError(); EapTlsTrace("ImpersonateLoggedOnUser(%d) failed and returned 0x%x", pEapTlsCb->hTokenImpersonateUser, dwErr); pEapOutput->Action = EAPACTION_NoAction; return(dwErr); } if (fServer) { dwErr = EapTlsSMakeMessage(pEapTlsCb, pReceivePacket, pSendPacket, cbSendPacket, pEapOutput, pEapInput); } else { dwErr = EapTlsCMakeMessage(pEapTlsCb, pReceivePacket, pSendPacket, cbSendPacket, pEapOutput, pEapInput); if ( pEapOutput->Action == EAPACTION_Done && pEapOutput->dwAuthResultCode == NO_ERROR ) { // // Auth is done and result is good Cache Credentials here // and we are a client if ( !( pEapTlsCb->fFlags & EAPTLSCB_FLAG_USING_CACHED_CREDS ) ) { // // If we are not using cached credentials // SetCachedCredentials (pEapTlsCb); } } } if ( !fServer && !fRouter && !(EAPTLSCB_FLAG_LOGON & pEapTlsCb->fFlags) && !RevertToSelf()) { EapTlsTrace("RevertToSelf failed and returned 0x%x", GetLastError()); } return(dwErr); } /* Returns: Notes: */ DWORD AssociatePinWithCertificate( IN PCCERT_CONTEXT pCertContext, IN EAPTLS_USER_PROPERTIES* pUserProp, IN BOOL fErasePIN, IN BOOL fCheckNullPin ) { DWORD cbData; CRYPT_KEY_PROV_INFO* pCryptKeyProvInfo = NULL; HCRYPTPROV hProv = 0; DWORD count; CHAR* pszPin = NULL; UNICODE_STRING UnicodeString; DWORD dwErr = NO_ERROR; EapTlsTrace("AssociatePinWithCertificate"); cbData = 0; if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData)) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", dwErr); goto LDone; } pCryptKeyProvInfo = LocalAlloc(LPTR, cbData); if (NULL == pCryptKeyProvInfo) { dwErr = GetLastError(); EapTlsTrace("Out of memory"); goto LDone; } if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pCryptKeyProvInfo, &cbData)) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", dwErr); goto LDone; } if (!CryptAcquireContext( &hProv, pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, pCryptKeyProvInfo->dwProvType, (pCryptKeyProvInfo->dwFlags & ~CERT_SET_KEY_PROV_HANDLE_PROP_ID) | CRYPT_SILENT)) { dwErr = GetLastError(); EapTlsTrace("CryptAcquireContext failed: 0x%x", dwErr); goto LDone; } if (pUserProp->pwszPin[0] != 0) { if ( fErasePIN ) DecodePin(pUserProp); count = WideCharToMultiByte( CP_UTF8, 0, pUserProp->pwszPin, -1, NULL, 0, NULL, NULL); if (0 == count) { dwErr = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwErr); goto LDone; } pszPin = LocalAlloc(LPTR, count); if (NULL == pszPin) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed: 0x%x", dwErr); goto LDone; } count = WideCharToMultiByte( CP_UTF8, 0, pUserProp->pwszPin, -1, pszPin, count, NULL, NULL); if (0 == count) { dwErr = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwErr); goto LDone; } } else { if ( fCheckNullPin ) { // //we got an empty pin. So all we do is an alloc //and nothing else. // pszPin = LocalAlloc(LPTR, 5 ); if (NULL == pszPin) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed: 0x%x", dwErr); goto LDone; } count = 2; } } if ( pszPin ) { if (!CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, pszPin, 0)) { dwErr = GetLastError(); EapTlsTrace("CryptSetProvParam failed: 0x%x", dwErr); ZeroMemory(pszPin, count); goto LDone; } ZeroMemory(pszPin, count); } if (!CertSetCertificateContextProperty( pCertContext, CERT_KEY_PROV_HANDLE_PROP_ID, 0, (VOID*)hProv)) { dwErr = GetLastError(); EapTlsTrace("CertSetCertificateContextProperty failed: 0x%x", dwErr); goto LDone; } // Since I didn't set CERT_STORE_NO_CRYPT_RELEASE_FLAG in the above call, // the hProv is implicitly released when either the property is set to NULL // or on the final free of the CertContext. hProv = 0; LDone: if (0 != hProv) { CryptReleaseContext(hProv, 0); } LocalFree(pCryptKeyProvInfo); LocalFree(pszPin); // Nuke the PIN. if ( fErasePIN ) { pUserProp->usLength = 0; pUserProp->usMaximumLength = 0; ZeroMemory(pUserProp->pwszPin, wcslen(pUserProp->pwszPin) * sizeof(WCHAR)); } return(dwErr); } void FreeCachedCredentials ( EAPTLSCB * pEapTlsCb ) { SECURITY_STATUS Status; DWORD dwIndex = 0; EapTlsTrace ("FreeCachedCredentials"); if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_PEAP_CACHED_CREDS_INDEX; } else { dwIndex = VPN_PEAP_CACHED_CREDS_INDEX; } } else { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_CACHED_CREDS_INDEX; } else { dwIndex = VPN_CACHED_CREDS_INDEX; } } if ( NULL != g_CachedCreds[dwIndex].pbHash ) { LocalFree( g_CachedCreds[dwIndex].pbHash ); g_CachedCreds[dwIndex].pbHash = NULL; } if ( g_CachedCreds[dwIndex].fCachedCredentialInitialized ) { EapTlsTrace("Freeing Credentials handle for index %d.", dwIndex); Status = FreeCredentialsHandle(&(g_CachedCreds[dwIndex].hCachedCredential)); if (SEC_E_OK != Status) { EapTlsTrace("FreeCredentialsHandle failed and returned 0x%x", Status); } } g_CachedCreds[dwIndex].fCachedCredentialInitialized = FALSE; ZeroMemory( &(g_CachedCreds[dwIndex].hCachedCredential), sizeof(CredHandle)); g_CachedCreds[dwIndex].cbHash = 0; if ( g_CachedCreds[dwIndex].pcCachedCertContext ) { CertFreeCertificateContext ( g_CachedCreds[dwIndex].pcCachedCertContext ); g_CachedCreds[dwIndex].pcCachedCertContext = NULL; } } DWORD IsTLSSessionReconnect ( EAPTLSCB * pEapTlsCb, BOOL * pfIsReconnect ) { DWORD dwRetCode = NO_ERROR; SecPkgContext_SessionInfo SessionInfo; EapTlsTrace("IsTLSSessionReconnect"); ZeroMemory ( &SessionInfo, sizeof(SessionInfo) ); dwRetCode = QueryContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_SESSION_INFO, (PVOID)&SessionInfo ); if(dwRetCode != SEC_E_OK) { EapTlsTrace ("QueryContextAttributes failed querying session info 0x%x", dwRetCode); } else { *pfIsReconnect = ( SessionInfo.dwFlags & SSL_SESSION_RECONNECT ); } return dwRetCode; } // // TLS Fast reconnect and cookie management functions // DWORD SetTLSFastReconnect ( EAPTLSCB * pEapTlsCb , BOOL fEnable) { DWORD dwRetCode = NO_ERROR; BOOL fReconnect = FALSE; SCHANNEL_SESSION_TOKEN SessionToken = {0}; SecBufferDesc OutBuffer; SecBuffer OutBuffers[1]; EapTlsTrace ("SetTLSFastReconnect"); dwRetCode = IsTLSSessionReconnect ( pEapTlsCb, &fReconnect ); if ( SEC_E_OK != dwRetCode ) { return dwRetCode; } if ( fEnable ) { // // We ahve been asked to enable fast reconnects. // Check to see if we are already enabled for reconnects // If so, we dont have to do this again. // if ( fReconnect ) { EapTlsTrace ("The session is already setup for reconnects. No need to enable."); return NO_ERROR; } } else { if ( !fReconnect ) { EapTlsTrace("The session is not setup for fast reconnects. No need to disable."); return NO_ERROR; } } SessionToken.dwTokenType = SCHANNEL_SESSION; SessionToken.dwFlags = ( fEnable ? SSL_SESSION_ENABLE_RECONNECTS: SSL_SESSION_DISABLE_RECONNECTS ); OutBuffers[0].pvBuffer = &SessionToken; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = sizeof(SessionToken); OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; dwRetCode = ApplyControlToken (&(pEapTlsCb->hContext), &OutBuffer); if(dwRetCode != SEC_E_OK) { EapTlsTrace("Error enabling Fast Reconnects : 0x%x", dwRetCode); } else { EapTlsTrace ("Fast Reconnects Enabled/Disabled"); } return dwRetCode; } // // Get Session Cookie information // DWORD GetTLSSessionCookie ( EAPTLSCB * pEapTlsCb, PBYTE * ppbCookie, DWORD * pdwCookie, BOOL * pfIsReconnect ) { DWORD dwRetCode = NO_ERROR; SecPkgContext_SessionAppData AppData; RTASSERT(NULL != pEapTlsCb); RTASSERT(NULL != ppbCookie); RTASSERT(NULL != pdwCookie); EapTlsTrace ("GetTLSSessionCookie"); *ppbCookie = NULL; *pdwCookie = 0; *pfIsReconnect = FALSE; dwRetCode = IsTLSSessionReconnect ( pEapTlsCb, pfIsReconnect ); if(dwRetCode != SEC_E_OK) { EapTlsTrace ("QueryContextAttributes failed querying session info 0x%x", dwRetCode); } else { if ( *pfIsReconnect ) { EapTlsTrace ("Session Reconnected."); *pfIsReconnect = TRUE; // // Get the cookie // ZeroMemory(&AppData, sizeof(AppData)); dwRetCode = QueryContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_APP_DATA, (PVOID)&AppData); if(dwRetCode != SEC_E_OK) { EapTlsTrace("QueryContextAttributes failed querying session cookie. Error 0x%x", dwRetCode); } else { *ppbCookie = (PBYTE)LocalAlloc (LPTR, AppData.cbAppData ); if ( NULL == *ppbCookie ) { EapTlsTrace("Failed allocating memory for session cookie"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; } else { CopyMemory( *ppbCookie, AppData.pbAppData, AppData.cbAppData ); *pdwCookie = AppData.cbAppData; } // Free returned buffer. FreeContextBuffer(AppData.pbAppData); } } } return dwRetCode; } // // Setup the session attributes to set the cookie and to enable/disable // session reconnects // DWORD SetTLSSessionCookie ( EAPTLSCB * pEapTlsCb, PBYTE pbCookie, DWORD cbCookie ) { DWORD dwRetCode = NO_ERROR; SecPkgContext_SessionAppData AppData; EapTlsTrace ("SetTLSSessionCookie"); ZeroMemory(&AppData, sizeof(AppData)); AppData.pbAppData = pbCookie; AppData.cbAppData = cbCookie; dwRetCode = SetContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_APP_DATA, (PVOID)&AppData, sizeof(AppData) ); if(dwRetCode != SEC_E_OK) { EapTlsTrace ("SetContextAttributes returned error 0x%x setting session cookie\n", dwRetCode); } else { EapTlsTrace ("Session cookie set successfully \n"); } return dwRetCode; } void SetCachedCredentials (EAPTLSCB * pEapTlsCb) { DWORD dwIndex = 0; TOKEN_STATISTICS TokenStats; DWORD TokenStatsSize = 0; HANDLE CurrentThreadToken = NULL; DWORD dwRetCode=0; EapTlsTrace("SetCachedCredentials"); if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_PEAP_CACHED_CREDS_INDEX; } else { dwIndex = VPN_PEAP_CACHED_CREDS_INDEX; } } else { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_CACHED_CREDS_INDEX; } else { dwIndex = VPN_CACHED_CREDS_INDEX; } } EnterCriticalSection ( &g_csProtectCachedCredentials ); // // Free any existing cached credentials. // FreeCachedCredentials(pEapTlsCb); if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER ) ) { if ( ! OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &CurrentThreadToken ) ) { EapTlsTrace("OpenThreadToken Failed with Error 0x%x", GetLastError() ); goto done; } ZeroMemory ( &TokenStats, sizeof(TokenStats) ); if ( !GetTokenInformation( CurrentThreadToken, TokenStatistics, &TokenStats, sizeof(TOKEN_STATISTICS), &TokenStatsSize ) ) { EapTlsTrace("OpenThreadToken Failed with Error 0x%x", GetLastError() ); goto done; } } if ( EAPTLSCB_FLAG_LOGON & pEapTlsCb->fFlags ) { EapTlsTrace("Not setting cached credentials."); goto done; } g_CachedCreds[dwIndex].pbHash = (PBYTE)LocalAlloc ( LPTR, pEapTlsCb->pUserProp->Hash.cbHash ); if ( NULL != g_CachedCreds[dwIndex].pbHash ) { memcpy ( g_CachedCreds[dwIndex].pbHash , pEapTlsCb->pUserProp->Hash.pbHash, pEapTlsCb->pUserProp->Hash.cbHash ); g_CachedCreds[dwIndex].cbHash = pEapTlsCb->pUserProp->Hash.cbHash; g_CachedCreds[dwIndex].hCachedCredential = pEapTlsCb->hCredential; g_CachedCreds[dwIndex].pcCachedCertContext = pEapTlsCb->pCertContext; g_CachedCreds[dwIndex].fCachedCredentialInitialized = TRUE; if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER ) ) { CopyMemory ( &g_CachedCreds[dwIndex].AuthenticatedSessionLUID, &TokenStats.AuthenticationId, sizeof( g_CachedCreds[dwIndex].AuthenticatedSessionLUID) ); } } done: LeaveCriticalSection ( &g_csProtectCachedCredentials ); return; } BOOL GetCachedCredentials ( EAPTLSCB * pEapTlsCb ) { BOOL fCachedCreds = FALSE; DWORD dwIndex = 0; TOKEN_STATISTICS TokenStats; DWORD TokenStatsSize = 0; HANDLE CurrentThreadToken = NULL; DWORD dwRetCode=0; EapTlsTrace("GetCachedCredentials"); if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_PEAP_CACHED_CREDS_INDEX; } else { dwIndex = VPN_PEAP_CACHED_CREDS_INDEX; } } else { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { dwIndex = WIRELESS_CACHED_CREDS_INDEX; } else { dwIndex = VPN_CACHED_CREDS_INDEX; } } EnterCriticalSection ( &g_csProtectCachedCredentials ); if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER ) ) { if ( ! OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &CurrentThreadToken ) ) { EapTlsTrace("OpenThreadToken Failed with Error 0x%x", GetLastError() ); FreeCachedCredentials(pEapTlsCb); goto LDone; } ZeroMemory ( &TokenStats, sizeof(TokenStats) ); if ( !GetTokenInformation( CurrentThreadToken, TokenStatistics, &TokenStats, sizeof(TOKEN_STATISTICS), &TokenStatsSize ) ) { EapTlsTrace("OpenThreadToken Failed with Error 0x%x", GetLastError() ); FreeCachedCredentials(pEapTlsCb); goto LDone; } } if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { if ( FALSE == g_CachedCreds[dwIndex].fCachedCredentialInitialized || NULL == pEapTlsCb->pUserProp || EAPTLSCB_FLAG_LOGON & pEapTlsCb->fFlags ) { // // bad or missing cached data. Or Winlogon scenario // so cleanup the creds setup globally and return // FreeCachedCredentials(pEapTlsCb); goto LDone; } //set the stuff in CopyMemory ( &pEapTlsCb->hCredential, &g_CachedCreds[dwIndex].hCachedCredential, sizeof(CredHandle) ); EapTlsTrace("PEAP GetCachedCredentials: Using cached credentials."); fCachedCreds = TRUE; } else { if ( FALSE == g_CachedCreds[dwIndex].fCachedCredentialInitialized || NULL == g_CachedCreds[dwIndex].pbHash || 0 == g_CachedCreds[dwIndex].cbHash || NULL == g_CachedCreds[dwIndex].pcCachedCertContext || NULL == pEapTlsCb->pUserProp || EAPTLSCB_FLAG_LOGON & pEapTlsCb->fFlags ) { // // bad or missing cached data. Or Winlogon scenario // so cleanup the creds setup globally and return // FreeCachedCredentials(pEapTlsCb); goto LDone; } // // If we are not a server check to see if // the LUID in the cache is same as one in the cache // If not, wipe out the creds. // if ( !( pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) ) { if ( !( RtlEqualLuid(&(TokenStats.AuthenticationId), &(g_CachedCreds[dwIndex].AuthenticatedSessionLUID) ) ) ) { FreeCachedCredentials(pEapTlsCb); goto LDone; } } //check to see if the hash matches if ( g_CachedCreds[dwIndex].cbHash == pEapTlsCb->pUserProp->Hash.cbHash && !memcmp ( g_CachedCreds[dwIndex].pbHash, pEapTlsCb->pUserProp->Hash.pbHash, g_CachedCreds[dwIndex].cbHash ) ) { //Hash matches //set the stuff in CopyMemory ( &pEapTlsCb->hCredential, &(g_CachedCreds[dwIndex].hCachedCredential), sizeof(CredHandle) ); pEapTlsCb->pCertContext = g_CachedCreds[dwIndex].pcCachedCertContext; fCachedCreds = TRUE; EapTlsTrace("GetCachedCredentials: Using cached credentials."); } } LDone: LeaveCriticalSection( &g_csProtectCachedCredentials ); return fCachedCreds; } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h Notes: Get the credentials. fServer is TRUE iff we are the server. Remember to call FreeCredentialsHandle(hCredential) at some point. */ DWORD GetCredentials( IN EAPTLSCB* pEapTlsCb ) { SCHANNEL_CRED SchannelCred; TimeStamp tsExpiry; DWORD dwErr = NO_ERROR; SECURITY_STATUS Status; HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwCertFlags; DWORD dwSchCredFlags = 0; BOOL fServer = FALSE; BOOL fRouter = FALSE; WCHAR* pwszName = NULL; CRYPT_HASH_BLOB HashBlob; EapTlsTrace("GetCredentials"); if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) { fServer = TRUE; dwCertFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE; EapTlsTrace("Flag is Server and Store is local Machine"); } else if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_ROUTER) { fRouter = TRUE; dwCertFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE; EapTlsTrace("Flag is Router and Store is local Machine"); } else if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_MACHINE_AUTH ) { dwCertFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE; EapTlsTrace("Flag is Machine Auth and Store is local Machine"); } else { dwCertFlags = CERT_SYSTEM_STORE_CURRENT_USER; EapTlsTrace("Flag is Client and Store is Current User"); } if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_WINLOGON_DATA) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP) ) { if( !(pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_REGISTRY) && (pEapTlsCb->pUserProp->pwszPin[0] != 0)) { // // Make a copy of the pin and save it in the control block // for saving it in the credmgr. This will be wiped out // when ppp engine queries for the creds. // DecodePin(pEapTlsCb->pUserProp); pEapTlsCb->pSavedPin = LocalAlloc(LPTR, sizeof(EAPTLS_PIN)); if(NULL != pEapTlsCb->pSavedPin) { pEapTlsCb->pSavedPin->pwszPin = LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen(pEapTlsCb->pUserProp->pwszPin) + 1)); if(NULL != pEapTlsCb->pSavedPin->pwszPin) { UNICODE_STRING UnicodeStringPin; UCHAR ucSeed; wcscpy(pEapTlsCb->pSavedPin->pwszPin, pEapTlsCb->pUserProp->pwszPin); RtlInitUnicodeString(&UnicodeStringPin, pEapTlsCb->pSavedPin->pwszPin); RtlRunEncodeUnicodeString(&ucSeed, &UnicodeStringPin); pEapTlsCb->pSavedPin->usLength = UnicodeStringPin.Length; pEapTlsCb->pSavedPin->usMaximumLength = UnicodeStringPin.MaximumLength; pEapTlsCb->pSavedPin->ucSeed = ucSeed; } else { LocalFree(pEapTlsCb->pSavedPin); pEapTlsCb->pSavedPin = NULL; } } EncodePin(pEapTlsCb->pUserProp); } } //Check to see if we can get cached credentials if ( GetCachedCredentials ( pEapTlsCb ) ) { //got cached creds pEapTlsCb->fFlags |= EAPTLSCB_FLAG_USING_CACHED_CREDS; goto LDone; } if (EAPTLSCB_FLAG_WINLOGON_DATA & pEapTlsCb->fFlags) { dwErr = GetCertFromLogonInfo(pEapTlsCb->pUserData, pEapTlsCb->dwSizeOfUserData, &pCertContext); if (NO_ERROR != dwErr) { goto LDone; } } else { if ( ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS || pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) && !fServer && !fRouter ) { //if this is guest access and this is a client then //no need to get the cert etc. EapTlsTrace("No Cert Store. Guest Access requested"); } else { // Open the "MY" certificate store. hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0, dwCertFlags | CERT_STORE_READONLY_FLAG, "MY"); if (NULL == hCertStore) { dwErr = GetLastError(); EapTlsTrace("CertOpenStore failed and returned 0x%x", dwErr); goto LDone; } HashBlob.cbData = pEapTlsCb->pUserProp->Hash.cbHash; HashBlob.pbData = pEapTlsCb->pUserProp->Hash.pbHash; pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_HASH, &HashBlob, NULL); if (NULL == pCertContext) { if ( fServer || fRouter) { WCHAR* apwsz[1]; apwsz[0] = pEapTlsCb->awszIdentity; if (0 == HashBlob.cbData) { RouterLogInformation(pEapTlsCb->hEventLog, ROUTERLOG_EAP_TLS_CERT_NOT_CONFIGURED, 1, apwsz, 0); } else { RouterLogWarning(pEapTlsCb->hEventLog, ROUTERLOG_EAP_TLS_CERT_NOT_FOUND, 1, apwsz, 0); } dwErr = GetDefaultMachineCert(hCertStore, &pCertContext); if (NO_ERROR != dwErr) { goto LDone; } //Now set the user property correctly so that next time we //can find the certificate pEapTlsCb->pUserProp->Hash.cbHash = MAX_HASH_SIZE; if (!CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, pEapTlsCb->pUserProp->Hash.pbHash, &(pEapTlsCb->pUserProp->Hash.cbHash))) { //not an issue if it fails here. //next time on it will get the default machine cert again EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", GetLastError()); } // //Should not be an issue if this fails //Write the config back to the registry //It will always be local registry here // ServerConfigDataIO( FALSE , NULL , (PBYTE *)&(pEapTlsCb->pUserProp), sizeof(EAPTLS_USER_PROPERTIES) ); } else { dwErr = GetLastError(); EapTlsTrace("CertFindCertificateInStore failed and returned " "0x%x", dwErr); goto LDone; } } } } if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_WINLOGON_DATA) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP) ) { dwErr = AssociatePinWithCertificate( pCertContext, pEapTlsCb->pUserProp, TRUE, !(pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_REGISTRY)); if (NO_ERROR != dwErr) { goto LDone; } } if ( !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS ) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) ) { if (FCertToStr(pCertContext, 0, fServer || fRouter, &pwszName)) { EapTlsTrace("The name in the certificate is: %ws", pwszName); LocalFree(pwszName); } } else { EapTlsTrace("No Cert Name. Guest access requested"); } // Build Schannel credential structure. ZeroMemory(&SchannelCred, sizeof(SchannelCred)); SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS || pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { //Guest Access and server so set the cert context //or elase there is no need. if ( fServer ) { SchannelCred.cCreds = 1; SchannelCred.paCred = &pCertContext; } } else { SchannelCred.cCreds = 1; SchannelCred.paCred = &pCertContext; } SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1; if (fServer) { if (!g_fNoRevocationCheck) { if (g_fNoRootRevocationCheck) { dwSchCredFlags = SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; } else { dwSchCredFlags = SCH_CRED_REVOCATION_CHECK_CHAIN; } if (g_fIgnoreNoRevocationCheck) { dwSchCredFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK; } if (g_fIgnoreRevocationOffline) { dwSchCredFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE; } } // // Start with disabling fast for PEAP reconnects. // Once the full handshake is done, // decide if we want to allow reconnects. // dwSchCredFlags |= SCH_CRED_DISABLE_RECONNECTS; } else { dwSchCredFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS; if (EAPTLS_CONN_FLAG_NO_VALIDATE_CERT & pEapTlsCb->pConnProp->fFlags) { dwSchCredFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; EapTlsTrace("Will NOT validate server cert"); } else { dwSchCredFlags |= SCH_CRED_AUTO_CRED_VALIDATION; EapTlsTrace("Will validate server cert"); } } SchannelCred.dwFlags = dwSchCredFlags; // Create the SSPI credentials. Status = AcquireCredentialsHandle( NULL, // Name of principal UNISP_NAME, // Name of package // Flags indicating use fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, // Pointer to logon ID &SchannelCred, // Package specific data NULL, // Pointer to GetKey() func NULL, // Value to pass to GetKey() &(pEapTlsCb->hCredential), // (out) Credential Handle &tsExpiry); // (out) Lifetime (optional) if (SEC_E_OK != Status) { dwErr = Status; EapTlsTrace("AcquireCredentialsHandle failed and returned 0x%x", dwErr); goto LDone; } // We needn't store the cert context if we get it by calling // CertFindCertificateInStore. However, if we get it by calling // ScHelperGetCertFromLogonInfo, and we free it, then the hProv will become // invalid. In the former case, there is no hProv associated with the cert // context and schannel does the CryptAcquireContext itself. In the latter // case, there is an hProv associated, and it is invalid after the cert // context is freed. pEapTlsCb->pCertContext = pCertContext; pCertContext = NULL; // // If we are a server set cached creds here // if ( fServer ) { SetCachedCredentials ( pEapTlsCb ); } LDone: if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); // Always returns TRUE; } if (NULL != hCertStore) { if (!CertCloseStore(hCertStore, 0)) { EapTlsTrace("CertCloseStore failed and returned 0x%x", GetLastError()); } } if ( (dwErr != NO_ERROR) && fServer) { RouterLogErrorString(pEapTlsCb->hEventLog, ROUTERLOG_CANT_GET_SERVER_CRED, 0, NULL, dwErr, 0); } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Looks at *pEapTlsCb and builds an EAP TLS packet in *pSendPacket. cbSendPacket is the number of bytes available in pSendPacket. This function looks at the fields bCode, bId, fFlags, cbBlobOut, pbBlobOut, and dwBlobOutOffset in pEapTlsCb. It may also set the field dwBlobOutOffsetNew. */ DWORD BuildPacket( OUT EAPTLS_PACKET* pSendPacket, IN DWORD cbSendPacket, IN EAPTLSCB* pEapTlsCb ) { WORD wLength; BOOL fLengthIncluded = FALSE; DWORD dwBytesRemaining; DWORD dwBytesToBeSent; DWORD dwErr = NO_ERROR; EapTlsTrace("BuildPacket"); RTASSERT(NULL != pEapTlsCb); if (0xFFFF < cbSendPacket) { // We never send more than 0xFFFF bytes at a time, since the length // field in EAPTLS_PACKET has two octets cbSendPacket = 0xFFFF; } if ( (NULL == pSendPacket) || (cbSendPacket < PPP_EAP_PACKET_HDR_LEN)) { EapTlsTrace("pSendPacket is NULL or too small (size: %d)", cbSendPacket); dwErr = TYPE_E_BUFFERTOOSMALL; goto LDone; } pSendPacket->bCode = pEapTlsCb->bCode; pSendPacket->bId = pEapTlsCb->bId; HostToWireFormat16(PPP_EAP_PACKET_HDR_LEN, pSendPacket->pbLength); switch (pEapTlsCb->bCode) { case EAPCODE_Success: case EAPCODE_Failure: goto LDone; break; case EAPCODE_Request: case EAPCODE_Response: if (cbSendPacket < EAPTLS_PACKET_HDR_LEN_MAX + 1 /* Send at least one octet of the TLS blob */) { // We are being conservative here. It is possible that the buffer // is not really too small. EapTlsTrace("pSendPacket is too small. Size: %d", cbSendPacket); dwErr = TYPE_E_BUFFERTOOSMALL; goto LDone; } // pSendPacket->bCode = pEapTlsCb->bCode; // pSendPacket->bId = pEapTlsCb->bId; pSendPacket->bType = PPP_EAP_TLS; pSendPacket->bFlags = 0; break; default: EapTlsTrace("Unknown EAP code: %d", pEapTlsCb->bCode); RTASSERT(FALSE); dwErr = E_FAIL; goto LDone; break; } // If we reach here, it means that the packet is a request or a response if ( (0 == pEapTlsCb->cbBlobOut) || (NULL == pEapTlsCb->pbBlobOut)) { // We want to send an empty request or response if ( (EAPTLSCB_FLAG_SERVER & pEapTlsCb->fFlags) && (EAPTLS_STATE_INITIAL == pEapTlsCb->EapTlsState)) { pSendPacket->bFlags |= EAPTLS_PACKET_FLAG_TLS_START; } HostToWireFormat16(EAPTLS_PACKET_HDR_LEN, pSendPacket->pbLength); goto LDone; } // If we reach here, it means that the packet is a non empty request or // response. if (0 == pEapTlsCb->dwBlobOutOffset) { // We are sending the first bytes of the blob. Let us tell the peer how // big the blob is fLengthIncluded = TRUE; pSendPacket->bFlags |= EAPTLS_PACKET_FLAG_LENGTH_INCL; wLength = EAPTLS_PACKET_HDR_LEN_MAX; } else { wLength = EAPTLS_PACKET_HDR_LEN; } dwBytesRemaining = pEapTlsCb->cbBlobOut - pEapTlsCb->dwBlobOutOffset; dwBytesToBeSent = cbSendPacket - wLength; if (dwBytesRemaining < dwBytesToBeSent) { dwBytesToBeSent = dwBytesRemaining; } if (dwBytesRemaining > dwBytesToBeSent) { // We need to send more fragments pSendPacket->bFlags |= EAPTLS_PACKET_FLAG_MORE_FRAGMENTS; } RTASSERT(dwBytesToBeSent + EAPTLS_PACKET_HDR_LEN_MAX <= 0xFFFF); wLength += (WORD) dwBytesToBeSent; HostToWireFormat16(wLength, pSendPacket->pbLength); if (fLengthIncluded) { HostToWireFormat32(pEapTlsCb->cbBlobOut, pSendPacket->pbData); } RTASSERT(NULL != pEapTlsCb->pbBlobOut); CopyMemory(pSendPacket->pbData + (fLengthIncluded ? 4 : 0), pEapTlsCb->pbBlobOut + pEapTlsCb->dwBlobOutOffset, dwBytesToBeSent); pEapTlsCb->dwBlobOutOffsetNew = pEapTlsCb->dwBlobOutOffset + dwBytesToBeSent; LDone: if (NO_ERROR == dwErr) { PrintEapTlsPacket(pSendPacket, FALSE /* fInput */); } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: The pwszIdentity obtained from PPP_EAP_INPUT (on the server side) is of the form \. We ask schannel to map the cert to a user object, get the user name and domain name, and make sure that they match pwszIdentity. */ DWORD CheckUserName( IN CtxtHandle hContext, IN WCHAR* pwszIdentity ) { DWORD dwErr = NO_ERROR; SECURITY_STATUS Status; HANDLE Token; BOOL fTokenAcquired = FALSE; BOOL fImpersonating = FALSE; DWORD dwNumChars; WCHAR pwszUserName[UNLEN + DNLEN + 2]; EapTlsTrace("CheckUserName"); Status = QuerySecurityContextToken(&hContext, &Token); if (SEC_E_OK != Status) { EapTlsTrace("QuerySecurityContextToken failed and returned 0x%x", Status); dwErr = Status; goto LDone; } fTokenAcquired = TRUE; if (!ImpersonateLoggedOnUser(Token)) { dwErr = GetLastError(); EapTlsTrace("ImpersonateLoggedOnUser failed and returned 0x%x", dwErr); goto LDone; } fImpersonating = TRUE; dwNumChars = UNLEN + DNLEN + 2; if (!GetUserNameEx(NameSamCompatible, pwszUserName, &dwNumChars)) { dwErr = GetLastError(); EapTlsTrace("GetUserNameExA failed and returned %d", dwErr); goto LDone; } if (_wcsicmp(pwszIdentity, pwszUserName)) { EapTlsTrace("The user claims to be %ws, but is actually %ws", pwszIdentity, pwszUserName); dwErr = SEC_E_LOGON_DENIED; goto LDone; } LDone: if (fImpersonating) { if (!RevertToSelf()) { EapTlsTrace("RevertToSelf failed and returned 0x%x", GetLastError()); } } if (fTokenAcquired) { CloseHandle(Token); } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Checks to see if the cert sent by the server is of the right type. Also checks the name in the cert, the issuer, etc, if necessary. */ DWORD AuthenticateServer( IN EAPTLSCB* pEapTlsCb, OUT BOOL* pfWaitForUserOK ) { SECURITY_STATUS Status; PCERT_CONTEXT pCertContextServer = NULL; EAPTLS_HASH Hash; EAPTLS_HASH ServerCertHash; BOOL fHashOK = FALSE; BOOL fNameOK = FALSE; WCHAR* pwszRootCAName = NULL; WCHAR* pwszServerName = NULL; WCHAR* pwszSavedName = NULL; WCHAR awszTitle[NUM_CHARS_TITLE]; WCHAR* pwszFormat = NULL; WCHAR* pwszWarning = NULL; DWORD dwSizeOfWszWarning; DWORD dwStrLenSaved; DWORD dwStrLenServer; DWORD dwStrLenRootCA; EAPTLS_CONN_PROPERTIES_V1 * pConnProp = NULL; EAPTLS_VALIDATE_SERVER* pEapTlsValidateServer; DWORD dw = 0; DWORD dwErr = NO_ERROR; PCERT_ENHKEY_USAGE pUsage = NULL; DWORD dwSizeOfGPRootHashBlob = 0; BOOL fRootCheckRequired = TRUE; //By default root check is required HCERTSTORE hCertStore = NULL; PBYTE pbHashTemp = NULL; EapTlsTrace("AuthenticateServer"); RTASSERT(NULL != pEapTlsCb); *pfWaitForUserOK = FALSE; // //if we are doing guest authentication, we always have to authenticate the //server. // if ( ! (pEapTlsCb->fFlags & EAPTLSCB_FLAG_GUEST_ACCESS ) && EAPTLS_CONN_FLAG_NO_VALIDATE_CERT & pEapTlsCb->pConnProp->fFlags) { // We are done goto LDone; } Status = QueryContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); if (SEC_E_OK != Status) { RTASSERT(NULL == pCertContextServer); EapTlsTrace("QueryContextAttributes failed and returned 0x%x", Status); dwErr = Status; goto LDone; } if ( ( dwErr = DwGetEKUUsage ( pCertContextServer, &pUsage )) != ERROR_SUCCESS ) { EapTlsTrace("The server's cert does not have the 'Server " "Authentication' usage"); goto LDone; } if (!FCheckUsage(pCertContextServer, pUsage, TRUE /* fServer */)) { EapTlsTrace("The server's cert does not have the 'Server " "Authentication' usage"); dwErr = E_FAIL; goto LDone; } dwErr = GetRootCertHashAndNameVerifyChain(pCertContextServer, &Hash, &pwszRootCAName, (pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ), &fRootCheckRequired); if (NO_ERROR != dwErr) { goto LDone; } EapTlsTrace("Root CA name: %ws", pwszRootCAName); dwStrLenRootCA = wcslen(pwszRootCAName); //If there is no root check required, the stuff is good to go. if ( !fRootCheckRequired ) fHashOK = TRUE; #if 0 // //Check to see if the new flag has been passed in to //see if GP needs to be validated. // if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH ) { EapTlsTrace( "8021X Flag Set. Will check for group policy hashes."); // //Lookup GP and see if we have the hashes to //check against. // dwErr = ReadGPCARootHashes( &dwSizeOfGPRootHashBlob, &pbGPRootHashBlob ); if ( ERROR_SUCCESS == dwErr && pbGPRootHashBlob ) { for ( dw = 0; dw < dwSizeOfGPRootHashBlob/MAX_HASH_SIZE; dw++ ) { if ( !memcmp( pbGPRootHashBlob + ( dw * MAX_HASH_SIZE ), Hash.pbHash, MAX_HASH_SIZE ) ) { EapTlsTrace( "8021X Found Hash match in GP"); fHashOK = TRUE; break; } } } else { dwErr = NO_ERROR; EapTlsTrace ("Could not get group policy hashes to check cert. Ignoring check."); } } else { EapTlsTrace( "8021X Flag NOT Set. Will not check for group policy hashes."); } #endif // //Check to see if the hash for the root cert is in the list of //hashes saved // if ( !fHashOK ) { for ( dw = 0; dw < pEapTlsCb->pConnProp->dwNumHashes; dw ++ ) { if (!memcmp( ( pEapTlsCb->pConnProp->bData + ( dw * sizeof(EAPTLS_HASH) ) ), &Hash, sizeof(EAPTLS_HASH) ) ) { EapTlsTrace ("Found Hash"); fHashOK = TRUE; break; } } } pwszSavedName = (LPWSTR)(pEapTlsCb->pConnProp->bData + sizeof(EAPTLS_HASH) * pEapTlsCb->pConnProp->dwNumHashes); if (!FCertToStr(pCertContextServer, 0, TRUE, &pwszServerName)) { dwErr = E_FAIL; goto LDone; } EapTlsTrace("Server name: %ws", pwszServerName); EapTlsTrace("Server name specified: %ws", pwszSavedName); dwStrLenServer = wcslen(pwszServerName); dwStrLenSaved = wcslen(pwszSavedName); if (pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_NO_VALIDATE_NAME) { fNameOK = TRUE; } // // Check to see if the new server name is in the list that is // saved. // if ( (0 != dwStrLenSaved) && StrStrI(pwszSavedName, pwszServerName )) { fNameOK = TRUE; dwStrLenServer=0; } if ( fHashOK && fNameOK) { goto LDone; } if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_NON_INTERACTIVE) { EapTlsTrace("No interactive UI's allowed"); dwErr = E_FAIL; goto LDone; } if (!LoadString(GetHInstance(), IDS_VALIDATE_SERVER_TITLE, awszTitle, NUM_CHARS_TITLE)) { awszTitle[0] = 0; } if (fNameOK && !fHashOK) { pwszFormat = WszFromId(GetResouceDLLHInstance(), IDS_VALIDATE_SERVER_TEXT); if (NULL == pwszFormat) { dwErr = ERROR_ALLOCATING_MEMORY; EapTlsTrace("WszFromId(%d) failed", IDS_VALIDATE_SERVER_TEXT); goto LDone; } dwSizeOfWszWarning = (wcslen(pwszFormat) + dwStrLenRootCA) * sizeof(WCHAR); pwszWarning = LocalAlloc(LPTR, dwSizeOfWszWarning); if (NULL == pwszWarning) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } swprintf(pwszWarning, pwszFormat, pwszRootCAName); } else if (!fNameOK && fHashOK) { pwszFormat = WszFromId(GetResouceDLLHInstance(), IDS_VALIDATE_NAME_TEXT); if (NULL == pwszFormat) { dwErr = ERROR_ALLOCATING_MEMORY; EapTlsTrace("WszFromId(%d) failed", IDS_VALIDATE_NAME_TEXT); goto LDone; } dwSizeOfWszWarning = (wcslen(pwszFormat) + dwStrLenServer) * sizeof(WCHAR); pwszWarning = LocalAlloc(LPTR, dwSizeOfWszWarning); if (NULL == pwszWarning) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } swprintf(pwszWarning, pwszFormat, pwszServerName); } else { RTASSERT(!fNameOK && !fHashOK); pwszFormat = WszFromId(GetResouceDLLHInstance(), IDS_VALIDATE_SERVER_WITH_NAME_TEXT); if (NULL == pwszFormat) { dwErr = ERROR_ALLOCATING_MEMORY; EapTlsTrace("WszFromId(%d) failed", IDS_VALIDATE_SERVER_WITH_NAME_TEXT); goto LDone; } dwSizeOfWszWarning = (wcslen(pwszFormat) + dwStrLenRootCA + dwStrLenServer) * sizeof(WCHAR); pwszWarning = LocalAlloc(LPTR, dwSizeOfWszWarning); if (NULL == pwszWarning) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } swprintf(pwszWarning, pwszFormat, pwszServerName, pwszRootCAName); } // If the server name is itg1.msft.com, we want to only store msft.com /* for (dw = 0; dw < dwStrLenServer; dw++) { if (L'.' == pwszServerName[dw]) { break; } } */ // //We need to add a new entry to conn prop here // Add new hash and append the server name... // pConnProp = LocalAlloc( LPTR, sizeof(EAPTLS_CONN_PROPERTIES_V1) + sizeof(EAPTLS_HASH) * ( pEapTlsCb->pConnProp->dwNumHashes + 1 ) + dwStrLenServer * sizeof(WCHAR) + sizeof(WCHAR) + //This is for the NULL sizeof(WCHAR) + //This is for the delimiter dwStrLenSaved * sizeof(WCHAR)); if (NULL == pConnProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pConnProp, pEapTlsCb->pConnProp, sizeof(EAPTLS_CONN_PROPERTIES_V1)); //One extra char for ; pConnProp->dwSize = sizeof(EAPTLS_CONN_PROPERTIES_V1) + sizeof(EAPTLS_HASH) * ( pEapTlsCb->pConnProp->dwNumHashes + 1 ) + dwStrLenServer * sizeof(WCHAR) + sizeof(WCHAR) + sizeof(WCHAR) + dwStrLenSaved * sizeof(WCHAR); pConnProp->dwNumHashes ++; CopyMemory( pConnProp->bData, pEapTlsCb->pConnProp->bData, sizeof(EAPTLS_HASH) * pEapTlsCb->pConnProp->dwNumHashes); CopyMemory( pConnProp->bData + sizeof(EAPTLS_HASH) * pEapTlsCb->pConnProp->dwNumHashes, &Hash, sizeof(EAPTLS_HASH) ); if ( dwStrLenSaved ) { wcsncpy ( (LPWSTR)(pConnProp->bData + sizeof(EAPTLS_HASH) * pConnProp->dwNumHashes), (LPWSTR)(pEapTlsCb->pConnProp->bData + sizeof(EAPTLS_HASH) * pEapTlsCb->pConnProp->dwNumHashes), dwStrLenSaved ); if ( dwStrLenServer ) { wcscat ( (LPWSTR)(pConnProp->bData + sizeof(EAPTLS_HASH) * pConnProp->dwNumHashes + dwStrLenSaved * sizeof(WCHAR)), L";" ); wcscat ( (LPWSTR)(pConnProp->bData + sizeof(EAPTLS_HASH) * pConnProp->dwNumHashes + dwStrLenSaved * sizeof(WCHAR) + sizeof(WCHAR)), pwszServerName ); } } else { wcscpy((LPWSTR)(pConnProp->bData + sizeof(EAPTLS_HASH) * pConnProp->dwNumHashes + dwStrLenSaved * sizeof(WCHAR)), pwszServerName ); } LocalFree(pEapTlsCb->pUIContextData); pEapTlsCb->pUIContextData = LocalAlloc(LPTR, sizeof(EAPTLS_VALIDATE_SERVER) + dwSizeOfWszWarning); if (NULL == pEapTlsCb->pUIContextData) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } // // Get the Hash of server certificate // ZeroMemory( &ServerCertHash, sizeof(ServerCertHash) ); ServerCertHash.cbHash = MAX_HASH_SIZE; if (!CertGetCertificateContextProperty(pCertContextServer, CERT_HASH_PROP_ID, ServerCertHash.pbHash, &(ServerCertHash.cbHash))) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", dwErr); goto LDone; } // // Open my store in local machine and add this certificate in it // hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"CA" ); if ( NULL == hCertStore ) { dwErr = GetLastError(); EapTlsTrace("CertOpenStore failed with error 0x%x", dwErr ); goto LDone; } // // add this context to the store // if ( !CertAddCertificateContextToStore( hCertStore, pCertContextServer, CERT_STORE_ADD_ALWAYS, NULL ) ) { dwErr = GetLastError(); EapTlsTrace("CertAddCertCertificateContextToStore failed with error 0x%x", dwErr ); goto LDone; } pEapTlsValidateServer =(EAPTLS_VALIDATE_SERVER*)(pEapTlsCb->pUIContextData); pEapTlsValidateServer->dwSize = sizeof(EAPTLS_VALIDATE_SERVER) + dwSizeOfWszWarning; //Show this button iff it is not winlogon scenario. pEapTlsValidateServer->fShowCertDetails = !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_LOGON); //pEapTlsValidateServer->fShowCertDetails = TRUE; CopyMemory( &(pEapTlsValidateServer->Hash), &ServerCertHash, sizeof(ServerCertHash) ); wcscpy(pEapTlsValidateServer->awszTitle, awszTitle); wcscpy(pEapTlsValidateServer->awszWarning, pwszWarning); *pfWaitForUserOK = TRUE; LDone: if (NO_ERROR == dwErr) { LocalFree(pEapTlsCb->pNewConnProp); pEapTlsCb->pNewConnProp = pConnProp; pConnProp = NULL; } if ( pUsage ) { LocalFree(pUsage); pUsage = NULL; } if (NULL != pCertContextServer) { CertFreeCertificateContext(pCertContextServer); // Always returns TRUE; } if ( hCertStore ) { CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ); } LocalFree(pwszRootCAName); LocalFree(pwszServerName); LocalFree(pwszWarning); LocalFree(pwszFormat); LocalFree(pConnProp); #if 0 if (NULL != pbGPRootHashBlob) { LocalFree(pbGPRootHashBlob); } #endif if (NO_ERROR != dwErr) { dwErr = ERROR_UNABLE_TO_AUTHENTICATE_SERVER; } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: */ DWORD AuthenticateUser( IN EAPTLSCB* pEapTlsCb ) { PCERT_CONTEXT pCertContextUser = NULL; SECURITY_STATUS Status; DWORD dwErr = NO_ERROR; PCERT_ENHKEY_USAGE pUsage = NULL; PCCERT_CHAIN_CONTEXT pCCertChainContext = NULL; EapTlsTrace("AuthenticateUser"); RTASSERT(NULL != pEapTlsCb); Status = QueryContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextUser); if (SEC_E_OK != Status) { RTASSERT(NULL == pCertContextUser); EapTlsTrace("QueryContextAttributes failed and returned 0x%x", Status); // //Now that we have default setting of guest access, //if there are no credentials it is fine. Just send the error back to IAS //and let it decide what has to be done here. // if ( Status != SEC_E_NO_CREDENTIALS ) { dwErr = SEC_E_LOGON_DENIED; } else { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { EapTlsTrace("Got no credentials from the client and executing PEAP. This is a success for eaptls."); dwErr = NO_ERROR; goto LDone; } else { EapTlsTrace("Got no credentials from the client. Will send success with authresult as SEC_E_NO_CREDENTIALS"); dwErr = Status; } } goto LDone; } if ( ( dwErr = DwGetEKUUsage ( pCertContextUser,&pUsage ) ) != ERROR_SUCCESS ) { EapTlsTrace("The user's cert does not have correct usage"); goto LDone; } #if WINVER > 0x0500 // // Check to see if the certificate policy is all // fine. Now that we allow guest access we definitely need to // see manually if the cert chain is valid. // if ( ( dwErr = DwCheckCertPolicy(pCertContextUser, &pCCertChainContext ) ) != ERROR_SUCCESS) { EapTlsTrace("The user's cert does not have correct usage."); goto LDone; } if ( NULL == pCCertChainContext ) { EapTlsTrace("No Chain Context for from the certificate."); dwErr = SEC_E_CERT_UNKNOWN; goto LDone; } #else // // We dont have to check Usage separately any more // This will be done as a part of policy verification // on the chain. // if (!FCheckUsage(pCertContextUser, pUsage, FALSE)) { EapTlsTrace("The user's cert does not have correct usage"); dwErr = SEC_E_CERT_UNKNOWN; goto LDone; } #endif dwErr = CheckUserName(pEapTlsCb->hContext, pEapTlsCb->awszIdentity); if (NO_ERROR != dwErr) { //Special case handling for bug id:96347 //if ( dwErr != SEC_E_MULTIPLE_ACCOUNTS ) //dwErr = SEC_E_LOGON_DENIED; goto LDone; } //Put OIDs in RAS Attributes so that we can send then across to IAS dwErr = CreateOIDAttributes ( pEapTlsCb, pUsage, pCCertChainContext ); if ( NO_ERROR != dwErr ) { dwErr = SEC_E_LOGON_DENIED; goto LDone; } LDone: if ( pUsage ) { LocalFree ( pUsage ); pUsage = NULL; } if (NULL != pCertContextUser) { CertFreeCertificateContext(pCertContextUser); // Always returns TRUE; } if ( pCCertChainContext ) { CertFreeCertificateChain ( pCCertChainContext ); } return(dwErr); } DWORD CreateOIDAttributes ( IN EAPTLSCB * pEapTlsCb, PCERT_ENHKEY_USAGE pUsage, PCCERT_CHAIN_CONTEXT pCCertChainContext ) { DWORD dwErr = NO_ERROR; DWORD dwIndex, dwIndex1; DWORD dwNumAttrs = 0; PCERT_ENHKEY_USAGE pIssuanceUsage = NULL; EapTlsTrace("CreateOIDAttributes"); #if WINVER > 0x0500 if ( pCCertChainContext ) pIssuanceUsage = pCCertChainContext->rgpChain[0]->rgpElement[0]->pIssuanceUsage; #endif if (NULL != pEapTlsCb->pAttributes) { RasAuthAttributeDestroy(pEapTlsCb->pAttributes); pEapTlsCb->pAttributes = NULL; } if ( pIssuanceUsage ) { dwNumAttrs = pIssuanceUsage->cUsageIdentifier; } dwNumAttrs+=pUsage->cUsageIdentifier; //Need to allocate one extra for raatMinimum. The function automatically terminates // with raat minimum pEapTlsCb->pAttributes = RasAuthAttributeCreate(dwNumAttrs); if (NULL == pEapTlsCb->pAttributes) { dwErr = GetLastError(); EapTlsTrace("RasAuthAttributeCreate failed and returned %d", dwErr); goto LDone; } dwIndex = 0; while (pUsage->cUsageIdentifier) { dwErr = RasAuthAttributeInsert( dwIndex, pEapTlsCb->pAttributes, raatCertificateOID, FALSE, strlen(pUsage->rgpszUsageIdentifier[dwIndex]), pUsage->rgpszUsageIdentifier[dwIndex]); if (NO_ERROR != dwErr) { EapTlsTrace("RasAuthAttributeInsert failed for EKU usage and returned %d", dwErr); goto LDone; } dwIndex++; pUsage->cUsageIdentifier--; } dwIndex1 = 0; while ( pIssuanceUsage && pIssuanceUsage->cUsageIdentifier ) { dwErr = RasAuthAttributeInsert( dwIndex, pEapTlsCb->pAttributes, raatCertificateOID, FALSE, strlen(pIssuanceUsage->rgpszUsageIdentifier[dwIndex1]), pIssuanceUsage->rgpszUsageIdentifier[dwIndex1]); if (NO_ERROR != dwErr) { EapTlsTrace("RasAuthAttributeInsert failed for Issuance Usage and returned %d", dwErr); goto LDone; } dwIndex++; dwIndex1++; pIssuanceUsage->cUsageIdentifier--; } LDone: return dwErr; } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Creates a RAS_AUTH_ATTRIBUTE with the MPPE key in pEapTlsCb->pAttributes. */ DWORD CreateMPPEKeyAttributes( IN EAPTLSCB* pEapTlsCb ) { SECURITY_STATUS Status; SecPkgContext_EapKeyBlock EapKeyBlock; BYTE MPPEKey[56]; BYTE* pSendKey; BYTE* pRecvKey; DWORD dwErr = NO_ERROR; RAS_AUTH_ATTRIBUTE * pAttrTemp = NULL; EapTlsTrace("CreateMPPEKeyAttributes"); Status = QueryContextAttributes(&(pEapTlsCb->hContext), SECPKG_ATTR_EAP_KEY_BLOCK, &EapKeyBlock); if (SEC_E_OK != Status) { EapTlsTrace("QueryContextAttributes failed and returned 0x%x", Status); dwErr = Status; goto LDone; } #if 0 if (NULL != pEapTlsCb->pAttributes) { RasAuthAttributeDestroy(pEapTlsCb->pAttributes); pEapTlsCb->pAttributes = NULL; } pEapTlsCb->pAttributes = RasAuthAttributeCreate(2); #endif pAttrTemp = RasAuthAttributeCopyWithAlloc ( pEapTlsCb->pAttributes, 2 ); if (NULL == pAttrTemp ) { dwErr = GetLastError(); EapTlsTrace("RasAuthAttributeCopyWithAlloc failed and returned %d", dwErr); goto LDone; } if ( pEapTlsCb->pAttributes ) { RasAuthAttributeDestroy(pEapTlsCb->pAttributes); } pEapTlsCb->pAttributes = pAttrTemp; if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) { pSendKey = EapKeyBlock.rgbKeys + 32; pRecvKey = EapKeyBlock.rgbKeys; } else { pSendKey = EapKeyBlock.rgbKeys; pRecvKey = EapKeyBlock.rgbKeys + 32; } #if 0 EapTlsTrace("Send Key"); TraceDumpEx(g_dwEapTlsTraceId, 1, pSendKey, 32, 4,1,NULL); EapTlsTrace("Receive Key"); TraceDumpEx(g_dwEapTlsTraceId, 1, pRecvKey, 32, 4,1,NULL); #endif ZeroMemory(MPPEKey, sizeof(MPPEKey)); HostToWireFormat32(311, MPPEKey); // Vendor Id MPPEKey[4] = 16; // MS-MPPE-Send-Key MPPEKey[5] = 1 + 1 + 2 + 1 + 32 + 15; // Vendor Length // MPPEKey[6-7] is the zero-filled salt field MPPEKey[8] = 32; // Key-Length CopyMemory(MPPEKey + 9, pSendKey, 32); // Key // MPPEKey[41-55] is the Padding (zero octets) dwErr = RasAuthAttributeInsert( 0, pEapTlsCb->pAttributes, raatVendorSpecific, FALSE, 56, MPPEKey); if (NO_ERROR != dwErr) { EapTlsTrace("RasAuthAttributeInsert failed and returned %d", dwErr); goto LDone; } // Change only the fields that are different for MS-MPPE-Recv-Key MPPEKey[4] = 17; // MS-MPPE-Recv-Key CopyMemory(MPPEKey + 9, pRecvKey, 32); // Key dwErr = RasAuthAttributeInsert( 1, pEapTlsCb->pAttributes, raatVendorSpecific, FALSE, 56, MPPEKey); if (NO_ERROR != dwErr) { EapTlsTrace("RasAuthAttributeInsert failed and returned %d", dwErr); goto LDone; } LDone: return(dwErr); } /* Returns: Notes: */ VOID RespondToResult( IN EAPTLSCB* pEapTlsCb, IN PPP_EAP_OUTPUT* pEapOutput ) { EAPTLS_USER_PROPERTIES* pUserProp; DWORD dwErr = NO_ERROR; PBYTE pbEncPIN = NULL; //Encrypted PIN DWORD cbEncPIN = 0; RTASSERT( (EAPTLSCB_FLAG_SUCCESS & pEapTlsCb->fFlags) || (NO_ERROR != pEapTlsCb->dwAuthResultCode)); EapTlsTrace("Negotiation %s", (EAPTLSCB_FLAG_SUCCESS & pEapTlsCb->fFlags) ? "successful" : "unsuccessful"); pEapOutput->pUserAttributes = pEapTlsCb->pAttributes; pEapOutput->dwAuthResultCode = pEapTlsCb->dwAuthResultCode; // //Duplicate checks all over are bogus. Need to cleanup this //part later. // if (!(pEapTlsCb->fFlags & EAPTLSCB_FLAG_ROUTER) && !(pEapTlsCb->fFlags & EAPTLSCB_FLAG_WINLOGON_DATA) ) { // // If this is a 802.1x and a smart card based // client then dont instruct to save user // data. if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH && !(pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_REGISTRY) ) { pEapOutput->fSaveUserData = FALSE; } else { pEapOutput->fSaveUserData = TRUE; } } if ( (EAPTLSCB_FLAG_SUCCESS & pEapTlsCb->fFlags) && (NULL != pEapTlsCb->pUserProp)) { if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_8021X_AUTH && !(pEapTlsCb->pConnProp->fFlags & EAPTLS_CONN_FLAG_REGISTRY) ) { // // Encrypt PIN and send it back // dwErr = EncryptData ( (PBYTE)pEapTlsCb->pUserProp->pwszPin, lstrlen(pEapTlsCb->pUserProp->pwszPin) * sizeof(WCHAR), &pbEncPIN, &cbEncPIN ); if ( NO_ERROR != dwErr ) { // //Encryption failed. So wipe Out the PIN //Do a dummy allocation // pbEncPIN = (PBYTE)LocalAlloc(LPTR,5); cbEncPIN = lstrlen( (LPWSTR)pbEncPIN ); } } else { pbEncPIN = (PBYTE)LocalAlloc(LPTR, 5);; cbEncPIN = lstrlen( (LPWSTR)pbEncPIN ); } dwErr = AllocUserDataWithNewPin(pEapTlsCb->pUserProp, pbEncPIN, cbEncPIN, &pUserProp); LocalFree(pEapTlsCb->pUserProp); pEapTlsCb->pUserProp = pUserProp; pEapOutput->pUserData = (BYTE*)pUserProp; if (NULL != pUserProp) { pEapOutput->dwSizeOfUserData = pUserProp->dwSize; } else { pEapOutput->dwSizeOfUserData = 0; } if (NULL != pEapTlsCb->pNewConnProp) { pEapOutput->fSaveConnectionData = TRUE; // //Convert back to the cludgy v0 + v1 extra here // dwErr = ConnPropGetV0Struct ( pEapTlsCb->pNewConnProp, (EAPTLS_CONN_PROPERTIES ** )&(pEapOutput->pConnectionData) ); pEapOutput->dwSizeOfConnectionData = ((EAPTLS_CONN_PROPERTIES *) (pEapOutput->pConnectionData) )->dwSize; } } else { pEapOutput->pUserData = NULL; pEapOutput->dwSizeOfUserData = 0; } LocalFree ( pbEncPIN ); pEapOutput->Action = EAPACTION_Done; } /* Returns: Notes: */ VOID GetAlert( IN EAPTLSCB* pEapTlsCb, IN EAPTLS_PACKET* pReceivePacket ) { BOOL fLengthIncluded; DWORD dwBlobSizeReceived; SecBuffer InBuffers[4]; SecBufferDesc Input; DWORD dwAuthResultCode; SECURITY_STATUS Status; EapTlsTrace("GetAlert"); if (PPP_EAP_TLS != pReceivePacket->bType) { dwAuthResultCode = E_FAIL; goto LDone; } if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_HCTXT_INVALID) { EapTlsTrace("hContext is not valid"); dwAuthResultCode = ERROR_INVALID_HANDLE; goto LDone; } fLengthIncluded = pReceivePacket->bFlags & EAPTLS_PACKET_FLAG_LENGTH_INCL; dwBlobSizeReceived = WireToHostFormat16(pReceivePacket->pbLength) - (fLengthIncluded ? EAPTLS_PACKET_HDR_LEN_MAX : EAPTLS_PACKET_HDR_LEN); if (dwBlobSizeReceived > pEapTlsCb->cbBlobInBuffer) { EapTlsTrace("Reallocating input TLS blob buffer"); LocalFree(pEapTlsCb->pbBlobIn); pEapTlsCb->pbBlobIn = NULL; pEapTlsCb->cbBlobInBuffer = 0; pEapTlsCb->pbBlobIn = LocalAlloc(LPTR, dwBlobSizeReceived); if (NULL == pEapTlsCb->pbBlobIn) { dwAuthResultCode = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwAuthResultCode); goto LDone; } pEapTlsCb->cbBlobInBuffer = dwBlobSizeReceived; } CopyMemory(pEapTlsCb->pbBlobIn, pReceivePacket->pbData + (fLengthIncluded ? 4 : 0), dwBlobSizeReceived); pEapTlsCb->cbBlobIn = dwBlobSizeReceived; InBuffers[0].pvBuffer = pEapTlsCb->pbBlobIn; InBuffers[0].cbBuffer = pEapTlsCb->cbBlobIn; InBuffers[0].BufferType = SECBUFFER_DATA; InBuffers[1].BufferType = SECBUFFER_EMPTY; InBuffers[2].BufferType = SECBUFFER_EMPTY; InBuffers[3].BufferType = SECBUFFER_EMPTY; Input.cBuffers = 4; Input.pBuffers = InBuffers; Input.ulVersion = SECBUFFER_VERSION; Status = DecryptMessage(&(pEapTlsCb->hContext), &Input, 0, 0); dwAuthResultCode = Status; LDone: if (SEC_E_OK == dwAuthResultCode) { RTASSERT(FALSE); dwAuthResultCode = E_FAIL; } EapTlsTrace("Error 0x%x", dwAuthResultCode); pEapTlsCb->dwAuthResultCode = dwAuthResultCode; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_SUCCESS; } /* Returns: Notes: Calls [Initialize|Accept]SecurityContext. */ SECURITY_STATUS SecurityContextFunction( IN EAPTLSCB* pEapTlsCb ) { SecBufferDesc Input; SecBuffer InBuffers[2]; SecBufferDesc Output; SecBuffer OutBuffers[1]; DWORD dwBlobSizeRequired; ULONG fContextAttributes; ULONG fContextReq; TimeStamp tsExpiry; BOOL fServer; BOOL fTlsStart; BOOL fRepeat; SECURITY_STATUS Status; SECURITY_STATUS StatusTemp; EapTlsTrace("SecurityContextFunction"); RTASSERT(NULL != pEapTlsCb); fServer = pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER; if (fServer) { fTlsStart = (EAPTLS_STATE_SENT_START == pEapTlsCb->EapTlsState); } else { fTlsStart = (EAPTLS_STATE_INITIAL == pEapTlsCb->EapTlsState); } fContextReq = pEapTlsCb->fContextReq; fRepeat = TRUE; while (fRepeat) { // Set up the input buffers. InBuffers[0] is used to pass in data // received from the server. Schannel will consume some or all of this. // The amount of the leftover data (if any) will be placed in // InBuffers[1].cbBuffer and InBuffers[1].BufferType will be set to // SECBUFFER_EXTRA. InBuffers[0].pvBuffer = pEapTlsCb->pbBlobIn; InBuffers[0].cbBuffer = pEapTlsCb->cbBlobIn; InBuffers[0].BufferType = SECBUFFER_TOKEN; InBuffers[1].pvBuffer = NULL; InBuffers[1].cbBuffer = 0; InBuffers[1].BufferType = SECBUFFER_EMPTY; Input.cBuffers = 2; Input.pBuffers = InBuffers; Input.ulVersion = SECBUFFER_VERSION; // Set up the output buffers. OutBuffers[0].pvBuffer = NULL; OutBuffers[0].cbBuffer = 0; OutBuffers[0].BufferType= SECBUFFER_TOKEN; Output.cBuffers = 1; Output.pBuffers = OutBuffers; Output.ulVersion = SECBUFFER_VERSION; if (fServer) { // Call AcceptSecurityContext. Status = AcceptSecurityContext( &(pEapTlsCb->hCredential), fTlsStart ? NULL : &(pEapTlsCb->hContext), &Input, fContextReq, SECURITY_NETWORK_DREP, &(pEapTlsCb->hContext), &Output, &fContextAttributes, &tsExpiry); EapTlsTrace("AcceptSecurityContext returned 0x%x", Status); } else { // Call InitializeSecurityContext. // The pszTargetName is used for cache indexing, so if you pass in // NULL then you will likely take a perf hit, maybe a large one. Status = InitializeSecurityContext( &(pEapTlsCb->hCredential), fTlsStart ? NULL : &(pEapTlsCb->hContext), pEapTlsCb->awszIdentity /* pszTargetName */, fContextReq, 0, SECURITY_NETWORK_DREP, (fTlsStart) ? NULL : &Input, 0, &(pEapTlsCb->hContext), &Output, &fContextAttributes, &tsExpiry); EapTlsTrace("InitializeSecurityContext returned 0x%x", Status); } if (!FAILED(Status)) { // If the first call to ASC fails (perhaps because the client sent // bad stuff, even though we have nothing wrong on our side), then // schannel will not return an hContext to the application. The // application should not call DSC in this case, even if it looks // like schannel messed up and returned a handle. pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_HCTXT_INVALID; } // If [Accept|Initialize]SecurityContext was successful (or if the error // was one of the special extended ones), send the contents of the // output buffer to the peer. if (SEC_E_OK == Status || SEC_I_CONTINUE_NEEDED == Status || FAILED(Status) && (fContextAttributes & ISC_RET_EXTENDED_ERROR)) { if (0 != OutBuffers[0].cbBuffer && NULL != OutBuffers[0].pvBuffer) { dwBlobSizeRequired = OutBuffers[0].cbBuffer; if (dwBlobSizeRequired > pEapTlsCb->cbBlobOutBuffer) { LocalFree(pEapTlsCb->pbBlobOut); pEapTlsCb->pbBlobOut = NULL; pEapTlsCb->cbBlobOut = 0; pEapTlsCb->cbBlobOutBuffer = 0; } if (NULL == pEapTlsCb->pbBlobOut) { pEapTlsCb->pbBlobOut = LocalAlloc(LPTR, dwBlobSizeRequired); if (NULL == pEapTlsCb->pbBlobOut) { pEapTlsCb->cbBlobOut = 0; pEapTlsCb->cbBlobOutBuffer = 0; Status = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", Status); goto LWhileEnd; } pEapTlsCb->cbBlobOutBuffer = dwBlobSizeRequired; } CopyMemory(pEapTlsCb->pbBlobOut, OutBuffers[0].pvBuffer, dwBlobSizeRequired); pEapTlsCb->cbBlobOut = dwBlobSizeRequired; pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew = 0; } } // Copy any leftover data from the "extra" buffer. if (InBuffers[1].BufferType == SECBUFFER_EXTRA) { MoveMemory(pEapTlsCb->pbBlobIn, pEapTlsCb->pbBlobIn + (pEapTlsCb->cbBlobIn - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer); pEapTlsCb->cbBlobIn = InBuffers[1].cbBuffer; } else { pEapTlsCb->cbBlobIn = 0; } LWhileEnd: if (NULL != OutBuffers[0].pvBuffer) { StatusTemp = FreeContextBuffer(OutBuffers[0].pvBuffer); if (SEC_E_OK != StatusTemp) { EapTlsTrace("FreeContextBuffer failed and returned 0x%x", StatusTemp); } } // ASC (and ISC) will sometimes only consume part of the input buffer, // and return a zero length output buffer. In this case, we must just // call ASC again (with the input buffer adjusted appropriately). if ( (0 == OutBuffers[0].cbBuffer) && (SEC_I_CONTINUE_NEEDED == Status)) { EapTlsTrace("Reapeating SecurityContextFunction loop..."); } else { fRepeat = FALSE; } } return(Status); } /* Returns: A TLS alert corresponding to the error. Notes: */ DWORD AlertFromError( IN DWORD * pdwErr, IN BOOL fTranslateError ) { DWORD dwAlert; DWORD dwErr = *pdwErr; switch (dwErr) { case SEC_E_MESSAGE_ALTERED: dwAlert = TLS1_ALERT_BAD_RECORD_MAC; break; case SEC_E_DECRYPT_FAILURE: dwAlert = TLS1_ALERT_DECRYPTION_FAILED; break; case SEC_E_CERT_UNKNOWN: dwAlert = TLS1_ALERT_BAD_CERTIFICATE; break; case CRYPT_E_REVOKED: dwAlert = TLS1_ALERT_CERTIFICATE_REVOKED; break; case SEC_E_CERT_EXPIRED: dwAlert = TLS1_ALERT_CERTIFICATE_EXPIRED; break; case SEC_E_UNTRUSTED_ROOT: dwAlert = TLS1_ALERT_UNKNOWN_CA; break; case SEC_E_LOGON_DENIED: case ERROR_UNABLE_TO_AUTHENTICATE_SERVER: case SEC_E_NO_IMPERSONATION: dwAlert = TLS1_ALERT_ACCESS_DENIED; break; case SEC_E_ILLEGAL_MESSAGE: dwAlert = TLS1_ALERT_DECODE_ERROR; break; case SEC_E_UNSUPPORTED_FUNCTION: dwAlert = TLS1_ALERT_PROTOCOL_VERSION; break; case SEC_E_ALGORITHM_MISMATCH: dwAlert = TLS1_ALERT_INSUFFIENT_SECURITY; break; #if WINVER > 0x0500 case SEC_E_MULTIPLE_ACCOUNTS: //Special case handling for : 96347 dwAlert = TLS1_ALERT_CERTIFICATE_UNKNOWN; break; #endif default: dwAlert = TLS1_ALERT_ACCESS_DENIED; //We have been instructed to translate the error. So do that. if ( fTranslateError ) *pdwErr = SEC_E_LOGON_DENIED; break; } return(dwAlert); } /* Returns: Notes: */ VOID MakeAlert( IN EAPTLSCB* pEapTlsCb, IN DWORD dwAlert, IN BOOL fManualAlert ) { #define NUM_ALERT_BYTES 7 static BYTE pbAlert[NUM_ALERT_BYTES - 1] = {0x15, 0x03, 0x01, 0x00, 0x02, 0x02}; SCHANNEL_ALERT_TOKEN Token; SecBufferDesc OutBuffer; SecBuffer OutBuffers[1]; BOOL fZeroBlobOut = TRUE; DWORD Status; DWORD dwErr; EapTlsTrace("MakeAlert(%d, %s)", dwAlert, fManualAlert ? "Manual" : "Schannel"); if (fManualAlert) { if (NUM_ALERT_BYTES > pEapTlsCb->cbBlobOutBuffer) { LocalFree(pEapTlsCb->pbBlobOut); pEapTlsCb->pbBlobOut = NULL; pEapTlsCb->cbBlobOut = 0; pEapTlsCb->cbBlobOutBuffer = 0; } if (NULL == pEapTlsCb->pbBlobOut) { pEapTlsCb->pbBlobOut = LocalAlloc(LPTR, NUM_ALERT_BYTES); if (NULL == pEapTlsCb->pbBlobOut) { pEapTlsCb->cbBlobOut = 0; pEapTlsCb->cbBlobOutBuffer = 0; dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pEapTlsCb->cbBlobOutBuffer = NUM_ALERT_BYTES; } CopyMemory(pEapTlsCb->pbBlobOut, pbAlert, NUM_ALERT_BYTES - 1); pEapTlsCb->pbBlobOut[NUM_ALERT_BYTES - 1] = (BYTE) dwAlert; pEapTlsCb->cbBlobOut = NUM_ALERT_BYTES; fZeroBlobOut = FALSE; goto LDone; } if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_HCTXT_INVALID) { EapTlsTrace("hContext is not valid"); goto LDone; } Token.dwTokenType = SCHANNEL_ALERT; Token.dwAlertType = TLS1_ALERT_FATAL; Token.dwAlertNumber = dwAlert; OutBuffers[0].pvBuffer = &Token; OutBuffers[0].cbBuffer = sizeof(Token); OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; Status = ApplyControlToken(&(pEapTlsCb->hContext), &OutBuffer); if (FAILED(Status)) { EapTlsTrace("ApplyControlToken failed and returned 0x%x", Status); goto LDone; } Status = SecurityContextFunction(pEapTlsCb); fZeroBlobOut = FALSE; LDone: if (fZeroBlobOut) { pEapTlsCb->cbBlobOut = pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew = 0; } } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Called to process an incoming packet. We collect all the fragments that the peer want to send and only then call SecurityContextFunction. There are two reasons for this: 1) [Initialize|Accept]SecurityContext sometimes generates an output even when the incoming message is incomplete. The RFC requires us to send an empty Request/Response. 2) A rogue peer may carefully construct a valid 80 MB TLS blob in a denial of service attack. There is no easy way to guard against this. Errors should be returned from this function for things like LocalAlloc failing. Not because we got something bad from the peer. If an error is returned from this function, then we should use EAPACTION_NoAction. Perhaps we can succeed next time. Before calling [Initialize|Accept]SecurityContext, it is OK to return an error for things like LocalAlloc failing. After calling the function, however, we should always return NO_ERROR, and if something failed, set pEapTlsCb->dwAuthResultCode, and go to the state nFinalState. This is because we cannot call [Initialize|Accept]SecurityContext again. */ DWORD MakeReplyMessage( IN EAPTLSCB* pEapTlsCb, IN EAPTLS_PACKET* pReceivePacket ) { BOOL fLengthIncluded = FALSE; BOOL fMoreFragments = FALSE; BOOL fManualAlert = FALSE; BOOL fWaitForUserOK = FALSE; BOOL fServer; DWORD dwBlobSizeReceived; DWORD dwBlobSizeRequired = 0; DWORD dwBlobSizeNew; BYTE* pbBlobOld; int nFinalState; SECURITY_STATUS Status; DWORD dwAlert = 0; WCHAR* apwszWarning[1]; DWORD dwAuthResultCode = NO_ERROR; DWORD dwErr = NO_ERROR; DWORD dwNoCredCode = NO_ERROR; BOOL fTranslateError = FALSE; EapTlsTrace("MakeReplyMessage"); RTASSERT(NULL != pEapTlsCb); RTASSERT(NULL != pReceivePacket); if (PPP_EAP_TLS != pReceivePacket->bType) { // Don't go to LDone. We don't want to change // pEapTlsCb->dwAuthResultCode return(ERROR_PPP_INVALID_PACKET); } fLengthIncluded = pReceivePacket->bFlags & EAPTLS_PACKET_FLAG_LENGTH_INCL; if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_SERVER) { fServer = TRUE; nFinalState = EAPTLS_STATE_SENT_FINISHED; } else { fServer = FALSE; nFinalState = EAPTLS_STATE_RECD_FINISHED; } fMoreFragments = pReceivePacket->bFlags & EAPTLS_PACKET_FLAG_MORE_FRAGMENTS; dwBlobSizeReceived = WireToHostFormat16(pReceivePacket->pbLength) - (fLengthIncluded ? EAPTLS_PACKET_HDR_LEN_MAX : EAPTLS_PACKET_HDR_LEN); if (!(pEapTlsCb->fFlags & EAPTLSCB_FLAG_RECEIVING_FRAGMENTS)) { // We haven't received any fragment yet. Make sure that we have the // right amount of memory allocated in pbBlobIn. if (!fMoreFragments) { dwBlobSizeRequired = pEapTlsCb->cbBlobIn + dwBlobSizeReceived; } else { // This is the first of many fragments. if (!fLengthIncluded) { EapTlsTrace("TLS Message Length is required"); dwAuthResultCode = ERROR_INVALID_PARAMETER; dwAlert = TLS1_ALERT_ILLEGAL_PARAMETER; goto LDone; } else { dwBlobSizeNew = WireToHostFormat32(pReceivePacket->pbData); if (g_dwMaxBlobSize < dwBlobSizeNew) { EapTlsTrace("Blob size %d is unacceptable", dwBlobSizeNew); dwAuthResultCode = ERROR_INVALID_PARAMETER; dwAlert = TLS1_ALERT_ILLEGAL_PARAMETER; goto LDone; } else { dwBlobSizeRequired = pEapTlsCb->cbBlobIn + dwBlobSizeNew; pEapTlsCb->dwBlobInRemining = dwBlobSizeNew; pEapTlsCb->fFlags |= EAPTLSCB_FLAG_RECEIVING_FRAGMENTS; } } } if (dwBlobSizeRequired > pEapTlsCb->cbBlobInBuffer) { EapTlsTrace("Reallocating input TLS blob buffer"); pbBlobOld = pEapTlsCb->pbBlobIn; pEapTlsCb->pbBlobIn = LocalAlloc(LPTR, dwBlobSizeRequired); if (NULL == pEapTlsCb->pbBlobIn) { pEapTlsCb->pbBlobIn = pbBlobOld; dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pEapTlsCb->cbBlobInBuffer = dwBlobSizeRequired; if (0 != pEapTlsCb->cbBlobIn) { RTASSERT(NULL != pbBlobOld); CopyMemory(pEapTlsCb->pbBlobIn, pbBlobOld, pEapTlsCb->cbBlobIn); } LocalFree(pbBlobOld); } } if (pEapTlsCb->fFlags & EAPTLSCB_FLAG_RECEIVING_FRAGMENTS) { if (pEapTlsCb->dwBlobInRemining < dwBlobSizeReceived) { EapTlsTrace("Peer is sending more bytes than promised"); dwAuthResultCode = ERROR_INVALID_PARAMETER; dwAlert = TLS1_ALERT_ILLEGAL_PARAMETER; goto LDone; } else { pEapTlsCb->dwBlobInRemining -= dwBlobSizeReceived; if (0 == pEapTlsCb->dwBlobInRemining) { pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_RECEIVING_FRAGMENTS; if (fMoreFragments) { // No need to send an alert here. EapTlsTrace("Peer has sent the entire TLS blob, but wants " "to send more."); } } } } // Now we are sure that pEapTlsCb->pbBlobIn is big enough to hold all // the information. CopyMemory(pEapTlsCb->pbBlobIn + pEapTlsCb->cbBlobIn, pReceivePacket->pbData + (fLengthIncluded ? 4 : 0), dwBlobSizeReceived); pEapTlsCb->cbBlobIn += dwBlobSizeReceived; if (!(pEapTlsCb->fFlags & EAPTLSCB_FLAG_RECEIVING_FRAGMENTS)) { Status = SecurityContextFunction(pEapTlsCb); // // Need to write a function to map the SSPI error // to nice and rosy RAS error // #if WINVER > 0x0500 if ( Status == SEC_E_UNTRUSTED_ROOT ) { dwAuthResultCode = ERROR_VALIDATING_SERVER_CERT; } else { dwAuthResultCode = Status; } #else dwAuthResultCode = Status; #endif if (SEC_E_OK == Status) { if (fServer) { /* Normally, the server calls ASC for the last time (from state EAPTLS_STATE_SENT_HELLO), gets the blob that contains TLS change_cipher_spec, and then checks to see if the user is OK (AuthenticateUser, etc). However, if the server then wants to send an alert, and wants schannel to create it, it has to undo the TLS change_cipher_spec first. Instead, it constructs the alert itself. */ fManualAlert = TRUE; dwAuthResultCode = AuthenticateUser(pEapTlsCb); if ( SEC_E_NO_CREDENTIALS == dwAuthResultCode ) { dwNoCredCode = dwAuthResultCode; dwAuthResultCode = NO_ERROR; } fTranslateError = FALSE; } else { dwAuthResultCode = AuthenticateServer(pEapTlsCb, &fWaitForUserOK); fTranslateError = TRUE; } if (NO_ERROR != dwAuthResultCode ) { dwAlert = AlertFromError(&dwAuthResultCode, fTranslateError); goto LDone; } // // Since we've started with no fast reconnect // Session established successfully. // Now setup TLS fast reconnect. // if ( fServer ) { dwAuthResultCode = SetTLSFastReconnect(pEapTlsCb, TRUE); if ( NO_ERROR != dwAuthResultCode ) { dwAlert = TLS1_ALERT_INTERNAL_ERROR; goto LDone; } } dwAuthResultCode = CreateMPPEKeyAttributes(pEapTlsCb); if (NO_ERROR != dwAuthResultCode) { dwAlert = TLS1_ALERT_INTERNAL_ERROR; goto LDone; } if (fWaitForUserOK) { pEapTlsCb->EapTlsState = EAPTLS_STATE_WAIT_FOR_USER_OK; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); goto LDone; } } if (SEC_E_OK == Status) { pEapTlsCb->fFlags |= EAPTLSCB_FLAG_SUCCESS; pEapTlsCb->EapTlsState = nFinalState; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); } if (SEC_I_CONTINUE_NEEDED == dwAuthResultCode) { dwAuthResultCode = NO_ERROR; pEapTlsCb->EapTlsState = fServer ? g_nEapTlsServerNextState[pEapTlsCb->EapTlsState]: g_nEapTlsClientNextState[pEapTlsCb->EapTlsState]; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); } } LDone: if (0 != dwAlert) { RTASSERT(NO_ERROR != dwAuthResultCode); pEapTlsCb->cbBlobIn = pEapTlsCb->dwBlobInRemining = 0; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_RECEIVING_FRAGMENTS; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_SUCCESS; MakeAlert(pEapTlsCb, dwAlert, fManualAlert); } if (NO_ERROR != dwAuthResultCode) { pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_SUCCESS; if (nFinalState != pEapTlsCb->EapTlsState) { pEapTlsCb->EapTlsState = nFinalState; EapTlsTrace("State change to %s. Error: 0x%x", g_szEapTlsState[pEapTlsCb->EapTlsState], dwAuthResultCode); } //Commented per bug #'s 475244 and 478128 /* if (fServer) { apwszWarning[0] = pEapTlsCb->awszIdentity ? pEapTlsCb->awszIdentity : L""; RouterLogErrorString(pEapTlsCb->hEventLog, ROUTERLOG_EAP_AUTH_FAILURE, 1, apwszWarning, dwAuthResultCode, 1); } */ } if ( dwNoCredCode == NO_ERROR ) { pEapTlsCb->dwAuthResultCode = dwAuthResultCode; } else { EapTlsTrace ( "No Credentials got from the client. Returning 0x%d", dwNoCredCode); pEapTlsCb->dwAuthResultCode = dwNoCredCode; } return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Called by the client to process an incoming packet and/or send a packet. cbSendPacket is the size in bytes of the buffer pointed to by pSendPacket. This function is called only after FValidPacket(pReceivePacket) returns TRUE. If pEapOutput->Action is going to be EAPACTION_SendAndDone or EAPACTION_Done, make sure that pEapOutput->dwAuthResultCode has been set. If dwAuthResultCode is NO_ERROR, make sure that pEapOutput->pUserAttributes has been set. */ DWORD EapTlsCMakeMessage( IN EAPTLSCB* pEapTlsCb, IN EAPTLS_PACKET* pReceivePacket, OUT EAPTLS_PACKET* pSendPacket, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { EAPTLS_USER_PROPERTIES* pUserProp = NULL; DWORD dwAuthResultCode; DWORD dwErr = NO_ERROR; EapTlsTrace("EapTlsCMakeMessage"); // Response packets should not be sent with any timeout if ( (NULL != pReceivePacket) && (EAPCODE_Request == pReceivePacket->bCode)) { if ( (pEapTlsCb->bId == pReceivePacket->bId) && (EAPTLS_STATE_INITIAL != pEapTlsCb->EapTlsState)) { // The server is repeating its request. Resend our last response. pEapTlsCb->bCode = EAPCODE_Response; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { EapTlsTrace("Resending response for request %d", pEapTlsCb->bId); pEapOutput->Action = EAPACTION_Send; } goto LDone; } else if (pReceivePacket->bFlags & EAPTLS_PACKET_FLAG_TLS_START) { // The server wants to renogitiate dwErr = EapTlsReset(pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; goto LDone; } } } if (NULL != pReceivePacket) { // We are not getting the same old request. Therefore, whatever we sent // last time has reached the server. pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew; if (pEapTlsCb->dwBlobOutOffset == pEapTlsCb->cbBlobOut) { // We have sent whatever we wanted to send pEapTlsCb->cbBlobOut = 0; pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew = 0; } } switch (pEapTlsCb->EapTlsState) { case EAPTLS_STATE_INITIAL: case EAPTLS_STATE_SENT_HELLO: case EAPTLS_STATE_SENT_FINISHED: if (NULL == pReceivePacket) { // We are called once in the initial state. Since we are the // authenticatee, we do nothing, and wait for a request pakcet // from the authenticator. pEapOutput->Action = EAPACTION_NoAction; goto LDone; } /* else if (EAPCODE_Failure == pReceivePacket->bCode) { EapTlsTrace("Negotiation result according to peer: failure"); pEapTlsCb->dwAuthResultCode = E_FAIL; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_SUCCESS; RespondToResult(pEapTlsCb, pEapOutput); goto LDone; } */ else if (EAPCODE_Request != pReceivePacket->bCode) { // We shouldn't get any other packet in this state so // we simply drop this invalid packet EapTlsTrace("Code %d unexpected in state %s", pReceivePacket->bCode, g_szEapTlsState[pEapTlsCb->EapTlsState]); pEapOutput->Action = EAPACTION_NoAction; dwErr = ERROR_PPP_INVALID_PACKET; goto LDone; } else { if (0 != pEapTlsCb->cbBlobOut) { // We still have some stuff to send if (WireToHostFormat16(pReceivePacket->pbLength) == EAPTLS_PACKET_HDR_LEN) { // The server is asking for more stuff by sending an empty // request. pEapTlsCb->bId = pReceivePacket->bId; pEapTlsCb->bCode = EAPCODE_Response; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { pEapOutput->Action = EAPACTION_Send; } goto LDone; } else { // We had more stuff to send, but the peer already wants to // say something. Let us forget our stuff. pEapTlsCb->cbBlobOut = 0; pEapTlsCb->dwBlobOutOffset = 0; pEapTlsCb->dwBlobOutOffsetNew = 0; } } // Build the response packet dwErr = MakeReplyMessage(pEapTlsCb, pReceivePacket); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; goto LDone; } if (EAPTLS_STATE_WAIT_FOR_USER_OK == pEapTlsCb->EapTlsState) { EAPTLS_VALIDATE_SERVER* pEapTlsValidateServer; pEapOutput->Action = EAPACTION_NoAction; pEapOutput->fInvokeInteractiveUI = TRUE; pEapTlsValidateServer = (EAPTLS_VALIDATE_SERVER*) (pEapTlsCb->pUIContextData); pEapOutput->dwSizeOfUIContextData = pEapTlsValidateServer->dwSize; pEapOutput->pUIContextData = pEapTlsCb->pUIContextData; pEapTlsCb->bNextId = pReceivePacket->bId; } else { pEapTlsCb->bId = pReceivePacket->bId; pEapTlsCb->bCode = EAPCODE_Response; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { pEapOutput->Action = EAPACTION_Send; } } goto LDone; } break; case EAPTLS_STATE_WAIT_FOR_USER_OK: if ( (NULL == pEapInput) || (!pEapInput->fDataReceivedFromInteractiveUI)) { pEapOutput->Action = EAPACTION_NoAction; break; } LocalFree(pEapTlsCb->pUIContextData); pEapTlsCb->pUIContextData = NULL; if ( (pEapInput->dwSizeOfDataFromInteractiveUI != sizeof(BYTE)) || (IDNO == *(pEapInput->pDataFromInteractiveUI))) { EapTlsTrace("User chose not to accept the server", dwErr); dwAuthResultCode = ERROR_UNABLE_TO_AUTHENTICATE_SERVER; pEapTlsCb->cbBlobIn = pEapTlsCb->dwBlobInRemining = 0; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_RECEIVING_FRAGMENTS; pEapTlsCb->fFlags &= ~EAPTLSCB_FLAG_SUCCESS; MakeAlert(pEapTlsCb, AlertFromError(&dwAuthResultCode, TRUE), FALSE); } else { EapTlsTrace("User chose to accept the server", dwErr); pEapTlsCb->fFlags |= EAPTLSCB_FLAG_SUCCESS; dwAuthResultCode = NO_ERROR; } pEapTlsCb->EapTlsState = EAPTLS_STATE_RECD_FINISHED; EapTlsTrace("State change to %s. Error: 0x%x", g_szEapTlsState[pEapTlsCb->EapTlsState], dwAuthResultCode); pEapTlsCb->dwAuthResultCode = dwAuthResultCode; pEapTlsCb->bId = pEapTlsCb->bNextId; pEapTlsCb->bCode = EAPCODE_Response; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { pEapOutput->Action = EAPACTION_Send; } break; case EAPTLS_STATE_RECD_FINISHED: if (NULL == pReceivePacket) { // If we did not receive a packet then we check to see if the // fSuccessPacketReceived flag is set (we received an NCP packet: // an implicit EAP-Success). if ( (NULL != pEapInput) && (pEapInput->fSuccessPacketReceived)) { // The peer thinks that the negotiation was successful EapTlsTrace("Negotiation result according to peer: success"); RespondToResult(pEapTlsCb, pEapOutput); } else { pEapOutput->Action = EAPACTION_NoAction; } goto LDone; } else { switch (pReceivePacket->bCode) { case EAPCODE_Success: case EAPCODE_Failure: if (pReceivePacket->bId != pEapTlsCb->bId) { EapTlsTrace("Success/Failure packet has invalid id: %d. " "Expected: %d", pReceivePacket->bId, pEapTlsCb->bId); } EapTlsTrace("Negotiation result according to peer: %s", (EAPCODE_Success == pReceivePacket->bCode) ? "success" : "failure"); RespondToResult(pEapTlsCb, pEapOutput); goto LDone; break; case EAPCODE_Request: case EAPCODE_Response: default: if ( pEapTlsCb->fFlags & EAPTLSCB_FLAG_EXECUTING_PEAP ) { // // if we are in peap, no success is send back. // instead identity request is send across. // Complete if ( pReceivePacket->bCode == EAPCODE_Request ) { RespondToResult(pEapTlsCb, pEapOutput); goto LDone; } EapTlsTrace("Unexpected code: %d in state %s", pReceivePacket->bCode, g_szEapTlsState[pEapTlsCb->EapTlsState]); pEapOutput->Action = EAPACTION_NoAction; } else { EapTlsTrace("Unexpected code: %d in state %s", pReceivePacket->bCode, g_szEapTlsState[pEapTlsCb->EapTlsState]); pEapOutput->Action = EAPACTION_NoAction; goto LDone; } break; } } break; default: EapTlsTrace("Why is the client in this state: %d?", pEapTlsCb->EapTlsState); RTASSERT(FALSE); pEapOutput->Action = EAPACTION_NoAction; dwErr = ERROR_PPP_INVALID_PACKET; break; } LDone: return(dwErr); } /* Returns: Error codes only from winerror.h, raserror.h or mprerror.h. Notes: Called by the server to process an incoming packet and/or send a packet. cbSendPacket is the size in bytes of the buffer pointed to by pSendPacket. This function is called only after FValidPacket(pReceivePacket) returns TRUE. If pEapOutput->Action is going to be EAPACTION_SendAndDone or EAPACTION_Done, make sure that pEapOutput->dwAuthResultCode has been set. If dwAuthResultCode is NO_ERROR, make sure that pEapOutput->pUserAttributes has been set. */ DWORD EapTlsSMakeMessage( IN EAPTLSCB* pEapTlsCb, IN EAPTLS_PACKET* pReceivePacket, OUT EAPTLS_PACKET* pSendPacket, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { DWORD dwErr = NO_ERROR; BOOL fSessionResumed = FALSE; EapTlsTrace("EapTlsSMakeMessage"); if ( (NULL != pReceivePacket) && (EAPCODE_Response == pReceivePacket->bCode) && (pReceivePacket->bId == pEapTlsCb->bId)) { // Whatever we sent last time has reached the client. pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew; if (pEapTlsCb->dwBlobOutOffset == pEapTlsCb->cbBlobOut) { // We have sent whatever we wanted to send pEapTlsCb->cbBlobOut = 0; pEapTlsCb->dwBlobOutOffset = pEapTlsCb->dwBlobOutOffsetNew = 0; } } switch (pEapTlsCb->EapTlsState) { case EAPTLS_STATE_INITIAL: // Create a Request packet dwErr = EapTlsReset(pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; goto LDone; } // pEapTlsCb->bId already has bInitialId pEapTlsCb->bCode = EAPCODE_Request; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; goto LDone; } // Request messages must be sent with a timeout pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pEapTlsCb->EapTlsState = EAPTLS_STATE_SENT_START; EapTlsTrace("State change to %s", g_szEapTlsState[pEapTlsCb->EapTlsState]); goto LDone; break; case EAPTLS_STATE_SENT_START: case EAPTLS_STATE_SENT_HELLO: case EAPTLS_STATE_SENT_FINISHED: if (NULL == pReceivePacket) { // We timed out waiting for a response from the authenticatee. // we need to resend with the same Id. pEapTlsCb->bCode = EAPCODE_Request; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { EapTlsTrace("Resending request %d", pEapTlsCb->bId); pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; } goto LDone; } else if (EAPCODE_Response != pReceivePacket->bCode) { // We should only get responses EapTlsTrace("Ignoring non response packet from client"); pEapOutput->Action = EAPACTION_NoAction; dwErr = ERROR_PPP_INVALID_PACKET; goto LDone; } else if (pReceivePacket->bId != pEapTlsCb->bId) { EapTlsTrace("Ignoring duplicate response packet"); pEapOutput->Action = EAPACTION_NoAction; goto LDone; } else { // We have received a response with the right Id. if (0 != pEapTlsCb->cbBlobOut) { // We still have some stuff to send if (WireToHostFormat16(pReceivePacket->pbLength) == EAPTLS_PACKET_HDR_LEN) { // The client is asking for more stuff by sending an empty // response. pEapTlsCb->bId++; pEapTlsCb->bCode = EAPCODE_Request; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; } goto LDone; } else { // We had more stuff to send, but the peer already wants to // say something. Let us forget our stuff. pEapTlsCb->cbBlobOut = 0; pEapTlsCb->dwBlobOutOffset = 0; pEapTlsCb->dwBlobOutOffsetNew = 0; } } if (EAPTLS_STATE_SENT_FINISHED != pEapTlsCb->EapTlsState) { // We don't have any more stuff to send. // Build the response packet dwErr = MakeReplyMessage(pEapTlsCb, pReceivePacket); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; goto LDone; } if ( (0 == pEapTlsCb->cbBlobOut) && (EAPTLS_STATE_SENT_FINISHED == pEapTlsCb->EapTlsState)) { // If the client sent an alert, send Failure immediately. // Do not send one more request. if (!(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SUCCESS)) { RTASSERT(NO_ERROR != pEapTlsCb->dwAuthResultCode); } else { fSessionResumed = TRUE; } } else { pEapTlsCb->bId++; pEapTlsCb->bCode = EAPCODE_Request; dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; } goto LDone; } } if (!(pEapTlsCb->fFlags & EAPTLSCB_FLAG_SUCCESS)) { EapTlsTrace("Negotiation unsuccessful"); pEapTlsCb->bCode = EAPCODE_Failure; } else { if ( (WireToHostFormat16(pReceivePacket->pbLength) == EAPTLS_PACKET_HDR_LEN) || fSessionResumed) { EapTlsTrace("Negotiation successful"); pEapTlsCb->bCode = EAPCODE_Success; } else { // We got an alert from the client EapTlsTrace("Client sent an alert; " "negotiation unsuccessful"); GetAlert(pEapTlsCb, pReceivePacket); pEapTlsCb->bCode = EAPCODE_Failure; } } // pEapTlsCb->bId should be the same as that of the last // request. dwErr = BuildPacket(pSendPacket, cbSendPacket, pEapTlsCb); if (NO_ERROR != dwErr) { pEapOutput->Action = EAPACTION_NoAction; } else { #if 0 RTASSERT( ( EAPCODE_Failure == pEapTlsCb->bCode && NO_ERROR != pEapTlsCb->dwAuthResultCode) || ( EAPCODE_Success == pEapTlsCb->bCode && NO_ERROR == pEapTlsCb->dwAuthResultCode)); #endif EapTlsTrace ("AuthResultCode = (%ld), bCode = (%ld)", pEapTlsCb->dwAuthResultCode, pEapTlsCb->bCode); pEapOutput->pUserAttributes = pEapTlsCb->pAttributes; pEapOutput->dwAuthResultCode = pEapTlsCb->dwAuthResultCode; pEapOutput->Action = EAPACTION_SendAndDone; } goto LDone; } break; default: EapTlsTrace("Why is the server in this state: %d?", pEapTlsCb->EapTlsState); RTASSERT(FALSE); pEapOutput->Action = EAPACTION_NoAction; dwErr = ERROR_PPP_INVALID_PACKET; break; } LDone: return(dwErr); } DWORD RasEapGetCredentials( IN DWORD dwTypeId, IN VOID * pWorkBuf, OUT VOID ** ppCredentials) { EAPTLSCB *pEapTlsCb = (EAPTLSCB *)pWorkBuf; if(PPP_EAP_PEAP == dwTypeId) { return PeapGetCredentials( pWorkBuf, ppCredentials); } else if( (PPP_EAP_TLS != dwTypeId) || (NULL == pEapTlsCb)) { return E_INVALIDARG; } // // Get TLS credentials here and return them in the // pCredentials blob. // return GetCredentialsFromUserProperties(pEapTlsCb, ppCredentials); } /////////////////////////////All PEAP related stuff ////////////////////////////////// DWORD EapPeapInitialize( IN BOOL fInitialize ) { DWORD dwRetCode = NO_ERROR; static DWORD dwRefCount = 0; EapTlsInitialize(fInitialize); // // Get a list of all EapTypes that can be Peap enabled // if ( fInitialize ) { if ( !dwRefCount ) { ZeroMemory ( &(g_CachedCreds[VPN_PEAP_CACHED_CREDS_INDEX]), sizeof(EAPTLS_CACHED_CREDS) ); ZeroMemory ( &(g_CachedCreds[WIRELESS_PEAP_CACHED_CREDS_INDEX]), sizeof(EAPTLS_CACHED_CREDS) ); dwRetCode = PeapEapInfoGetList ( NULL, &g_pEapInfo); } dwRefCount++; } else { dwRefCount --; if ( !dwRefCount ) { PeapEapInfoFreeList( g_pEapInfo ); g_pEapInfo = NULL; } } return dwRetCode; } DWORD EapPeapBegin( OUT VOID** ppWorkBuffer, IN PPP_EAP_INPUT* pPppEapInput ) { DWORD dwRetCode = NO_ERROR; PPEAPCB pPeapCB = NULL; PPP_EAP_INPUT PppEapInputToTls; EAPTLS_USER_PROPERTIES EapTlsUserProp; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * pEntryConnProp = NULL; EapTlsTrace("EapPeapBegin"); RTASSERT(NULL != ppWorkBuffer); RTASSERT(NULL != pPppEapInput); #if 0 dwRetCode = VerifyCallerTrust(_ReturnAddress()); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Unauthorized use of PEAP attempted"); goto LDone; } #endif pPeapCB = (PPEAPCB)LocalAlloc(LPTR, sizeof(PEAPCB) ); if ( NULL == pPeapCB ) { EapTlsTrace("Error allocating memory for PEAPCB"); dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } // // Get info for each of the configured eap types and call // initialze and then begin // pPeapCB->PeapState = PEAP_STATE_INITIAL; if ( pPppEapInput->fAuthenticator ) { pPeapCB->dwFlags |= PEAPCB_FLAG_SERVER; } pPppEapInput->fFlags & RAS_EAP_FLAG_ROUTER ? pPeapCB->dwFlags |= PEAPCB_FLAG_ROUTER:0; pPppEapInput->fFlags & RAS_EAP_FLAG_NON_INTERACTIVE ? pPeapCB->dwFlags |= PEAPCB_FLAG_NON_INTERACTIVE:0; pPppEapInput->fFlags & RAS_EAP_FLAG_LOGON ? pPeapCB->dwFlags |= PEAPCB_FLAG_LOGON:0; pPppEapInput->fFlags & RAS_EAP_FLAG_PREVIEW ? pPeapCB->dwFlags |= PEAPCB_FLAG_PREVIEW:0; pPppEapInput->fFlags & RAS_EAP_FLAG_FIRST_LINK ? pPeapCB->dwFlags |= PEAPCB_FLAG_FIRST_LINK:0; pPppEapInput->fFlags & RAS_EAP_FLAG_MACHINE_AUTH ? pPeapCB->dwFlags |= PEAPCB_FLAG_MACHINE_AUTH:0; pPppEapInput->fFlags & RAS_EAP_FLAG_GUEST_ACCESS? pPeapCB->dwFlags |= PEAPCB_FLAG_GUEST_ACCESS :0; pPppEapInput->fFlags & RAS_EAP_FLAG_8021X_AUTH ? pPeapCB->dwFlags |= PEAPCB_FLAG_8021X_AUTH:0; pPeapCB->hTokenImpersonateUser = pPppEapInput->hTokenImpersonateUser; if ( pPppEapInput->pwszPassword ) { wcsncpy ( pPeapCB->awszPassword, pPppEapInput->pwszPassword, PWLEN ); } if ( pPeapCB->dwFlags & PEAPCB_FLAG_SERVER ) { // // Read Server Configuration from the registry // dwRetCode = PeapServerConfigDataIO(TRUE /* fRead */, NULL /* pwszMachineName */, (BYTE**)&(pPeapCB->pUserProp), 0); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error reading server configuration. 0x%x", dwRetCode ); goto LDone; } // // For all configured PEAP types load EAPINFO // dwRetCode = PeapGetFirstEntryUserProp ( pPeapCB->pUserProp, &pEntryUserProp ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error PEAP not configured correctly. 0x%x", dwRetCode ); goto LDone; } // // Get the selected EAP type // dwRetCode = PeapEapInfoCopyListNode ( pEntryUserProp->dwEapTypeId, g_pEapInfo, &pPeapCB->pEapInfo ); if ( NO_ERROR != dwRetCode || NULL == pPeapCB->pEapInfo ) { EapTlsTrace("Cannot find configured PEAP in the list of EAP Types on this machine."); goto LDone; } // // Check to see if we are enabled to do fast reconnect // if ( pPeapCB->pUserProp->dwFlags & PEAP_USER_FLAG_FAST_ROAMING ) { pPeapCB->dwFlags |= PEAPCB_FAST_ROAMING; } } else { // // This is a client. So get PEAP conn prop and // user prop // dwRetCode = PeapReadConnectionData( ( pPppEapInput->fFlags & RAS_EAP_FLAG_8021X_AUTH ), pPppEapInput->pConnectionData, pPppEapInput->dwSizeOfConnectionData, &(pPeapCB->pConnProp) ); if (NO_ERROR != dwRetCode) { EapTlsTrace("Error Reading Connection Data. 0x%x", dwRetCode); goto LDone; } // // Read user data now // dwRetCode = PeapReadUserData( pPppEapInput->pUserData, pPppEapInput->dwSizeOfUserData, &(pPeapCB->pUserProp) ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error Reading User Data. 0x%x", dwRetCode); goto LDone; } dwRetCode = PeapGetFirstEntryConnProp ( pPeapCB->pConnProp, &pEntryConnProp ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error PEAP not configured correctly. 0x%x", dwRetCode ); goto LDone; } // // Get the selected EAP type // dwRetCode = PeapEapInfoCopyListNode ( pEntryConnProp->dwEapTypeId, g_pEapInfo, &pPeapCB->pEapInfo ); if ( NO_ERROR != dwRetCode || NULL == pPeapCB->pEapInfo ) { EapTlsTrace("Cannot find configured PEAP in the list of EAP Types on this machine."); goto LDone; } // // Check to see if we are enabled to do fast reconnect // if ( pPeapCB->pConnProp->dwFlags & PEAP_CONN_FLAG_FAST_ROAMING ) { pPeapCB->dwFlags |= PEAPCB_FAST_ROAMING; } } // // Call Initialize and Begin for the // configured EAP type. // Call Begin for EapTls. // We need to create PPP_EAP_INFO for this // // // Call Begin for EapTlsBegin first // ZeroMemory ( &PppEapInputToTls, sizeof(PppEapInputToTls) ); CopyMemory ( &PppEapInputToTls, pPppEapInput, sizeof(PppEapInputToTls) ); PppEapInputToTls.dwSizeInBytes = sizeof(PppEapInputToTls); PppEapInputToTls.fFlags = pPppEapInput->fFlags | EAPTLSCB_FLAG_EXECUTING_PEAP; if ( pPeapCB->pConnProp ) { // // Get the V0 struct required by eaptls // ConnPropGetV0Struct ( &(pPeapCB->pConnProp->EapTlsConnProp), (EAPTLS_CONN_PROPERTIES **) &(PppEapInputToTls.pConnectionData) ); PppEapInputToTls.dwSizeOfConnectionData = ((EAPTLS_CONN_PROPERTIES *) (PppEapInputToTls.pConnectionData) )->dwSize; } ZeroMemory( &EapTlsUserProp, sizeof(EapTlsUserProp) ); EapTlsUserProp.dwVersion = 1; EapTlsUserProp.dwSize = sizeof(EapTlsUserProp); CopyMemory ( &EapTlsUserProp.Hash, &(pPeapCB->pUserProp->CertHash), sizeof(EapTlsUserProp.Hash) ); PppEapInputToTls.pUserData = (VOID *)&EapTlsUserProp; PppEapInputToTls.dwSizeOfUserData = sizeof(EapTlsUserProp); dwRetCode = EapTlsBegin ( (VOID **)&(pPeapCB->pEapTlsCB), &PppEapInputToTls ); // // Save the identity for later use // wcsncpy(pPeapCB->awszIdentity, pPppEapInput->pwszIdentity ? pPppEapInput->pwszIdentity : L"", UNLEN + DNLEN); *ppWorkBuffer = (VOID *)pPeapCB; LDone: if ( PppEapInputToTls.pConnectionData ) LocalFree ( PppEapInputToTls.pConnectionData ); EapTlsTrace("EapPeapBegin done"); return dwRetCode; } DWORD EapPeapEnd( IN PPEAPCB pPeapCb ) { DWORD dwRetCode = NO_ERROR; EapTlsTrace("EapPeapEnd"); // // call end for eaptls and each of the peap types // configured first and then execute code for // peap end. if ( pPeapCb ) { dwRetCode = EapTlsEnd((VOID *)pPeapCb->pEapTlsCB); //Call the embedded type's end here if ( pPeapCb->pEapInfo ) { dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapEnd ( pPeapCb->pEapInfo->pWorkBuf); pPeapCb->pEapInfo->pWorkBuf = NULL; LocalFree ( pPeapCb->pEapInfo ); pPeapCb->pEapInfo = NULL; } LocalFree ( pPeapCb->pConnProp ); LocalFree ( pPeapCb->pUserProp ); LocalFree ( pPeapCb->pUIContextData ); if ( pPeapCb->pPrevReceivePacket ) { LocalFree ( pPeapCb->pPrevReceivePacket ); } if ( pPeapCb->pPrevDecData ) { LocalFree ( pPeapCb->pPrevDecData ); } #ifdef USE_CUSTOM_TUNNEL_KEYS if ( pPeapCb->hSendKey ) { CryptDestroyKey ( pPeapCb->hSendKey ); } if ( pPeapCb->hRecvKey ) { CryptDestroyKey ( pPeapCb->hRecvKey ); } #endif if ( pPeapCb->hProv ) { CryptReleaseContext(pPeapCb->hProv, 0 ); } if ( pPeapCb->pbIoBuffer ) { LocalFree ( pPeapCb->pbIoBuffer ); } LocalFree ( pPeapCb ); pPeapCb = NULL; } EapTlsTrace("EapPeapEnd done"); return dwRetCode; } // // Check to see if this is a duplicate packet received. // BOOL IsDuplicatePacket ( IN PPEAPCB pPeapCb, IN PPP_EAP_PACKET * pNewPacket ) { BOOL fRet = FALSE; WORD wPacketLen = 0; EapTlsTrace("IsDuplicatePacket"); wPacketLen = WireToHostFormat16 ( pNewPacket->Length ); if ( wPacketLen == pPeapCb->cbPrevReceivePacket ) { // // We have the same packet length // Now compare the packet and see // if it is the same // if ( pPeapCb->pPrevReceivePacket ) { if ( !memcmp( pNewPacket, pPeapCb->pPrevReceivePacket, wPacketLen ) ) { // // we got a dup packet // EapTlsTrace("Got Duplicate Packet"); fRet = TRUE; } } } return fRet; } DWORD PeapDecryptTunnelData ( IN PPEAPCB pPeapCb, IN OUT PBYTE pbData, IN DWORD dwSizeofData ) { SecBufferDesc SecBufferDesc; SecBuffer SecBuffer[4]; SECURITY_STATUS status; INT i = 0; EapTlsTrace("PeapDecryptTunnelData dwSizeofData = 0x%x, pData = 0x%x", dwSizeofData, pbData); // // Use the schannel context to encrypt data // SecBufferDesc.ulVersion = SECBUFFER_VERSION; SecBufferDesc.cBuffers = 4; SecBufferDesc.pBuffers = SecBuffer; SecBuffer[0].cbBuffer = dwSizeofData; SecBuffer[0].BufferType = SECBUFFER_DATA; SecBuffer[0].pvBuffer = pbData; SecBuffer[1].BufferType = SECBUFFER_EMPTY; SecBuffer[2].BufferType = SECBUFFER_EMPTY; SecBuffer[3].BufferType = SECBUFFER_EMPTY; status = DecryptMessage ( &(pPeapCb->pEapTlsCB->hContext), &SecBufferDesc, 0, 0 ); EapTlsTrace("PeapDecryptTunnelData completed with status 0x%x", status); if ( SEC_E_OK == status ) { // // Copy over the decrypted data to our io buffer // while ( i < 4 ) { if(SecBuffer[i].BufferType == SECBUFFER_DATA) { CopyMemory ( pPeapCb->pbIoBuffer, SecBuffer[i].pvBuffer, SecBuffer[i].cbBuffer ); pPeapCb->dwIoBufferLen = SecBuffer[i].cbBuffer; break; } i++; } } return status; } // // Use this function on client side. // This will first check to see if this is a duplicate packet // If so, it will replace the current packet with duplicate // one. Or else it will continue with decryption // DWORD PeapClientDecryptTunnelData ( IN PPEAPCB pPeapCb, IN PPP_EAP_PACKET* pReceivePacket, IN WORD wOffset ) { DWORD dwRetCode = NO_ERROR; EapTlsTrace ("PeapClientDecryptTunnelData"); if ( !pReceivePacket ) { EapTlsTrace ("Got an empty packet"); goto LDone; } if ( IsDuplicatePacket ( pPeapCb, pReceivePacket ) ) { // // Received a duplicate packet // // So set the data to what was decrypted in the past... // if ( pPeapCb->pPrevDecData ) { CopyMemory ( &(pReceivePacket->Data[wOffset]), pPeapCb->pPrevDecData, pPeapCb->cbPrevDecData ); pPeapCb->pbIoBuffer = pPeapCb->pPrevDecData; pPeapCb->dwIoBufferLen = pPeapCb->cbPrevDecData; HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->cbPrevDecData +1), pReceivePacket->Length ); } else { EapTlsTrace("Got an unexpected duplicate packet"); dwRetCode = SEC_E_MESSAGE_ALTERED; } } else { if ( pPeapCb->pPrevReceivePacket ) { LocalFree(pPeapCb->pPrevReceivePacket); pPeapCb->pPrevReceivePacket = NULL; pPeapCb->cbPrevReceivePacket = 0; } if ( pPeapCb->pPrevDecData ) { LocalFree ( pPeapCb->pPrevDecData ); pPeapCb->pPrevDecData = NULL; pPeapCb->cbPrevDecData = 0; } pPeapCb->pPrevReceivePacket = (PPP_EAP_PACKET*)LocalAlloc(LPTR, WireToHostFormat16(pReceivePacket->Length) ); if ( pPeapCb->pPrevReceivePacket ) { pPeapCb->cbPrevReceivePacket = WireToHostFormat16(pReceivePacket->Length); CopyMemory ( pPeapCb->pPrevReceivePacket, pReceivePacket, pPeapCb->cbPrevReceivePacket ); } // // Received a new packet. So we need to decrypt it. // dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), WireToHostFormat16(pReceivePacket->Length) - ( sizeof(PPP_EAP_PACKET) + 1 ) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace ("Failed to decrypt packet."); // // Wipe out the prev receive packet // if ( pPeapCb->pPrevReceivePacket ) { LocalFree(pPeapCb->pPrevReceivePacket); pPeapCb->pPrevReceivePacket = NULL; pPeapCb->cbPrevReceivePacket = 0; } goto LDone; } CopyMemory ( &(pReceivePacket->Data[wOffset]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); pPeapCb->pPrevDecData = (PBYTE)LocalAlloc ( LPTR, pPeapCb->dwIoBufferLen ); if ( pPeapCb->pPrevDecData ) { CopyMemory ( pPeapCb->pPrevDecData , pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); pPeapCb->cbPrevDecData = (WORD)pPeapCb->dwIoBufferLen; } HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->dwIoBufferLen +1), pReceivePacket->Length ); } LDone: return dwRetCode; } DWORD PeapEncryptTunnelData ( IN PPEAPCB pPeapCb, IN OUT PBYTE pbData, IN DWORD dwSizeofData ) { SecBufferDesc SecBufferDesc; SecBuffer SecBuffer[4]; SECURITY_STATUS status; EapTlsTrace("PeapEncryptTunnelData"); // // Use the schannel context to encrypt data // SecBufferDesc.ulVersion = SECBUFFER_VERSION; SecBufferDesc.cBuffers = 4; SecBufferDesc.pBuffers = SecBuffer; SecBuffer[0].cbBuffer = pPeapCb->PkgStreamSizes.cbHeader; SecBuffer[0].BufferType = SECBUFFER_STREAM_HEADER; SecBuffer[0].pvBuffer = pPeapCb->pbIoBuffer; CopyMemory ( pPeapCb->pbIoBuffer+pPeapCb->PkgStreamSizes.cbHeader, pbData, dwSizeofData ); SecBuffer[1].cbBuffer = dwSizeofData; SecBuffer[1].BufferType = SECBUFFER_DATA; SecBuffer[1].pvBuffer = pPeapCb->pbIoBuffer+pPeapCb->PkgStreamSizes.cbHeader; SecBuffer[2].cbBuffer = pPeapCb->PkgStreamSizes.cbTrailer; SecBuffer[2].BufferType = SECBUFFER_STREAM_TRAILER; SecBuffer[2].pvBuffer = pPeapCb->pbIoBuffer + pPeapCb->PkgStreamSizes.cbHeader + dwSizeofData; SecBuffer[3].BufferType = SECBUFFER_EMPTY; status = EncryptMessage ( &(pPeapCb->pEapTlsCB->hContext), 0, &SecBufferDesc, 0 ); pPeapCb->dwIoBufferLen = SecBuffer[0].cbBuffer + SecBuffer[1].cbBuffer + SecBuffer[2].cbBuffer ; EapTlsTrace("PeapEncryptTunnelData completed with status 0x%x", status); return status; } DWORD PeapGetTunnelProperties ( IN PPEAPCB pPeapCb ) { SECURITY_STATUS status; EapTlsTrace("PeapGetTunnelProperties"); status = QueryContextAttributes ( &(pPeapCb->pEapTlsCB->hContext), SECPKG_ATTR_CONNECTION_INFO, &(pPeapCb->PkgConnInfo) ); if (SEC_E_OK != status) { EapTlsTrace ( "QueryContextAttributes for CONN_INFO failed with error 0x%x", status ); goto LDone; } status = QueryContextAttributes ( &(pPeapCb->pEapTlsCB->hContext), SECPKG_ATTR_STREAM_SIZES, &(pPeapCb->PkgStreamSizes) ); if (SEC_E_OK != status) { EapTlsTrace ( "QueryContextAttributes for STREAM_SIZES failed with error 0x%x", status ); goto LDone; } EapTlsTrace ( "Successfully negotiated TLS with following parameters" "dwProtocol = 0x%x, Cipher= 0x%x, CipherStrength=0x%x, Hash=0x%x", pPeapCb->PkgConnInfo.dwProtocol, pPeapCb->PkgConnInfo.aiCipher, pPeapCb->PkgConnInfo.dwCipherStrength, pPeapCb->PkgConnInfo.aiHash ); pPeapCb->pbIoBuffer = (PBYTE)LocalAlloc ( LPTR, pPeapCb->PkgStreamSizes.cbHeader + pPeapCb->PkgStreamSizes.cbTrailer + pPeapCb->PkgStreamSizes.cbMaximumMessage ); if ( NULL == pPeapCb->pbIoBuffer ) { EapTlsTrace ( "Cannot allocate memory for IoBuffer"); status = ERROR_OUTOFMEMORY; } LDone: EapTlsTrace("PeapGetTunnelProperties done"); return status; } DWORD EapPeapCMakeMessage( IN PPEAPCB pPeapCb, IN PPP_EAP_PACKET* pReceivePacket, OUT PPP_EAP_PACKET* pSendPacket, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { DWORD dwRetCode = NO_ERROR; PPP_EAP_INPUT EapTypeInput; WORD wPacketLength; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * pEntryConnProp = NULL; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; PBYTE pbCookie = NULL; DWORD cbCookie = 0; BOOL fIsReconnect = FALSE; BOOL fReceivedTLV = FALSE; DWORD dwVersion = 0; WORD wValue = 0; BOOL fImpersonating = FALSE; EapTlsTrace("EapPeapCMakeMessage"); if ( !(pPeapCb->dwFlags & PEAPCB_FLAG_SERVER ) && !(pPeapCb->dwFlags & PEAPCB_FLAG_ROUTER) && !(pPeapCb->dwFlags & PEAPCB_FLAG_MACHINE_AUTH ) && !(pPeapCb->dwFlags & PEAPCB_FLAG_LOGON ) ) { if(!ImpersonateLoggedOnUser(pPeapCb->hTokenImpersonateUser) ) { dwRetCode = GetLastError(); EapTlsTrace ("PEAP: ImpersonateLoggedonUser failed and returned 0x%x", dwRetCode); return dwRetCode; } fImpersonating = TRUE; } switch ( pPeapCb->PeapState ) { case PEAP_STATE_INITIAL: EapTlsTrace("PEAP:PEAP_STATE_INITIAL"); // // Start the EapTls Conversation here. // //Receive Packet will be NULL. Call EapTlsSMakeMessage // if ( pReceivePacket ) { pReceivePacket->Data[0] = PPP_EAP_TLS; } dwRetCode = EapTlsCMakeMessage( pPeapCb->pEapTlsCB, (EAPTLS_PACKET *)pReceivePacket, (EAPTLS_PACKET *)pSendPacket, cbSendPacket, pEapOutput, pEapInput ); if ( NO_ERROR == dwRetCode ) { //change the packet to show peap pSendPacket->Data[0] = PPP_EAP_PEAP; if ( pReceivePacket ) dwVersion = ((EAPTLS_PACKET *)pReceivePacket)->bFlags & 0x03; if ( dwVersion != EAPTLS_PACKET_CURRENT_VERSION ) { ((EAPTLS_PACKET *)pSendPacket)->bFlags |= EAPTLS_PACKET_LOWEST_SUPPORTED_VERSION; } else { ((EAPTLS_PACKET *)pSendPacket)->bFlags |= EAPTLS_PACKET_CURRENT_VERSION; } } pPeapCb->PeapState = PEAP_STATE_TLS_INPROGRESS; break; case PEAP_STATE_TLS_INPROGRESS: EapTlsTrace("PEAP:PEAP_STATE_TLS_INPROGRESS"); if ( pReceivePacket && ( pReceivePacket->Code == EAPCODE_Request || pReceivePacket->Code == EAPCODE_Response ) ) { pReceivePacket->Data[0] = PPP_EAP_TLS; } // // We could either get a TLV_Success Request or // and identity request as a termination packet. // Both of these are encrypted using the keys. // PAss them on to TLS and when it sends a success // back, decrypt to see if it was a success or // identity. // dwRetCode = EapTlsCMakeMessage( pPeapCb->pEapTlsCB, (EAPTLS_PACKET *)pReceivePacket, (EAPTLS_PACKET *)pSendPacket, cbSendPacket, pEapOutput, pEapInput ); if ( NO_ERROR == dwRetCode ) { // // if interactive UI was requested, wrap the data in // in PEAP interactive UI structure // if ( pEapOutput->fInvokeInteractiveUI ) { if ( pPeapCb->pUIContextData ) { LocalFree ( pPeapCb->pUIContextData ); pPeapCb->pUIContextData = NULL; } pPeapCb->pUIContextData = (PPEAP_INTERACTIVE_UI) LocalAlloc(LPTR, sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData ); if ( NULL == pPeapCb->pUIContextData ) { EapTlsTrace("Error allocating memory for PEAP context data"); dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } pPeapCb->pUIContextData->dwEapTypeId = PPP_EAP_TLS; pPeapCb->pUIContextData->dwSizeofUIContextData = pEapOutput->dwSizeOfUIContextData; CopyMemory( pPeapCb->pUIContextData->bUIContextData, pEapOutput->pUIContextData, pEapOutput->dwSizeOfUIContextData ); pEapOutput->pUIContextData = (PBYTE)pPeapCb->pUIContextData; pEapOutput->dwSizeOfUIContextData = sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData; } else if ( pEapOutput->Action == EAPACTION_Done ) { if ( pEapOutput->dwAuthResultCode == NO_ERROR ) { // // PEAP auth was successful. Carefully keep the MPPE // session keys returned so that we can encrypt the // channel. From now on everything will be encrypted. // // if we're enabled for fast reconnect check to see if this // was a reconnect and see if the cookie is valid. // // // Check to see if we were send back a success or if we were send back an // identity request. // if ( !( pPeapCb->pEapTlsCB->fFlags & EAPTLSCB_FLAG_USING_CACHED_CREDS ) ) { // // If we are not using cached credentials // SetCachedCredentials (pPeapCb->pEapTlsCB); } pPeapCb->pTlsUserAttributes = pEapOutput->pUserAttributes; dwRetCode = PeapGetTunnelProperties ( pPeapCb ); if (NO_ERROR != dwRetCode ) { break; } pEapOutput->pUserAttributes = NULL; pEapOutput->Action = EAPACTION_NoAction; // // Check to see if we need to save connection and user data // for TLS if ( pEapOutput->fSaveConnectionData ) { // // save connection data in PEAP control // block and then finally when auth is done // We send back a save command. // if ( ConnPropGetV1Struct ( (EAPTLS_CONN_PROPERTIES *) pEapOutput->pConnectionData, &(pPeapCb->pNewTlsConnProp) ) == NO_ERROR ) { pPeapCb->fTlsConnPropDirty = TRUE; } pEapOutput->fSaveConnectionData = FALSE; } if ( pEapOutput->fSaveUserData ) { // // There is nothing to save in user data for PEAP. // But the flag is left here just in case... pPeapCb->fTlsUserPropDirty = TRUE; pEapOutput->fSaveUserData = FALSE; } case PEAP_STATE_FAST_ROAMING_IDENTITY_REQUEST: // // EAPTLS terminated with an identity request // so process the received identity request here if ( pReceivePacket ) { // // This can be either an identity packet or // an TLV_Success packet. // dwRetCode = PeapClientDecryptTunnelData(pPeapCb,pReceivePacket, 2); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } /* wPacketLength = WireToHostFormat16(pReceivePacket->Length); dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), wPacketLength - ( sizeof(PPP_EAP_PACKET) + 1 ) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } CopyMemory ( &(pReceivePacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->dwIoBufferLen +1), pReceivePacket->Length ); */ pReceivePacket->Data[0] = PPP_EAP_PEAP; //This is an AVP message if ( pReceivePacket->Data[8] == MS_PEAP_AVP_TYPE_STATUS && pReceivePacket->Data[6] == PEAP_TYPE_AVP ) { //This is a TLV message dwRetCode = GetPEAPTLVStatusMessageValue ( pPeapCb, pReceivePacket, &wValue ); if ( NO_ERROR != dwRetCode || wValue != MS_PEAP_AVP_VALUE_SUCCESS ) { EapTlsTrace("Got invalid TLV when expecting TLV_SUCCESS. Silently discarding."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } // // Check to see if this is a success. If this is a success, server wants to // do fast roaming. Check to see if this is a fast reconnect and // if this is a fast reconnect, get cookie and compare it // if all's fine then send a success response or else send a failure // response. If none of this works then fail auth with an internal // error. // if ( pPeapCb->dwFlags & PEAPCB_FAST_ROAMING ) { dwRetCode = GetTLSSessionCookie ( pPeapCb->pEapTlsCB, &pbCookie, &cbCookie, &fIsReconnect ); if ( NO_ERROR != dwRetCode) { //There was an error getting session cookie. //Or there is no cookie and this is a reconnet // So fail the request. EapTlsTrace("Error getting cookie for a reconnected session. Failing auth"); // We cannot encrypt and send stuff across here. // Reconnected Session state is invalid. pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } if ( fIsReconnect ) { if ( cbCookie == 0 ) { //There was an error getting session cookie. //Or there is no cookie and this is a reconnet // So fail the request. EapTlsTrace("Error getting cookie for a reconnected session. Failing auth"); dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); // We cannot encrypt and send stuff across here. // Reconnected Session state is invalid. dwRetCode = ERROR_INTERNAL_ERROR; pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } // // This is a server // Check to see if the cookie is fine. // If it is fine then there is no need to reauth. // So send back a PEAP_SUCCESS response packet // and change our state to PEAP_SUCCESS_SEND // EapTlsTrace ("TLS session fast reconnected"); dwRetCode = PeapCheckCookie ( pPeapCb, (PPEAP_COOKIE)pbCookie, cbCookie ); if ( NO_ERROR != dwRetCode ) { // // So invalidate the session for fast reconnect // and fail auth. Next time a full reconnect will happen // dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } EapTlsTrace ("Error validating the cookie. Failing auth"); pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } else { // // Cookie is fine. // // // Send a PEAP success TLV response. // dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, FALSE, //Response MS_PEAP_AVP_VALUE_SUCCESS ); if ( NO_ERROR != dwRetCode ) { // Internal error break; } pEapOutput->Action = EAPACTION_Send; pPeapCb->PeapState = PEAP_STATE_PEAP_SUCCESS_SEND; pPeapCb->dwAuthResultCode = NO_ERROR; break; } } else { // server is expecting fast roaming and we are not configured to do it. // send a fail tlv here EapTlsTrace("Server expects fast roaming and we dont. Sending PEAP_Failure"); dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, FALSE, //Response MS_PEAP_AVP_VALUE_FAILURE ); if ( NO_ERROR != dwRetCode ) { // Internal error break; } pEapOutput->Action = EAPACTION_Send; pPeapCb->PeapState = PEAP_STATE_FAST_ROAMING_IDENTITY_REQUEST; pPeapCb->dwAuthResultCode = NO_ERROR; break; } } else { // Server is requesting fast roaming but we're not setup to do so. // So send back a fail request so that the auth fails. dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, FALSE, //Response MS_PEAP_AVP_VALUE_FAILURE ); if ( NO_ERROR != dwRetCode ) { // Internal error break; } // // We can expect to get back an encrypted identity request // Since the client is not setup to do fast roaming and the // server is, we fail the success and expect identity // request. // pEapOutput->Action = EAPACTION_Send; pPeapCb->PeapState = PEAP_STATE_FAST_ROAMING_IDENTITY_REQUEST; pPeapCb->dwAuthResultCode = NO_ERROR; break; } break; } else { if ( pReceivePacket->Data[2] != PEAP_EAPTYPE_IDENTITY ) { EapTlsTrace ("Got unexpected packet when expecting PEAP identity request. Silently discarding packet."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } } //If we've come this far, it must be an identity request. pSendPacket->Code = EAPCODE_Response; pSendPacket->Id = pReceivePacket->Id; CopyMemory ( pPeapCb->awszTypeIdentity, pPeapCb->awszIdentity, ( DNLEN+ UNLEN) * sizeof(WCHAR) ); // //length = sizeof header + 1 byte for code identity + strlen of identity // pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pSendPacket->Data[2] = PEAP_EAPTYPE_IDENTITY; //copy the identity over if ( 0 == WideCharToMultiByte( CP_ACP, 0, pPeapCb->awszIdentity, -1, (LPSTR)&(pSendPacket->Data[3]), UNLEN + DNLEN+ 1, NULL, NULL ) ) { // // This is an internal error. There is no concept of PEAP_SUCCESS/FAIL TLV here. // dwRetCode = GetLastError(); EapTlsTrace("Unable to convert from widechar to multibyte 0x%x", dwRetCode ); goto LDone; } dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[2]), 1+wcslen(pPeapCb->awszIdentity) ); if ( NO_ERROR != dwRetCode ) { break; } //Copy over the encrypted data into send buffer CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+ 1 +pPeapCb->dwIoBufferLen), pSendPacket->Length ); pEapOutput->Action = EAPACTION_Send; pPeapCb->PeapState = PEAP_STATE_IDENTITY_RESPONSE_SENT; } else { pEapOutput->Action = EAPACTION_NoAction; EapTlsTrace("Got empty packet when expecting identity request. Ignoring."); } } } else { //change the packet to show peap pSendPacket->Data[0] = PPP_EAP_PEAP; } } break; case PEAP_STATE_IDENTITY_RESPONSE_SENT: EapTlsTrace("PEAP:PEAP_STATE_IDENTITY_RESPONSE_SENT"); // // Call begin for eap dll // // Check to see if we are configured to do this eap type. // if not send a NAK back with desired EAP type. // if ( !pPeapCb->fInvokedInteractiveUI ) { if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Failure ) { dwRetCode = PeapClientDecryptTunnelData ( pPeapCb, pReceivePacket, 0); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } /* wPacketLength = WireToHostFormat16( pReceivePacket->Length ); dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), wPacketLength - ( sizeof(PPP_EAP_PACKET) + 1 ) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } CopyMemory ( pReceivePacket->Data, pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->dwIoBufferLen -1), pReceivePacket->Length ); */ } else if ( pReceivePacket && pReceivePacket->Code == EAPCODE_Failure ) { // // Fail auth because we have not yet got the Success/Fail TLV // The server may not be configured to handle this EAP Type. // EapTlsTrace ( "Got a failure when negotiating EAP types in PEAP."); pEapOutput->Action = EAPACTION_Done; pEapOutput->dwAuthResultCode = ERROR_AUTHENTICATION_FAILURE; break; } } if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Request ) { EapTlsTrace("Invalid packet received. Ignoring"); pEapOutput->Action = EAPACTION_NoAction; } else { // // Check to see if this is a TLV packet other than success/fail. // If so, send back a NAK. // if ( !pPeapCb->fInvokedInteractiveUI && pPeapCb->pEapInfo->dwTypeId != pReceivePacket->Data[0] ) { //Send a NAK back with the desired typeid pSendPacket->Code = EAPCODE_Response; pSendPacket->Id = pReceivePacket->Id; pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pSendPacket->Data[2] = PEAP_EAPTYPE_NAK; pSendPacket->Data[3] = (BYTE)pPeapCb->pEapInfo->dwTypeId; //Encrypt 2 bytes of our NAK dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[2]), 2 ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. Cant do much here but to drop the // connection. // break; } // // Copy over the buffer and readjust the lengths // CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + 1 +pPeapCb->dwIoBufferLen), pSendPacket->Length ); pEapOutput->Action = EAPACTION_Send; } else { //call begin and then make message ZeroMemory ( &EapTypeInput, sizeof(EapTypeInput) ); CopyMemory( &EapTypeInput, pEapInput, sizeof(EapTypeInput) ); EapTypeInput.pwszIdentity = pPeapCb->awszTypeIdentity; if ( !pPeapCb->fInvokedInteractiveUI ) { // // Set the user and connection data from peap cb // dwRetCode = PeapGetFirstEntryConnProp ( pPeapCb->pConnProp, &pEntryConnProp ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error getting entry connection properties. 0x%x", dwRetCode); goto LDone; } dwRetCode = PeapGetFirstEntryUserProp ( pPeapCb->pUserProp, &pEntryUserProp ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error getting entry user properties. 0x%x", dwRetCode); goto LDone; } EapTypeInput.pConnectionData = pEntryConnProp->bData; EapTypeInput.hTokenImpersonateUser = pPeapCb->hTokenImpersonateUser; EapTypeInput.dwSizeOfConnectionData = pEntryConnProp->dwSize - sizeof(PEAP_ENTRY_CONN_PROPERTIES) + 1; pPeapCb->dwFlags & PEAPCB_FLAG_ROUTER? EapTypeInput.fFlags |= RAS_EAP_FLAG_ROUTER :0; pPeapCb->dwFlags & PEAPCB_FLAG_NON_INTERACTIVE? EapTypeInput.fFlags |= RAS_EAP_FLAG_NON_INTERACTIVE:0; pPeapCb->dwFlags & PEAPCB_FLAG_LOGON? EapTypeInput.fFlags |= RAS_EAP_FLAG_LOGON :0; pPeapCb->dwFlags & PEAPCB_FLAG_PREVIEW ? EapTypeInput.fFlags |= RAS_EAP_FLAG_PREVIEW:0; pPeapCb->dwFlags & PEAPCB_FLAG_FIRST_LINK? EapTypeInput.fFlags |= RAS_EAP_FLAG_FIRST_LINK :0; pPeapCb->dwFlags & PEAPCB_FLAG_MACHINE_AUTH ? EapTypeInput.fFlags |= RAS_EAP_FLAG_MACHINE_AUTH:0; pPeapCb->dwFlags & PEAPCB_FLAG_GUEST_ACCESS? EapTypeInput.fFlags |= RAS_EAP_FLAG_GUEST_ACCESS :0; pPeapCb->dwFlags & PEAPCB_FLAG_8021X_AUTH ? EapTypeInput.fFlags |= RAS_EAP_FLAG_8021X_AUTH:0; if ( pEntryUserProp->fUsingPeapDefault ) { PPEAP_DEFAULT_CREDENTIALS pDefaultCred = (PPEAP_DEFAULT_CREDENTIALS)pEntryUserProp->bData; // // there is no user data to send in this case. // just set the identity and password. // EapTypeInput.pwszPassword = pDefaultCred->wszPassword; } else { EapTypeInput.pUserData = pEntryUserProp->bData; EapTypeInput.dwSizeOfUserData = pEntryUserProp->dwSize - sizeof(PEAP_ENTRY_USER_PROPERTIES) + 1; } if ( pPeapCb->awszPassword[0] ) { EapTypeInput.pwszPassword = pPeapCb->awszPassword; } EapTypeInput.bInitialId = pReceivePacket->Id; //Call begin function dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapBegin( &(pPeapCb->pEapInfo->pWorkBuf ), &EapTypeInput ); } if ( NO_ERROR == dwRetCode ) { if ( pPeapCb->fInvokedInteractiveUI ) { pPeapCb->fInvokedInteractiveUI = FALSE; } //Call make message now dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapMakeMessage ( pPeapCb->pEapInfo->pWorkBuf, pReceivePacket, pSendPacket, cbSendPacket-200, pEapOutput, &EapTypeInput ); if ( NO_ERROR == dwRetCode ) { if ( pEapOutput->fInvokeInteractiveUI ) { if ( pPeapCb->pUIContextData ) { LocalFree ( pPeapCb->pUIContextData ); pPeapCb->pUIContextData = NULL; } pPeapCb->pUIContextData = (PPEAP_INTERACTIVE_UI) LocalAlloc(LPTR, sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData ); if ( NULL == pPeapCb->pUIContextData ) { EapTlsTrace("Error allocating memory for PEAP context data"); dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } pPeapCb->pUIContextData->dwEapTypeId = pPeapCb->pEapInfo->dwTypeId; pPeapCb->pUIContextData->dwSizeofUIContextData = pEapOutput->dwSizeOfUIContextData; CopyMemory( pPeapCb->pUIContextData->bUIContextData, pEapOutput->pUIContextData, pEapOutput->dwSizeOfUIContextData ); pEapOutput->pUIContextData = (PBYTE)pPeapCb->pUIContextData; pEapOutput->dwSizeOfUIContextData = sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData; pPeapCb->fInvokedInteractiveUI = TRUE; } else { wPacketLength = WireToHostFormat16(pSendPacket->Length); dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[0]), wPacketLength -sizeof(PPP_EAP_PACKET)+1 ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. // Cant do much here but to terminate connection // break; } pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+1+pPeapCb->dwIoBufferLen), pSendPacket->Length ); //Set the Id of the packet send. This should have been set //by the eap type. pPeapCb->bId = pSendPacket->Id; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_INPROGRESS; } } else { EapTlsTrace ( "EapType %d failed in RasEapMakeMEssage and returned 0x%x", pPeapCb->pEapInfo->dwTypeId, dwRetCode ); } } else { EapTlsTrace ( "EapType %d failed in RasEapBegin and returned 0x%x", pPeapCb->pEapInfo->dwTypeId, dwRetCode ); // // Send a PEAP failure here and wait for response from server. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; } } } break; case PEAP_STATE_EAP_TYPE_INPROGRESS: EapTlsTrace("PEAP:PEAP_STATE_EAP_TYPE_INPROGRESS"); if ( pPeapCb->fInvokedInteractiveUI && !pEapInput->fDataReceivedFromInteractiveUI) { EapTlsTrace("Waiting on interactive UI. Discarding packets sliently..."); pEapOutput->Action = EAPACTION_NoAction; break; } ZeroMemory ( &EapTypeInput, sizeof(EapTypeInput) ); CopyMemory( &EapTypeInput, pEapInput, sizeof(EapTypeInput) ); EapTypeInput.pwszIdentity = pPeapCb->awszTypeIdentity; EapTypeInput.hTokenImpersonateUser = pPeapCb->hTokenImpersonateUser; // if we are executing interactive ui ,pReceivePacket will be NULL // if ( pReceivePacket && !pPeapCb->fInvokedInteractiveUI ) { //Decrypt the packet if ( pReceivePacket->Code != EAPCODE_Success && pReceivePacket->Code != EAPCODE_Failure) { dwRetCode = PeapClientDecryptTunnelData ( pPeapCb, pReceivePacket, 2); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } /* wPacketLength = WireToHostFormat16( pReceivePacket->Length ); dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), wPacketLength - ( sizeof(PPP_EAP_PACKET) + 1 ) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } */ if ( pPeapCb->pbIoBuffer[6] == MS_PEAP_AVP_TYPE_STATUS && pPeapCb->pbIoBuffer[4] == PEAP_TYPE_AVP ) { wValue = 0; CopyMemory ( &(pReceivePacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)( sizeof(PPP_EAP_PACKET) + 1 + pPeapCb->dwIoBufferLen), pReceivePacket->Length ); // // This is a TLV. So the auth succeeded or failed. // Send a manufactured success or fail to the current // EAP type and then based on what the EAP type returns // send a PEAP success/fail to the server. Change the // state then to accept the EAP success or failure. // dwRetCode = GetPEAPTLVStatusMessageValue ( pPeapCb, pReceivePacket, &wValue ); if ( NO_ERROR == dwRetCode ) { if ( wValue == MS_PEAP_AVP_VALUE_SUCCESS ) { pReceivePacket->Code = EAPCODE_Success; EapTypeInput.fSuccessPacketReceived = TRUE; } else if ( wValue == MS_PEAP_AVP_VALUE_FAILURE ) { pReceivePacket->Code = EAPCODE_Failure; } else { EapTlsTrace("Got an unrecognized TLV Message. Silently discarding the packet"); dwRetCode = NO_ERROR; break; } pPeapCb->fReceivedTLVSuccessFail = TRUE; HostToWireFormat16 ( (WORD)4, pReceivePacket->Length ); } else { EapTlsTrace("Got an unrecognized TLV Message. Silently discarding the packet"); dwRetCode = NO_ERROR; break; } } else { // // Check to see if it is any type of TLV message // We send a NAK back for any TLV message other than status // // if ( fIsPEAPTLVMessage ( pPeapCb, pReceivePacket ) ) { //Send back a NAK dwRetCode = CreatePEAPTLVNAKMessage ( pPeapCb, pSendPacket, cbSendPacket ); if ( NO_ERROR != dwRetCode ) { // this is an internal error. So cannot do much here // but to fail auth EapTlsTrace ( "Error creating TLV NAK message. Failing auth"); } break; } else { CopyMemory ( pReceivePacket->Data, pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->dwIoBufferLen-1), pReceivePacket->Length ); } } } else { pReceivePacket->Data[0] = (BYTE)pPeapCb->pEapInfo->dwTypeId; } } if ( pEapInput->fDataReceivedFromInteractiveUI ) { //we are done with interactive UI stuff... pPeapCb->fInvokedInteractiveUI = FALSE; } dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapMakeMessage ( pPeapCb->pEapInfo->pWorkBuf, pReceivePacket, pSendPacket, cbSendPacket-200, pEapOutput, &EapTypeInput ); if ( NO_ERROR == dwRetCode ) { // // if interactive UI was requested, wrap the data in // in PEAP interactive UI structure // if ( pEapOutput->fInvokeInteractiveUI ) { if ( pPeapCb->pUIContextData ) { LocalFree ( pPeapCb->pUIContextData ); pPeapCb->pUIContextData = NULL; } pPeapCb->pUIContextData = (PPEAP_INTERACTIVE_UI) LocalAlloc(LPTR, sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData ); if ( NULL == pPeapCb->pUIContextData ) { EapTlsTrace("Error allocating memory for PEAP context data"); dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } pPeapCb->pUIContextData->dwEapTypeId = pPeapCb->pEapInfo->dwTypeId; pPeapCb->pUIContextData->dwSizeofUIContextData = pEapOutput->dwSizeOfUIContextData; CopyMemory( pPeapCb->pUIContextData->bUIContextData, pEapOutput->pUIContextData, pEapOutput->dwSizeOfUIContextData ); pEapOutput->pUIContextData = (PBYTE)pPeapCb->pUIContextData; pEapOutput->dwSizeOfUIContextData = sizeof(PEAP_INTERACTIVE_UI) + pEapOutput->dwSizeOfUIContextData; pPeapCb->fInvokedInteractiveUI = TRUE; } else if ( pEapOutput->Action == EAPACTION_Done || pEapOutput->Action == EAPACTION_Send ) { if ( pEapOutput->Action == EAPACTION_Done ) { // We are done with auth. if ( pPeapCb->fReceivedTLVSuccessFail != TRUE ) { // // Check to see what is the Auth result. // Based on that, we should send back a // PEAPSuccess / Fail // EapTlsTrace ("Failing Auth because we got a success/fail without TLV."); dwRetCode = ERROR_INTERNAL_ERROR; pPeapCb->fReceivedTLVSuccessFail = FALSE; break; } pPeapCb->dwAuthResultCode = pEapOutput->dwAuthResultCode; dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, FALSE, //This is a response ( pEapOutput->dwAuthResultCode == NO_ERROR ? MS_PEAP_AVP_VALUE_SUCCESS: MS_PEAP_AVP_VALUE_FAILURE ) ); if ( pEapOutput->dwAuthResultCode == NO_ERROR ) { pPeapCb->PeapState = PEAP_STATE_PEAP_SUCCESS_SEND; } else { pPeapCb->PeapState = PEAP_STATE_PEAP_FAIL_SEND; } pEapOutput->Action = EAPACTION_Send; if ( pEapOutput->dwAuthResultCode == NO_ERROR ) { //Check to see if connectiondata and user data need to be saved if ( pEapOutput->fSaveConnectionData ) { // // save connection data in PEAP control // block and then finally when auth is done // We send back a save command. // pPeapCb->pEapInfo->pbNewClientConfig = pEapOutput->pConnectionData; pPeapCb->pEapInfo->dwNewClientConfigSize = pEapOutput->dwSizeOfConnectionData; pPeapCb->fEntryConnPropDirty = TRUE; } if ( pEapOutput->fSaveUserData ) { pPeapCb->pEapInfo->pbUserConfigNew = pEapOutput->pUserData; pPeapCb->pEapInfo->dwNewUserConfigSize = pEapOutput->dwSizeOfUserData; pPeapCb->fEntryUserPropDirty = TRUE; } } } else if ( pEapOutput->Action == EAPACTION_Send ) { //This has to be a request response. So if the length is < sizeof(PPP_EAP_PACKET) // We have a problem wPacketLength = WireToHostFormat16(pSendPacket->Length); if ( wPacketLength >= sizeof(PPP_EAP_PACKET) ) { dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[0]), wPacketLength - sizeof(PPP_EAP_PACKET)+1 ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. So cannot send TLV's here // break; } CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); wPacketLength = (WORD)(sizeof(PPP_EAP_PACKET)+ 1 + pPeapCb->dwIoBufferLen); pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; //Readjust the length HostToWireFormat16 ( wPacketLength, pSendPacket->Length ); } } else { EapTlsTrace("Invalid length returned by Length"); dwRetCode = ERROR_INTERNAL_ERROR; } } else { // we can get send / done / noaction from the client . // So this is a no action. pass it on to EAP without // any modification } } break; case PEAP_STATE_EAP_TYPE_FINISHED: EapTlsTrace("PEAP:PEAP_STATE_EAP_TYPE_FINISHED"); break; case PEAP_STATE_PEAP_SUCCESS_SEND: EapTlsTrace("PEAP:PEAP_STATE_PEAP_SUCCESS_SEND"); // // We got a PEAP_SUCCESS TLV inside the protected channel and have send // a PEAP_SUCCESS response TLV. So we should get an EAP_Success now. // Anything else will cause the connection to disconnect. // if ( pReceivePacket && pReceivePacket->Code == EAPCODE_Success ) { // // Check to see if there is a need to create a new conn and/or user blob // if ( pPeapCb->fEntryConnPropDirty || pPeapCb->fTlsConnPropDirty ) { PPEAP_CONN_PROP pNewConnProp = NULL; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * pNewEntryProp = NULL; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * pEntryProp = NULL; DWORD dwSize = 0; // // We need to recreate our PEAP conn prop structure // dwSize = sizeof(PEAP_CONN_PROP); if ( pPeapCb->fTlsConnPropDirty ) { dwSize += pPeapCb->pNewTlsConnProp->dwSize; } else { dwSize += pPeapCb->pConnProp->EapTlsConnProp.dwSize; } if ( pPeapCb->fEntryConnPropDirty ) { dwSize += sizeof(PEAP_ENTRY_CONN_PROPERTIES) + pPeapCb->pEapInfo->dwNewClientConfigSize -1; } else { PeapGetFirstEntryConnProp ( pPeapCb->pConnProp, &pNewEntryProp ); dwSize += pNewEntryProp->dwSize; } pNewConnProp = (PPEAP_CONN_PROP)LocalAlloc (LPTR, dwSize ); if ( pNewConnProp ) { pNewConnProp->dwVersion = 1; pNewConnProp->dwSize = dwSize; pNewConnProp->dwNumPeapTypes = 1; if ( pPeapCb->fTlsConnPropDirty ) CopyMemory ( &pNewConnProp->EapTlsConnProp, pPeapCb->pNewTlsConnProp, pPeapCb->pNewTlsConnProp->dwSize ); else CopyMemory ( &pNewConnProp->EapTlsConnProp, &pPeapCb->pConnProp->EapTlsConnProp, pPeapCb->pConnProp->EapTlsConnProp.dwSize ); PeapGetFirstEntryConnProp ( pNewConnProp, &pNewEntryProp ); if ( pPeapCb->fEntryConnPropDirty ) { pNewEntryProp->dwVersion = 1; pNewEntryProp->dwEapTypeId = pPeapCb->pEapInfo->dwTypeId; pNewEntryProp->dwSize = sizeof( PEAP_ENTRY_CONN_PROPERTIES) + pPeapCb->pEapInfo->dwNewClientConfigSize -1; CopyMemory ( pNewEntryProp->bData, pPeapCb->pEapInfo->pbNewClientConfig, pPeapCb->pEapInfo->dwNewClientConfigSize ); } else { PeapGetFirstEntryConnProp ( pPeapCb->pConnProp, &pEntryProp ); CopyMemory ( pNewEntryProp, pEntryProp, pEntryProp->dwSize ); } LocalFree ( pPeapCb->pConnProp ); pPeapCb->pConnProp = pNewConnProp; pEapOutput->fSaveConnectionData = TRUE; pEapOutput->pConnectionData = (PBYTE)pNewConnProp; pEapOutput->dwSizeOfConnectionData = pNewConnProp->dwSize; } } // // check to see if the user props need to be saved // if ( pPeapCb->fEntryUserPropDirty ) { PPEAP_USER_PROP pNewUserProp = NULL; pNewUserProp = (PPEAP_USER_PROP) LocalAlloc( LPTR, sizeof( PEAP_USER_PROP ) + pPeapCb->pEapInfo->dwNewUserConfigSize -1); if ( pNewUserProp ) { pNewUserProp->dwVersion = pPeapCb->pUserProp->dwVersion; pNewUserProp->dwSize = sizeof( PEAP_USER_PROP ) + pPeapCb->pEapInfo->dwNewUserConfigSize -1; pNewUserProp->UserProperties.dwVersion = 1; pNewUserProp->UserProperties.dwSize = sizeof(PEAP_ENTRY_USER_PROPERTIES) + pPeapCb->pEapInfo->dwNewUserConfigSize -1; pNewUserProp->UserProperties.dwEapTypeId = pPeapCb->pEapInfo->dwTypeId; CopyMemory ( pNewUserProp->UserProperties.bData, pPeapCb->pEapInfo->pbUserConfigNew, pPeapCb->pEapInfo->dwNewUserConfigSize ); LocalFree ( pPeapCb->pUserProp); pPeapCb->pUserProp = pNewUserProp; pEapOutput->pUserData = (PBYTE)pNewUserProp; pEapOutput->dwSizeOfUserData = pNewUserProp->dwSize; pEapOutput->fSaveUserData = TRUE; } // // Set the cookie if we're enabled to do fast reconnect // if ( pPeapCb->dwFlags & PEAPCB_FAST_ROAMING ) { dwRetCode = PeapCreateCookie ( pPeapCb, &pbCookie, &cbCookie ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Failed to create session cookie. Resetting fast reconnect"); dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } } dwRetCode = SetTLSSessionCookie ( pPeapCb->pEapTlsCB , pbCookie, cbCookie ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Failed to create session cookie. Resetting fast reconnect"); dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } } } } pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->pUserAttributes = pPeapCb->pTlsUserAttributes; pEapOutput->dwAuthResultCode = pPeapCb->dwAuthResultCode; pEapOutput->Action = EAPACTION_Done; } else if ( pReceivePacket && pReceivePacket->Code == EAPCODE_Failure ) { // // We fail the connection. Even though there is a success from PEAP // EapTlsTrace("We got a EAP_failure after we got a PEAP_SUCCESS. Failing auth."); dwRetCode = ERROR_INTERNAL_ERROR; pEapOutput->dwAuthResultCode = ERROR_INTERNAL_ERROR; pEapOutput->Action = EAPACTION_Done; } else { if ( pReceivePacket ) EapTlsTrace ("Received Packet with code %x when expecting success", pReceivePacket->Code); break; } break; case PEAP_STATE_PEAP_FAIL_SEND: EapTlsTrace("PEAP:PEAP_STATE_PEAP_FAIL_SEND"); // // We have send PEAP_FAILURE TLV inside the protected channel // So the only thing we should expect is a EAP_Failure from now on // Send back a EAP_FAIL with EAP_Done action. // if ( pReceivePacket && pReceivePacket->Code == EAPCODE_Failure ) { pEapOutput->dwAuthResultCode = pPeapCb->dwAuthResultCode; //We have failed auth so uncache the creds FreeCachedCredentials (pPeapCb->pEapTlsCB); pEapOutput->Action = EAPACTION_Done; } else if ( pReceivePacket && pReceivePacket->Code == EAPCODE_Success ) { // // We fail the connection. Even though there is a success from PEAP // EapTlsTrace("We got a EAP_Success after we got a PEAP_FAILURE. Failing auth."); dwRetCode = ERROR_INTERNAL_ERROR; pEapOutput->dwAuthResultCode = ERROR_INTERNAL_ERROR; //We have failed auth so uncache the creds FreeCachedCredentials (pPeapCb->pEapTlsCB); pEapOutput->Action = EAPACTION_Done; } else { if ( pReceivePacket ) EapTlsTrace ("Received Packet with code %x when expecting success", pReceivePacket->Code); break; } default: EapTlsTrace("PEAP:Invalid state"); } if ( fImpersonating ) { if (!RevertToSelf() ) { dwRetCode = GetLastError(); EapTlsTrace("PEAP:RevertToSelf Failed and returned 0x%x", dwRetCode ); } } LDone: EapTlsTrace("EapPeapCMakeMessage done"); return dwRetCode; } DWORD EapPeapSMakeMessage( IN PPEAPCB pPeapCb, IN PPP_EAP_PACKET* pReceivePacket, OUT PPP_EAP_PACKET* pSendPacket, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { DWORD dwRetCode = NO_ERROR; PPP_EAP_INPUT EapTypeInput; WORD wPacketLength; DWORD dwVersion; PBYTE pbCookie = NULL; DWORD cbCookie = 0; BOOL fIsReconnect = FALSE; EapTlsTrace("EapPeapSMakeMessage"); switch ( pPeapCb->PeapState ) { case PEAP_STATE_INITIAL: EapTlsTrace("PEAP:PEAP_STATE_INITIAL"); // // Start the EapTls Conversation here. // //Receive Packet will be NULL. Call EapTlsSMakeMessage // //Note: Our version currently is 0. So the packet will // be the same as eaptls packet. In future, this needs // to change dwRetCode = EapTlsSMakeMessage( pPeapCb->pEapTlsCB, (EAPTLS_PACKET *)pReceivePacket, (EAPTLS_PACKET *)pSendPacket, cbSendPacket, pEapOutput, pEapInput ); if ( NO_ERROR == dwRetCode ) { //change the packet to show peap pSendPacket->Data[0] = PPP_EAP_PEAP; //Set version ((EAPTLS_PACKET *)pSendPacket)->bFlags |= EAPTLS_PACKET_HIGHEST_SUPPORTED_VERSION; } pPeapCb->PeapState = PEAP_STATE_TLS_INPROGRESS; break; case PEAP_STATE_TLS_INPROGRESS: EapTlsTrace("PEAP:PEAP_STATE_TLS_INPROGRESS"); if ( pReceivePacket ) { if ( !(pPeapCb->dwFlags & PEAPCB_VERSION_OK) ) { // // We have not done the version check yet. // dwVersion = ((EAPTLS_PACKET *)pReceivePacket)->bFlags & 0x03; if ( dwVersion > EAPTLS_PACKET_LOWEST_SUPPORTED_VERSION ) { // // Send a fail code back and we're done. The versions // of PEAP did not match EapTlsTrace("Could not negotiate version successfully."); EapTlsTrace("Requested version %ld, our lowest version %ld", dwVersion, EAPTLS_PACKET_LOWEST_SUPPORTED_VERSION ); pSendPacket->Code = EAPCODE_Failure; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } else { pPeapCb->dwFlags |= PEAPCB_VERSION_OK; } } pReceivePacket->Data[0] = PPP_EAP_TLS; } dwRetCode = EapTlsSMakeMessage( pPeapCb->pEapTlsCB, (EAPTLS_PACKET *)pReceivePacket, (EAPTLS_PACKET *)pSendPacket, cbSendPacket, pEapOutput, pEapInput ); if ( NO_ERROR == dwRetCode ) { //We are done with auth. if ( pEapOutput->dwAuthResultCode == NO_ERROR && pSendPacket->Code == EAPCODE_Success ) { // // auth was successful. Carefully keep the MPPE // session keys returned so that we can encrypt the // channel. From now on everything will be encrypted. // pPeapCb->pTlsUserAttributes = pEapOutput->pUserAttributes; dwRetCode = PeapGetTunnelProperties ( pPeapCb ); if (NO_ERROR != dwRetCode ) { break; } // // Check to see if we get the cookie. If we get the cookie, // and it is a fast reconnect, then we compare which auth method // was used previously. If it is good, then // if ( pPeapCb->dwFlags & PEAPCB_FAST_ROAMING ) { dwRetCode = GetTLSSessionCookie ( pPeapCb->pEapTlsCB, &pbCookie, &cbCookie, &fIsReconnect ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Error getting cookie for a reconnected session. Failing auth"); // We cannot encrypt and send stuff across here. // Reconnected Session state is invalid. pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } if ( fIsReconnect ) { if ( cbCookie == 0 ) { //There was an error getting session cookie. //Or there is no cookie and this is a reconnet // So fail the request. dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); EapTlsTrace("Error getting cookie for a reconnected session. Failing auth"); // We cannot encrypt and send stuff across here. // Reconnected Session state is invalid. dwRetCode = ERROR_INTERNAL_ERROR; pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } // // This is a server // Check to see if the cookie is fine. // If it is fine then there is no need to reauth. // So send back a PEAP_SUCCESS request packet // and change our state to PEAP_SUCCESS_SEND // If not, proceed with auth and send identity request // EapTlsTrace ("TLS session fast reconnected"); dwRetCode = PeapCheckCookie ( pPeapCb, (PPEAP_COOKIE)pbCookie, cbCookie ); if ( NO_ERROR != dwRetCode ) { // // So invalidate the session for fast reconnect // and fail auth. Next time a full reconnect will happen // dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } EapTlsTrace ("Error validating the cookie. Failing auth"); pEapOutput->dwAuthResultCode = dwRetCode; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->Action = EAPACTION_Done; break; } else { // // Cookie is fine. // // // Send a PEAP success TLV here. // We need to do this so that // the client does not think he is spoofed. // dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, TRUE, MS_PEAP_AVP_VALUE_SUCCESS ); if ( NO_ERROR != dwRetCode ) { break; } pPeapCb->fSendTLVSuccessforFastRoaming = TRUE; pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pPeapCb->PeapState = PEAP_STATE_PEAP_SUCCESS_SEND; pPeapCb->dwAuthResultCode = pEapOutput->dwAuthResultCode; break; } } else { // // Go ahead with the auth and at the end, save the cookie // Check to see if fast reconnect has been enabled. If it has been enabled, // Set the TLS state to enable fast reconnect. If not do nothing at the // end of auth. EapTlsTrace ("Full TLS handshake"); } } //Transfer the Id from eaptls control block to peap cb pPeapCb->bId = ++pPeapCb->pEapTlsCB->bId; pEapOutput->pUserAttributes = NULL; pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pSendPacket->Code = EAPCODE_Request; pSendPacket->Id = pPeapCb->bId; // // Send encrypted identity request. // pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pSendPacket->Data[2] = PEAP_EAPTYPE_IDENTITY; // // Identity request needs to be encrypted. // dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[2]), 1 ); if ( NO_ERROR != dwRetCode ) { break; } CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+1 + pPeapCb->dwIoBufferLen), pSendPacket->Length ); pPeapCb->PeapState = PEAP_STATE_IDENTITY_REQUEST_SENT; } else { //change the packet to show peap pSendPacket->Data[0] = PPP_EAP_PEAP; } } break; case PEAP_STATE_IDENTITY_REQUEST_SENT: EapTlsTrace("PEAP:PEAP_STATE_IDENTITY_REQUEST_SENT"); // // Should get only identity response and nothing else // NOTE: in this implementation, this should match // the outer identity. But at a later point, we // may get many identities and any one of them should // match the outer identity. // // call begin in the eap dll and send back the blob got // from begin. //decrypt tunnel data here if ( pReceivePacket ) { dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), WireToHostFormat16(pReceivePacket->Length) - (sizeof(PPP_EAP_PACKET)+1) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } } if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Response && pPeapCb->pbIoBuffer[0] != PEAP_EAPTYPE_IDENTITY ) { EapTlsTrace("Invalid packet received when expecting identity response"); pEapOutput->Action = EAPACTION_NoAction; } else { if ( pReceivePacket && pReceivePacket->Id != pPeapCb->bId ) { EapTlsTrace ("Ignoring packet with mismatched ids"); pEapOutput->Action = EAPACTION_NoAction; break; } if ( pReceivePacket ) { // // get the identity and create a ppp input and pass it on to dll begin // of the configured eap type. // MultiByteToWideChar( CP_ACP, 0, &pPeapCb->pbIoBuffer[1], pPeapCb->dwIoBufferLen - 1, pPeapCb->awszTypeIdentity, DNLEN+UNLEN ); ZeroMemory ( &EapTypeInput, sizeof(EapTypeInput) ); if ( pEapInput ) { CopyMemory( &EapTypeInput, pEapInput, sizeof(EapTypeInput) ); } else { EapTypeInput.fFlags = RAS_EAP_FLAG_NON_INTERACTIVE; EapTypeInput.fAuthenticator = TRUE; } EapTypeInput.pwszIdentity = pPeapCb->awszIdentity; EapTypeInput.bInitialId = ++ pPeapCb->bId; // // Call begin function // dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapBegin( &(pPeapCb->pEapInfo->pWorkBuf ), &EapTypeInput ); } if ( NO_ERROR == dwRetCode ) { // // Call make message now. This MakeMessage is called in for the first time // So send the identity request that came across into this MakeMessage. // dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapMakeMessage ( pPeapCb->pEapInfo->pWorkBuf, NULL, pSendPacket, cbSendPacket-200, pEapOutput, &EapTypeInput ); if ( NO_ERROR == dwRetCode ) { if ( pEapOutput->Action == EAPACTION_Authenticate ) { // // do nothing here. We are passing this on to RADIUS as is // } else { wPacketLength = WireToHostFormat16(pSendPacket->Length); //Encrypt the entire packet that we need to nest dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[0]), wPacketLength - sizeof(PPP_EAP_PACKET)+1 ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. So we cannot send // a PEAP_Failure here // break; } pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] =EAPTLS_PACKET_CURRENT_VERSION; CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); //Readjust the length wPacketLength = (WORD)(sizeof(PPP_EAP_PACKET) + 1 + pPeapCb->dwIoBufferLen); HostToWireFormat16 ( wPacketLength, pSendPacket->Length ); //Set the Id of the packet send. This should have been set //by the eap type. pPeapCb->bId = pSendPacket->Id; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_INPROGRESS; } } else { EapTlsTrace ( "EapType %d failed in RasEapMakeMEssage and returned 0x%x", pPeapCb->pEapInfo->dwTypeId, dwRetCode ); } } else { EapTlsTrace ( "EapType %d failed in RasEapBegin and returned 0x%x", pPeapCb->pEapInfo->dwTypeId, dwRetCode ); // // Send a PEAP failure TLV here. We need to do this so that // the client does not think he is spoofed. // dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, TRUE, MS_PEAP_AVP_VALUE_FAILURE ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. So we cannot send // a PEAP_Failure here // break; } pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pPeapCb->PeapState = PEAP_STATE_PEAP_FAIL_SEND; } } break; case PEAP_STATE_EAP_TYPE_INPROGRESS: EapTlsTrace("PEAP:PEAP_STATE_EAP_TYPE_INPROGRESS"); // // Since we are doing only one EAP type now, if we get a NAK here, // it means that the client cannot execute the EAP Type that was send // so send back a EAP_FAIL with proper error code // if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Response && pReceivePacket->Id != pPeapCb->bId ) { EapTlsTrace("Received packet with some other code than response or the id does not match. Ignoring packet"); pEapOutput->Action = EAPACTION_NoAction; } else { if ( pReceivePacket ) { dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), WireToHostFormat16(pReceivePacket->Length) - ( sizeof(PPP_EAP_PACKET) + 1 ) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } } if ( pReceivePacket && pPeapCb->pbIoBuffer[0] == PEAP_EAPTYPE_NAK ) { // // Since we are setup to do only one EAP Type // this is an error. Or else we should call begin // for next configured eap type. // EapTlsTrace("Error: Got NAK for selection protocol. Discontinuing auth."); pEapOutput->Action = EAPACTION_Done; pEapOutput->dwAuthResultCode = ERROR_PROTOCOL_NOT_CONFIGURED; dwRetCode = NO_ERROR; } else { ZeroMemory ( &EapTypeInput, sizeof(EapTypeInput) ); if ( pEapInput ) { CopyMemory( &EapTypeInput, pEapInput, sizeof(EapTypeInput) ); } else { EapTypeInput.fFlags = RAS_EAP_FLAG_NON_INTERACTIVE; EapTypeInput.fAuthenticator = TRUE; } FFormatUserIdentity ( pPeapCb->awszTypeIdentity, &EapTypeInput.pwszIdentity ); if ( pReceivePacket ) { CopyMemory ( pReceivePacket->Data, pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET) + pPeapCb->dwIoBufferLen -1), pReceivePacket->Length ); } dwRetCode = pPeapCb->pEapInfo->PppEapInfo.RasEapMakeMessage ( pPeapCb->pEapInfo->pWorkBuf, pReceivePacket, pSendPacket, cbSendPacket-200, pEapOutput, &EapTypeInput ); if ( NO_ERROR == dwRetCode ) { // // Need to translate ActionSendDone to ActionSend and then send peap // success. // if ( pEapOutput->Action == EAPACTION_SendAndDone ) { // // If the code is request or response, send the data across // and save our state in the context. If it is success or fail // there is no data to send back so the following logic will // work. // if ( pSendPacket->Code == EAPCODE_Request ) { EapTlsTrace ("Invalid Code EAPCODE_Request send for Action Send and Done"); // // Auth fails here. We cannot handle EAPCODE_Request yet. // dwRetCode = ERROR_PPP_INVALID_PACKET; break; } } if ( pSendPacket->Code == EAPCODE_Success ) { // // Send a PEAP success TLV here. // We need to do this so that // the client does not think he is spoofed. // dwRetCode = CreatePEAPTLVStatusMessage ( pPeapCb, pSendPacket, cbSendPacket, TRUE, MS_PEAP_AVP_VALUE_SUCCESS ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. So we cannot send // a PEAP_Failure here // break; } pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pPeapCb->PeapState = PEAP_STATE_PEAP_SUCCESS_SEND; pPeapCb->dwAuthResultCode = pEapOutput->dwAuthResultCode; } else if ( pSendPacket->Code == EAPCODE_Failure ) { // // Send a PEAP failure TLV here. // We need to do this so that // the client does not think he is spoofed. // dwRetCode = CreatePEAPTLVStatusMessage (pPeapCb, pSendPacket, cbSendPacket, TRUE, MS_PEAP_AVP_VALUE_FAILURE ); if ( NO_ERROR != dwRetCode ) { // // This is an internal error. So we cannot send // a PEAP_Failure here // break; } pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pPeapCb->PeapState = PEAP_STATE_PEAP_FAIL_SEND; pPeapCb->dwAuthResultCode = pEapOutput->dwAuthResultCode; } else if ( pEapOutput->Action == EAPACTION_Authenticate ) { // // do nothing here. We are passing this on to RADIUS as is // } else { //This is an action send wPacketLength = WireToHostFormat16(pSendPacket->Length); dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[0]), wPacketLength - sizeof(PPP_EAP_PACKET)+1 ); pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); pPeapCb->bId = pSendPacket->Id; wPacketLength = (WORD)(sizeof(PPP_EAP_PACKET) + 1+ pPeapCb->dwIoBufferLen); HostToWireFormat16 ( wPacketLength, pSendPacket->Length ); } } if ( EapTypeInput.pwszIdentity ) LocalFree ( EapTypeInput.pwszIdentity ); } } break; case PEAP_STATE_EAP_TYPE_FINISHED: EapTlsTrace("PEAP:PEAP_STATE_EAP_TYPE_FINISHED"); break; case PEAP_STATE_PEAP_SUCCESS_SEND: EapTlsTrace("PEAP:PEAP_STATE_PEAP_SUCCESS_SEND"); // // We have send PEAP_SUCCESS TLV inside the protected channel // So the only thing we should expect is a PEAP_SUCCESS TLV response // or a PEAP_FAILURE // If we get a PEAP_SUCCESS response back, then send back EAP_SUCCESS // with EAP_Done action. // if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Response && pReceivePacket->Id != pPeapCb->bId ) { EapTlsTrace("Received packet with some other code than response or the id does not match. Ignoring packet"); pEapOutput->Action = EAPACTION_NoAction; } else { WORD wValue =0; if ( pReceivePacket ) { dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), WireToHostFormat16(pReceivePacket->Length) - (sizeof(PPP_EAP_PACKET)+1) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } CopyMemory ( &(pReceivePacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); wPacketLength = WireToHostFormat16(pReceivePacket->Length); } if ( GetPEAPTLVStatusMessageValue ( pPeapCb, pReceivePacket, &wValue ) == ERROR_PPP_INVALID_PACKET ) { EapTlsTrace ("Got invalid packet when expecting TLV SUCCESS. Silently discarding packet."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } if ( wValue == MS_PEAP_AVP_VALUE_SUCCESS ) { pbCookie = NULL; cbCookie = 0; // // If we're enabled for fast reconnect, setup the cookie in the session // so that we can consume it later // if ( pPeapCb->dwFlags & PEAPCB_FAST_ROAMING ) { dwRetCode = PeapCreateCookie ( pPeapCb, &pbCookie, &cbCookie ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Failed to create session cookie. Resetting fast reconnect"); dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } } dwRetCode = SetTLSSessionCookie ( pPeapCb->pEapTlsCB , pbCookie, cbCookie ); if ( NO_ERROR != dwRetCode ) { EapTlsTrace("Failed to create session cookie. Resetting fast reconnect"); dwRetCode = SetTLSFastReconnect ( pPeapCb->pEapTlsCB , FALSE); if ( NO_ERROR != dwRetCode ) { // // This is an internal error // So disconnect the session. // pEapOutput->dwAuthResultCode = dwRetCode; pEapOutput->Action = EAPACTION_Done; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; break; } } } pSendPacket->Code = EAPCODE_Success; } else if ( wValue == MS_PEAP_AVP_VALUE_FAILURE ) { if ( pPeapCb->fSendTLVSuccessforFastRoaming ) { // // We got a failure because the client does not support fast roaming. // // Send encrypted identity request. // This is not good. We need to review and cleanup code // and make common functions. pPeapCb->fSendTLVSuccessforFastRoaming = FALSE; pSendPacket->Code = EAPCODE_Request; pSendPacket->Id = ++ pPeapCb->bId; pSendPacket->Data[0] = PPP_EAP_PEAP; pSendPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pSendPacket->Data[2] = PEAP_EAPTYPE_IDENTITY; // // Identity request needs to be encrypted. // dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pSendPacket->Data[2]), 1 ); if ( NO_ERROR != dwRetCode ) { break; } CopyMemory ( &(pSendPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+1 + pPeapCb->dwIoBufferLen), pSendPacket->Length ); pEapOutput->Action = EAPACTION_SendWithTimeoutInteractive; pPeapCb->PeapState = PEAP_STATE_IDENTITY_REQUEST_SENT; break; } else { EapTlsTrace ("Got TLV_Failure when expecting TLV SUCCESS. Failing Auth."); pSendPacket->Code = EAPCODE_Failure; } } else { EapTlsTrace ("Got invalid packet when expecting TLV SUCCESS. Silently discarding packet."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } pSendPacket->Id = ++ pPeapCb->bId; HostToWireFormat16( 4, pSendPacket->Length); // // Now we're done with Auth. So send back a EAP_Success // //We are done with auth. No need to encrypt the packet here pEapOutput->Action = EAPACTION_SendAndDone; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->dwAuthResultCode = pEapOutput->dwAuthResultCode; if ( pEapOutput->dwAuthResultCode == NO_ERROR ) { //return back the MPPE keys pEapOutput->pUserAttributes = pPeapCb->pTlsUserAttributes; } } break; case PEAP_STATE_PEAP_FAIL_SEND: EapTlsTrace("PEAP:PEAP_STATE_PEAP_FAIL_SEND"); // // We have send PEAP_FAIL TLV inside the protected channel // So the only thing we should expect is a PEAP_FAILURE TLV response // Send back a EAP_FAIL with EAP_Done action. // // // We have send PEAP_SUCCESS TLV inside the protected channel // So the only thing we should expect is a PEAP_SUCCESS TLV response // or a PEAP_FAILURE // If we get a PEAP_SUCCESS response back, then send back EAP_SUCCESS // with EAP_Done action. // if ( pReceivePacket && pReceivePacket->Code != EAPCODE_Response && pReceivePacket->Id != pPeapCb->bId ) { EapTlsTrace("Received packet with some other code than response or the id does not match. Ignoring packet"); pEapOutput->Action = EAPACTION_NoAction; } else { WORD wValue =0; if ( pReceivePacket ) { dwRetCode = PeapDecryptTunnelData ( pPeapCb, &(pReceivePacket->Data[2]), WireToHostFormat16(pReceivePacket->Length) - (sizeof(PPP_EAP_PACKET)+1) ); if ( NO_ERROR != dwRetCode ) { // We could not decrypt the tunnel traffic // So we silently discard this packet. EapTlsTrace("PeapDecryptTunnelData failed: silently discarding packet"); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } CopyMemory ( &(pReceivePacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); wPacketLength = WireToHostFormat16(pReceivePacket->Length); } if ( GetPEAPTLVStatusMessageValue ( pPeapCb, pReceivePacket, &wValue ) == ERROR_PPP_INVALID_PACKET ) { EapTlsTrace ("Got invalid packet when expecting TLV FAIL response. Silently discarding packet."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } if ( wValue != MS_PEAP_AVP_VALUE_FAILURE ) { EapTlsTrace ("Got invalid packet when expecting TLV FAILURE response. Silently discarding packet."); dwRetCode = NO_ERROR; pEapOutput->Action = EAPACTION_NoAction; break; } // // Now we're done with Auth. So send back a EAP_Success // pSendPacket->Code = EAPCODE_Failure; pSendPacket->Id = ++ pPeapCb->bId; HostToWireFormat16( 4, pSendPacket->Length); pEapOutput->Action = EAPACTION_SendAndDone; pPeapCb->PeapState = PEAP_STATE_EAP_TYPE_FINISHED; pEapOutput->dwAuthResultCode = pEapOutput->dwAuthResultCode; } break; #if 0 /* case PEAP_STATE_REQUEST_SENDANDDONE: EapTlsTrace("PEAP:PEAP_STATE_REQUEST_SENDANDDONE"); break; */ #endif default: EapTlsTrace("PEAP:Invalid state"); } if ( pbCookie ) LocalFree (pbCookie); EapTlsTrace("EapPeapSMakeMessage done"); return dwRetCode; } BOOL FValidPeapPacket ( EAPTLS_PACKET * pReceivePacket ) { BOOL fRet = FALSE; WORD wLength; if ( NULL == pReceivePacket ) { fRet = TRUE; goto done; } wLength = WireToHostFormat16( pReceivePacket->pbLength ); switch (pReceivePacket->bCode) { case EAPCODE_Success: case EAPCODE_Failure: if (PPP_EAP_PACKET_HDR_LEN != wLength) { EapTlsTrace("PEAP Success/Fail packet has length %d", wLength); return(FALSE); } break; case EAPCODE_Request: case EAPCODE_Response: if (PPP_EAP_PEAP != pReceivePacket->bType && pReceivePacket->bType != PEAP_EAPTYPE_IDENTITY && pReceivePacket->bType != PEAP_EAPTYPE_NAK ) { // We are not concerned with this packet. It is not TLS. EapTlsTrace("Got packet with type id other than PEAP and identity."); goto done; } break; } fRet = TRUE; done: return fRet; } DWORD EapPeapMakeMessage( IN PPEAPCB pPeapCb, IN PPP_EAP_PACKET* pInput, OUT PPP_EAP_PACKET* pOutput, IN DWORD cbSendPacket, OUT PPP_EAP_OUTPUT* pEapOutput, IN PPP_EAP_INPUT* pEapInput ) { DWORD dwRetCode = NO_ERROR; EapTlsTrace("EapPeapMakeMessage"); // // Inititally this will start as eaptls // and then will go into each PEAP type configured. // For this release we have only eapmschap v2 // if (!FValidPeapPacket((EAPTLS_PACKET *)pInput)) { pEapOutput->Action = EAPACTION_NoAction; return(ERROR_PPP_INVALID_PACKET); } if (pPeapCb->dwFlags & PEAPCB_FLAG_SERVER) { dwRetCode = EapPeapSMakeMessage( pPeapCb, pInput, pOutput, cbSendPacket, pEapOutput, pEapInput ); } else { dwRetCode = EapPeapCMakeMessage( pPeapCb, pInput, pOutput, cbSendPacket, pEapOutput, pEapInput ); } EapTlsTrace("EapPeapMakeMessage done"); return dwRetCode; } DWORD CreatePEAPTLVNAKMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, DWORD cbPacket ) { DWORD dwRetCode = NO_ERROR; EapTlsTrace("CreatePEAPTLVNAKMessage"); pPacket->Code = EAPCODE_Response ; pPacket->Id = pPeapCb->bId ; // // The format of this packet is following: // Code = Request/Response // Id // Length // Data[0] = Type = PPP_EAP_PEAP // Data[1] = Flags + Version // //Data[2]Code - Request/Response //3 Id - Can be same as outer Id //4,5 Length - Length this packet //6 Type - PEAP_TYPE_AVP //7,8 Type - High Bit is set to Mandatory if it is so ( 2 octets ) //9,10 Length - 2 octets //11... Value // // // pPacket->Length is set below. // pPacket->Data[0] = (BYTE)PPP_EAP_PEAP; pPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pPacket->Data[2] = EAPCODE_Response; pPacket->Data[3] = pPacket->Id; // Data 3 and 4 will have the length of inner packet. // HostToWireFormat16 ( 7, &(pPacket->Data[4]) ); pPacket->Data[6] = (BYTE)PEAP_TYPE_AVP; pPacket->Data[7] = PEAP_AVP_FLAG_MANDATORY; pPacket->Data[8] = PEAP_EAPTYPE_NAK; // // Encrypt the TLV part of the packet // dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pPacket->Data[2]), 7 ); if ( NO_ERROR != dwRetCode ) { return dwRetCode; } CopyMemory ( &(pPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+ 1 + pPeapCb->dwIoBufferLen), pPacket->Length ); return dwRetCode; } // Format: // Code - Request/Resp // Id // Type - PEAP // Method - PEAP_TLV // TLV - Type - PEAPSuccess/PEAPFailure // Flags - // Length - // Value - // DWORD CreatePEAPTLVStatusMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, DWORD cbPacket, BOOL fRequest, WORD wValue //Success or Failure ) { DWORD dwRetCode = NO_ERROR; EapTlsTrace("CreatePEAPTLVStatusMessage"); pPacket->Code = ( fRequest ? EAPCODE_Request : EAPCODE_Response ); pPacket->Id = ( fRequest ? ++ pPeapCb->bId : pPeapCb->bId ); // // The format of this packet is following: // Code = Request/Response // Id // Length // Data[0] = Type = PPP_EAP_PEAP // Data[1] = Flags + Version // //Data[2]Code - Request/Response //3 Id - Can be same as outer Id //4,5 Length - Length this packet //6 Type - PEAP_TYPE_AVP //7,8 Type - High Bit is set to Mandatory if it is so ( 2 octets ) //9,10 Length - 2 octets //11... Value // // // pPacket->Length is set below. // pPacket->Data[0] = (BYTE)PPP_EAP_PEAP; pPacket->Data[1] = EAPTLS_PACKET_CURRENT_VERSION; pPacket->Data[2] = ( fRequest ? EAPCODE_Request : EAPCODE_Response ); pPacket->Data[3] = pPacket->Id; // Data 3 and 4 will have the length of inner packet. // HostToWireFormat16 ( 11, &(pPacket->Data[4]) ); pPacket->Data[6] = (BYTE)PEAP_TYPE_AVP; pPacket->Data[7] = PEAP_AVP_FLAG_MANDATORY; pPacket->Data[8] = MS_PEAP_AVP_TYPE_STATUS; //Value Size HostToWireFormat16 ( 2, &(pPacket->Data[9]) ); //Value HostToWireFormat16 ( wValue, &(pPacket->Data[11]) ); // // Encrypt the TLV part of the packet // dwRetCode = PeapEncryptTunnelData ( pPeapCb, &(pPacket->Data[2]), 11 ); if ( NO_ERROR != dwRetCode ) { return dwRetCode; } CopyMemory ( &(pPacket->Data[2]), pPeapCb->pbIoBuffer, pPeapCb->dwIoBufferLen ); HostToWireFormat16 ( (WORD)(sizeof(PPP_EAP_PACKET)+ 1 + pPeapCb->dwIoBufferLen), pPacket->Length ); return dwRetCode; } // // Check to see if this packet is other than success/fail // TLV // BOOL fIsPEAPTLVMessage ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket ) { WORD wPacketLength = WireToHostFormat16 ( pPacket->Length ); if ( wPacketLength < 6 ) return FALSE; if ( pPacket->Data[6] != PEAP_TYPE_AVP ) return FALSE; //minimum length required to hold at least one success/fail tlv if ( wPacketLength > 17 ) { if ( pPacket->Data[8] != MS_PEAP_AVP_TYPE_STATUS ) { //Save the Id for response if ( pPacket->Code == EAPCODE_Request ) pPeapCb->bId = pPacket->Id; return TRUE; } } return ( FALSE); } DWORD GetPEAPTLVStatusMessageValue ( PPEAPCB pPeapCb, PPP_EAP_PACKET * pPacket, WORD * pwValue ) { DWORD dwRetCode = ERROR_PPP_INVALID_PACKET; WORD wLength = 0; EapTlsTrace("GetPEAPTLVStatusMessageValue"); // // Check to see if this is a status message // // // The format of this packet is following: // Code = Request/Response // Id // Length // Data[0] = Type = PPP_EAP_PEAP // Data[1] = Flags + Version // //Data[2]Code - Request/Response //3 Id - Can be same as outer Id //4,5 Length - Length this packet //6 Type - PEAP_TYPE_AVP //7,8 Type - High Bit is set to Mandatory if it is so ( 2 octets ) //9,10 Length - 2 octets //11... Value // if ( pPacket->Data[0] != (BYTE)PPP_EAP_PEAP ) { goto done; } if ( pPacket->Data[2] != EAPCODE_Request && pPacket->Data[2] != EAPCODE_Response ) { goto done; } if ( pPacket->Data[6] != PEAP_TYPE_AVP ) { goto done; } if ( pPacket->Data[8] != MS_PEAP_AVP_TYPE_STATUS ) { goto done; } *pwValue = WireToHostFormat16 (&(pPacket->Data[11])); //Save the Id for response if ( pPacket->Code == EAPCODE_Request ) pPeapCb->bId = pPacket->Id; dwRetCode = NO_ERROR; done: return dwRetCode; } // // PEAP cookie management functions // // Create a new cookie to store in cached session // DWORD PeapCreateCookie ( PPEAPCB pPeapCb, PBYTE * ppbCookie, DWORD * pcbCookie ) { DWORD dwRetCode = NO_ERROR; DWORD wCookieSize = 0; PPEAP_COOKIE pCookie = NULL; RAS_AUTH_ATTRIBUTE * pAttribute = pPeapCb->pTlsUserAttributes; EapTlsTrace("PeapCreateCookie"); wCookieSize = sizeof(PEAP_COOKIE); if ( pPeapCb->dwFlags & PEAPCB_FLAG_SERVER ) { wCookieSize += pPeapCb->pUserProp->dwSize; } else { wCookieSize += pPeapCb->pConnProp->dwSize; } pCookie = (PPEAP_COOKIE)LocalAlloc (LPTR, wCookieSize); if ( NULL == pCookie ) { EapTlsTrace ("Error allocating cookie"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; goto done; } wcscpy ( pCookie->awszIdentity, pPeapCb->awszIdentity ); if ( pPeapCb->dwFlags & PEAPCB_FLAG_SERVER ) { CopyMemory ( pCookie->Data, pPeapCb->pUserProp, pPeapCb->pUserProp->dwSize ); } else { CopyMemory ( pCookie->Data, pPeapCb->pConnProp, pPeapCb->pConnProp->dwSize ); } *ppbCookie = (PBYTE)pCookie; *pcbCookie = wCookieSize; done: return dwRetCode; } // // Verify current information against the cached // cookie // DWORD PeapCheckCookie ( PPEAPCB pPeapCb, PEAP_COOKIE *pCookie, DWORD cbCookie ) { DWORD dwRetCode = NO_ERROR; PEAP_CONN_PROP * pCookieConnProp; PEAP_USER_PROP * pCookieUserProp; EapTlsTrace ( "PeapCheckCookie"); // // Check to see if the saved and configured PEAP info matches // if ( pPeapCb->dwFlags & PEAPCB_FLAG_SERVER ) { pCookieUserProp = (PEAP_USER_PROP *)pCookie->Data; if ( pCookieUserProp->dwSize != pPeapCb->pUserProp->dwSize ) { EapTlsTrace ("Server config changed since the cookie was cached. Failing auth"); dwRetCode = ERROR_INVALID_PEAP_COOKIE_CONFIG; goto done; } if ( memcmp ( pCookieUserProp, pPeapCb->pUserProp, pPeapCb->pUserProp->dwSize ) ) { EapTlsTrace ("Server config changed since the cookie was cached. Failing auth"); dwRetCode = ERROR_INVALID_PEAP_COOKIE_CONFIG; goto done; } } else { pCookieConnProp = (PEAP_CONN_PROP *)pCookie->Data; if ( pCookieConnProp->dwSize != pPeapCb->pConnProp->dwSize ) { EapTlsTrace ("Connection Properties changed since the cookie was cached. Failing auth"); dwRetCode = ERROR_INVALID_PEAP_COOKIE_CONFIG; goto done; } if ( memcmp ( pCookieConnProp, pPeapCb->pConnProp, pPeapCb->pConnProp->dwSize ) ) { EapTlsTrace ("Connection Properties changed since the cookie was cached. Failing auth"); dwRetCode = ERROR_INVALID_PEAP_COOKIE_CONFIG; goto done; } } if ( _wcsicmp ( pCookie->awszIdentity, pPeapCb->awszIdentity ) ) { EapTlsTrace ("Identity in the cookie is %ws and peap got %ws", pCookie->awszIdentity, pPeapCb->awszIdentity ); dwRetCode = ERROR_INVALID_PEAP_COOKIE_USER; goto done; } //config and Id matches so we are ok. done: return dwRetCode; } //phew! DWORD PeapGetCredentials( IN VOID * pWorkBuf, OUT VOID ** ppCredentials) { PEAPCB *pPeapCb = (PEAPCB *) pWorkBuf; DWORD dwRetCode; if(NULL == pPeapCb) { return E_INVALIDARG; } // // Redirect the call to the actual peap module. // if(pPeapCb->pEapInfo->RasEapGetCredentials != NULL) { return pPeapCb->pEapInfo->RasEapGetCredentials( pPeapCb->pEapInfo->dwTypeId, pPeapCb->pEapInfo->pWorkBuf, ppCredentials); } return ERROR_FILE_NOT_FOUND; }