//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1999 // // File: events.c // // Contents: Schannel event log functions. // // Functions: SchInitializeEvents // SchReportEvent // SchShutdownEvents // // History: 03-05-99 jbanes Created // //------------------------------------------------------------------------ #include "sslp.h" #include #include HANDLE g_hEventLog = NULL; HANDLE g_hDiscardDupEventLog = NULL; WCHAR EventSourceName[] = TEXT("Schannel"); #define MAX_EVENT_STRINGS 8 #define SCH_MESSAGE_FILENAME TEXT("%SystemRoot%\\system32\\lsasrv.dll") LPWSTR pszClientString = NULL; LPWSTR pszServerString = NULL; NTSTATUS SchGetMessageString( LPVOID Resource, DWORD Index, LPWSTR * pRetString); //+--------------------------------------------------------------------------- // // Function: SchInitializeEvents // // Synopsis: Connects to event log service. // // Arguments: (none) // // History: 03-05-99 jbanes Created // // Notes: // //---------------------------------------------------------------------------- BOOL SchInitializeEvents(void) { HKEY hKey; int err; DWORD disp; HMODULE hResource; // // Create registry entries, whether event logging is currently // enabled or not. // err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\EventLog\\System\\Schannel"), 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &disp); if(err) { return(FALSE); } if (disp == REG_CREATED_NEW_KEY) { RegSetValueEx( hKey, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (PBYTE)SCH_MESSAGE_FILENAME, sizeof(SCH_MESSAGE_FILENAME) ); // RegSetValueEx( hKey, // TEXT("CategoryMessageFile"), // 0, // REG_EXPAND_SZ, // (PBYTE)SCH_MESSAGE_FILENAME, // sizeof(SCH_MESSAGE_FILENAME) ); disp = 7; RegSetValueEx( hKey, TEXT("TypesSupported"), 0, REG_DWORD, (PBYTE) &disp, sizeof(DWORD) ); // disp = CATEGORY_MAX_CATEGORY - 1; // RegSetValueEx( hKey, // TEXT("CategoryCount"), // 0, // REG_DWORD, // (PBYTE) &disp, // sizeof(DWORD) ); RegFlushKey(hKey); } RegCloseKey(hKey); // // Read the event text strings from the resource file. // hResource = (HMODULE)LoadLibrary(TEXT("lsasrv.dll")); if(hResource == NULL) { return(FALSE); } SchGetMessageString(hResource, SSLEVENTTEXT_CLIENT, &pszClientString); SchGetMessageString(hResource, SSLEVENTTEXT_SERVER, &pszServerString); FreeLibrary(hResource); return(TRUE); } //+--------------------------------------------------------------------------- // // Function: SchReportEvent // // Synopsis: Reports an event to the event log // // Arguments: [EventType] -- EventType (ERROR, WARNING, etc.) // [EventId] -- Event ID // [SizeOfRawData] -- Size of raw data // [RawData] -- Raw data // [NumberOfStrings] -- number of strings // ... -- PWSTRs to string data // // History: 03-05-99 jbanes Created // // Notes: // //---------------------------------------------------------------------------- DWORD SchReportEvent( IN DWORD LogLevel, IN DWORD EventType, IN DWORD EventId, IN DWORD Category, IN DWORD SizeOfRawData, IN PVOID RawData, IN DWORD NumberOfStrings, ... ) { va_list arglist; ULONG i; PWSTR Strings[ MAX_EVENT_STRINGS ]; PSTR StringsA[ MAX_EVENT_STRINGS ]; DWORD Status; BOOL fDiscardDuplicates = TRUE; BOOL fSuccess; // // Is this event supposed to be logged? // if ((g_dwEventLogging & LogLevel) == 0) { return ERROR_SUCCESS; } // // Open the event log if necessary. // if(g_dwEventLogging == DEFAULT_EVENT_LOGGING_SETTING) { // Only log identical event once per hour. if(g_hDiscardDupEventLog == NULL) { g_hDiscardDupEventLog = NetpEventlogOpen(EventSourceName, 60000*60); if(g_hDiscardDupEventLog == NULL) { Status = GetLastError(); DebugLog((DEB_ERROR, "Could not open duplicate discard event log, error %d\n", Status)); return Status; } } } else { // Log all events. if(g_hEventLog == NULL) { g_hEventLog = RegisterEventSource(NULL, EventSourceName); if(g_hEventLog == NULL) { Status = GetLastError(); DebugLog((DEB_ERROR, "Could not open duplicate discard event log, error %d\n", Status)); return Status; } } fDiscardDuplicates = FALSE; } // // Look at the strings, if they were provided // va_start( arglist, NumberOfStrings ); if (NumberOfStrings > MAX_EVENT_STRINGS) { NumberOfStrings = MAX_EVENT_STRINGS; } for (i=0; idwProvType, wszType, 10); _ltow(pProvInfo->dwFlags, wszFlags, 16); switch(pProvInfo->dwKeySpec) { case AT_KEYEXCHANGE: pwszKeySpec = L"key exchange"; break; case AT_SIGNATURE: pwszKeySpec = L"signature"; break; default: pwszKeySpec = L"unknown"; } SchReportEvent( DEB_TRACE, EVENTLOG_INFORMATION_TYPE, SSLEVENT_CRED_PROPERTIES, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 6, (dwProtocol & SP_PROT_SERVERS) ? pszServerString : pszClientString, pProvInfo->pwszProvName, wszType, pProvInfo->pwszContainerName, pwszKeySpec, wszFlags); } void LogNoPrivateKeyEvent( DWORD dwProtocol) { SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_NO_PRIVATE_KEY, 0, 0, NULL, 1, (dwProtocol & SP_PROT_SERVERS) ? pszServerString : pszClientString); } void LogCredAcquireContextFailedEvent( DWORD dwProtocol, DWORD Status) { WCHAR wszStatus[20]; _ltow(Status, wszStatus, 16); SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_CRED_ACQUIRE_CONTEXT_FAILED, 0, 0, NULL, 2, (dwProtocol & SP_PROT_SERVERS) ? pszServerString : pszClientString, wszStatus); } void LogCreateCredFailedEvent( DWORD dwProtocol) { SchReportEvent(DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_CREATE_CRED_FAILED, 0, 0, NULL, 1, (dwProtocol & SP_PROT_SERVERS) ? pszServerString : pszClientString); } void LogNoDefaultServerCredEvent(void) { SchReportEvent(DEB_ERROR, EVENTLOG_WARNING_TYPE, SSLEVENT_NO_DEFAULT_SERVER_CRED, 0, 0, NULL, 0, NULL); } void LogNoCiphersSupportedEvent(void) { SchReportEvent(DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_NO_CIPHERS_SUPPORTED, 0, 0, NULL, 0, NULL); } void LogCipherMismatchEvent(void) { SchReportEvent(DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_CIPHER_MISMATCH, 0, 0, NULL, 0, NULL); } void LogNoClientCertFoundEvent(void) { SchReportEvent(DEB_WARN, EVENTLOG_WARNING_TYPE, SSLEVENT_NO_CLIENT_CERT_FOUND, 0, 0, NULL, 0, NULL); } void LogBogusServerCertEvent( PCCERT_CONTEXT pCertContext, LPWSTR pwszServerName, DWORD Status) { WCHAR wszStatus[20]; if(!(g_dwEventLogging & DEB_ERROR)) { return; } switch(Status) { case SEC_E_CERT_EXPIRED: SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_EXPIRED_SERVER_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 0, NULL); break; case SEC_E_UNTRUSTED_ROOT: SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_UNTRUSTED_SERVER_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 0, NULL); break; case CRYPT_E_REVOKED: SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_REVOKED_SERVER_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 0, NULL); break; case SEC_E_WRONG_PRINCIPAL: SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_NAME_MISMATCHED_SERVER_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 1, pwszServerName); break; default: _ltow(Status, wszStatus, 16); SchReportEvent( DEB_ERROR, EVENTLOG_ERROR_TYPE, SSLEVENT_BOGUS_SERVER_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 1, wszStatus); } } void LogBogusClientCertEvent( PCCERT_CONTEXT pCertContext, DWORD Status) { WCHAR wszStatus[20]; if(!(g_dwEventLogging & DEB_WARN)) { return; } _ltow(Status, wszStatus, 16); SchReportEvent( DEB_WARN, EVENTLOG_WARNING_TYPE, SSLEVENT_BOGUS_CLIENT_CERT, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 1, wszStatus); } void LogFastMappingFailureEvent( PCCERT_CONTEXT pCertContext, DWORD Status) { WCHAR wszStatus[20]; if(!(g_dwEventLogging & DEB_WARN)) { return; } _ltow(Status, wszStatus, 16); SchReportEvent( DEB_WARN, EVENTLOG_WARNING_TYPE, SSLEVENT_FAST_MAPPING_FAILURE, 0, pCertContext->cbCertEncoded, pCertContext->pbCertEncoded, 1, wszStatus); } void LogCertMappingFailureEvent( DWORD Status) { WCHAR wszStatus[20]; if(!(g_dwEventLogging & DEB_WARN)) { return; } _ltow(Status, wszStatus, 16); SchReportEvent( DEB_WARN, EVENTLOG_WARNING_TYPE, SSLEVENT_CERT_MAPPING_FAILURE, 0, 0, NULL, 1, wszStatus); } void LogHandshakeInfoEvent( DWORD dwProtocol, PCipherInfo pCipherInfo, PHashInfo pHashInfo, PKeyExchangeInfo pExchangeInfo, DWORD dwExchangeStrength) { WCHAR wszCipherStrength[20]; WCHAR wszExchangeStrength[20]; LPWSTR pwszProtocol; LPWSTR pwszCipher; LPWSTR pwszHash; LPWSTR pwszExchange; if(!(g_dwEventLogging & DEB_TRACE)) { return; } switch(dwProtocol) { case SP_PROT_PCT1_SERVER: case SP_PROT_PCT1_CLIENT: pwszProtocol = L"PCT"; break; case SP_PROT_SSL2_SERVER: case SP_PROT_SSL2_CLIENT: pwszProtocol = L"SSL 2.0"; break; case SP_PROT_SSL3_SERVER: case SP_PROT_SSL3_CLIENT: pwszProtocol = L"SSL 3.0"; break; case SP_PROT_TLS1_SERVER: case SP_PROT_TLS1_CLIENT: pwszProtocol = L"TLS (SSL 3.1)"; break; default: pwszProtocol = L"unknown"; } switch(pCipherInfo->aiCipher) { case CALG_RC4: pwszCipher = L"RC4"; break; case CALG_3DES: pwszCipher = L"Triple-DES"; break; case CALG_RC2: pwszCipher = L"RC2"; break; case CALG_DES: pwszCipher = L"DES"; break; case CALG_SKIPJACK: pwszCipher = L"Skipjack"; break; default: pwszCipher = L"unknown"; } _ltow(pCipherInfo->dwStrength, wszCipherStrength, 10); switch(pHashInfo->aiHash) { case CALG_MD5: pwszHash = L"MD5"; break; case CALG_SHA: pwszHash = L"SHA"; break; default: pwszHash = L"unknown"; } switch(pExchangeInfo->aiExch) { case CALG_RSA_SIGN: case CALG_RSA_KEYX: pwszExchange = L"RSA"; break; case CALG_KEA_KEYX: pwszExchange = L"KEA"; break; case CALG_DH_EPHEM: pwszExchange = L"Ephemeral DH"; break; default: pwszExchange = L"unknown"; } _ltow(dwExchangeStrength, wszExchangeStrength, 10); SchReportEvent( DEB_TRACE, EVENTLOG_INFORMATION_TYPE, SSLEVENT_HANDSHAKE_INFO, 0, 0, NULL, 7, (dwProtocol & SP_PROT_SERVERS) ? pszServerString : pszClientString, pwszProtocol, pwszCipher, wszCipherStrength, pwszHash, pwszExchange, wszExchangeStrength); } NTSTATUS SchGetMessageString( LPVOID Resource, DWORD Index, LPWSTR * pRetString) { DWORD Length; *pRetString = NULL; Length = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, Resource, Index, 0, // Use caller's language (LPWSTR)pRetString, 0, NULL); if(Length == 0 || *pRetString == NULL) { return(STATUS_RESOURCE_DATA_NOT_FOUND); } // // Note that we are retrieving a message from a message file. // This message will have a cr/lf tacked on the end of it // (0x0d 0x0a) that we don't want to be part of our returned // strings. However, we do need to null terminate our string // so we will convert the 0x0d into a null terminator. // // Also note that FormatMessage() returns a character count, // not a byte count. So, we have to do some adjusting to make // the string lengths correct. // ASSERT(Length >= 2); // We always expect cr/lf on our strings // // Adjust character count // Length -= 1; // For the lf - we'll convert the cr. // // Set null terminator // (*pRetString)[Length - 1] = 0; return(STATUS_SUCCESS); }