You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
589 lines
14 KiB
589 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dpapi.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the DPAPI initialization routines, called by the LSA
|
|
|
|
Author:
|
|
|
|
Pete Skelly (petesk) 22-Mar-00
|
|
--*/
|
|
|
|
|
|
#include <pch.cpp>
|
|
#pragma hdrstop
|
|
#include "pasrec.h"
|
|
|
|
CCryptProvList* g_pCProvList = NULL;
|
|
|
|
TOKEN_SOURCE DPAPITokenSource;
|
|
|
|
PLSA_SECPKG_FUNCTION_TABLE g_pSecpkgTable;
|
|
|
|
|
|
#ifdef RETAIL_LOG_SUPPORT
|
|
HANDLE g_hParamEvent = NULL;
|
|
HKEY g_hKeyParams = NULL;
|
|
HANDLE g_hWait = NULL;
|
|
|
|
DEFINE_DEBUG2(DPAPI);
|
|
|
|
DEBUG_KEY DPAPIDebugKeys[] = {
|
|
{DEB_ERROR, "Error"},
|
|
{DEB_WARN, "Warn"},
|
|
{DEB_TRACE, "Trace"},
|
|
{DEB_TRACE_API, "API"},
|
|
{DEB_TRACE_CRED, "Cred"},
|
|
{DEB_TRACE_CTXT, "Ctxt"},
|
|
{DEB_TRACE_LSESS, "LSess"},
|
|
{DEB_TRACE_LOGON, "Logon"},
|
|
{DEB_TRACE_TIME, "Time"},
|
|
{DEB_TRACE_LOCKS, "Locks"},
|
|
{DEB_TRACE_LEAKS, "Leaks"},
|
|
{0, NULL},
|
|
};
|
|
|
|
VOID
|
|
DPAPIWatchParamKey(
|
|
PVOID pCtxt,
|
|
BOOLEAN fWaitStatus);
|
|
|
|
VOID
|
|
DPAPIInitializeDebugging(
|
|
BOOL fMonitorRegistry)
|
|
{
|
|
DPAPIInitDebug(DPAPIDebugKeys);
|
|
|
|
if(fMonitorRegistry)
|
|
{
|
|
g_hParamEvent = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (NULL == g_hParamEvent)
|
|
{
|
|
D_DebugLog((DEB_WARN, "CreateEvent for ParamEvent failed - 0x%x\n", GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
DPAPIWatchParamKey(g_hParamEvent, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: DPAPIGetRegParams
|
|
//
|
|
// Synopsis: Gets the debug paramaters from the registry
|
|
//
|
|
// Arguments: HKEY to HKLM/System/CCS/LSA/DPAPI
|
|
//
|
|
// Notes: Sets DPAPIInfolevel for debug spew
|
|
//
|
|
void
|
|
DPAPIGetRegParams(HKEY ParamKey)
|
|
{
|
|
|
|
DWORD cbType, tmpInfoLevel = DPAPIInfoLevel, cbSize = sizeof(DWORD);
|
|
DWORD dwErr;
|
|
|
|
dwErr = RegQueryValueExW(
|
|
ParamKey,
|
|
WSZ_DPAPIDEBUGLEVEL,
|
|
NULL,
|
|
&cbType,
|
|
(LPBYTE)&tmpInfoLevel,
|
|
&cbSize
|
|
);
|
|
if (dwErr != ERROR_SUCCESS)
|
|
{
|
|
if (dwErr == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
// no registry value is present, don't want info
|
|
// so reset to defaults
|
|
#if DBG
|
|
DPAPIInfoLevel = DEB_ERROR;
|
|
|
|
#else // fre
|
|
DPAPIInfoLevel = 0;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
D_DebugLog((DEB_WARN, "Failed to query DebugLevel: 0x%x\n", dwErr));
|
|
}
|
|
}
|
|
|
|
// TBD: Validate flags?
|
|
DPAPIInfoLevel = tmpInfoLevel;
|
|
dwErr = RegQueryValueExW(
|
|
ParamKey,
|
|
WSZ_FILELOG,
|
|
NULL,
|
|
&cbType,
|
|
(LPBYTE)&tmpInfoLevel,
|
|
&cbSize
|
|
);
|
|
|
|
if (dwErr == ERROR_SUCCESS)
|
|
{
|
|
DPAPISetLoggingOption((BOOL)tmpInfoLevel);
|
|
}
|
|
else if (dwErr == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
DPAPISetLoggingOption(FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: DPAPIWaitCleanup
|
|
//
|
|
// Synopsis: Cleans up wait from DPAPIWatchParamKey()
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Notes: .
|
|
//
|
|
void
|
|
DPAPIWaitCleanup()
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (NULL != g_hWait)
|
|
{
|
|
Status = RtlDeregisterWait(g_hWait);
|
|
if (NT_SUCCESS(Status) && NULL != g_hParamEvent )
|
|
{
|
|
CloseHandle(g_hParamEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: DPAPIWatchParamKey
|
|
//
|
|
// 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
|
|
DPAPIWatchParamKey(
|
|
PVOID pCtxt,
|
|
BOOLEAN fWaitStatus)
|
|
{
|
|
NTSTATUS Status;
|
|
LONG lRes = ERROR_SUCCESS;
|
|
|
|
if (NULL == g_hKeyParams) // first time we've been called.
|
|
{
|
|
lRes = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
DPAPI_PARAMETER_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&g_hKeyParams);
|
|
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
D_DebugLog((DEB_WARN,"Failed to open DPAPI key: 0x%x\n", lRes));
|
|
goto Reregister;
|
|
}
|
|
}
|
|
|
|
if (NULL != g_hWait)
|
|
{
|
|
Status = RtlDeregisterWait(g_hWait);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status));
|
|
goto Reregister;
|
|
}
|
|
|
|
}
|
|
|
|
lRes = RegNotifyChangeKeyValue(
|
|
g_hKeyParams,
|
|
FALSE,
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
(HANDLE) pCtxt,
|
|
TRUE);
|
|
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes));
|
|
// we're tanked now. No further notifications, so get this one
|
|
}
|
|
|
|
DPAPIGetRegParams(g_hKeyParams);
|
|
|
|
Reregister:
|
|
|
|
Status = RtlRegisterWait(&g_hWait,
|
|
(HANDLE) pCtxt,
|
|
DPAPIWatchParamKey,
|
|
(HANDLE) pCtxt,
|
|
INFINITE,
|
|
WT_EXECUTEINPERSISTENTIOTHREAD|
|
|
WT_EXECUTEONLYONCE);
|
|
|
|
}
|
|
|
|
#endif // RETAIL_LOG_SUPPORT
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
ProtectCallback(
|
|
RPC_IF_HANDLE idIF,
|
|
PVOID pCtx)
|
|
{
|
|
RPC_STATUS Status;
|
|
PWSTR pBinding = NULL;
|
|
PWSTR pProtSeq = NULL;
|
|
|
|
Status = RpcBindingToStringBinding(pCtx, &pBinding);
|
|
|
|
if(Status != RPC_S_OK)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = RpcStringBindingParse(pBinding,
|
|
NULL,
|
|
&pProtSeq,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if(Status != RPC_S_OK)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
// Make sure caller is using local RPC
|
|
if(CompareString(LOCALE_INVARIANT,
|
|
NORM_IGNORECASE,
|
|
pProtSeq,
|
|
-1,
|
|
DPAPI_LOCAL_PROT_SEQ,
|
|
-1) != CSTR_EQUAL)
|
|
{
|
|
Status = ERROR_ACCESS_DENIED;
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = RPC_S_OK;
|
|
|
|
cleanup:
|
|
|
|
if(pProtSeq)
|
|
{
|
|
RpcStringFree(&pProtSeq);
|
|
}
|
|
|
|
if(pBinding)
|
|
{
|
|
RpcStringFree(&pBinding);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DPAPIInitialize
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
|
|
DWORD
|
|
NTAPI
|
|
DPAPIInitialize(
|
|
LSA_SECPKG_FUNCTION_TABLE *pSecpkgTable)
|
|
{
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
BOOL fStartedKeyService = FALSE;
|
|
BOOL bListConstruct = FALSE;
|
|
LONG lRes = ERROR_SUCCESS;
|
|
|
|
RPC_STATUS status;
|
|
|
|
dwLastError = RtlInitializeCriticalSection(&g_csCredHistoryCache);
|
|
if(!NT_SUCCESS(dwLastError))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
DPAPIInitializeDebugging(TRUE);
|
|
|
|
|
|
// Initialize stuff necessary to create tokens etc, just as if
|
|
// we're a security package.
|
|
g_pSecpkgTable = pSecpkgTable;
|
|
|
|
CopyMemory( DPAPITokenSource.SourceName, DPAPI_PACKAGE_NAME_A, strlen(DPAPI_PACKAGE_NAME_A) );
|
|
AllocateLocallyUniqueId( &DPAPITokenSource.SourceIdentifier );
|
|
|
|
|
|
|
|
g_pCProvList = new CCryptProvList;
|
|
if(g_pCProvList)
|
|
{
|
|
if(!g_pCProvList->Initialize())
|
|
{
|
|
delete g_pCProvList;
|
|
g_pCProvList = NULL;
|
|
}
|
|
}
|
|
|
|
IntializeGlobals();
|
|
|
|
if(!InitializeKeyManagement())
|
|
{
|
|
dwLastError = STATUS_NO_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = RpcServerUseProtseqEpW(DPAPI_LOCAL_PROT_SEQ, //ncalrpc
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
DPAPI_LOCAL_ENDPOINT, //protected_storage
|
|
NULL); //Security Descriptor
|
|
|
|
if(RPC_S_DUPLICATE_ENDPOINT == status)
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
if (status)
|
|
{
|
|
dwLastError = status;
|
|
goto cleanup;
|
|
}
|
|
status = RpcServerUseProtseqEpW(DPAPI_BACKUP_PROT_SEQ, //ncacn_np
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
DPAPI_BACKUP_ENDPOINT, //protected_storage
|
|
NULL); //Security Descriptor
|
|
|
|
if(RPC_S_DUPLICATE_ENDPOINT == status)
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
if (status)
|
|
{
|
|
dwLastError = status;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = RpcServerRegisterIfEx(s_ICryptProtect_v1_0_s_ifspec,
|
|
NULL,
|
|
NULL,
|
|
RPC_IF_AUTOLISTEN,
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
ProtectCallback);
|
|
|
|
if (status)
|
|
{
|
|
dwLastError = status;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
status = RpcServerRegisterIfEx(s_PasswordRecovery_v1_0_s_ifspec,
|
|
NULL,
|
|
NULL,
|
|
RPC_IF_AUTOLISTEN,
|
|
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
|
|
ProtectCallback);
|
|
|
|
if (status)
|
|
{
|
|
dwLastError = status;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Start the Backup Key server
|
|
// note: it only starts when the current machine is an domain controller.
|
|
//
|
|
|
|
dwLastError = StartBackupKeyServer();
|
|
if(dwLastError != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
return dwLastError;
|
|
|
|
cleanup:
|
|
DPAPIShutdown();
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
NTAPI
|
|
DPAPIShutdown( )
|
|
{
|
|
//
|
|
// ignore errors because we are shutting down
|
|
//
|
|
|
|
(void)RpcServerUnregisterIf(s_ICryptProtect_v1_0_s_ifspec, 0, 0);
|
|
|
|
//
|
|
// stop backup key server
|
|
// Note: this function knows internally whether the backup key server
|
|
// really started or not.
|
|
//
|
|
|
|
StopBackupKeyServer();
|
|
|
|
|
|
if(g_pCProvList)
|
|
{
|
|
delete g_pCProvList;
|
|
g_pCProvList = NULL;
|
|
}
|
|
|
|
TeardownKeyManagement();
|
|
|
|
ShutdownGlobals();
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
#ifdef RETAIL_LOG_SUPPORT
|
|
VOID
|
|
DPAPIDumpHexData(
|
|
DWORD LogLevel,
|
|
PSTR pszPrefix,
|
|
PBYTE pbData,
|
|
DWORD cbData)
|
|
{
|
|
DWORD i,count;
|
|
CHAR digits[]="0123456789abcdef";
|
|
CHAR pbLine[MAX_PATH];
|
|
DWORD cbLine;
|
|
DWORD cbHeader;
|
|
DWORD_PTR address;
|
|
|
|
if((DPAPIInfoLevel & LogLevel) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(pbData == NULL || cbData == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(pszPrefix)
|
|
{
|
|
strcpy(pbLine, pszPrefix);
|
|
cbHeader = strlen(pszPrefix);
|
|
}
|
|
else
|
|
{
|
|
pbLine[0] = '\0';
|
|
cbHeader = 0;
|
|
}
|
|
|
|
for(; cbData ; cbData -= count, pbData += count)
|
|
{
|
|
count = (cbData > 16) ? 16:cbData;
|
|
|
|
cbLine = cbHeader;
|
|
|
|
address = (DWORD_PTR)pbData;
|
|
|
|
#if defined(_WIN64)
|
|
pbLine[cbLine++] = digits[(address >> 0x3c) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x38) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x34) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x30) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x2c) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x28) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x24) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x20) & 0x0f];
|
|
#endif
|
|
|
|
pbLine[cbLine++] = digits[(address >> 0x1c) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x18) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x14) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x10) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x0c) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x08) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address >> 0x04) & 0x0f];
|
|
pbLine[cbLine++] = digits[(address ) & 0x0f];
|
|
pbLine[cbLine++] = ' ';
|
|
pbLine[cbLine++] = ' ';
|
|
|
|
for(i = 0; i < count; i++)
|
|
{
|
|
pbLine[cbLine++] = digits[pbData[i]>>4];
|
|
pbLine[cbLine++] = digits[pbData[i]&0x0f];
|
|
if(i == 7)
|
|
{
|
|
pbLine[cbLine++] = ':';
|
|
}
|
|
else
|
|
{
|
|
pbLine[cbLine++] = ' ';
|
|
}
|
|
}
|
|
|
|
for(; i < 16; i++)
|
|
{
|
|
pbLine[cbLine++] = ' ';
|
|
pbLine[cbLine++] = ' ';
|
|
pbLine[cbLine++] = ' ';
|
|
}
|
|
|
|
pbLine[cbLine++] = ' ';
|
|
|
|
for(i = 0; i < count; i++)
|
|
{
|
|
|
|
//
|
|
// 37 is for %
|
|
//
|
|
|
|
if(pbData[i] < 32 || pbData[i] > 126 || pbData[i] == 37)
|
|
{
|
|
pbLine[cbLine++] = '.';
|
|
}
|
|
else
|
|
{
|
|
pbLine[cbLine++] = pbData[i];
|
|
}
|
|
}
|
|
|
|
pbLine[cbLine++] = '\n';
|
|
pbLine[cbLine++] = 0;
|
|
|
|
D_DebugLog((LogLevel, pbLine));
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|