//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: spreg.c // // Contents: Schannel registry management routines. // // Classes: // // Functions: // // History: 11-24-97 jbanes Enabled TLS // //---------------------------------------------------------------------------- #include #include "spreg.h" #include #include HKEY g_hkBase = NULL; HANDLE g_hParamEvent = NULL; HANDLE g_hWait = NULL; HKEY g_hkFipsBase = NULL; HANDLE g_hFipsParamEvent = NULL; HANDLE g_hFipsWait = NULL; BOOL g_fManualCredValidation = MANUAL_CRED_VALIDATION_SETTING; BOOL g_PctClientDisabledByDefault = PCT_CLIENT_DISABLED_SETTING; BOOL g_Ssl2ClientDisabledByDefault = SSL2_CLIENT_DISABLED_SETTING; DWORD g_dwEventLogging = DEFAULT_EVENT_LOGGING_SETTING; DWORD g_ProtEnabled = DEFAULT_ENABLED_PROTOCOLS_SETTING; BOOL g_fSendIssuerList = TRUE; DWORD g_dwCertMappingMethods = DEFAULT_CERTMAP_SETTING; BOOL g_fFipsMode = FALSE; BOOL g_fFranceLocale = FALSE; BOOL g_SslS4U2SelfInitialized = FALSE; typedef struct enamap { TCHAR *pKey; DWORD Mask; } enamap; enamap g_ProtMap[] = { {SP_REG_KEY_PCT1 TEXT("\\") SP_REG_KEY_CLIENT, SP_PROT_PCT1_CLIENT}, {SP_REG_KEY_PCT1 TEXT("\\") SP_REG_KEY_SERVER, SP_PROT_PCT1_SERVER}, {SP_REG_KEY_SSL2 TEXT("\\") SP_REG_KEY_CLIENT, SP_PROT_SSL2_CLIENT}, {SP_REG_KEY_SSL2 TEXT("\\") SP_REG_KEY_SERVER, SP_PROT_SSL2_SERVER}, {SP_REG_KEY_SSL3 TEXT("\\") SP_REG_KEY_CLIENT, SP_PROT_SSL3_CLIENT}, {SP_REG_KEY_SSL3 TEXT("\\") SP_REG_KEY_SERVER, SP_PROT_SSL3_SERVER}, {SP_REG_KEY_TLS1 TEXT("\\") SP_REG_KEY_CLIENT, SP_PROT_TLS1_CLIENT}, {SP_REG_KEY_TLS1 TEXT("\\") SP_REG_KEY_SERVER, SP_PROT_TLS1_SERVER} }; VOID SslWatchParamKey( PVOID pCtxt, BOOLEAN fWaitStatus); VOID FipsWatchParamKey( PVOID pCtxt, BOOLEAN fWaitStatus); BOOL SslReadRegOptions( BOOL fFirstTime); BOOL SPLoadRegOptions(void) { g_hParamEvent = CreateEvent(NULL, FALSE, FALSE, NULL); SslWatchParamKey(g_hParamEvent, FALSE); g_hFipsParamEvent = CreateEvent(NULL, FALSE, FALSE, NULL); FipsWatchParamKey(g_hFipsParamEvent, FALSE); return TRUE; } void SPUnloadRegOptions(void) { if (NULL != g_hWait) { RtlDeregisterWait(g_hWait); g_hWait = NULL; } if(NULL != g_hkBase) { RegCloseKey(g_hkBase); } if(NULL != g_hParamEvent) { CloseHandle(g_hParamEvent); } if (NULL != g_hFipsWait) { RtlDeregisterWait(g_hFipsWait); g_hFipsWait = NULL; } if(NULL != g_hkFipsBase) { RegCloseKey(g_hkFipsBase); } if(NULL != g_hFipsParamEvent) { CloseHandle(g_hFipsParamEvent); } } BOOL ReadRegistrySetting( HKEY hReadKey, HKEY hWriteKey, LPCTSTR pszValueName, DWORD * pdwValue, DWORD dwDefaultValue) { DWORD dwSize; DWORD dwType; DWORD dwOriginalValue = *pdwValue; dwSize = sizeof(DWORD); if(RegQueryValueEx(hReadKey, pszValueName, NULL, &dwType, (PUCHAR)pdwValue, &dwSize) != STATUS_SUCCESS) { *pdwValue = dwDefaultValue; if(hWriteKey) { RegSetValueEx(hWriteKey, pszValueName, 0, REG_DWORD, (PUCHAR)pdwValue, sizeof(DWORD)); } } return (dwOriginalValue != *pdwValue); } //////////////////////////////////////////////////////////////////// // // Name: SslWatchParamKey // // Synopsis: Sets RegNotifyChangeKeyValue() on param key, initializes // debug level, then utilizes thread pool to wait on // changes to this registry key. Enables dynamic debug // level changes, as this function will also be callback // if registry key modified. // // Arguments: pCtxt is actually a HANDLE to an event. This event // will be triggered when key is modified. // // Notes: . // VOID SslWatchParamKey( PVOID pCtxt, BOOLEAN fWaitStatus) { NTSTATUS Status; LONG lRes = ERROR_SUCCESS; BOOL fFirstTime = FALSE; DWORD disp; UNREFERENCED_PARAMETER(fWaitStatus); if(g_hkBase == NULL) { // First time we've been called. Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SP_REG_KEY_BASE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &g_hkBase, &disp); if(Status) { DebugLog((DEB_WARN,"Failed to open SCHANNEL key: 0x%x\n", Status)); return; } fFirstTime = TRUE; } if(pCtxt != NULL) { if (NULL != g_hWait) { Status = RtlDeregisterWait(g_hWait); if(!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status)); goto Reregister; } } lRes = RegNotifyChangeKeyValue( g_hkBase, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, (HANDLE)pCtxt, TRUE); if (ERROR_SUCCESS != lRes) { DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes)); // we're tanked now. No further notifications, so get this one } } SslReadRegOptions(fFirstTime); #if DBG InitDebugSupport(g_hkBase); #endif Reregister: if(pCtxt != NULL) { Status = RtlRegisterWait(&g_hWait, (HANDLE)pCtxt, SslWatchParamKey, (HANDLE)pCtxt, INFINITE, WT_EXECUTEINPERSISTENTIOTHREAD| WT_EXECUTEONLYONCE); } } //////////////////////////////////////////////////////////////////// // // Name: FipsWatchParamKey // // Synopsis: Sets RegNotifyChangeKeyValue() on param key, initializes // debug level, then utilizes thread pool to wait on // changes to this registry key. Enables dynamic debug // level changes, as this function will also be callback // if registry key modified. // // Arguments: pCtxt is actually a HANDLE to an event. This event // will be triggered when key is modified. // // Notes: . // VOID FipsWatchParamKey( PVOID pCtxt, BOOLEAN fWaitStatus) { NTSTATUS Status; LONG lRes = ERROR_SUCCESS; DWORD disp; UNREFERENCED_PARAMETER(fWaitStatus); if(g_hkFipsBase == NULL) { // First time we've been called. Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SP_REG_FIPS_BASE_KEY, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &g_hkFipsBase, &disp); if(Status) { DebugLog((DEB_WARN,"Failed to open FIPS key: 0x%x\n", Status)); return; } } if(pCtxt != NULL) { if (NULL != g_hFipsWait) { Status = RtlDeregisterWait(g_hFipsWait); if(!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status)); goto Reregister; } } lRes = RegNotifyChangeKeyValue( g_hkFipsBase, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, (HANDLE)pCtxt, TRUE); if (ERROR_SUCCESS != lRes) { DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes)); // we're tanked now. No further notifications, so get this one } } SslReadRegOptions(FALSE); Reregister: if(pCtxt != NULL) { Status = RtlRegisterWait(&g_hFipsWait, (HANDLE)pCtxt, FipsWatchParamKey, (HANDLE)pCtxt, INFINITE, WT_EXECUTEINPERSISTENTIOTHREAD| WT_EXECUTEONLYONCE); } } BOOL SslReadRegOptions( BOOL fFirstTime) { DWORD err; DWORD dwType; DWORD fVal; DWORD dwSize; HKEY hKey; HKEY hWriteKey; DWORD disp; DWORD i; HKEY hkProtocols = NULL; HKEY hkCiphers = NULL; HKEY hkHashes = NULL; HKEY hkKeyExch = NULL; DWORD dwSetting = 0; BOOL fSettingsChanged = FALSE; DWORD dwOriginalValue; DebugLog((DEB_TRACE,"Load configuration parameters from registry.\n")); // "FipsAlgorithmPolicy" ReadRegistrySetting( g_hkFipsBase, 0, SP_REG_FIPS_POLICY, &dwSetting, 0); if((dwSetting == 1) != g_fFipsMode) { g_fFipsMode = (dwSetting == 1); fSettingsChanged = TRUE; } // // Read top-level configuration options. // // Open top-level key that has write access. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, SP_REG_KEY_BASE, 0, KEY_READ | KEY_SET_VALUE, &hWriteKey) != STATUS_SUCCESS) { hWriteKey = 0; } // "EventLogging" if(ReadRegistrySetting( g_hkBase, hWriteKey, SP_REG_VAL_EVENTLOG, &g_dwEventLogging, DEFAULT_EVENT_LOGGING_SETTING)) { fSettingsChanged = TRUE; } // "ManualCredValidation" ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_MANUAL_CRED_VALIDATION, &dwSetting, MANUAL_CRED_VALIDATION_SETTING); if((dwSetting != 0) != g_fManualCredValidation) { g_fManualCredValidation = (dwSetting != 0); fSettingsChanged = TRUE; } // "ClientCacheTime" if(ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_CLIENT_CACHE_TIME, &SchannelCache.dwClientLifespan, SP_CACHE_CLIENT_LIFESPAN)) { fSettingsChanged = TRUE; } // "ServerCacheTime" if(ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_SERVER_CACHE_TIME, &SchannelCache.dwServerLifespan, SP_CACHE_SERVER_LIFESPAN)) { fSettingsChanged = TRUE; } // "MaximumCacheSize" if(ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_MAXUMUM_CACHE_SIZE, &SchannelCache.dwMaximumEntries, SP_MAXIMUM_CACHE_ELEMENTS)) { fSettingsChanged = TRUE; } if(fFirstTime) { SchannelCache.dwCacheSize = SchannelCache.dwMaximumEntries; } // "MultipleProcessClientCache" ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_MULTI_PROC_CLIENT_CACHE, &dwSetting, FALSE); if((dwSetting != 0) != g_fMultipleProcessClientCache) { g_fMultipleProcessClientCache = (dwSetting != 0); fSettingsChanged = TRUE; } // "SendTrustedIssuerList" ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_SEND_ISSUER_LIST, &dwSetting, TRUE); if((dwSetting != 0) != g_fSendIssuerList) { g_fSendIssuerList = (dwSetting != 0); fSettingsChanged = TRUE; } // "CertificateMappingMethods" if(ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_CERT_MAPPING_METHODS, &g_dwCertMappingMethods, DEFAULT_CERTMAP_SETTING)) { fSettingsChanged = TRUE; if((g_dwCertMappingMethods & SP_REG_CERTMAP_SUBJECT_FLAG) == 0) { DebugLog((DEB_TRACE, "Subject/Issuer certificate mapping disabled\n")); } if((g_dwCertMappingMethods & SP_REG_CERTMAP_ISSUER_FLAG) == 0) { DebugLog((DEB_TRACE, "Issuer certificate mapping disabled\n")); } if((g_dwCertMappingMethods & SP_REG_CERTMAP_UPN_FLAG) == 0) { DebugLog((DEB_TRACE, "UPN certificate mapping disabled\n")); } if((g_dwCertMappingMethods & SP_REG_CERTMAP_S4U2SELF_FLAG) == 0) { DebugLog((DEB_TRACE, "S4U2Self certificate mapping disabled\n")); } } if(hWriteKey) { RegCloseKey(hWriteKey); hWriteKey = 0; } // "IssuerCacheTime" ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_ISSUER_CACHE_TIME, &IssuerCache.dwLifespan, ISSUER_CACHE_LIFESPAN); // "IssuerCacheSize" ReadRegistrySetting( g_hkBase, 0, SP_REG_VAL_ISSUER_CACHE_SIZE, &IssuerCache.dwMaximumEntries, ISSUER_CACHE_SIZE); if(fFirstTime) { IssuerCache.dwCacheSize = IssuerCache.dwMaximumEntries; } #ifdef ROGUE_DC if(g_hSslRogueKey == NULL) { if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, SP_REG_ROGUE_BASE_KEY, 0, KEY_READ, &g_hSslRogueKey) != STATUS_SUCCESS) { DebugLog((DEB_WARN,"Failed to open \"rogue\" ssl key\n" )); g_hSslRogueKey = NULL; } } #endif // // Enable/Disable Protocols // if(g_fFipsMode) { // Disable everything except TLS. g_ProtEnabled = SP_PROT_TLS1; } else { DWORD dwProtEnabled = DEFAULT_ENABLED_PROTOCOLS_SETTING; err = RegCreateKeyEx( g_hkBase, SP_REG_KEY_PROTOCOL, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkProtocols, &disp); if(err == ERROR_SUCCESS) { for(i=0; i < (sizeof(g_ProtMap)/sizeof(enamap)); i++) { if(g_ProtMap[i].Mask & SP_PROT_PCT1) { if(g_fFranceLocale) { // Don't allow PCT to be enabled in France. continue; } } err = RegCreateKeyEx( hkProtocols, g_ProtMap[i].pKey, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &disp); if(!err) { dwSize = sizeof(DWORD); err = RegQueryValueEx(hKey, SP_REG_VAL_ENABLED, NULL, &dwType, (PUCHAR)&fVal, &dwSize); if(!err) { if(fVal) { dwProtEnabled |= g_ProtMap[i].Mask; } else { dwProtEnabled &= ~g_ProtMap[i].Mask; } } if(g_ProtMap[i].Mask & SP_PROT_PCT1_CLIENT) { // "DisabledByDefault" ReadRegistrySetting( hKey, 0, SP_REG_VAL_DISABLED_BY_DEFAULT, &dwSetting, PCT_CLIENT_DISABLED_SETTING); g_PctClientDisabledByDefault = (dwSetting != 0); } if(g_ProtMap[i].Mask & SP_PROT_SSL2_CLIENT) { // "DisabledByDefault" ReadRegistrySetting( hKey, 0, SP_REG_VAL_DISABLED_BY_DEFAULT, &dwSetting, SSL2_CLIENT_DISABLED_SETTING); g_Ssl2ClientDisabledByDefault = (dwSetting != 0); } RegCloseKey(hKey); } } RegCloseKey(hkProtocols); } if(g_ProtEnabled != dwProtEnabled) { g_ProtEnabled = dwProtEnabled; fSettingsChanged = TRUE; } } // // Enable/Disable Ciphers // if(g_fFipsMode) { // Disable everything except 3DES. for(i=0; i < g_cAvailableCiphers; i++) { if(g_AvailableCiphers[i].aiCipher != CALG_3DES) { g_AvailableCiphers[i].fProtocol = 0; } } } else { err = RegCreateKeyEx( g_hkBase, SP_REG_KEY_CIPHERS, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkCiphers, &disp); if(err == ERROR_SUCCESS) { for(i=0; i < g_cAvailableCiphers; i++) { dwOriginalValue = g_AvailableCiphers[i].fProtocol; g_AvailableCiphers[i].fProtocol = g_AvailableCiphers[i].fDefault; fVal = g_AvailableCiphers[i].fDefault; err = RegCreateKeyExA( hkCiphers, g_AvailableCiphers[i].szName, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &disp); if(!err) { dwSize = sizeof(DWORD); err = RegQueryValueEx(hKey, SP_REG_VAL_ENABLED, NULL, &dwType, (PUCHAR)&fVal, &dwSize); if(err) { fVal = g_AvailableCiphers[i].fDefault; } RegCloseKey(hKey); } g_AvailableCiphers[i].fProtocol &= fVal; if(g_AvailableCiphers[i].fProtocol != dwOriginalValue) { fSettingsChanged = TRUE; } } RegCloseKey(hkCiphers); } } // // Enable/Disable Hashes // err = RegCreateKeyEx( g_hkBase, SP_REG_KEY_HASHES, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkHashes, &disp); if(err == ERROR_SUCCESS) { for(i = 0; i < g_cAvailableHashes; i++) { dwOriginalValue = g_AvailableHashes[i].fProtocol; g_AvailableHashes[i].fProtocol = g_AvailableHashes[i].fDefault; fVal = g_AvailableHashes[i].fDefault; err = RegCreateKeyExA( hkHashes, g_AvailableHashes[i].szName, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &disp); if(!err) { dwSize = sizeof(DWORD); err = RegQueryValueEx(hKey, SP_REG_VAL_ENABLED, NULL, &dwType, (PUCHAR)&fVal, &dwSize); if(err) { fVal = g_AvailableHashes[i].fDefault; } RegCloseKey(hKey); } g_AvailableHashes[i].fProtocol &= fVal; if(dwOriginalValue != g_AvailableHashes[i].fProtocol) { fSettingsChanged = TRUE; } } RegCloseKey(hkHashes); } // // Enable/Disable Key Exchange algs. // if(g_fFipsMode) { // Disable everything except RSA. for(i=0; i < g_cAvailableExch; i++) { if(g_AvailableExch[i].aiExch != CALG_RSA_KEYX && g_AvailableExch[i].aiExch != CALG_RSA_SIGN) { g_AvailableExch[i].fProtocol = 0; } } } else { err = RegCreateKeyEx( g_hkBase, SP_REG_KEY_KEYEXCH, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkKeyExch, &disp); if(err == ERROR_SUCCESS) { for(i = 0; i < g_cAvailableExch; i++) { dwOriginalValue = g_AvailableExch[i].fProtocol; g_AvailableExch[i].fProtocol = g_AvailableExch[i].fDefault; fVal = g_AvailableExch[i].fDefault; err = RegCreateKeyExA( hkKeyExch, g_AvailableExch[i].szName, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &disp); if(!err) { dwSize = sizeof(DWORD); err = RegQueryValueEx(hKey, SP_REG_VAL_ENABLED, NULL, &dwType, (PUCHAR)&fVal, &dwSize); if(err) { fVal = g_AvailableExch[i].fDefault; } g_AvailableExch[i].fProtocol &= fVal; RegCloseKey(hKey); } if(dwOriginalValue != g_AvailableExch[i].fProtocol) { fSettingsChanged = TRUE; } } RegCloseKey(hkKeyExch); } } // // Purge the session cache. // if(g_fCacheInitialized && fSettingsChanged) { SPCachePurgeEntries(NULL, 0, NULL, SSL_PURGE_CLIENT_ALL_ENTRIES | SSL_PURGE_SERVER_ALL_ENTRIES); } return TRUE; }