/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: ScLogon Abstract: This module provides helper functions for use by winlogon (GINA, Kerberos) Author: Amanda Matlosz (amatlosz) 10/22/1997 Environment: Win32, C++ w/ Exceptions Notes: 03-11-98 Wrap calls to GetLastError() to workaround bug where LastErr gets clobbered. Added event logging to make logon smoother. 04-02-98 Removed all references to WinVerifyTrust; this is something Kerberos itself is responsible for. --*/ ///////////////////////////////////////////////////////////////////////////// // // Includes #if !defined(_AMD64_) && !defined(_X86_) && !defined(_IA64_) #define _X86_ 1 #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #ifndef UNICODE #define UNICODE #endif #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif extern "C" { #include #include #include #include } #include #include #include #include #include #include #include "sclogon.h" #include "unicodes.h" #include #include #include #include #ifndef KP_KEYEXCHANGE_PIN #define KP_KEYEXCHANGE_PIN 32 #else #if 32 != KP_KEYEXCHANGE_PIN #error Invalid KP_KEYEXCHANGE_PIN assumption #endif #endif #ifndef CRYPT_SILENT #define CRYPT_SILENT 0x40 #else #if 0x40 != CRYPT_SILENT #error Duplicate CRYPT_SILENT definition #endif #endif #ifndef SCARD_PROVIDER_CSP #define SCARD_PROVIDER_CSP 2 #else #if 2 != SCARD_PROVIDER_CSP #error Invalid SCARD_PROVIDER_CSP definition #endif #endif #if defined(DBG) || defined(DEBUG) BOOL SCDebug = TRUE; #define DebugPrint(a) _DebugPrint a void __cdecl _DebugPrint( LPCSTR szFormat, ... ) { if (SCDebug) { CHAR szBuffer[512]; va_list ap; va_start(ap, szFormat); vsprintf(szBuffer, szFormat, ap); OutputDebugStringA(szBuffer); } } #else #define DebugPrint(a) #endif // TODO: The following logging is still proving useful. // TODO: leave in for B3: integrate more tightly w/ winlogon/kerberos ?? #include // A Global class used to maintain internal state. class CSCLogonInit { public: // Runs at image creation CSCLogonInit( BOOL *pfResult) { m_hCrypt = NULL; *pfResult = TRUE; }; // Runs at image termination ~CSCLogonInit() { Release(); }; // Cleans up current state. void Release( void) { if (NULL != m_hCrypt) { CryptReleaseContext(m_hCrypt, 0); m_hCrypt = NULL; } } // Relinquish control of the crypto context. HCRYPTPROV RelinquishCryptCtx( LogonInfo* pLogon) { HCRYPTPROV hProv; hProv = CryptCtx(pLogon); m_hCrypt = NULL; return hProv; }; // Get the crypto context, creating it if it's not there. HCRYPTPROV CryptCtx( LogonInfo* pLogon) { HCRYPTPROV hProv; LPCTSTR szRdr = NULL; LPCTSTR szCntr = NULL; LPTSTR szFQCN = NULL; LONG lLen = 0; if (NULL == m_hCrypt) { BOOL fSts; // Prepare FullyQualifiedContainerName for CryptAcCntx call szRdr = GetReaderName((LPBYTE)pLogon); szCntr = GetContainerName((LPBYTE)pLogon); lLen = (lstrlen(szRdr) + lstrlen(szCntr) + 10)*sizeof(TCHAR); szFQCN = (LPTSTR)LocalAlloc(LPTR, lLen); if (NULL != szFQCN) { wsprintf(szFQCN, TEXT("\\\\.\\%s\\%s"), szRdr, szCntr); fSts = CryptAcquireContext( &m_hCrypt, szFQCN, GetCSPName((LPBYTE)pLogon), PROV_RSA_FULL, // ?TODO? from pbLogonInfo CRYPT_SILENT | CRYPT_MACHINE_KEYSET ); LocalFree(szFQCN); } else { fSts = FALSE; } } hProv = m_hCrypt; return hProv; } protected: HCRYPTPROV m_hCrypt; }; // For tracing errors in ScHelper* NTSTATUS LogEvent(NTSTATUS NtErr, DWORD dwEventID) { DWORD dwErr; // // Convert the error back to a Win32 error // switch (NtErr) { case STATUS_INVALID_PARAMETER: dwErr = ERROR_INVALID_DATA; break; case STATUS_SMARTCARD_SUBSYSTEM_FAILURE: // A Cryptxxx API just failed dwErr = GetLastError(); switch (dwErr) { case SCARD_W_WRONG_CHV: case SCARD_E_INVALID_CHV: NtErr = STATUS_SMARTCARD_WRONG_PIN; break; case SCARD_W_CHV_BLOCKED: NtErr = STATUS_SMARTCARD_CARD_BLOCKED; break; case SCARD_W_REMOVED_CARD: case SCARD_E_NO_SMARTCARD: NtErr = STATUS_SMARTCARD_NO_CARD; break; case NTE_BAD_KEYSET: case NTE_KEYSET_NOT_DEF: NtErr = STATUS_SMARTCARD_NO_KEY_CONTAINER; break; case SCARD_E_NO_SUCH_CERTIFICATE: case SCARD_E_CERTIFICATE_UNAVAILABLE: NtErr = STATUS_SMARTCARD_NO_CERTIFICATE; break; case NTE_NO_KEY: NtErr = STATUS_SMARTCARD_NO_KEYSET; break; case SCARD_E_TIMEOUT: case SCARD_F_COMM_ERROR: case SCARD_E_COMM_DATA_LOST: NtErr = STATUS_SMARTCARD_IO_ERROR; break; //default: // Nothing, leave NtErr unchanged } break; case STATUS_INSUFFICIENT_RESOURCES: case STATUS_NO_MEMORY: dwErr = ERROR_OUTOFMEMORY; break; case STATUS_BUFFER_TOO_SMALL: dwErr = SEC_E_BUFFER_TOO_SMALL; break; default: dwErr = SCARD_E_UNEXPECTED; } if (0 == dwErr) { return NtErr; } // // Initialize log as necessary // HKEY hKey; DWORD disp; long err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\EventLog\\Application\\Smart Card Logon"), 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &disp ); if (ERROR_SUCCESS != err) { return NtErr; } if (disp == REG_CREATED_NEW_KEY) { PBYTE l_szModulePath = (PBYTE)TEXT("%SystemRoot%\\System32\\scarddlg.dll"); ULONG l_uLen = (_tcslen((LPCTSTR)l_szModulePath) + 1)*sizeof(TCHAR); RegSetValueEx( hKey, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, l_szModulePath, l_uLen ); disp = (DWORD)( EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE ); RegSetValueEx( hKey, TEXT("TypesSupported"), 0, REG_DWORD, (PBYTE) &disp, sizeof(DWORD) ); } RegCloseKey(hKey); HANDLE hEventSource = RegisterEventSource( NULL, TEXT("Smart Card Logon") ); if (NULL != hEventSource) { DWORD dwLen = 0; LPTSTR szErrorString = NULL; TCHAR szBuffer[2+8+1]; // Enough for "0x????????" dwLen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, LANG_NEUTRAL, (LPTSTR)&szErrorString, 0, NULL); if (dwLen == 0) { _stprintf(szBuffer, _T("0x%08lX"), dwErr); szErrorString = szBuffer; } ReportEvent( hEventSource, EVENTLOG_ERROR_TYPE, 0, // event category dwEventID, // event identifier // resourceID for the messagetable entry... NULL, // user security identifier (optional) 1, // number of strings to merge with message sizeof(long), // size of binary data, in bytes (LPCTSTR*)&szErrorString, // array of strings to merge with message (LPVOID)&dwErr // address of binary data ); DeregisterEventSource(hEventSource); if ((NULL != szErrorString) && (szErrorString != szBuffer)) { LocalFree((LPVOID)szErrorString); } } return NtErr; } ////////////////////////////////////////////////////////////////////////////// // // Structs ////////////////////////////////////////////////////////////////////////////// // // Functions // // Internal helpers: called by the ScLogon APIs to perform certain tedious work /*++ GetReaderName: GetCardName: GetContainerName: GetCSPName: : Intended for accessing the LogonInformation glob Author: Amanda Matlosz Note: Some of these are made available to outside callers; see sclogon.h --*/ extern "C" PBYTE WINAPI ScBuildLogonInfo( LPCTSTR szCard, LPCTSTR szReader, LPCTSTR szContainer, LPCTSTR szCSP) { // No assumptions are made regarding the values of the incoming parameters; // At this point, it is legal for them all to be empty. // It is also possible that NULL values are being passed in -- if this is the case, // they must be replaced with empty strings. LPCTSTR szCardI = TEXT(""); LPCTSTR szReaderI = TEXT(""); LPCTSTR szContainerI = TEXT(""); LPCTSTR szCSPI = TEXT(""); if (NULL != szCard) { szCardI = szCard; } if (NULL != szReader) { szReaderI = szReader; } if (NULL != szContainer) { szContainerI = szContainer; } if (NULL != szCSP) { szCSPI = szCSP; } // // Build the LogonInfo glob using strings (or empty strings) // DWORD cbLi = offsetof(LogonInfo, bBuffer) + (lstrlen(szCardI) + 1) * sizeof(TCHAR) + (lstrlen(szReaderI) + 1) * sizeof(TCHAR) + (lstrlen(szContainerI) + 1) * sizeof(TCHAR) + (lstrlen(szCSPI) + 1) * sizeof(TCHAR); LogonInfo* pLI = (LogonInfo*)LocalAlloc(LPTR, cbLi); if (NULL == pLI) { return NULL; } pLI->ContextInformation = NULL; pLI->dwLogonInfoLen = cbLi; LPTSTR pBuffer = pLI->bBuffer; pLI->nCardNameOffset = 0; lstrcpy(pBuffer, szCardI); pBuffer += (lstrlen(szCardI)+1); pLI->nReaderNameOffset = (ULONG) (pBuffer-pLI->bBuffer); lstrcpy(pBuffer, szReaderI); pBuffer += (lstrlen(szReaderI)+1); pLI->nContainerNameOffset = (ULONG) (pBuffer-pLI->bBuffer); lstrcpy(pBuffer, szContainerI); pBuffer += (lstrlen(szContainerI)+1); pLI->nCSPNameOffset = (ULONG) (pBuffer-pLI->bBuffer); lstrcpy(pBuffer, szCSPI); pBuffer += (lstrlen(szCSPI)+1); _ASSERTE(cbLi == (DWORD)((LPBYTE)pBuffer - (LPBYTE)pLI)); return (PBYTE)pLI; } LPCTSTR WINAPI GetReaderName(PBYTE pbLogonInfo) { LogonInfo* pLI = (LogonInfo*)pbLogonInfo; if (NULL == pLI) { return NULL; } return &pLI->bBuffer[pLI->nReaderNameOffset]; }; LPCTSTR WINAPI GetCardName(PBYTE pbLogonInfo) { LogonInfo* pLI = (LogonInfo*)pbLogonInfo; if (NULL == pLI) { return NULL; } return &pLI->bBuffer[pLI->nCardNameOffset]; }; LPCTSTR WINAPI GetContainerName(PBYTE pbLogonInfo) { LogonInfo* pLI = (LogonInfo*)pbLogonInfo; if (NULL == pLI) { return NULL; } return &pLI->bBuffer[pLI->nContainerNameOffset]; }; LPCTSTR WINAPI GetCSPName(PBYTE pbLogonInfo) { LogonInfo* pLI = (LogonInfo*)pbLogonInfo; if (NULL == pLI) { return NULL; } return &pLI->bBuffer[pLI->nCSPNameOffset]; }; /*++ BuildCertContext: Generates a certificate context with (static) keyprov info suitable for CertStore-based operations. If the PIN is provided, it is assumed the hProv (if provided) has not had the PIN parameter set... Arguments: hProv -- must be a valid HCRYPTPROV pucPIN -- may be empty; used to set the PIN for hProv pbCert -- assumed to be a valid certificate; must not be NULL dwCertLen CertificateContext -- pointer to a pointer to the resultant CertContext Return Value: NTSTATUS indicating STATUS_SUCCESS or error (see winerror.h or scarderr.h) Author: Amanda Matlosz Note: --*/ NTSTATUS BuildCertContext( IN HCRYPTPROV hProv, IN PUNICODE_STRING pucPIN, IN PBYTE pbCert, IN DWORD dwCertLen, OUT PCCERT_CONTEXT *CertificateContext ) { NTSTATUS lResult = STATUS_SUCCESS; BOOL fSts = FALSE; CRYPT_KEY_PROV_INFO KeyProvInfo; LPSTR szContainerName = NULL; LPSTR szProvName = NULL; CUnicodeString wszContainerName, wszProvName; DWORD cbContainerName, cbProvName; // // Check params // if ((NULL == hProv) || (NULL == pbCert || 0 == dwCertLen)) { ASSERT(FALSE); lResult = STATUS_INVALID_PARAMETER; goto ErrorExit; } // // Convert the certificate into a Cert Context. // *CertificateContext = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, dwCertLen); if (NULL == *CertificateContext) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Associate cryptprovider w/ the private key property of this cert // // ... need the container name fSts = CryptGetProvParam( hProv, PP_CONTAINER, NULL, // out &cbContainerName, // in/out 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } szContainerName = (LPSTR)LocalAlloc(LPTR, cbContainerName); if (NULL == szContainerName) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } fSts = CryptGetProvParam( hProv, PP_CONTAINER, (PBYTE)szContainerName, // out &cbContainerName, // in/out 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } wszContainerName = szContainerName; // ... need the provider name fSts = CryptGetProvParam( hProv, PP_NAME, NULL, // out &cbProvName, // in/out 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } szProvName = (LPSTR)LocalAlloc(LPTR, cbProvName); if (NULL == szProvName) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } fSts = CryptGetProvParam( hProv, PP_NAME, (PBYTE)szProvName, // out &cbProvName, // in/out 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } wszProvName = szProvName; // // Set the cert context properties to reflect the prov info // KeyProvInfo.pwszContainerName = (LPWSTR)(LPCWSTR)wszContainerName; KeyProvInfo.pwszProvName = (LPWSTR)(LPCWSTR)wszProvName; KeyProvInfo.dwProvType = PROV_RSA_FULL; KeyProvInfo.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; KeyProvInfo.cProvParam = 0; KeyProvInfo.rgProvParam = NULL; KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE; KeyProvInfo.dwFlags |= CERT_SET_KEY_CONTEXT_PROP_ID; fSts = CertSetCertificateContextProperty( *CertificateContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (void *)&KeyProvInfo); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; // the cert's been incorrectly created -- scrap it. CertFreeCertificateContext(*CertificateContext); *CertificateContext = NULL; goto ErrorExit; } CERT_KEY_CONTEXT certKeyContext; certKeyContext.cbSize = sizeof(CERT_KEY_CONTEXT); certKeyContext.hCryptProv = hProv; certKeyContext.dwKeySpec = KeyProvInfo.dwKeySpec; fSts = CertSetCertificateContextProperty( *CertificateContext, CERT_KEY_CONTEXT_PROP_ID, 0, (void *)&certKeyContext); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; // the cert's been incorrectly created -- scrap it. CertFreeCertificateContext(*CertificateContext); *CertificateContext = NULL; goto ErrorExit; } ErrorExit: if(NULL != szContainerName) { LocalFree(szContainerName); szContainerName = NULL; } if(NULL != szProvName) { LocalFree(szProvName); szProvName = NULL; } if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_BUILDCC); } return lResult; } /////////////////////////////////////////////////////////////////////////////// // // ScLogon APIs // /*++ ScHelperInitializeContext: Prepares contextual information to be used by LSA while handling this smart card session. Arguments: None. Return Value: None Author: Richard Ward Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperInitializeContext( IN OUT PBYTE pbLogonInfo, IN ULONG cbLogonInfo ) { ULONG AllowedSize; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; if ((cbLogonInfo < sizeof(ULONG)) || (cbLogonInfo != pLI->dwLogonInfoLen)) { return(STATUS_INVALID_PARAMETER); } AllowedSize = (cbLogonInfo - sizeof(LogonInfo) ) / sizeof(TCHAR) + sizeof(DWORD) ; // // Verify the other fields of the logon info // if ((pLI->nCardNameOffset > pLI->nReaderNameOffset) || (pLI->bBuffer[pLI->nReaderNameOffset-1] != TEXT('\0'))) { return(STATUS_INVALID_PARAMETER); } if ((pLI->nReaderNameOffset > pLI->nContainerNameOffset) || (pLI->bBuffer[pLI->nContainerNameOffset-1] != TEXT('\0'))) { return(STATUS_INVALID_PARAMETER); } if ((pLI->nContainerNameOffset > pLI->nCSPNameOffset) || (pLI->bBuffer[pLI->nCSPNameOffset-1] != TEXT('\0'))) { return(STATUS_INVALID_PARAMETER); } if ((pLI->nCSPNameOffset > AllowedSize) || (pLI->bBuffer[AllowedSize-1] != TEXT('\0'))) { return(STATUS_INVALID_PARAMETER); } _ASSERTE(pLI->ContextInformation == NULL); BOOL fResult = 0; pLI->ContextInformation = new CSCLogonInit(&fResult); if (pLI->ContextInformation == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } else { if (!fResult) { delete pLI->ContextInformation; pLI->ContextInformation = NULL; return(STATUS_INSUFFICIENT_RESOURCES); } } return(STATUS_SUCCESS); } /*++ ScHelperRelease: Releases contextual information used by LSA while handling this smart card session. Arguments: None. Return Value: None Author: Richard Ward Note: Used by LSA. --*/ VOID WINAPI ScHelperRelease( IN OUT PBYTE pbLogonInfo ) { _ASSERTE(NULL != pbLogonInfo); LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; if (LogonInit != NULL) { LogonInit->Release(); delete LogonInit; pLI->ContextInformation = NULL; } } /*++ ScHelperGetCertFromLogonInfo: Returns a CertificateContext for the cert on the card specified by the LogonInfo. Creates the cert context by calling BuildCertContext, which generates a certificate context with (static) keyprov info suitable for CertStore-based operations. Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: None Author: Amanda Matlosz Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperGetCertFromLogonInfo( IN PBYTE pbLogonInfo, IN PUNICODE_STRING pucPIN, OUT PCCERT_CONTEXT *CertificateContext ) { _ASSERTE(NULL != pbLogonInfo); LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; BOOL fSts; NTSTATUS lResult = STATUS_SUCCESS; PCCERT_CONTEXT pCertCtx = NULL; HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; LPBYTE pbCert = NULL; DWORD cbCertLen; // // Make sure we've got a Crypto Provider up and running. // hProv = LogonInit->RelinquishCryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Get the key handle. // fSts = CryptGetUserKey( hProv, AT_KEYEXCHANGE, &hKey); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Upload the certificate. // fSts = CryptGetKeyParam( hKey, KP_CERTIFICATE, NULL, &cbCertLen, 0); if (!fSts) { DWORD dwGLE = GetLastError(); if (ERROR_MORE_DATA != dwGLE) { if (NTE_NOT_FOUND == dwGLE) { SetLastError(SCARD_E_NO_SUCH_CERTIFICATE); } lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } pbCert = (LPBYTE)LocalAlloc(LPTR, cbCertLen); if (NULL == pbCert) { lResult = STATUS_NO_MEMORY; goto ErrorExit; } fSts = CryptGetKeyParam( hKey, KP_CERTIFICATE, pbCert, &cbCertLen, 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } lResult = BuildCertContext( hProv, pucPIN, pbCert, cbCertLen, &pCertCtx); if (NT_SUCCESS(lResult)) { // The cert context will take care of the crypt context now. hProv = NULL; } // // Clean up and return. // ErrorExit: *CertificateContext = pCertCtx; // Do this early so GetLastError is not clobbered if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_GETCERT); } if (NULL != hKey) { CryptDestroyKey(hKey); } if (NULL != hProv) { CryptReleaseContext(hProv, 0); } if (NULL != pbCert) { LocalFree(pbCert); } return lResult; } /*++ ScHelperGetProvParam: This API wraps the CryptGetProvParam routine for use with a smart card. Arguments: pucPIN supplies a Unicode string containing the card's PIN. pbLogonInfo supplies the information required to identify the card, csp, etc. It cannot be NULL. The other parameters are identical to CryptGetProvParam Return Value: A STATUS_SUCECSS for success, or an error --*/ NTSTATUS WINAPI ScHelperGetProvParam( IN PUNICODE_STRING pucPIN, IN PBYTE pbLogonInfo, DWORD dwParam, BYTE*pbData, DWORD *pdwDataLen, DWORD dwFlags ) { _ASSERTE(NULL != pbLogonInfo); LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; BOOL fSts; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { return LogEvent(STATUS_SMARTCARD_SUBSYSTEM_FAILURE, (DWORD)EVENT_ID_GETPROVPARAM); } fSts = CryptGetProvParam( hProv, dwParam, pbData, pdwDataLen, dwFlags ); if (!fSts) { if (GetLastError() == ERROR_NO_MORE_ITEMS) { return (STATUS_NO_MORE_ENTRIES); } else { return LogEvent(STATUS_SMARTCARD_SUBSYSTEM_FAILURE, (DWORD)EVENT_ID_GETPROVPARAM); } } return(STATUS_SUCCESS); } /*++ ScHelperVerifyCard: This API provides an easy way to verify the integrity of the card identified by pbLogonInfo (ie, that it has the private key associated w/ the public key contained in the certificate it returned via ScHelperGetCertFromLogonInfo) and, in so doing, authenticates the user to the card. Arguments: pucPIN supplies a Unicode string containing the card's PIN. CertificateContext supplies the cert context received via ScHelperGetCertFromLogonInfo. hCertStore supplies the handle of a cert store which contains a CTL to use during certificate verification, or NULL to use the system default store. pbLogonInfo supplies the information required to identify the card, csp, etc. It cannot be NULL. Return Value: A 32-bit value indicating whether or not the service completed successfully. STATUS_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. --*/ NTSTATUS WINAPI ScHelperVerifyCard( IN PUNICODE_STRING pucPIN, IN PCCERT_CONTEXT CertificateContext, IN HCERTSTORE hCertStore, IN PBYTE pbLogonInfo ) { _ASSERTE(NULL != pbLogonInfo); LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; NTSTATUS lResult = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; PBYTE pbBlob = NULL; ULONG ulBlobLen = 32; PBYTE pbSignature = NULL; ULONG ulSigLen = 0; BOOL fSts; // // Make sure we've got a Crypto Provider up and running. // hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Generate a random key blob as the message to sign // pbBlob = (LPBYTE)LocalAlloc(LPTR, ulBlobLen); if (NULL == pbBlob) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } fSts = CryptGenRandom(hProv, ulBlobLen, pbBlob); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // The card signs a hash of the message... // lResult = ScHelperSignMessage( pucPIN, pbLogonInfo, NULL, CALG_MD5, pbBlob, ulBlobLen, pbSignature, &ulSigLen); if (STATUS_BUFFER_TOO_SMALL != lResult) { goto ErrorExit; } pbSignature = (LPBYTE)LocalAlloc(LPTR, ulSigLen); if (NULL == pbSignature) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } lResult = ScHelperSignMessage( pucPIN, pbLogonInfo, NULL, CALG_MD5, pbBlob, ulBlobLen, pbSignature, &ulSigLen); if (!NT_SUCCESS(lResult)) { goto ErrorExit; } // // Verify the signature is correct // lResult = ScHelperVerifyMessage( pbLogonInfo, NULL, CertificateContext, CALG_MD5, pbBlob, ulBlobLen, pbSignature, ulSigLen); // // Clean up and return. // ErrorExit: // Do this early so GetLastError is not clobbered if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_VERIFYCARD); } if (NULL != hKey) { CryptDestroyKey(hKey); } if (NULL != pbSignature) { LocalFree(pbSignature); } if (NULL != pbBlob) { LocalFree(pbBlob); } return lResult; } NTSTATUS WINAPI ScHelperGenRandBits( IN PBYTE pbLogonInfo, IN OUT ScHelper_RandomCredBits* psc_rcb ) { _ASSERTE(NULL != pbLogonInfo); LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; BOOL fSts = FALSE; // // Make sure we've got a Crypto Provider up and running. // hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } memset(psc_rcb, 0, sizeof(*psc_rcb)); fSts = CryptGenRandom(hProv, 32, psc_rcb->bR1); if (fSts) { fSts = CryptGenRandom(hProv, 32, psc_rcb->bR2); } if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } ErrorExit: if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_GENRANDBITS); } return lResult; } /*++ ScHelperCreateCredKeys: This routine (called by ScHelperVerifyCardAndCreds and ScHelperEncryptCredentials) munges a R1 and R2 to derive symmetric keys for encrypting and decrypting KDC creds, and or genearting an HMAC. Arguments: pucPIN supplies a Unicode string containing the card's PIN. pbLogonInfo supplies the information required to identify the card, csp, etc. It cannot be NULL. psc_rcb supplies the R1 and R2, previously generated by a call to ScHelperGenRandBits. phHmacKey recieves the generated HMAC key. phRc4Key receives the generated RC4 key. Return Value: A 32-bit value indicating whether or not the service completed successfully. STATUS_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Remarks: You may supply a value of NULL to EncryptedData to receive only the required size of the EncryptedData buffer. Author: Amanda Matlosz (amatlosz) 6/23/1999 --*/ NTSTATUS WINAPI ScHelperCreateCredKeys( IN PUNICODE_STRING pucPIN, IN PBYTE pbLogonInfo, IN ScHelper_RandomCredBits* psc_rcb, IN OUT HCRYPTKEY* phHmacKey, IN OUT HCRYPTKEY* phRc4Key, IN OUT HCRYPTPROV* phProv ) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; HCRYPTPROV hProv = NULL; PBYTE pbR1Sig = NULL; DWORD dwR1SigLen = 0; HCRYPTHASH hKHash = NULL; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CUnicodeString szPin(pucPIN); BOOL fSts = FALSE; *phProv = NULL; // check params if (NULL == psc_rcb || NULL == phHmacKey || NULL == phRc4Key) { return(STATUS_INVALID_PARAMETER); } // Get hProv for smart card if (NULL != pucPIN) { if (!szPin.Valid()) { return(STATUS_INSUFFICIENT_RESOURCES); } } CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // Sign R1 w/ smart card fSts = CryptCreateHash( hProv, CALG_SHA1, NULL, NULL, &hHash); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptHashData( hHash, psc_rcb->bR1, 32, // TODO: const 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Declare the PIN. // if (NULL != pucPIN) { fSts = CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, (LPBYTE)((LPCSTR)szPin), 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } fSts = CryptSignHash( hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &dwR1SigLen); // if (fSts || ERROR_MORE_DATA != GetLastError()) if (0 >= dwR1SigLen) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } pbR1Sig = (LPBYTE)LocalAlloc(LPTR, dwR1SigLen); if (NULL == pbR1Sig) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } fSts = CryptSignHash( hHash, AT_KEYEXCHANGE, NULL, 0, pbR1Sig, &dwR1SigLen); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // TODO: sigR1 is the key to hash R2 with; // for now, just hash 'em together; use generic CSP fSts = CryptAcquireContext( phProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptCreateHash( *phProv, CALG_SHA1, NULL, NULL, &hKHash ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptHashData( hKHash, pbR1Sig, dwR1SigLen, NULL ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptHashData( hKHash, psc_rcb->bR2, 32, // TODO: use a const NULL ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // create the rc4 key for the cred&hmac encryption fSts = CryptDeriveKey( *phProv, CALG_RC4, // stream cipher, hKHash, NULL, phRc4Key ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // create the key for the HMAC from the hash of R1&2 fSts = CryptDeriveKey( *phProv, CALG_RC2, hKHash, NULL, phHmacKey ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } ErrorExit: // // cleanup // if (NULL != hHash) { CryptDestroyHash(hHash); } if (NULL != hKHash) { CryptDestroyHash(hKHash); } if (NULL != pbR1Sig) { LocalFree(pbR1Sig); } return lResult; } NTSTATUS WINAPI ScHelperCreateCredHMAC( IN HCRYPTPROV hProv, IN HCRYPTKEY hHmacKey, IN PBYTE CleartextData, IN ULONG CleartextDataSize, IN OUT PBYTE* ppbHmac, IN OUT DWORD* pdwHmacLen ) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTHASH hHMAC = NULL; HMAC_INFO hmac_info; BOOL fSts = FALSE; fSts = CryptCreateHash( hProv, CALG_HMAC, hHmacKey, NULL, &hHMAC ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } memset(&hmac_info, 0, sizeof(HMAC_INFO)); hmac_info.HashAlgid = CALG_SHA1; fSts = CryptSetHashParam( hHMAC, HP_HMAC_INFO, (PBYTE)&hmac_info, NULL ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptHashData( hHMAC, CleartextData, CleartextDataSize, NULL); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CryptGetHashParam( hHMAC, HP_HASHVAL, *ppbHmac, pdwHmacLen, NULL ); if (0 >= *pdwHmacLen) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } *ppbHmac = (PBYTE)LocalAlloc(LPTR, *pdwHmacLen); if (NULL == *ppbHmac) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } fSts = CryptGetHashParam( hHMAC, HP_HASHVAL, *ppbHmac, pdwHmacLen, NULL ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } ErrorExit: if (NULL != hHMAC) { CryptDestroyHash(hHMAC); } return lResult; } /*++ ScHelperVerifyCardAndCreds: This routine combines Card Verification and Credential Decryption. Arguments: pucPIN supplies a Unicode string containing the card's PIN. CertificateContext supplies the cert context received via ScHelperGetCertFromLogonInfo. hCertStore supplies the handle of a cert store which contains a CTL to use during certificate verification, or NULL to use the system default store. pbLogonInfo supplies the information required to identify the card, csp, etc. It cannot be NULL. EncryptedData receives the encrypted credential blob. EncryptedDataSize supplies the size of the EncryptedData buffer in bytes, and receives the actual size of the encrypted blob. CleartextData supplies a credential blob to be encrypted. CleartextDataSize supplies the size of the blob, in bytes. Return Value: A 32-bit value indicating whether or not the service completed successfully. STATUS_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Remarks: You may supply a value of NULL to EncryptedData to receive only the required size of the EncryptedData buffer. Author: Doug Barlow (dbarlow) 5/24/1999 --*/ NTSTATUS WINAPI ScHelperVerifyCardAndCreds( IN PUNICODE_STRING pucPIN, IN PCCERT_CONTEXT CertificateContext, IN HCERTSTORE hCertStore, IN PBYTE pbLogonInfo, IN PBYTE EncryptedData, IN ULONG EncryptedDataSize, OUT OPTIONAL PBYTE CleartextData, OUT PULONG CleartextDataSize ) { NTSTATUS lResult = STATUS_SUCCESS; // Verify the Card lResult = ScHelperVerifyCard( pucPIN, CertificateContext, hCertStore, pbLogonInfo); // Decrypt the Creds if (NT_SUCCESS(lResult)) { lResult = ScHelperDecryptCredentials( pucPIN, CertificateContext, hCertStore, pbLogonInfo, EncryptedData, EncryptedDataSize, CleartextData, CleartextDataSize); } return lResult; } /*++ ScHelperDecryptCredentials: This routine decrypts an encrypted credential blob. Arguments: pucPIN supplies a Unicode string containing the card's PIN. CertificateContext supplies the cert context received via ScHelperGetCertFromLogonInfo. hCertStore supplies the handle of a cert store which contains a CTL to use during certificate verification, or NULL to use the system default store. EncryptedData supplies the encrypted credential blob. EncryptedDataSize supplies the length of the encrypted credential blob, in bytes. CleartextData receives the decrypted credential blob. CleartextDataSize supplies the length of the CleartextData buffer, and receives the actual length of returned decrypted credential blob. Return Value: A 32-bit value indicating whether or not the service completed successfully. STATUS_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Remarks: You may supply a value of NULL to CleartextData to receive only the required size of the buffer in CleartextDataSize. Author: Doug Barlow (dbarlow) 5/24/1999 --*/ NTSTATUS WINAPI ScHelperDecryptCredentials( IN PUNICODE_STRING pucPIN, IN PCCERT_CONTEXT CertificateContext, IN HCERTSTORE hCertStore, IN PBYTE pbLogonInfo, IN PBYTE EncryptedData, IN ULONG EncryptedDataSize, OUT OPTIONAL PBYTE CleartextData, OUT PULONG CleartextDataSize) { NTSTATUS lResult = STATUS_SUCCESS; PBYTE pbCredBlob = NULL; DWORD dwCredBlobSize = 0; PBYTE pbHmac = NULL; // the HMAC stored with the cred blob DWORD dwHmacSize = NULL; // size of HMAC stored with cred blob PBYTE pbNewHmac = NULL; // HMAC generated from cred blob for verify DWORD dwNewHmacSize = 0; // size of gen'd HMAC PBYTE pb = NULL; DWORD dw = 0; PBYTE pbPlainCred = NULL; DWORD dwPlainCredSize = 0; HCRYPTKEY hHmacKey = NULL; HCRYPTKEY hRc4Key = NULL; HCRYPTPROV hGenProv = NULL; BOOL fSts = FALSE; // pull the SCH_RCB out of the EncryptedData blob ScHelper_RandomCredBits* psch_rcb = (ScHelper_RandomCredBits*)EncryptedData; // and build a private copy of the blob itself dwCredBlobSize = EncryptedDataSize - sizeof(ScHelper_RandomCredBits); pbCredBlob = (PBYTE)LocalAlloc(LPTR, dwCredBlobSize); if (NULL == pbCredBlob) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } pb = EncryptedData + sizeof(ScHelper_RandomCredBits); CopyMemory(pbCredBlob, pb, dwCredBlobSize); // // Fetch the keys we need to decrypt & verify the cred blob // lResult = ScHelperCreateCredKeys( pucPIN, pbLogonInfo, psch_rcb, &hHmacKey, &hRc4Key, &hGenProv ); if (!NT_SUCCESS(lResult)) { goto ErrorExit; } // // Decrypt the cred blob // fSts = CryptDecrypt( hRc4Key, NULL, TRUE, NULL, pbCredBlob, &dwCredBlobSize); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // pull the HMAC out & verify it // dwHmacSize = (DWORD)*pbCredBlob; pbHmac = pbCredBlob + sizeof(DWORD); pbPlainCred = pbCredBlob + dwHmacSize + sizeof(DWORD); dwPlainCredSize = dwCredBlobSize - dwHmacSize - sizeof(DWORD); lResult = ScHelperCreateCredHMAC( hGenProv, hHmacKey, pbPlainCred, dwPlainCredSize, &pbNewHmac, &dwNewHmacSize); if (!NT_SUCCESS(lResult)) { goto ErrorExit; } lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; if (dwNewHmacSize == dwHmacSize) { for (dw = 0; (dw < dwNewHmacSize) && ((BYTE)*(pbHmac+dw)==(BYTE)*(pbNewHmac+dw)); dw++); if (dwNewHmacSize == dw) { // verification succeeded! lResult = STATUS_SUCCESS; } } if (!NT_SUCCESS(lResult)) { goto ErrorExit; } // // return the decrypted blob or just its length, as necessary // if ((NULL != CleartextData) && (0 < *CleartextDataSize)) { if (*CleartextDataSize >= dwPlainCredSize) { CopyMemory(CleartextData, pbPlainCred, dwPlainCredSize); } else lResult = STATUS_BUFFER_TOO_SMALL; } else { lResult = STATUS_BUFFER_TOO_SMALL; } *CleartextDataSize = dwPlainCredSize; // // Cleanup and return // ErrorExit: if (NULL != pbNewHmac) { LocalFree(pbNewHmac); } if (NULL != hHmacKey) { CryptDestroyKey(hHmacKey); } if (NULL != hRc4Key) { CryptDestroyKey(hRc4Key); } if (NULL != hGenProv) { CryptReleaseContext(hGenProv, NULL); } return lResult; } /*++ ScHelperEncryptCredentials: This routine encrypts a credential blob. Arguments: pucPIN supplies a Unicode string containing the card's PIN. CertificateContext supplies the cert context received via ScHelperGetCertFromLogonInfo. hCertStore supplies the handle of a cert store which contains a CTL to use during certificate verification, or NULL to use the system default store. CleartextData supplies the cleartext credential blob. CleartextDataSize supplies the length of the cleartext credential blob, in bytes. EncryptedData receives the encrypted credential blob. EncryptedDataSize supplies the length of the EncryptedData buffer, and receives the actual length of returned encrypted credential blob. Return Value: A 32-bit value indicating whether or not the service completed successfully. STATUS_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Remarks: You may supply a value of NULL to EncryptedData to receive only the required size of the buffer in EncryptedDataSize. Author: Doug Barlow (dbarlow) 5/24/1999 --*/ NTSTATUS WINAPI ScHelperEncryptCredentials( IN PUNICODE_STRING pucPIN, IN PCCERT_CONTEXT CertificateContext, IN HCERTSTORE hCertStore, IN ScHelper_RandomCredBits* psch_rcb, IN PBYTE pbLogonInfo, IN PBYTE CleartextData, IN ULONG CleartextDataSize, OUT OPTIONAL PBYTE EncryptedData, OUT PULONG EncryptedDataSize) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; BOOL fSts; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; ULONG SignedEncryptedCredSize = 0; PBYTE SignedEncryptedCred = NULL; // encrypted cred&sig, !including R1+R2 HCRYPTKEY hHmacKey = NULL; HCRYPTKEY hRc4Key = NULL; PBYTE pbHmac = NULL; DWORD dwHmacLen = 0; PBYTE pbCredsAndHmac = NULL; DWORD dwCredsAndHmacLen = 0; DWORD dwEncryptedCredSize = 0; PBYTE pb = NULL; // parameter checking? // // do stuff to determine size required for SignedEncryptedCred // lResult = ScHelperCreateCredKeys( pucPIN, pbLogonInfo, psch_rcb, &hHmacKey, &hRc4Key, &hProv ); if (!NT_SUCCESS(lResult)) { goto ErrorExit; } // HMAC creds lResult = ScHelperCreateCredHMAC( hProv, hHmacKey, CleartextData, CleartextDataSize, &pbHmac, &dwHmacLen); if (!NT_SUCCESS(lResult)) { goto ErrorExit; } // make a buffer with creds and HMAC pbCredsAndHmac = NULL; dwCredsAndHmacLen = dwHmacLen + CleartextDataSize + sizeof(DWORD); pbCredsAndHmac = (PBYTE)LocalAlloc(LPTR, dwCredsAndHmacLen); if (NULL == pbCredsAndHmac) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } pb = pbCredsAndHmac; CopyMemory(pb, &dwHmacLen, sizeof(DWORD)); pb += sizeof(DWORD); CopyMemory(pb, pbHmac, dwHmacLen); pb += dwHmacLen; CopyMemory(pb, CleartextData, CleartextDataSize); // Encrypt creds+HMAC dwEncryptedCredSize = dwCredsAndHmacLen; // After CryptEncrypt, dwCredsAndHmacLen describes the length of the data // to encrypt and dwEncryptedCredSize describes the req'd buffer length // TODO: VERIFY THE HANDLING OF dwEncryptedCredSize and dwCresAndHmacLen fSts = CryptEncrypt( hRc4Key, NULL, TRUE, NULL, pbCredsAndHmac, &dwEncryptedCredSize, dwCredsAndHmacLen ); if (!fSts) { if (GetLastError() != ERROR_MORE_DATA) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } // // Create the final blob for return, or inform user of size, as necessary // if ((NULL != EncryptedData) && (0 < *EncryptedDataSize)) { if (*EncryptedDataSize >= dwEncryptedCredSize + sizeof(ScHelper_RandomCredBits)) { // the user gave us enough space for the whole thing. // if the previous CryptEncrypt failed with ERROR_MORE_DATA // we can now do something about it... if (!fSts) { // resize pbCredsAndHmac LocalFree(pbCredsAndHmac); pbCredsAndHmac = (PBYTE)LocalAlloc(LPTR, dwCredsAndHmacLen); if (NULL == pbCredsAndHmac) { lResult = STATUS_INSUFFICIENT_RESOURCES; goto ErrorExit; } // reset pbCredsAndHmac pb = pbCredsAndHmac; CopyMemory(pb, &dwHmacLen, sizeof(DWORD)); pb += sizeof(DWORD); CopyMemory(pb, pbHmac, dwHmacLen); pb += dwHmacLen; CopyMemory(pb, CleartextData, CleartextDataSize); // re-encrypt CredsAndHmac fSts = CryptEncrypt( hRc4Key, NULL, TRUE, NULL, pbCredsAndHmac, &dwCredsAndHmacLen, // length of data dwEncryptedCredSize // length of buffer ); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } pb = EncryptedData; CopyMemory(pb, (PBYTE)psch_rcb, sizeof(ScHelper_RandomCredBits)); pb += sizeof(ScHelper_RandomCredBits); CopyMemory(pb, pbCredsAndHmac, dwCredsAndHmacLen); } else { lResult = STATUS_BUFFER_TOO_SMALL; } } else { lResult = STATUS_BUFFER_TOO_SMALL; } *EncryptedDataSize = dwEncryptedCredSize + sizeof(ScHelper_RandomCredBits); ErrorExit: // clean up! if (NULL != pbCredsAndHmac) { LocalFree(pbCredsAndHmac); } if (NULL != pbHmac) { LocalFree(pbHmac); } if (NULL != hRc4Key) { CryptDestroyKey(hRc4Key); } if (NULL != hHmacKey) { CryptDestroyKey(hHmacKey); } if (NULL != hProv) { CryptReleaseContext(hProv, NULL); } return lResult; } /*++ ScHelperSignMessage: ScHelperSignMessage() needs the logoninfo and PIN in order to find the card that will do the signing... Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: "success" or "failure" Author: Amanda Matlosz Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperSignMessage( IN PUNICODE_STRING pucPIN, IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN ULONG Algorithm, IN PBYTE Buffer, IN ULONG BufferLength, OUT PBYTE Signature, OUT PULONG SignatureLength ) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; HCRYPTPROV hProv = NULL; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CUnicodeString szPin(pucPIN); BOOL fSts; // // Make sure we've got a Crypto Provider up and running. // if (ARGUMENT_PRESENT(Provider)) { hProv = Provider; } else { if (NULL != pucPIN) { if (!szPin.Valid()) { return(STATUS_INSUFFICIENT_RESOURCES); } } CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } // // We'll need a hash handle, too. // fSts = CryptCreateHash( hProv, Algorithm, NULL, // HCRYPTKEY (used for keyed algs, like block ciphers 0, // reserved for future use &hHash); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Hash the input data. // fSts = CryptHashData( hHash, Buffer, BufferLength, 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } if (!ARGUMENT_PRESENT(Provider)) { // // Declare the PIN. // if (NULL != pucPIN) { fSts = CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, (LPBYTE)((LPCSTR)szPin), 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } } // // OK, sign it with the exchange key from the smart card or the supplied signature key. ???? // fSts = CryptSignHash( hHash, AT_KEYEXCHANGE, NULL, 0, Signature, SignatureLength); if (!fSts) { if (GetLastError() == ERROR_MORE_DATA) { lResult = STATUS_BUFFER_TOO_SMALL; } else { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } } // // All done, clean up and return. // ErrorExit: // Do this early so GetLastError is not clobbered if (!NT_SUCCESS(lResult)) { lResult = LogEvent( lResult, (DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_SIGNMSG_NOSC:EVENT_ID_SIGNMSG) ); } if (NULL != hHash) { CryptDestroyHash(hHash); } return lResult; } /*++ ScHelperVerifyMessage: // ScHelperVerifyMessage() returns STATUS_SUCCESS if the signature provided is // the hash of the buffer encrypted by the owner of the cert. Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: "success" or "failure" Author: Amanda Matlosz Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperVerifyMessage( IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN PCCERT_CONTEXT CertificateContext, IN ULONG Algorithm, IN PBYTE Buffer, IN ULONG BufferLength, IN PBYTE Signature, IN ULONG SignatureLength ) { HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; HCRYPTHASH hHash = NULL; PCERT_PUBLIC_KEY_INFO pInfo = NULL; BOOL fSts; NTSTATUS lResult = STATUS_SUCCESS; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; // // Make sure we've got a Crypto Provider up and running. // if (ARGUMENT_PRESENT(Provider)) { hProv = Provider; } else { CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } // // Convert the certificate handle into a Public Key handle. // fSts = CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &CertificateContext->pCertInfo->SubjectPublicKeyInfo, &hKey); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // We'll need a hash handle, too. // fSts = CryptCreateHash( hProv, Algorithm, NULL, // HCRYPTKEY (used for keyed algs, like block ciphers 0, // reserved for future use &hHash); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Hash the input data. // fSts = CryptHashData( hHash, Buffer, BufferLength, 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // So is this signature any good? // fSts = CryptVerifySignature( hHash, Signature, SignatureLength, hKey, NULL, 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // All done, clean up and return. // ErrorExit: // Do this early so GetLastError is not clobbered if (!NT_SUCCESS(lResult)) { lResult = LogEvent( lResult, (DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_VERIFYMSG_NOSC:EVENT_ID_VERIFYMSG) ); } if (NULL != hHash) { CryptDestroyHash(hHash); } if (NULL != hKey) { CryptDestroyKey(hKey); } return lResult; } /*++ ScHelperSignPkcsMessage: ScHelperSignMessage() needs the logoninfo and PIN in order to find the card that will do the signing... Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: "success" or "failure" Author: Amanda Matlosz Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperSignPkcsMessage( IN OPTIONAL PUNICODE_STRING pucPIN, IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN PCCERT_CONTEXT Certificate, IN PCRYPT_ALGORITHM_IDENTIFIER Algorithm, IN DWORD dwSignMessageFlags, IN PBYTE Buffer, IN ULONG BufferLength, OUT OPTIONAL PBYTE SignedBuffer, OUT OPTIONAL PULONG SignedBufferLength ) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; BOOL fSts; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CRYPT_SIGN_MESSAGE_PARA Parameter = {0}; CUnicodeString szPin(pucPIN); const BYTE * BufferArray = Buffer; if (NULL != pucPIN) { if (!szPin.Valid()) { return(STATUS_INSUFFICIENT_RESOURCES); } } // // Make sure we've got a Crypto Provider up and running. // if (ARGUMENT_PRESENT(Provider)) { hProv = Provider; } else { CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Declare the PIN. // if (NULL != pucPIN) { fSts = CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, (LPBYTE)((LPCSTR)szPin), 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } } // // Sign the message // Parameter.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA); Parameter.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; Parameter.pSigningCert = Certificate; Parameter.HashAlgorithm = *Algorithm; Parameter.cMsgCert = 1; Parameter.rgpMsgCert = &Certificate; Parameter.dwFlags = dwSignMessageFlags; fSts = CryptSignMessage( &Parameter, FALSE, // no detached signature 1, // one buffer to sign &BufferArray, &BufferLength, SignedBuffer, SignedBufferLength); if (!fSts) { if (GetLastError() == ERROR_MORE_DATA) { lResult = STATUS_BUFFER_TOO_SMALL; } else { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } goto ErrorExit; } // // All done, clean up and return. // ErrorExit: if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_SIGNMSG); } return lResult; } /*++ ScHelperVerifyPkcsMessage: // ScHelperVerifyMessage() returns STATUS_SUCCESS if the signature provided is // the hash of the buffer encrypted by the owner of the cert. Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: "success" or "failure" Author: Amanda Matlosz Note: Used by LSA. --*/ NTSTATUS WINAPI ScHelperVerifyPkcsMessage( IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN PBYTE Buffer, IN ULONG BufferLength, OUT OPTIONAL PBYTE DecodedBuffer, OUT OPTIONAL PULONG DecodedBufferLength, OUT OPTIONAL PCCERT_CONTEXT * CertificateContext ) { CRYPT_VERIFY_MESSAGE_PARA Parameter = {0}; BOOL fSts; NTSTATUS lResult = STATUS_SUCCESS; Parameter.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA); Parameter.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; Parameter.hCryptProv = NULL; // // Indicate that we want to get the certificate from the message // cert store. // Parameter.pfnGetSignerCertificate = NULL; fSts = CryptVerifyMessageSignature( &Parameter, 0, // only check first signer Buffer, BufferLength, DecodedBuffer, DecodedBufferLength, CertificateContext ); if (!fSts) { if (GetLastError() == ERROR_MORE_DATA) { lResult = STATUS_BUFFER_TOO_SMALL; } else { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } goto ErrorExit; } // // All done, clean up and return. // ErrorExit: if (!NT_SUCCESS(lResult)) { lResult = LogEvent(lResult, (DWORD)EVENT_ID_VERIFYMSG); } return lResult; } /*++ ScHelperEncryptMessage: Encrypts a message with the public key associated w/ the provided certificate. The resultant encoding is PKCS-7 compliant. Arguments: pucPIN may need the PIN to get a cert off certain SCs Return Value: "success" or "failure" Author: Amanda Matlosz (AMatlosz) 1-06-98 Note: Either pbLogonInfo or Provided must be set; if both are set, Provider is used. Algorithm expects a CRYPT_ALGORITHM_IDENTIFIER cai; If there are no parameters to the alg, cai.Parameters.cbData *must* be 0; CALG_RC4, no parameters: cai.pszObjId = szOID_RSA_RC4; cai.Parameters.cbData = 0; Used by LSA. --*/ NTSTATUS WINAPI ScHelperEncryptMessage( IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN PCCERT_CONTEXT CertificateContext, IN PCRYPT_ALGORITHM_IDENTIFIER Algorithm, IN PBYTE Buffer, // The data to encrypt IN ULONG BufferLength, // The length of that data OUT PBYTE CipherText, // Receives the formatted CipherText IN PULONG pCipherLength // Supplies size of CipherText buffer ) // Receives length of actual CipherText { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; BOOL fSts; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara; DWORD cbEncryptParaSize = 0; // // Make sure we've got a Crypto Provider up and running. // if (ARGUMENT_PRESENT(Provider)) { hProv = Provider; } else { CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } // // Encrypt the message // cbEncryptParaSize = sizeof(EncryptPara); memset(&EncryptPara, 0, cbEncryptParaSize); EncryptPara.cbSize = cbEncryptParaSize; EncryptPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; EncryptPara.hCryptProv = hProv; EncryptPara.ContentEncryptionAlgorithm = *Algorithm; fSts = CryptEncryptMessage( &EncryptPara, 1, &CertificateContext, Buffer, BufferLength, CipherText, pCipherLength); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } ErrorExit: if (!NT_SUCCESS(lResult)) { lResult = LogEvent( lResult, (DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_ENCMSG_NOSC:EVENT_ID_ENCMSG) ); } return lResult; } /*++ ScHelperDecryptMessage : Deciphers a PKCS-7 encoded message with the private key associated w/ the provided certificate. Arguments: Either pbLogonInfo or Provider must be set; if both are set, Provider is used. Return Value: "success" or "failure" Author: Amanda Matlosz (AMatlosz) 1-06-98 Note: ** CertificateContext subtleties: ** CryptDecryptMessage takes as a parameter a pointer to a certificate store; it will use the first appropriate certificate context it finds in that store to perform the decryption. In order to make this call, we create a CertificateStore in memory, and add the provided CertificateContext to it. CertAddCertificateContextToStore actually places a copy of the certificate context in the store. In so doing, it strips off any properties that are not permanent -- if a HCRYPTPROV is associated with the KeyContext of the source CertificateContext, it will NOT be associated with the KeyContext of the cert context in the store. Although this is appropriate behavior in most cases, we need that property to be kept intact when dealing with Smart Card CSPs (to avoid surprise "Insert PIN" dialogs), so after adding the CertificateContext to the store, we turn around and get the CERT_KEY_CONTEXT_PROP_ID from the source certcontext and (re)set it on the certcontext in the memory store. ** Algorithm notes: ** Algorithm expects a CRYPT_ALGORITHM_IDENTIFIER cai; If there are no parameters to the alg, cai.Parameters.cbData *must* be 0; for example: CALG_RC4, no parameters: cai.pszObjId = szOID_RSA_RC4; cai.Parameters.cbData = 0; Used by LSA. --*/ NTSTATUS WINAPI ScHelperDecryptMessage( IN PUNICODE_STRING pucPIN, IN OPTIONAL PBYTE pbLogonInfo, IN OPTIONAL HCRYPTPROV Provider, IN PCCERT_CONTEXT CertificateContext, IN PBYTE CipherText, // Supplies formatted CipherText IN ULONG CipherLength, // Supplies the length of the CiperText OUT PBYTE ClearText, // Receives decrypted message IN OUT PULONG pClearLength // Supplies length of buffer, receives actual length ) { NTSTATUS lResult = STATUS_SUCCESS; HCRYPTPROV hProv = NULL; PCCERT_CONTEXT pStoreCertContext = NULL; HCERTSTORE hCertStore = NULL; LogonInfo *pLI = (LogonInfo *)pbLogonInfo; CUnicodeString szPin(pucPIN); CERT_KEY_CONTEXT CertKeyContext; DWORD cbData = sizeof(CERT_KEY_CONTEXT); // PhilH swears this will not grow! BOOL fSts; // // Make sure we've got a Crypto Provider up and running. // if (ARGUMENT_PRESENT(Provider)) { hProv = Provider; } else { if (NULL != pucPIN) { if (!szPin.Valid()) { return(STATUS_INSUFFICIENT_RESOURCES); } } CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation; hProv = LogonInit->CryptCtx(pLI); if (NULL == hProv) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } // // Declare the PIN. // if (NULL != pucPIN ) { fSts = CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, (LPBYTE)((LPCSTR)szPin), 0); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } } // // Open a temporary certstore to hold this certcontext // hCertStore = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, // not applicable hProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, // auto-release hProv NOT OK NULL); if (NULL == hCertStore) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } fSts = CertAddCertificateContextToStore( hCertStore, CertificateContext, CERT_STORE_ADD_ALWAYS, &pStoreCertContext); // // NOW WE NEED TO RESET THE KEY CONTEXT PROPERTY ON THIS CERTCONTEXT // IN THE MEMORY STORE (see function header/notes) AS APPROPRIATE // // ie, IFF the certcontext we were give has the key_context property, // reset it (and fail if the resetting doesn't work) // fSts = CertGetCertificateContextProperty( CertificateContext, CERT_KEY_CONTEXT_PROP_ID, (void *)&CertKeyContext, &cbData); if (TRUE == fSts) { fSts = CertSetCertificateContextProperty( pStoreCertContext, CERT_KEY_CONTEXT_PROP_ID, CERT_STORE_NO_CRYPT_RELEASE_FLAG, // no auto-release hProv! (void *)&CertKeyContext); if (!fSts) { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; goto ErrorExit; } } // // Decrypt the message // CRYPT_DECRYPT_MESSAGE_PARA DecryptPara; DecryptPara.cbSize = sizeof(DecryptPara); DecryptPara.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; DecryptPara.cCertStore = 1; DecryptPara.rghCertStore = &hCertStore; fSts = CryptDecryptMessage( &DecryptPara, CipherText, CipherLength, ClearText, pClearLength, NULL); if (!fSts) { if (GetLastError() == ERROR_MORE_DATA) { lResult = STATUS_BUFFER_TOO_SMALL; } else { lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } goto ErrorExit; } ErrorExit: // Do this early so GetLastError is not clobbered if (!NT_SUCCESS(lResult)) { lResult = LogEvent( lResult, (DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_DECMSG_NOSC:EVENT_ID_DECMSG) ); } if (hCertStore != NULL) { fSts = CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG); if (!fSts) { if (!NT_SUCCESS(lResult)) lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE; } } return lResult; }