windows-nt/Source/XPSP1/NT/ds/security/services/ca/certlib/backup.cpp
2020-09-26 16:20:57 +08:00

3392 lines
75 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: backup.cpp
//
// Contents: Cert Server wrapper routines
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include "certdb.h"
#include "cscsp.h"
#include <esent.h>
#define __dwFILE__ __dwFILE_CERTLIB_BACKUP_CPP__
#define _64k (64 * 1024)
DWORD
_64kBlocks(
IN DWORD nFileSizeHigh,
IN DWORD nFileSizeLow)
{
LARGE_INTEGER li;
li.HighPart = nFileSizeHigh;
li.LowPart = nFileSizeLow;
return((DWORD) ((li.QuadPart + _64k - 1) / _64k));
}
HRESULT
myLargeAlloc(
OUT DWORD *pcbLargeAlloc,
OUT BYTE **ppbLargeAlloc)
{
HRESULT hr;
// at 512k the server begins doing efficient backups
*pcbLargeAlloc = 512 * 1024;
*ppbLargeAlloc = (BYTE *) VirtualAlloc(
NULL,
*pcbLargeAlloc,
MEM_COMMIT,
PAGE_READWRITE);
if (NULL == *ppbLargeAlloc)
{
// couldn't alloc a large chunk? Try 64k...
*pcbLargeAlloc = _64k;
*ppbLargeAlloc = (BYTE *) VirtualAlloc(
NULL,
*pcbLargeAlloc,
MEM_COMMIT,
PAGE_READWRITE);
if (NULL == *ppbLargeAlloc)
{
hr = myHLastError();
_JumpError(hr, error, "VirtualAlloc");
}
}
hr = S_OK;
error:
return(hr);
}
// Files to look for when checking for an existing DB, AND
// Files to delete when clearing out a DB or DB Log directory:
// Do NOT delete certsrv.mdb from Cert server 1.0!
WCHAR const * const g_apwszDBFileMatchPatterns[] =
{
L"res*.log",
TEXT(szDBBASENAMEPARM) L"*.log", // "edb*.log"
TEXT(szDBBASENAMEPARM) L"*.chk", // "edb*.chk"
L"*" wszDBFILENAMEEXT, // "*.edb"
NULL
};
HRESULT
myDeleteDBFilesInDir(
IN WCHAR const *pwszDir)
{
HRESULT hr;
WCHAR const * const *ppwsz;
for (ppwsz = g_apwszDBFileMatchPatterns; NULL != *ppwsz; ppwsz++)
{
hr = myDeleteFilePattern(pwszDir, *ppwsz, FALSE);
_JumpIfError(hr, error, "myDeleteFilePattern");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
DoFilesExistInDir(
IN WCHAR const *pwszDir,
IN WCHAR const *pwszPattern,
OUT BOOL *pfFilesExist,
OPTIONAL OUT WCHAR **ppwszFileInUse)
{
HRESULT hr;
HANDLE hf = INVALID_HANDLE_VALUE;
WCHAR *pwszFindPattern = NULL;
WIN32_FIND_DATA wfd;
*pfFilesExist = FALSE;
if (NULL != ppwszFileInUse)
{
*ppwszFileInUse = NULL;
}
hr = myBuildPathAndExt(pwszDir, pwszPattern, NULL, &pwszFindPattern);
_JumpIfError(hr, error, "myBuildPathAndExt");
hf = FindFirstFile(pwszFindPattern, &wfd);
if (INVALID_HANDLE_VALUE == hf)
{
hr = S_OK;
goto error;
}
do
{
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
//printf("File: %ws\n", wfd.cFileName);
*pfFilesExist = TRUE;
if (NULL != ppwszFileInUse)
{
WCHAR *pwszFile;
hr = myBuildPathAndExt(pwszDir, wfd.cFileName, NULL, &pwszFile);
_JumpIfError(hr, error, "myBuildPathAndExt");
if (myIsFileInUse(pwszFile))
{
DBGPRINT((
DBG_SS_CERTLIB,
"DoFilesExistInDir: File In Use: %ws\n",
pwszFile));
*ppwszFileInUse = pwszFile;
hr = S_OK;
goto error;
}
LocalFree(pwszFile);
}
} while (FindNextFile(hf, &wfd));
hr = S_OK;
error:
if (INVALID_HANDLE_VALUE != hf)
{
FindClose(hf);
}
if (NULL != pwszFindPattern)
{
LocalFree(pwszFindPattern);
}
return(hr);
}
HRESULT
myDoDBFilesExistInDir(
IN WCHAR const *pwszDir,
OUT BOOL *pfFilesExist,
OPTIONAL OUT WCHAR **ppwszFileInUse)
{
HRESULT hr;
WCHAR const * const *ppwsz;
*pfFilesExist = FALSE;
if (NULL != ppwszFileInUse)
{
*ppwszFileInUse = NULL;
}
hr = S_OK;
for (ppwsz = g_apwszDBFileMatchPatterns; NULL != *ppwsz; ppwsz++)
{
BOOL fFilesExist;
hr = DoFilesExistInDir(
pwszDir,
*ppwsz,
&fFilesExist,
ppwszFileInUse);
_JumpIfError(hr, error, "DoFilesExistInDir");
if (fFilesExist)
{
*pfFilesExist = TRUE;
}
if (NULL != ppwszFileInUse && NULL != *ppwszFileInUse)
{
break;
}
}
CSASSERT(S_OK == hr);
error:
return(hr);
}
HRESULT
DoDBFilesExistInRegDir(
IN WCHAR const *pwszRegName,
OUT BOOL *pfFilesExist,
OPTIONAL OUT WCHAR **ppwszFileInUse)
{
HRESULT hr;
WCHAR *pwszDir = NULL;
*pfFilesExist = FALSE;
if (NULL != ppwszFileInUse)
{
*ppwszFileInUse = NULL;
}
hr = myGetCertRegStrValue(NULL, NULL, NULL, pwszRegName, &pwszDir);
if (S_OK != hr)
{
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
{
// reg entry doesn't exist, that's fine
goto done;
}
_JumpError(hr, error, "myGetCertRegStrValue");
}
hr = myDoDBFilesExistInDir(pwszDir, pfFilesExist, ppwszFileInUse);
_JumpIfError(hr, error, "myDoDBFilesExistInDir");
done:
hr = S_OK;
error:
if (NULL != pwszDir)
{
LocalFree(pwszDir);
}
return(hr);
}
HRESULT
BuildDBFileName(
IN WCHAR const *pwszSanitizedName,
OUT WCHAR **ppwszDBFile)
{
HRESULT hr;
WCHAR *pwszDir = NULL;
*ppwszDBFile = NULL;
// get existing db path
hr = myGetCertRegStrValue(NULL, NULL, NULL, wszREGDBDIRECTORY, &pwszDir);
_JumpIfError(hr, error, "myGetCertRegStrValue");
// form existing db file path
hr = myBuildPathAndExt(
pwszDir,
pwszSanitizedName,
wszDBFILENAMEEXT,
ppwszDBFile);
_JumpIfError(hr, error, "myBuildPathAndExt");
error:
if (NULL != pwszDir)
{
LocalFree(pwszDir);
}
return(hr);
}
WCHAR const * const g_apwszDBRegNames[] =
{
wszREGDBDIRECTORY,
wszREGDBLOGDIRECTORY,
wszREGDBSYSDIRECTORY,
wszREGDBTEMPDIRECTORY,
NULL
};
// Verify that the DB and DB Log directories in the registry contain existing
// DB files, to decide whether the DB could be reused by cert server setup.
// Also see if any of the DB files are in use -- we don't want to point to the
// same directory as the DS DB and trash the DS, for example.
HRESULT
myDoDBFilesExist(
IN WCHAR const *pwszSanitizedName,
OUT BOOL *pfFilesExist,
OPTIONAL OUT WCHAR **ppwszFileInUse)
{
HRESULT hr;
WCHAR const * const *ppwsz;
WCHAR *pwszDBFile = NULL;
*pfFilesExist = FALSE;
if (NULL != ppwszFileInUse)
{
*ppwszFileInUse = NULL;
}
// this is very primitive, just check for existence
// get existing db file path
hr = BuildDBFileName(pwszSanitizedName, &pwszDBFile);
if (S_OK == hr)
{
// If the main DB file doesn't exist, there's no point in continuing!
if (!myDoesFileExist(pwszDBFile))
{
CSASSERT(S_OK == hr);
goto error;
}
*pfFilesExist = TRUE;
if (NULL != ppwszFileInUse && myIsFileInUse(pwszDBFile))
{
*ppwszFileInUse = pwszDBFile;
pwszDBFile = NULL;
CSASSERT(S_OK == hr);
goto error;
}
}
else
{
_PrintError(hr, "BuildDBFileName");
}
for (ppwsz = g_apwszDBRegNames; NULL != *ppwsz; ppwsz++)
{
BOOL fFilesExist;
hr = DoDBFilesExistInRegDir(*ppwsz, &fFilesExist, ppwszFileInUse);
_JumpIfError(hr, error, "DoDBFilesExistInRegDir");
if (fFilesExist)
{
*pfFilesExist = TRUE;
}
if (NULL != ppwszFileInUse && NULL != *ppwszFileInUse)
{
CSASSERT(S_OK == hr);
goto error;
}
}
CSASSERT(S_OK == hr);
error:
if (NULL != pwszDBFile)
{
LocalFree(pwszDBFile);
}
return(hr);
}
HRESULT
BackupCopyDBFile(
IN HCSBC hcsbc,
IN WCHAR const *pwszDBFile,
IN WCHAR const *pwszBackupFile,
IN DWORD dwPercentCompleteBase,
IN DWORD dwPercentCompleteDelta,
OUT DWORD *pdwPercentComplete)
{
HRESULT hr;
HRESULT hr2;
HANDLE hFileBackup = INVALID_HANDLE_VALUE;
BOOL fOpen = FALSE;
LARGE_INTEGER licbFile;
DWORD cbRead;
DWORD cbWritten;
DWORD dwPercentCompleteCurrent;
DWORD ReadLoopMax;
DWORD ReadLoopCurrent;
DWORD cbLargeAlloc;
BYTE *pbLargeAlloc = NULL;
hr = myLargeAlloc(&cbLargeAlloc, &pbLargeAlloc);
_JumpIfError(hr, error, "myLargeAlloc");
//printf("Copy %ws to %ws\n", pwszDBFile, pwszBackupFile);
hr = CertSrvBackupOpenFile(hcsbc, pwszDBFile, cbLargeAlloc, &licbFile);
_JumpIfError(hr, error, "CertSrvBackupOpenFile");
fOpen = TRUE;
hFileBackup = CreateFile(
pwszBackupFile,
GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
0,
NULL);
if (hFileBackup == INVALID_HANDLE_VALUE)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CreateFile", pwszBackupFile);
}
dwPercentCompleteCurrent = dwPercentCompleteBase;
ReadLoopMax =
(DWORD) ((licbFile.QuadPart + cbLargeAlloc - 1) / cbLargeAlloc);
//printf("BackupDBFile: Percent per Read = %u, read count = %u\n", dwPercentCompleteDelta / ReadLoopMax, ReadLoopMax);
ReadLoopCurrent = 0;
while (0 != licbFile.QuadPart)
{
hr = CertSrvBackupRead(hcsbc, pbLargeAlloc, cbLargeAlloc, &cbRead);
_JumpIfError(hr, error, "CertSrvBackupRead");
//printf("CertSrvBackupRead(%x)\n", cbRead);
if (!WriteFile(hFileBackup, pbLargeAlloc, cbRead, &cbWritten, NULL))
{
hr = myHLastError();
_JumpErrorStr(hr, error, "WriteFile", pwszBackupFile);
}
if (cbWritten != cbRead)
{
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
_JumpErrorStr(hr, error, "WriteFile", pwszBackupFile);
}
licbFile.QuadPart -= cbRead;
ReadLoopCurrent++;
dwPercentCompleteCurrent =
dwPercentCompleteBase +
(ReadLoopCurrent * dwPercentCompleteDelta) / ReadLoopMax;
CSASSERT(dwPercentCompleteCurrent <= dwPercentCompleteBase + dwPercentCompleteDelta);
CSASSERT(*pdwPercentComplete <= dwPercentCompleteCurrent);
*pdwPercentComplete = dwPercentCompleteCurrent;
//printf("BackupDBFile: PercentComplete = %u\n", *pdwPercentComplete);
}
CSASSERT(*pdwPercentComplete <= dwPercentCompleteBase + dwPercentCompleteDelta);
*pdwPercentComplete = dwPercentCompleteBase + dwPercentCompleteDelta;
//printf("BackupDBFile: PercentComplete = %u (EOF)\n", *pdwPercentComplete);
error:
if (INVALID_HANDLE_VALUE != hFileBackup)
{
CloseHandle(hFileBackup);
}
if (fOpen)
{
hr2 = CertSrvBackupClose(hcsbc);
_PrintIfError(hr2, "CertSrvBackupClose");
}
if (NULL != pbLargeAlloc)
{
VirtualFree(pbLargeAlloc, 0, MEM_RELEASE);
}
return(hr);
}
HRESULT
BackupDBFileList(
IN HCSBC hcsbc,
IN BOOL fDBFiles,
IN WCHAR const *pwszDir,
OUT DWORD *pdwPercentComplete)
{
HRESULT hr;
WCHAR *pwszzList = NULL;
WCHAR const *pwsz;
DWORD cfile;
DWORD cb;
WCHAR const *pwszFile;
WCHAR wszPath[MAX_PATH];
DWORD dwPercentCompleteCurrent;
DWORD dwPercentComplete1File;
if (fDBFiles)
{
hr = CertSrvBackupGetDatabaseNames(hcsbc, &pwszzList, &cb);
_JumpIfError(hr, error, "CertSrvBackupGetDatabaseNames");
}
else
{
hr = CertSrvBackupGetBackupLogs(hcsbc, &pwszzList, &cb);
_JumpIfError(hr, error, "CertSrvBackupGetBackupLogs");
}
// prefix complains this might happen, then deref'd below
if (pwszzList == NULL)
{
hr = E_UNEXPECTED;
_JumpError(hr, error, "BackupDBFileList");
}
cfile = 0;
for (pwsz = pwszzList; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
{
cfile++;
}
if (0 != cfile)
{
dwPercentCompleteCurrent = 0;
dwPercentComplete1File = 100 / cfile;
//printf("BackupDBFileList: Percent per File = %u\n", dwPercentComplete1File);
for (pwsz = pwszzList; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
{
pwszFile = wcsrchr(pwsz, L'\\');
if (NULL == pwszFile)
{
pwszFile = pwsz;
}
else
{
pwszFile++;
}
wcscpy(wszPath, pwszDir);
wcscat(wszPath, L"\\");
wcscat(wszPath, pwszFile);
DBGPRINT((
DBG_SS_CERTLIBI,
"BackupDBFileList: %x %ws -> %ws\n",
*pwsz,
&pwsz[1],
wszPath));
hr = BackupCopyDBFile(
hcsbc,
&pwsz[1],
wszPath,
dwPercentCompleteCurrent,
dwPercentComplete1File,
pdwPercentComplete);
_JumpIfError(hr, error, "BackupCopyDBFile");
dwPercentCompleteCurrent += dwPercentComplete1File;
CSASSERT(*pdwPercentComplete == dwPercentCompleteCurrent);
//printf("BackupDBFileList: PercentComplete = %u\n", *pdwPercentComplete);
}
}
CSASSERT(*pdwPercentComplete <= 100);
*pdwPercentComplete = 100;
//printf("BackupDBFileList: PercentComplete = %u (END)\n", *pdwPercentComplete);
hr = S_OK;
error:
if (NULL != pwszzList)
{
CertSrvBackupFree(pwszzList);
}
return(hr);
}
BOOL
myIsDirEmpty(
IN WCHAR const *pwszDir)
{
HANDLE hf;
WIN32_FIND_DATA wfd;
WCHAR wszpath[MAX_PATH];
BOOL fEmpty = TRUE;
wszpath[0] = L'\0';
wcscpy(wszpath, pwszDir);
wcscat(wszpath, L"\\*.*");
hf = FindFirstFile(wszpath, &wfd);
if (INVALID_HANDLE_VALUE != hf)
{
do {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
fEmpty = FALSE;
//printf("File: %ws\n", wfd.cFileName);
break;
} while (FindNextFile(hf, &wfd));
FindClose(hf);
}
return(fEmpty);
}
HRESULT
myForceDirEmpty(
IN WCHAR const *pwszDir)
{
HRESULT hr;
HANDLE hf;
WIN32_FIND_DATA wfd;
WCHAR *pwszFile;
WCHAR wszpath[MAX_PATH];
wszpath[0] = L'\0';
wcscpy(wszpath, pwszDir);
wcscat(wszpath, L"\\*.*");
pwszFile = &wszpath[wcslen(pwszDir)] + 1;
hf = FindFirstFile(wszpath, &wfd);
if (INVALID_HANDLE_VALUE == hf)
{
hr = myHLastError();
_JumpIfError(hr, error, "FindFirstFile");
}
do {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
wcscpy(pwszFile, wfd.cFileName);
//printf("File: %ws\n", wszpath);
DeleteFile(wszpath);
} while (FindNextFile(hf, &wfd));
FindClose(hf);
hr = S_OK;
error:
return(hr);
}
BOOL
myIsDirectory(IN WCHAR const *pwszDirectoryPath)
{
WIN32_FILE_ATTRIBUTE_DATA data;
return(
GetFileAttributesEx(pwszDirectoryPath, GetFileExInfoStandard, &data) &&
(FILE_ATTRIBUTE_DIRECTORY & data.dwFileAttributes));
}
BOOL
myIsFileInUse(
IN WCHAR const *pwszFile)
{
BOOL fInUse = FALSE;
HANDLE hFile;
hFile = CreateFile(
pwszFile,
GENERIC_WRITE, // dwDesiredAccess
0, // no share
NULL, // lpSecurityAttributes
OPEN_EXISTING, // open only & fail if doesn't exist
0, // dwFlagAndAttributes
NULL); // hTemplateFile
if (INVALID_HANDLE_VALUE == hFile)
{
if (ERROR_SHARING_VIOLATION == GetLastError())
{
fInUse = TRUE;
}
}
else
{
CloseHandle(hFile);
}
return(fInUse);
}
HRESULT
myCreateBackupDir(
IN WCHAR const *pwszDir,
IN BOOL fForceOverWrite)
{
HRESULT hr;
if (!myIsDirectory(pwszDir))
{
if (!CreateDirectory(pwszDir, NULL))
{
hr = myHLastError();
if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) != hr)
{
_JumpErrorStr(hr, error, "CreateDirectory", pwszDir);
}
} // else dir created successfully
} // else dir already exists
if (!myIsDirEmpty(pwszDir))
{
if (!fForceOverWrite)
{
hr = HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY);
_JumpErrorStr(hr, error, "myIsDirEmpty", pwszDir);
}
hr = myForceDirEmpty(pwszDir);
_JumpIfErrorStr(hr, error, "myForceDirEmpty", pwszDir);
if (!myIsDirEmpty(pwszDir))
{
hr = HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY);
_JumpErrorStr(hr, error, "myIsDirEmpty", pwszDir);
}
} // else is empty
hr = S_OK;
error:
return(hr);
}
// if Flags & CDBBACKUP_VERIFYONLY, create and verify the target directory is empty
HRESULT
myBackupDB(
IN WCHAR const *pwszConfig,
IN DWORD Flags,
IN WCHAR const *pwszBackupDir,
OPTIONAL OUT DBBACKUPPROGRESS *pdbp)
{
HRESULT hr;
HRESULT hr2;
BOOL fServerOnline;
HCSBC hcsbc;
BOOL fBegin = FALSE;
WCHAR *pwszPathDBDir = NULL;
WCHAR *pwszDATFile = NULL;
WCHAR *pwszzFileList = NULL;
DWORD cbList;
DBBACKUPPROGRESS dbp;
LONG grbitJet;
LONG BackupFlags;
BOOL fImpersonating = FALSE;
if (NULL == pwszConfig)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "NULL pwszConfig");
}
if (NULL == pdbp)
{
pdbp = &dbp;
}
ZeroMemory(pdbp, sizeof(*pdbp));
if (!ImpersonateSelf(SecurityImpersonation))
{
hr = myHLastError();
_JumpError(hr, error, "ImpersonateSelf");
}
fImpersonating = TRUE;
hr = myEnablePrivilege(SE_BACKUP_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
if (NULL == pwszBackupDir)
{
hr = E_POINTER;
_JumpError(hr, error, "NULL parm");
}
if (~CDBBACKUP_BACKUPVALID & Flags)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Flags");
}
if (!myIsDirectory(pwszBackupDir))
{
if (!CreateDirectory(pwszBackupDir, NULL))
{
hr = myHLastError();
if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) != hr)
{
_JumpError(hr, error, "CreateDirectory");
}
}
}
hr = myBuildPathAndExt(
pwszBackupDir,
wszDBBACKUPSUBDIR,
NULL,
&pwszPathDBDir);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = myCreateBackupDir(
pwszPathDBDir,
(CDBBACKUP_OVERWRITE & Flags)? TRUE : FALSE);
_JumpIfError(hr, error, "myCreateBackupDir");
//if (NULL != pwszConfig)
if (0 == (Flags & CDBBACKUP_VERIFYONLY))
{
hr = CertSrvIsServerOnline(pwszConfig, &fServerOnline);
_JumpIfError(hr, error, "CertSrvIsServerOnline");
//printf("Cert Server Online -> %d\n", fServerOnline);
if (!fServerOnline)
{
hr = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
_JumpError(hr, error, "CertSrvIsServerOnline");
}
BackupFlags = CSBACKUP_TYPE_FULL;
grbitJet = 0;
if (CDBBACKUP_INCREMENTAL & Flags)
{
grbitJet |= JET_bitBackupIncremental;
BackupFlags = CSBACKUP_TYPE_LOGS_ONLY;
}
if (CDBBACKUP_KEEPOLDLOGS & Flags)
{
// JetBeginExternalBackup can't handle setting this bit
// grbitJet |= JET_bitKeepOldLogs;
}
hr = CertSrvBackupPrepare(pwszConfig, grbitJet, BackupFlags, &hcsbc);
_JumpIfError(hr, error, "CertSrvBackupPrepare");
fBegin = TRUE;
if (0 == (CDBBACKUP_INCREMENTAL & Flags))
{
hr = CertSrvRestoreGetDatabaseLocations(hcsbc, &pwszzFileList, &cbList);
_JumpIfError(hr, error, "CertSrvRestoreGetDatabaseLocations");
hr = myBuildPathAndExt(
pwszPathDBDir,
wszDBBACKUPCERTBACKDAT,
NULL,
&pwszDATFile);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = EncodeToFileW(
pwszDATFile,
(BYTE const *) pwszzFileList,
cbList,
CRYPT_STRING_BINARY);
_JumpIfError(hr, error, "EncodeToFileW");
hr = BackupDBFileList(
hcsbc,
TRUE,
pwszPathDBDir,
&pdbp->dwDBPercentComplete);
_JumpIfError(hr, error, "BackupDBFileList(DB)");
}
else
{
pdbp->dwDBPercentComplete = 100;
}
//printf("DB Done: dwDBPercentComplete = %u\n", pdbp->dwDBPercentComplete);
hr = BackupDBFileList(
hcsbc,
FALSE,
pwszPathDBDir,
&pdbp->dwLogPercentComplete);
_JumpIfError(hr, error, "BackupDBFileList(Log)");
//printf("Log Done: dwLogPercentComplete = %u\n", pdbp->dwLogPercentComplete);
if (0 == (CDBBACKUP_KEEPOLDLOGS & Flags))
{
hr = CertSrvBackupTruncateLogs(hcsbc);
_JumpIfError(hr, error, "CertSrvBackupTruncateLogs");
}
pdbp->dwTruncateLogPercentComplete = 100;
//printf("Truncate Done: dwTruncateLogPercentComplete = %u\n", pdbp->dwTruncateLogPercentComplete);
}
error:
if (NULL != pwszzFileList)
{
CertSrvBackupFree(pwszzFileList);
}
if (fBegin)
{
hr2 = CertSrvBackupEnd(hcsbc);
_PrintIfError(hr2, "CertSrvBackupEnd");
if (S_OK == hr)
{
hr = hr2;
}
}
if (NULL != pwszDATFile)
{
LocalFree(pwszDATFile);
}
if (NULL != pwszPathDBDir)
{
LocalFree(pwszPathDBDir);
}
if (fImpersonating)
{
myEnablePrivilege(SE_BACKUP_NAME, FALSE);
RevertToSelf();
}
return(hr);
}
// Verify the backup file names only, and return the log file numeric range.
HRESULT
myVerifyBackupDirectory(
IN WCHAR const *pwszConfig,
IN DWORD Flags,
IN WCHAR const *pwszPathDBDir,
OUT DWORD *plogMin,
OUT DWORD *plogMax,
OUT DWORD *pc64kDBBlocks, // 64k blocks in DB files to be restored
OUT DWORD *pc64kLogBlocks) // 64k blocks in Log files to be restored
{
HRESULT hr;
HANDLE hf = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA wfd;
WCHAR wszpath[2 * MAX_PATH];
WCHAR wszfile[MAX_PATH];
BOOL fSawEDBFile = FALSE;
BOOL fSawDatFile = FALSE;
DWORD cLogFiles = 0;
WCHAR *pwszCA;
WCHAR *pwszRevertCA = NULL;
WCHAR *pwszSanitizedCA = NULL;
WCHAR *pwszExt;
WCHAR *pwsz;
DWORD log;
*plogMin = MAXDWORD;
*plogMax = 0;
*pc64kDBBlocks = 0;
*pc64kLogBlocks = 0;
wszpath[0] = L'\0';
pwszCA = wcschr(pwszConfig, L'\\');
if (NULL != pwszCA)
{
pwszCA++; // point to CA Name
hr = myRevertSanitizeName(pwszCA, &pwszRevertCA);
_JumpIfError(hr, error, "myRevertSanitizeName");
hr = mySanitizeName(pwszRevertCA, &pwszSanitizedCA);
_JumpIfError(hr, error, "mySanitizeName");
}
wcscpy(wszpath, pwszPathDBDir);
wcscat(wszpath, L"\\*.*");
hf = FindFirstFile(wszpath, &wfd);
if (INVALID_HANDLE_VALUE == hf)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "missing backup files");
}
hr = HRESULT_FROM_WIN32(ERROR_DIRECTORY);
do {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
//printf("File: %ws\n", wfd.cFileName);
wcscpy(wszfile, wfd.cFileName);
pwszExt = wcsrchr(wszfile, L'.');
if (NULL == pwszExt)
{
_JumpError(hr, error, "file missing extension");
}
*pwszExt++ = L'\0';
if (0 == lstrcmpi(&wszLOGFILENAMEEXT[1], pwszExt))
{
if (0 != _wcsnicmp(wszfile, wszDBBASENAMEPARM, 3))
{
_JumpErrorStr(hr, error, "bad log prefix", wfd.cFileName);
}
for (pwsz = &wszfile[3]; L'\0' != *pwsz; pwsz++)
{
if (!iswxdigit(*pwsz))
{
_JumpErrorStr(hr, error, "bad name digit", wfd.cFileName);
}
}
log = wcstoul(&wszfile[3], NULL, 16);
if (log > *plogMax)
{
//printf("Log %x: max = %x -> %x\n", log, *plogMax, log);
*plogMax = log;
}
if (log < *plogMin)
{
//printf("Log %x: min = %x -> %x\n", log, *plogMin, log);
*plogMin = log;
}
*pc64kLogBlocks += _64kBlocks(wfd.nFileSizeHigh, wfd.nFileSizeLow);
cLogFiles++;
}
else
if (0 == lstrcmpi(&wszDBFILENAMEEXT[1], pwszExt))
{
if (fSawEDBFile)
{
_JumpError(hr, error, "multiple *.edb files");
}
if (NULL != pwszSanitizedCA &&
0 != lstrcmpi(wszfile, pwszSanitizedCA))
{
_PrintErrorStr(hr, "expected base name", pwszSanitizedCA);
_JumpErrorStr(hr, error, "base name mismatch", wfd.cFileName);
}
*pc64kDBBlocks += _64kBlocks(wfd.nFileSizeHigh, wfd.nFileSizeLow);
fSawEDBFile = TRUE;
}
else
if (0 == lstrcmpi(&wszDATFILENAMEEXT[1], pwszExt))
{
if (fSawDatFile)
{
_JumpError(hr, error, "multiple *.dat files");
}
if (lstrcmpi(wfd.cFileName, wszDBBACKUPCERTBACKDAT))
{
_JumpErrorStr(hr, error, "unexpected file", wfd.cFileName);
}
fSawDatFile = TRUE;
}
else
{
_JumpErrorStr(hr, error, "unexpected extension", wfd.cFileName);
}
} while (FindNextFile(hf, &wfd));
//printf("clog=%u: %u - %u edb=%u\n", cLogFiles, *plogMin, *plogMax, fSawEDBFile);
if (0 == cLogFiles)
{
_JumpError(hr, error, "missing log file(s)");
}
if (0 == (CDBBACKUP_INCREMENTAL & Flags))
{
if (!fSawEDBFile || !fSawDatFile)
{
_JumpError(hr, error, "missing full backup file(s)");
}
}
else
{
if (fSawEDBFile || fSawDatFile)
{
_JumpError(hr, error, "unexpected incremental backup file(s)");
}
}
if (*plogMax - *plogMin + 1 != cLogFiles)
{
_JumpError(hr, error, "missing log file(s)");
}
hr = S_OK;
error:
if (NULL != pwszRevertCA)
{
LocalFree(pwszRevertCA);
}
if (NULL != pwszSanitizedCA)
{
LocalFree(pwszSanitizedCA);
}
if (INVALID_HANDLE_VALUE != hf)
{
FindClose(hf);
}
return(hr);
}
HRESULT
myGetRegUNCDBDir(
IN HKEY hkey,
IN WCHAR const *pwszReg,
OPTIONAL IN WCHAR const *pwszServer,
IN WCHAR const **ppwszUNCDir)
{
HRESULT hr;
DWORD dwType;
DWORD cb;
WCHAR *pwszDir = NULL;
WCHAR *pwszUNCDir;
*ppwszUNCDir = NULL;
hr = RegQueryValueEx(hkey, pwszReg, NULL, &dwType, NULL, &cb);
if (S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegQueryValueEx", pwszReg);
}
pwszDir = (WCHAR *) LocalAlloc(LMEM_FIXED, cb);
if (NULL == pwszDir)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
hr = RegQueryValueEx(hkey, pwszReg, NULL, &dwType, (BYTE *) pwszDir, &cb);
if (S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegQueryValueEx", pwszReg);
}
hr = myConvertLocalPathToUNC(pwszServer, pwszDir, &pwszUNCDir);
_JumpIfError(hr, error, "myConvertLocalPathToUNC");
*ppwszUNCDir = pwszUNCDir;
error:
if (NULL != pwszDir)
{
LocalFree(pwszDir);
}
return(hr);
}
HRESULT
myCopyUNCPath(
IN WCHAR const *pwszIn,
OPTIONAL IN WCHAR const *pwszDnsName,
OUT WCHAR const **ppwszOut)
{
HRESULT hr;
WCHAR *pwszOut;
WCHAR const *pwsz;
*ppwszOut = NULL;
if (L'\\' != pwszIn[0] || L'\\' != pwszIn[1])
{
hr = E_INVALIDARG;
_JumpError(hr, error, "bad parm");
}
if (NULL == pwszDnsName)
{
hr = myConvertUNCPathToLocal(pwszIn, &pwszOut);
_JumpIfError(hr, error, "myConvertUNCPathToLocal");
}
else
{
pwsz = wcschr(&pwszIn[2], L'\\');
if (NULL == pwsz)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "bad parm");
}
pwszOut = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(2 + wcslen(pwszDnsName) + wcslen(pwsz) + 1) * sizeof(WCHAR));
if (NULL == pwszOut)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(pwszOut, L"\\\\");
wcscat(pwszOut, pwszDnsName);
wcscat(pwszOut, pwsz);
}
*ppwszOut = pwszOut;
hr = S_OK;
error:
return(hr);
}
HRESULT
myGetDBPaths(
IN WCHAR const *pwszConfig,
OPTIONAL IN WCHAR const *pwszLogPath,
OPTIONAL IN WCHAR const *pwszzFileList,
OUT WCHAR const **ppwszDBDir,
OUT WCHAR const **ppwszLogDir,
OUT WCHAR const **ppwszSystemDir)
{
HRESULT hr;
HKEY hkey = NULL;
WCHAR *pwszDnsName = NULL;
WCHAR *pwszRegPath = NULL;
WCHAR *pwszDBDir = NULL;
WCHAR const *pwsz;
WCHAR const *pwszT;
BOOL fLocal;
*ppwszDBDir = NULL;
*ppwszLogDir = NULL;
*ppwszSystemDir = NULL;
hr = myIsConfigLocal(pwszConfig, NULL, &fLocal);
_JumpIfError(hr, error, "myIsConfigLocal");
if (fLocal)
{
pwszConfig = NULL;
}
else
{
hr = myGetMachineDnsName(&pwszDnsName);
_JumpIfError(hr, error, "myGetMachineDnsName");
}
hr = myRegOpenRelativeKey(
fLocal? NULL : pwszConfig,
L"",
0,
&pwszRegPath,
NULL, // ppwszName
&hkey);
_JumpIfErrorStr(hr, error, "myRegOpenRelativeKey", pwszConfig);
// Find old database path:
pwszT = NULL;
if (NULL != pwszzFileList)
{
for (pwsz = pwszzFileList; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
{
if (CSBFT_CERTSERVER_DATABASE == *pwsz)
{
pwsz++;
pwszT = wcsrchr(pwsz, L'\\');
break;
}
}
}
if (NULL != pwszT)
{
DWORD cwc = SAFE_SUBTRACT_POINTERS(pwszT, pwsz);
pwszDBDir = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
if (NULL == pwszDBDir)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(pwszDBDir, pwsz, cwc * sizeof(WCHAR));
pwszDBDir[cwc] = L'\0';
hr = myCopyUNCPath(pwszDBDir, pwszDnsName, ppwszDBDir);
_JumpIfError(hr, error, "myCopyUNCPath");
}
else
{
hr = myGetRegUNCDBDir(hkey, wszREGDBDIRECTORY, pwszDnsName, ppwszDBDir);
_JumpIfError(hr, error, "myGetRegUNCDBDir");
}
if (NULL != pwszLogPath)
{
hr = myCopyUNCPath(pwszLogPath, pwszDnsName, ppwszLogDir);
_JumpIfError(hr, error, "myCopyUNCPath");
}
else
{
hr = myGetRegUNCDBDir(
hkey,
wszREGDBLOGDIRECTORY,
pwszDnsName,
ppwszLogDir);
_JumpIfError(hr, error, "myGetRegUNCDBDir");
}
hr = myGetRegUNCDBDir(
hkey,
wszREGDBSYSDIRECTORY,
pwszDnsName,
ppwszSystemDir);
_JumpIfError(hr, error, "myGetRegUNCDBDir");
error:
if (S_OK != hr)
{
if (NULL != *ppwszDBDir)
{
LocalFree(const_cast<WCHAR *>(*ppwszDBDir));
*ppwszDBDir = NULL;
}
if (NULL != *ppwszLogDir)
{
LocalFree(const_cast<WCHAR *>(*ppwszLogDir));
*ppwszLogDir = NULL;
}
if (NULL != *ppwszSystemDir)
{
LocalFree(const_cast<WCHAR *>(*ppwszSystemDir));
*ppwszSystemDir = NULL;
}
}
if (NULL != hkey)
{
RegCloseKey(hkey);
}
if (NULL != pwszDBDir)
{
LocalFree(pwszDBDir);
}
if (NULL != pwszRegPath)
{
LocalFree(pwszRegPath);
}
if (NULL != pwszDnsName)
{
LocalFree(pwszDnsName);
}
return(hr);
}
HRESULT
RestoreCopyFile(
IN BOOL fForceOverWrite,
IN WCHAR const *pwszSourceDir,
IN WCHAR const *pwszTargetDir,
IN WCHAR const *pwszFile,
IN DWORD nFileSizeHigh,
IN DWORD nFileSizeLow,
IN DWORD c64kBlocksTotal, // total file size
IN OUT DWORD *pc64kBlocksCurrent, // current file size sum
IN OUT DWORD *pdwPercentComplete)
{
HRESULT hr;
WCHAR *pwszSource = NULL;
WCHAR *pwszTarget = NULL;
HANDLE hTarget = INVALID_HANDLE_VALUE;
HANDLE hSource = INVALID_HANDLE_VALUE;
DWORD dwPercentCompleteCurrent;
LARGE_INTEGER licb;
LARGE_INTEGER licbRead;
DWORD cbRead;
DWORD cbWritten;
DWORD cbLargeAlloc;
BYTE *pbLargeAlloc = NULL;
DWORD c64kBlocksFile;
DWORD dwPercentComplete;
licb.HighPart = nFileSizeHigh;
licb.LowPart = nFileSizeLow;
hr = myBuildPathAndExt(pwszSourceDir, pwszFile, NULL, &pwszSource);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = myBuildPathAndExt(pwszTargetDir, pwszFile, NULL, &pwszTarget);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = myLargeAlloc(&cbLargeAlloc, &pbLargeAlloc);
_JumpIfError(hr, error, "myLargeAlloc");
hSource = CreateFile(
pwszSource,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hSource == INVALID_HANDLE_VALUE)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CreateFile", pwszSource);
}
hTarget = CreateFile(
pwszTarget,
GENERIC_WRITE,
0,
NULL,
fForceOverWrite? CREATE_ALWAYS : CREATE_NEW,
0,
NULL);
if (hTarget == INVALID_HANDLE_VALUE)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CreateFile", pwszTarget);
}
licbRead.QuadPart = 0;
c64kBlocksFile = 0;
while (licbRead.QuadPart < licb.QuadPart)
{
if (!ReadFile(hSource, pbLargeAlloc, cbLargeAlloc, &cbRead, NULL))
{
hr = myHLastError();
_JumpError(hr, error, "ReadFile");
}
//printf("ReadFile(%x)\n", cbRead);
if (!WriteFile(hTarget, pbLargeAlloc, cbRead, &cbWritten, NULL))
{
hr = myHLastError();
_JumpErrorStr(hr, error, "WriteFile", pwszTarget);
}
if (cbWritten != cbRead)
{
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
_JumpErrorStr(hr, error, "WriteFile", pwszTarget);
}
licbRead.QuadPart += cbRead;
c64kBlocksFile = _64kBlocks(licbRead.HighPart, licbRead.LowPart);
dwPercentComplete =
(100 * (c64kBlocksFile + *pc64kBlocksCurrent)) / c64kBlocksTotal;
CSASSERT(*pdwPercentComplete <= dwPercentComplete);
*pdwPercentComplete = dwPercentComplete;
//printf("RestoreCopyFile0: PercentComplete = %u\n", *pdwPercentComplete);
}
*pc64kBlocksCurrent += c64kBlocksFile;
dwPercentComplete = (100 * *pc64kBlocksCurrent) / c64kBlocksTotal;
CSASSERT(*pdwPercentComplete <= dwPercentComplete);
*pdwPercentComplete = dwPercentComplete;
//printf("RestoreCopyFile1: PercentComplete = %u\n", *pdwPercentComplete);
hr = S_OK;
error:
if (INVALID_HANDLE_VALUE != hTarget)
{
CloseHandle(hTarget);
}
if (INVALID_HANDLE_VALUE != hSource)
{
CloseHandle(hSource);
}
if (NULL != pwszSource)
{
LocalFree(pwszSource);
}
if (NULL != pwszTarget)
{
LocalFree(pwszTarget);
}
if (NULL != pbLargeAlloc)
{
VirtualFree(pbLargeAlloc, 0, MEM_RELEASE);
}
return(hr);
}
HRESULT
RestoreCopyFilePattern(
IN BOOL fForceOverWrite,
IN WCHAR const *pwszSourceDir,
IN WCHAR const *pwszTargetDir,
IN WCHAR const *pwszFilePattern,
IN DWORD c64kBlocksTotal, // total file size
IN OUT DWORD *pc64kBlocksCurrent, // current file size sum
IN OUT DWORD *pdwPercentComplete)
{
HRESULT hr;
WCHAR *pwszPattern = NULL;
HANDLE hf = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA wfd;
hr = myBuildPathAndExt(pwszSourceDir, pwszFilePattern, NULL, &pwszPattern);
_JumpIfError(hr, error, "myBuildPathAndExt");
hf = FindFirstFile(pwszPattern, &wfd);
if (INVALID_HANDLE_VALUE == hf)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpErrorStr(hr, error, "missing source files", pwszPattern);
}
hr = HRESULT_FROM_WIN32(ERROR_DIRECTORY);
do {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
//printf("File: %ws\n", wfd.cFileName);
hr = RestoreCopyFile(
fForceOverWrite,
pwszSourceDir, // source dir
pwszTargetDir, // target dir
wfd.cFileName,
wfd.nFileSizeHigh,
wfd.nFileSizeLow,
c64kBlocksTotal, // total file size
pc64kBlocksCurrent, // current file size sum
pdwPercentComplete);
_JumpIfError(hr, error, "RestoreCopyFile");
} while (FindNextFile(hf, &wfd));
hr = S_OK;
error:
if (INVALID_HANDLE_VALUE != hf)
{
FindClose(hf);
}
if (NULL != pwszPattern)
{
LocalFree(pwszPattern);
}
return(hr);
}
HRESULT
myRestoreDBFiles(
IN WCHAR const *pwszConfig,
IN DWORD Flags,
IN WCHAR const *pwszBackupDir,
OPTIONAL IN WCHAR const *pwszLogPath,
OPTIONAL IN WCHAR const *pwszzFileList, // NULL if incremental restore
IN DWORD c64kDBBlocks,
IN DWORD c64kLogBlocks,
OPTIONAL OUT DBBACKUPPROGRESS *pdbp)
{
HRESULT hr;
DWORD i;
#define IDIR_DB 0
#define IDIR_LOG 1
#define IDIR_SYSTEM 2
WCHAR const *apwszDirs[3] = { NULL, NULL, NULL };
DWORD c64kBlocksCurrent;
BOOL fForceOverWrite = 0 != (CDBBACKUP_OVERWRITE & Flags);
WCHAR *pwszFileInUse = NULL;
// Get DB, Log & System paths from registry
hr = myGetDBPaths(
pwszConfig,
pwszLogPath,
pwszzFileList,
&apwszDirs[IDIR_DB],
&apwszDirs[IDIR_LOG],
&apwszDirs[IDIR_SYSTEM]);
_JumpIfError(hr, error, "myGetDBPaths");
DBGPRINT((DBG_SS_CERTLIBI, "DBDir: %ws\n", apwszDirs[IDIR_DB]));
DBGPRINT((DBG_SS_CERTLIBI, "LogDir: %ws\n", apwszDirs[IDIR_LOG]));
DBGPRINT((DBG_SS_CERTLIBI, "SysDir: %ws\n", apwszDirs[IDIR_SYSTEM]));
CSASSERT((NULL == pwszzFileList) ^ (0 == (CDBBACKUP_INCREMENTAL & Flags)));
for (i = 0; i < ARRAYSIZE(apwszDirs); i++)
{
BOOL fFilesExist;
if (!myIsDirectory(apwszDirs[i]))
{
hr = HRESULT_FROM_WIN32(ERROR_DIRECTORY);
_JumpErrorStr(hr, error, "not a directory", apwszDirs[i]);
}
hr = myDoDBFilesExistInDir(apwszDirs[i], &fFilesExist, &pwszFileInUse);
_JumpIfError(hr, error, "myDoDBFilesExistInDir");
if (NULL != pwszFileInUse)
{
_PrintErrorStr(
HRESULT_FROM_WIN32(ERROR_BUSY),
"myDoDBFilesExistInDir",
pwszFileInUse);
}
if (!fFilesExist)
{
if (CDBBACKUP_INCREMENTAL & Flags)
{
// Incremental restore -- some DB files should already exist
hr = HRESULT_FROM_WIN32(ERROR_DIRECTORY);
_JumpErrorStr(hr, error, "myDoDBFilesExistInDir", apwszDirs[i]);
}
}
else if (0 == (CDBBACKUP_INCREMENTAL & Flags))
{
// Full restore -- no DB files should exist yet
if (!fForceOverWrite)
{
hr = HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY);
_JumpErrorStr(
hr,
error,
"myDoDBFilesExistInDir",
NULL != pwszFileInUse? pwszFileInUse : apwszDirs[i]);
}
hr = myDeleteDBFilesInDir(apwszDirs[i]);
if (S_OK != hr)
{
_PrintErrorStr(hr, "myDeleteDBFilesInDir", apwszDirs[i]);
}
}
}
// copy files to appropriate target directories
if (0 == (CDBBACKUP_INCREMENTAL & Flags))
{
c64kBlocksCurrent = 0;
hr = RestoreCopyFilePattern(
fForceOverWrite,
pwszBackupDir, // source dir
apwszDirs[IDIR_DB], // target dir
L"*" wszDBFILENAMEEXT, // match pattern
c64kDBBlocks,
&c64kBlocksCurrent, // current total file size
&pdbp->dwDBPercentComplete);
_JumpIfError(hr, error, "RestoreCopyFile");
CSASSERT(c64kDBBlocks == c64kBlocksCurrent);
}
CSASSERT(100 >= pdbp->dwDBPercentComplete);
pdbp->dwDBPercentComplete = 100;
c64kBlocksCurrent = 0;
hr = RestoreCopyFilePattern(
fForceOverWrite,
pwszBackupDir, // source dir
apwszDirs[IDIR_LOG], // target dir
L"*" wszLOGFILENAMEEXT, // match pattern
c64kLogBlocks,
&c64kBlocksCurrent, // current total file size
&pdbp->dwLogPercentComplete);
_JumpIfError(hr, error, "RestoreCopyFile");
CSASSERT(c64kLogBlocks == c64kBlocksCurrent);
CSASSERT(100 >= pdbp->dwLogPercentComplete);
pdbp->dwLogPercentComplete = 100;
CSASSERT(100 >= pdbp->dwTruncateLogPercentComplete);
pdbp->dwTruncateLogPercentComplete = 100;
hr = S_OK;
error:
if (NULL != pwszFileInUse)
{
LocalFree(pwszFileInUse);
}
for (i = 0; i < ARRAYSIZE(apwszDirs); i++)
{
if (NULL != apwszDirs[i])
{
LocalFree(const_cast<WCHAR *>(apwszDirs[i]));
}
}
return(hr);
}
HRESULT
myDeleteRestoreInProgressKey(
IN WCHAR const *pwszConfig)
{
HRESULT hr;
HKEY hkey = NULL;
WCHAR *pwszRegPath = NULL;
hr = myRegOpenRelativeKey(
pwszConfig,
L"",
RORKF_CREATESUBKEYS,
&pwszRegPath,
NULL, // ppwszName
&hkey);
_JumpIfErrorStr(hr, error, "myRegOpenRelativeKey", pwszConfig);
hr = RegDeleteKey(hkey, wszREGKEYRESTOREINPROGRESS);
_JumpIfError(hr, error, "RegDeleteKey");
error:
if (NULL != hkey)
{
RegCloseKey(hkey);
}
if (NULL != pwszRegPath)
{
LocalFree(pwszRegPath);
}
return(hr);
}
// If CDBBACKUP_VERIFYONLY, only verify the passed directory contains valid
// files. If pwszBackupDir is NULL, delete the RestoreInProgress registry key.
HRESULT
myRestoreDB(
IN WCHAR const *pwszConfig,
IN DWORD Flags,
OPTIONAL IN WCHAR const *pwszBackupDir,
OPTIONAL IN WCHAR const *pwszCheckPointFilePath,
OPTIONAL IN WCHAR const *pwszLogPath,
OPTIONAL IN WCHAR const *pwszBackupLogPath,
OPTIONAL OUT DBBACKUPPROGRESS *pdbp)
{
HRESULT hr;
HRESULT hr2;
WCHAR buf[MAX_PATH];
WCHAR *pwszPathDBDir = NULL;
WCHAR *pwszDATFile = NULL;
WCHAR *pwszzFileList = NULL;
DWORD cbList;
CSEDB_RSTMAP RstMap[1];
DWORD crstmap = 0;
WCHAR *pwszFile;
DWORD logMin;
DWORD logMax;
HCSBC hcsbc;
BOOL fBegin = FALSE;
BOOL fImpersonating = FALSE;
DBBACKUPPROGRESS dbp;
DWORD c64kDBBlocks; // 64k blocks in DB files to be restored
DWORD c64kLogBlocks; // 64k blocks in Log files to be restored
if (NULL == pdbp)
{
pdbp = &dbp;
}
ZeroMemory(pdbp, sizeof(*pdbp));
if (!ImpersonateSelf(SecurityImpersonation))
{
hr = myHLastError();
_JumpError(hr, error, "ImpersonateSelf");
}
fImpersonating = TRUE;
hr = myEnablePrivilege(SE_RESTORE_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
hr = myEnablePrivilege(SE_BACKUP_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
if (NULL == pwszConfig ||
((CDBBACKUP_VERIFYONLY & Flags) && NULL == pwszBackupDir))
{
hr = E_POINTER;
_JumpError(hr, error, "NULL parm");
}
if (NULL != pwszBackupDir)
{
if (!GetFullPathName(pwszBackupDir, ARRAYSIZE(buf), buf, &pwszFile))
{
hr = myHLastError();
_JumpError(hr, error, "GetFullPathName");
}
hr = myBuildPathAndExt(buf, wszDBBACKUPSUBDIR, NULL, &pwszPathDBDir);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = myVerifyBackupDirectory(
pwszConfig,
Flags,
pwszPathDBDir,
&logMin,
&logMax,
&c64kDBBlocks,
&c64kLogBlocks);
_JumpIfError(hr, error, "myVerifyBackupDirectory");
DBGPRINT((
DBG_SS_CERTLIBI,
"c64kBlocks=%u+%u\n",
c64kDBBlocks,
c64kLogBlocks));
if (0 == (CDBBACKUP_INCREMENTAL & Flags))
{
hr = myBuildPathAndExt(
pwszPathDBDir,
wszDBBACKUPCERTBACKDAT,
NULL,
&pwszDATFile);
_JumpIfError(hr, error, "myBuildPathAndExt");
hr = DecodeFileW(
pwszDATFile,
(BYTE **) &pwszzFileList,
&cbList,
CRYPT_STRING_BINARY);
_JumpIfError(hr, error, "DecodeFileW");
if (2 * sizeof(WCHAR) >= cbList ||
L'\0' != pwszzFileList[cbList/sizeof(WCHAR) - 1] ||
L'\0' != pwszzFileList[cbList/sizeof(WCHAR) - 2])
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "pwszzFileList malformed");
}
RstMap[0].pwszDatabaseName = pwszzFileList;
RstMap[0].pwszNewDatabaseName = pwszzFileList;
crstmap = 1;
}
if (0 == (CDBBACKUP_VERIFYONLY & Flags))
{
hr = myRestoreDBFiles(
pwszConfig,
Flags,
pwszPathDBDir,
pwszLogPath,
pwszzFileList,
c64kDBBlocks,
c64kLogBlocks,
pdbp);
_JumpIfError(hr, error, "myRestoreDBFiles");
hr = CertSrvRestorePrepare(pwszConfig, CSRESTORE_TYPE_FULL, &hcsbc);
_JumpIfError(hr, error, "CertSrvRestorePrepare");
fBegin = TRUE;
hr = CertSrvRestoreRegister(
hcsbc,
pwszCheckPointFilePath,
pwszLogPath,
0 == crstmap? NULL : RstMap,
crstmap,
pwszBackupLogPath,
logMin,
logMax);
// When running only as backup operator, we don't have rights
// in the registry and CertSrvRestoreRegister fails with access
// denied. We try to mark for restore through a file.
if (E_ACCESSDENIED == hr)
{
hr = CertSrvRestoreRegisterThroughFile(
hcsbc,
pwszCheckPointFilePath,
pwszLogPath,
0 == crstmap? NULL : RstMap,
crstmap,
pwszBackupLogPath,
logMin,
logMax);
_JumpIfError(hr, error, "CertSrvRestoreRegisterThroughFile");
}
else
{
_JumpIfError(hr, error, "CertSrvRestoreRegister");
hr = CertSrvRestoreRegisterComplete(hcsbc, S_OK);
_JumpIfError(hr, error, "CertSrvRestoreRegisterComplete");
}
}
}
else if (0 == (CDBBACKUP_VERIFYONLY & Flags))
{
hr = myDeleteRestoreInProgressKey(pwszConfig);
_JumpIfError(hr, error, "myDeleteRestoreInProgressKey");
}
hr = S_OK;
error:
if (fBegin)
{
hr2 = CertSrvRestoreEnd(hcsbc);
_PrintIfError(hr2, "CertSrvBackupEnd");
if (S_OK == hr)
{
hr = hr2;
}
}
if (NULL != pwszzFileList)
{
LocalFree(pwszzFileList);
}
if (NULL != pwszDATFile)
{
LocalFree(pwszDATFile);
}
if (NULL != pwszPathDBDir)
{
LocalFree(pwszPathDBDir);
}
if (fImpersonating)
{
myEnablePrivilege(SE_BACKUP_NAME, FALSE);
myEnablePrivilege(SE_RESTORE_NAME, FALSE);
RevertToSelf();
}
return(hr);
}
HRESULT
myPFXExportCertStore(
IN HCERTSTORE hStore,
OUT CRYPT_DATA_BLOB *ppfx,
IN WCHAR const *pwszPassword,
IN DWORD dwFlags)
{
HRESULT hr;
ppfx->pbData = NULL;
if (!PFXExportCertStore(hStore, ppfx, pwszPassword, dwFlags))
{
hr = myHLastError();
_JumpError(hr, error, "PFXExportCertStore");
}
ppfx->pbData = (BYTE *) LocalAlloc(LMEM_FIXED, ppfx->cbData);
if (NULL == ppfx->pbData)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "no memory for PFX blob");
}
if (!PFXExportCertStore(hStore, ppfx, pwszPassword, dwFlags))
{
hr = myHLastError();
_JumpError(hr, error, "PFXExportCertStore");
}
hr = S_OK;
error:
return(hr);
}
////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////
HRESULT
myAddChainToMemoryStore(
IN HCERTSTORE hMemoryStore,
IN CERT_CONTEXT const *pCertContext)
{
HRESULT hr;
DWORD i;
CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
CERT_CHAIN_PARA CertChainPara;
CERT_SIMPLE_CHAIN *pSimpleChain;
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
CertChainPara.cbSize = sizeof(CertChainPara);
if (!CertGetCertificateChain(
HCCE_LOCAL_MACHINE,
pCertContext,
NULL,
NULL,
&CertChainPara,
0,
NULL,
&pCertChainContext))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCertificateChain");
}
// make sure there is at least 1 simple chain
if (0 == pCertChainContext->cChain)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "pCertChainContext->cChain");
}
pSimpleChain = pCertChainContext->rgpChain[0];
for (i = 0; i < pSimpleChain->cElement; i++)
{
if (!CertAddCertificateContextToStore(
hMemoryStore,
pSimpleChain->rgpElement[i]->pCertContext,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL))
{
hr = myHLastError();
_JumpError(hr, error, "CertAddCertificateContextToStore");
}
}
hr = S_OK;
error:
if (pCertChainContext != NULL)
{
CertFreeCertificateChain(pCertChainContext);
}
return(hr);
}
HRESULT
SaveCACertChainToMemoryStore(
IN WCHAR const *pwszSanitizedName,
IN DWORD iCert,
IN HCERTSTORE hMyStore,
IN HCERTSTORE hTempMemoryStore)
{
HRESULT hr;
CERT_CONTEXT const *pccCA = NULL;
CRYPT_KEY_PROV_INFO *pkpi = NULL;
DWORD NameId;
hr = myFindCACertByHashIndex(
hMyStore,
pwszSanitizedName,
CSRH_CASIGCERT,
iCert,
&NameId,
&pccCA);
_JumpIfError(hr, error, "myFindCACertByHashIndex");
hr = myRepairCertKeyProviderInfo(pccCA, TRUE, &pkpi);
if (S_OK != hr)
{
if (CRYPT_E_NOT_FOUND != hr)
{
_JumpError(hr, error, "myRepairCertKeyProviderInfo");
}
}
else if (NULL != pkpi)
{
BOOL fMatchingKey;
hr = myVerifyPublicKey(
pccCA,
FALSE,
NULL, // pKeyProvInfo
NULL, // pPublicKeyInfo
&fMatchingKey);
if (S_OK != hr)
{
if (!IsHrSkipPrivateKey(hr))
{
_JumpError(hr, error, "myVerifyPublicKey");
}
_PrintError2(hr, "myVerifyPublicKey", hr);
}
else if (!fMatchingKey)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "Key doesn't match cert");
}
}
// Begin Chain Building
hr = myAddChainToMemoryStore(hTempMemoryStore, pccCA);
_JumpIfError(hr, error, "myAddChainToMemoryStore");
// End Chain Building
error:
if (NULL != pkpi)
{
LocalFree(pkpi);
}
if (NULL != pccCA)
{
CertFreeCertificateContext(pccCA);
}
return(hr);
}
HRESULT
myCertServerExportPFX(
IN WCHAR const *pwszCA,
IN WCHAR const *pwszBackupDir,
IN WCHAR const *pwszPassword,
IN BOOL fForceOverWrite,
IN BOOL fMustExportPrivateKeys,
OPTIONAL OUT WCHAR **ppwszPFXFile)
{
HRESULT hr;
HCERTSTORE hMyStore = NULL;
HCERTSTORE hTempMemoryStore = NULL;
CRYPT_DATA_BLOB pfx;
WCHAR *pwszPFXFile = NULL;
BOOL fImpersonating = FALSE;
WCHAR *pwszSanitizedCA = NULL;
WCHAR *pwszRevertCA = NULL;
DWORD cCACert;
DWORD cCACertSaved;
DWORD i;
pfx.pbData = NULL;
if (!ImpersonateSelf(SecurityImpersonation))
{
hr = myHLastError();
_JumpError(hr, error, "ImpersonateSelf");
}
fImpersonating = TRUE;
hr = myEnablePrivilege(SE_BACKUP_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
if (NULL != ppwszPFXFile)
{
*ppwszPFXFile = NULL;
}
while (TRUE)
{
hr = mySanitizeName(pwszCA, &pwszSanitizedCA);
_JumpIfError(hr, error, "mySanitizeName");
// get CA cert count
hr = myGetCARegHashCount(pwszSanitizedCA, CSRH_CASIGCERT, &cCACert);
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr &&
NULL == pwszRevertCA)
{
LocalFree(pwszSanitizedCA);
pwszSanitizedCA = NULL;
hr = myRevertSanitizeName(pwszCA, &pwszRevertCA);
_JumpIfError(hr, error, "myRevertSanitizeName");
pwszCA = pwszRevertCA;
continue;
}
_JumpIfError(hr, error, "myGetCARegHashCount");
if (NULL != pwszRevertCA)
{
DBGPRINT((
DBG_SS_CERTLIB,
"myCertServerExportPFX called with Sanitized Name: %ws\n",
pwszSanitizedCA));
}
break;
}
if (!myIsDirectory(pwszBackupDir))
{
if (!CreateDirectory(pwszBackupDir, NULL))
{
hr = myHLastError();
if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) != hr)
{
_JumpError(hr, error, "CreateDirectory");
}
}
}
pwszPFXFile = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(wcslen(pwszBackupDir) +
1 +
wcslen(pwszSanitizedCA) +
ARRAYSIZE(wszPFXFILENAMEEXT)) *
sizeof(WCHAR));
if (NULL == pwszPFXFile)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(pwszPFXFile, pwszBackupDir);
wcscat(pwszPFXFile, L"\\");
wcscat(pwszPFXFile, pwszSanitizedCA);
wcscat(pwszPFXFile, wszPFXFILENAMEEXT);
DBGPRINT((DBG_SS_CERTLIBI, "myCertServerExportPFX(%ws)\n", pwszPFXFile));
hMyStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING,
NULL, // hProv
CERT_STORE_OPEN_EXISTING_FLAG |
CERT_STORE_ENUM_ARCHIVED_FLAG |
CERT_SYSTEM_STORE_LOCAL_MACHINE |
CERT_STORE_READONLY_FLAG,
wszMY_CERTSTORE);
if (NULL == hMyStore)
{
hr = myHLastError();
_JumpError(hr, error, "CertOpenStore");
}
hTempMemoryStore = CertOpenStore(
CERT_STORE_PROV_MEMORY,
X509_ASN_ENCODING,
NULL,
0,
NULL);
if (NULL == hTempMemoryStore)
{
hr = myHLastError();
_JumpError(hr, error, "CertOpenStore");
}
cCACertSaved = 0;
for (i = 0; i < cCACert; i++)
{
hr = SaveCACertChainToMemoryStore(
pwszSanitizedCA,
i,
hMyStore,
hTempMemoryStore);
_PrintIfError(hr, "SaveCACertChainToMemoryStore");
if (S_FALSE != hr)
{
_JumpIfError(hr, error, "SaveCACertChainToMemoryStore");
cCACertSaved++;
}
}
if (0 == cCACertSaved)
{
hr = CRYPT_E_NOT_FOUND;
_JumpError(hr, error, "SaveCACertChainToMemoryStore");
}
// done, have built entire chain for all CA Certs
// GemPlus returns NTE_BAD_TYPE instead of NTE_BAD_KEY, blowing up
// REPORT_NOT_ABLE* filtering. if they ever get this right, we can pass
// "[...] : EXPORT_PRIVATE_KEYS"
hr = myPFXExportCertStore(
hTempMemoryStore,
&pfx,
pwszPassword,
fMustExportPrivateKeys? (EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY) : 0);
_JumpIfError(hr, error, "myPFXExportCertStore");
hr = EncodeToFileW(
pwszPFXFile,
pfx.pbData,
pfx.cbData,
CRYPT_STRING_BINARY | (fForceOverWrite? DECF_FORCEOVERWRITE : 0));
_JumpIfError(hr, error, "EncodeToFileW");
if (NULL != ppwszPFXFile)
{
*ppwszPFXFile = pwszPFXFile;
pwszPFXFile = NULL;
}
error:
if (NULL != pwszSanitizedCA)
{
LocalFree(pwszSanitizedCA);
}
if (NULL != pwszRevertCA)
{
LocalFree(pwszRevertCA);
}
if (NULL != pwszPFXFile)
{
LocalFree(pwszPFXFile);
}
if (NULL != hMyStore)
{
CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != hTempMemoryStore)
{
CertCloseStore(hTempMemoryStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != pfx.pbData)
{
LocalFree(pfx.pbData);
}
if (fImpersonating)
{
myEnablePrivilege(SE_BACKUP_NAME, FALSE);
RevertToSelf();
}
return(hr);
}
HRESULT
FindKeyUsage(
IN DWORD cExtension,
IN CERT_EXTENSION const *rgExtension,
OUT DWORD *pdwUsage)
{
HRESULT hr;
DWORD i;
CRYPT_BIT_BLOB *pblob = NULL;
*pdwUsage = 0;
for (i = 0; i < cExtension; i++)
{
CERT_EXTENSION const *pce;
pce = &rgExtension[i];
if (0 == strcmp(pce->pszObjId, szOID_KEY_USAGE))
{
DWORD cb;
// Decode CRYPT_BIT_BLOB:
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_KEY_USAGE,
pce->Value.pbData,
pce->Value.cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pblob,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
if (1 > pblob->cbData || 8 < pblob->cUnusedBits)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Key Usage Extension too small");
}
*pdwUsage = *pblob->pbData;
hr = S_OK;
goto error;
}
}
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "no Key Usage Extension");
error:
if (NULL != pblob)
{
LocalFree(pblob);
}
return(hr);
}
HRESULT
mySetKeySpec(
IN CERT_CONTEXT const *pCert,
OUT DWORD *pdwKeySpec)
{
HRESULT hr;
DWORD dwKeyUsage;
*pdwKeySpec = AT_SIGNATURE;
hr = FindKeyUsage(
pCert->pCertInfo->cExtension,
pCert->pCertInfo->rgExtension,
&dwKeyUsage);
_JumpIfError(hr, error, "FindKeyUsage");
if (CERT_KEY_ENCIPHERMENT_KEY_USAGE & dwKeyUsage)
{
*pdwKeySpec = AT_KEYEXCHANGE;
}
hr = S_OK;
error:
// Ignore errors because the Key Usage extension may not exist:
hr = S_OK;
return(hr);
}
HRESULT
myRepairKeyProviderInfo(
IN CERT_CONTEXT const *pCert,
IN BOOL fForceMachineKey,
IN OUT CRYPT_KEY_PROV_INFO *pkpi)
{
HRESULT hr;
BOOL fModified = FALSE;
if (0 == pkpi->dwProvType)
{
pkpi->dwProvType = PROV_RSA_FULL;
fModified = TRUE;
}
if (0 == pkpi->dwKeySpec)
{
hr = mySetKeySpec(pCert, &pkpi->dwKeySpec);
_JumpIfError(hr, error, "mySetKeySpec");
fModified = TRUE;
}
if (fForceMachineKey && 0 == (CRYPT_MACHINE_KEYSET & pkpi->dwFlags))
{
pkpi->dwFlags |= CRYPT_MACHINE_KEYSET;
fModified = TRUE;
}
if (fModified)
{
if (!CertSetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
0,
pkpi))
{
hr = myHLastError();
_JumpError(hr, error, "CertSetCertificateContextProperty");
}
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myRepairCertKeyProviderInfo(
IN CERT_CONTEXT const *pCert,
IN BOOL fForceMachineKey,
OPTIONAL OUT CRYPT_KEY_PROV_INFO **ppkpi)
{
HRESULT hr;
CRYPT_KEY_PROV_INFO *pkpi = NULL;
if (NULL != ppkpi)
{
*ppkpi = NULL;
}
hr = myCertGetKeyProviderInfo(pCert, &pkpi);
_JumpIfError2(hr, error, "myCertGetKeyProviderInfo", CRYPT_E_NOT_FOUND);
CSASSERT(NULL != pkpi);
hr = myRepairKeyProviderInfo(pCert, fForceMachineKey, pkpi);
_JumpIfError(hr, error, "myRepairKeyProviderInfo");
if (NULL != ppkpi)
{
*ppkpi = pkpi;
pkpi = NULL;
}
error:
if (NULL != pkpi)
{
LocalFree(pkpi);
}
return(hr);
}
HRESULT
myGetCertSubjectCommonName(
IN CERT_CONTEXT const *pCert,
OUT WCHAR **ppwszCommonName)
{
HRESULT hr;
CERT_NAME_INFO *pCertNameInfo = NULL;
DWORD cbCertNameInfo;
WCHAR const *pwszName;
if (!myDecodeName(
X509_ASN_ENCODING,
X509_UNICODE_NAME,
pCert->pCertInfo->Subject.pbData,
pCert->pCertInfo->Subject.cbData,
CERTLIB_USE_LOCALALLOC,
&pCertNameInfo,
&cbCertNameInfo))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeName");
}
hr = myGetCertNameProperty(pCertNameInfo, szOID_COMMON_NAME, &pwszName);
_JumpIfError(hr, error, "myGetCertNameProperty");
*ppwszCommonName = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(wcslen(pwszName) + 1) * sizeof(WCHAR));
if (NULL == *ppwszCommonName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(*ppwszCommonName, pwszName);
hr = S_OK;
error:
if (NULL != pCertNameInfo)
{
LocalFree(pCertNameInfo);
}
return(hr);
}
HRESULT
myGetChainArrayFromStore(
IN HCERTSTORE hStore,
IN BOOL fCAChain,
IN BOOL fUserStore,
OPTIONAL OUT WCHAR **ppwszCommonName,
IN OUT DWORD *pcRestoreChain,
OPTIONAL OUT RESTORECHAIN *paRestoreChain)
{
HRESULT hr;
CERT_CONTEXT const *pCert = NULL;
WCHAR *pwszCommonName = NULL;
CERT_CHAIN_PARA ChainParams;
CRYPT_KEY_PROV_INFO *pkpi = NULL;
DWORD iRestoreChain = 0;
if (NULL != ppwszCommonName)
{
*ppwszCommonName = NULL;
}
if (NULL != paRestoreChain)
{
ZeroMemory(paRestoreChain, *pcRestoreChain * sizeof(paRestoreChain[0]));
}
// Look for certificates with keys. There should be at least one.
while (TRUE)
{
BOOL fMatchingKey;
WCHAR *pwszCommonNameT;
CERT_CHAIN_CONTEXT const *pChain;
DWORD NameId;
pCert = CertEnumCertificatesInStore(hStore, pCert);
if (NULL == pCert)
{
break;
}
if (NULL != pkpi)
{
LocalFree(pkpi);
pkpi = NULL;
}
hr = myRepairCertKeyProviderInfo(pCert, !fUserStore, &pkpi);
if (S_OK != hr)
{
if (CRYPT_E_NOT_FOUND == hr)
{
continue;
}
_JumpError(hr, error, "myRepairCertKeyProviderInfo");
}
if (NULL == pkpi || NULL == pkpi->pwszContainerName)
{
continue;
}
hr = myVerifyPublicKey(
pCert,
FALSE,
pkpi, // pKeyProvInfo
NULL, // pPublicKeyInfo
&fMatchingKey);
_JumpIfError(hr, error, "myVerifyPublicKey");
if (!fMatchingKey)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "Key doesn't match cert");
}
hr = myGetCertSubjectCommonName(pCert, &pwszCommonNameT);
_JumpIfError(hr, error, "myGetCertSubjectCommonName");
if (NULL == pwszCommonName)
{
pwszCommonName = pwszCommonNameT;
}
else
{
if (0 != lstrcmp(pwszCommonName, pwszCommonNameT))
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "multiple CommonNames");
}
LocalFree(pwszCommonNameT);
}
if (fCAChain)
{
hr = myGetNameId(pCert, &NameId);
_PrintIfError(hr, "myGetNameId");
}
else
{
NameId = 0;
}
if (NULL != paRestoreChain && iRestoreChain >= *pcRestoreChain)
{
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
_JumpError(hr, error, "Chain array full");
}
ZeroMemory(&ChainParams, sizeof(ChainParams));
ChainParams.cbSize = sizeof(ChainParams);
ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
// Get the chain and verify the cert:
if (!CertGetCertificateChain(
HCCE_LOCAL_MACHINE, // hChainEngine
pCert, // pCertContext
NULL, // pTime
hStore, // hAdditionalStore
&ChainParams, // pChainPara
0, // dwFlags
NULL, // pvReserved
&pChain)) // ppChainContext
{
hr = myHLastError();
_JumpIfError(hr, error, "CertGetCertificateChain");
}
if (NULL != paRestoreChain)
{
paRestoreChain[iRestoreChain].pChain = pChain;
paRestoreChain[iRestoreChain].NameId = NameId;
}
else
{
CertFreeCertificateChain(pChain);
}
iRestoreChain++;
}
if (NULL != ppwszCommonName)
{
*ppwszCommonName = pwszCommonName;
pwszCommonName = NULL;
}
*pcRestoreChain = iRestoreChain;
hr = S_OK;
error:
if (S_OK != hr && NULL != paRestoreChain)
{
for (iRestoreChain = 0; iRestoreChain < *pcRestoreChain; iRestoreChain++)
{
if (NULL != paRestoreChain[iRestoreChain].pChain)
{
CertFreeCertificateChain(paRestoreChain[iRestoreChain].pChain);
paRestoreChain[iRestoreChain].pChain = NULL;
}
}
}
if (NULL != pwszCommonName)
{
LocalFree(pwszCommonName);
}
if (NULL != pkpi)
{
LocalFree(pkpi);
}
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
return(hr);
}
HRESULT
myCopyKeys(
IN CRYPT_KEY_PROV_INFO const *pkpi,
IN WCHAR const *pwszOldContainer,
IN WCHAR const *pwszNewContainer,
IN BOOL fOldUserKey,
IN BOOL fNewUserKey,
IN BOOL fForceOverWrite)
{
HRESULT hr;
HCRYPTPROV hProvOld = NULL;
HCRYPTKEY hKeyOld = NULL;
HCRYPTPROV hProvNew = NULL;
HCRYPTKEY hKeyNew = NULL;
CRYPT_BIT_BLOB PrivateKey;
BOOL fKeyContainerNotFound = FALSE;
PrivateKey.pbData = NULL;
if (!myCertSrvCryptAcquireContext(
&hProvOld,
pwszOldContainer,
pkpi->pwszProvName,
pkpi->dwProvType,
pkpi->dwFlags,
!fOldUserKey))
{
hr = myHLastError();
_JumpError(hr, error, "myCertSrvCryptAcquireContext");
}
if (!CryptGetUserKey(hProvOld, pkpi->dwKeySpec, &hKeyOld))
{
hr = myHLastError();
_JumpError(hr, error, "CryptGetUserKey");
}
hr = myCryptExportPrivateKey(
hKeyOld,
&PrivateKey.pbData,
&PrivateKey.cbData);
_JumpIfError(hr, error, "myCryptExportPrivateKey");
if (myCertSrvCryptAcquireContext(
&hProvNew,
pwszNewContainer,
pkpi->pwszProvName,
pkpi->dwProvType,
pkpi->dwFlags,
!fNewUserKey))
{
if (!fForceOverWrite)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
_JumpErrorStr(hr, error, "Key Container Exists", pwszNewContainer);
}
// Delete the target key container
CryptReleaseContext(hProvNew, 0);
if (myCertSrvCryptAcquireContext(
&hProvNew,
pwszNewContainer,
pkpi->pwszProvName,
pkpi->dwProvType,
pkpi->dwFlags | CRYPT_DELETEKEYSET,
!fNewUserKey))
{
fKeyContainerNotFound = TRUE;
}
hProvNew = NULL;
}
else
{
fKeyContainerNotFound = TRUE;
}
if (!myCertSrvCryptAcquireContext(
&hProvNew,
pwszNewContainer,
pkpi->pwszProvName,
pkpi->dwProvType,
pkpi->dwFlags |
fKeyContainerNotFound? CRYPT_NEWKEYSET : 0,
!fNewUserKey))
{
hr = myHLastError();
_JumpError(hr, error, "myCertSrvCryptAcquireContext");
}
if (!CryptImportKey(
hProvNew,
PrivateKey.pbData,
PrivateKey.cbData,
NULL, // HCRYPTKEY hPubKey
CRYPT_EXPORTABLE,
&hKeyNew))
{
hr = myHLastError();
_JumpError(hr, error, "CryptImportKey");
}
error:
if (NULL != PrivateKey.pbData)
{
LocalFree(PrivateKey.pbData);
}
if (NULL != hKeyNew)
{
CryptDestroyKey(hKeyNew);
}
if (NULL != hProvNew)
{
CryptReleaseContext(hProvNew, 0);
}
if (NULL != hKeyOld)
{
CryptDestroyKey(hKeyOld);
}
if (NULL != hProvOld)
{
CryptReleaseContext(hProvOld, 0);
}
return(hr);
}
HRESULT
myImportChainAndKeys(
IN WCHAR const *pwszSanitizedCA,
IN DWORD iCert,
IN DWORD iKey,
IN BOOL fForceOverWrite,
IN CERT_CHAIN_CONTEXT const *pChain,
OPTIONAL OUT CERT_CONTEXT const **ppccNewestCA)
{
HRESULT hr;
BOOL fDeleted = FALSE;
CRYPT_KEY_PROV_INFO *pkpi = NULL;
CERT_CHAIN_ELEMENT **ppChainElement;
WCHAR *pwszKeyContainerName = NULL;
hr = myAllocIndexedName(pwszSanitizedCA, iKey, &pwszKeyContainerName);
_JumpIfError(hr, error, "myAllocIndexedName");
ppChainElement = pChain->rgpChain[0]->rgpElement;
hr = myCertGetKeyProviderInfo(ppChainElement[0]->pCertContext, &pkpi);
_JumpIfError(hr, error, "myCertGetKeyProviderInfo");
if (iCert == iKey)
{
hr = myCopyKeys(
pkpi,
pkpi->pwszContainerName, // pwszOldContainer
pwszKeyContainerName, // pwszNewContainer
FALSE, // fOldUserKey
FALSE, // fNewUserKey
fForceOverWrite);
_JumpIfError(hr, error, "myCopyKeys");
}
pkpi->pwszContainerName = pwszKeyContainerName;
hr = mySaveChainAndKeys(
pChain->rgpChain[0],
wszMY_CERTSTORE,
CERT_SYSTEM_STORE_LOCAL_MACHINE |
CERT_STORE_BACKUP_RESTORE_FLAG,
pkpi,
ppccNewestCA);
_JumpIfError(hr, error, "mySaveChainAndKeys");
hr = S_OK;
error:
if (NULL != pkpi)
{
LocalFree(pkpi);
}
if (NULL != pwszKeyContainerName)
{
LocalFree(pwszKeyContainerName);
}
return(hr);
}
HRESULT
FindPFXInBackupDir(
IN WCHAR const *pwszBackupDir,
OUT WCHAR **ppwszPFXFile)
{
HRESULT hr;
HANDLE hf;
WIN32_FIND_DATA wfd;
WCHAR wszpath[MAX_PATH];
WCHAR wszfile[MAX_PATH];
DWORD cFile = 0;
*ppwszPFXFile = NULL;
wcscpy(wszpath, pwszBackupDir);
wcscat(wszpath, L"\\*");
wcscat(wszpath, wszPFXFILENAMEEXT);
hf = FindFirstFile(wszpath, &wfd);
if (INVALID_HANDLE_VALUE != hf)
{
do {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
continue;
}
cFile++;
wcscpy(wszfile, wfd.cFileName);
//printf("File: %ws\n", wszfile);
break;
} while (FindNextFile(hf, &wfd));
FindClose(hf);
}
if (0 == cFile)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "no *.p12 files");
}
if (1 < cFile)
{
hr = HRESULT_FROM_WIN32(ERROR_DIRECTORY);
_JumpError(hr, error, "Too many *.p12 files");
}
*ppwszPFXFile = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(wcslen(pwszBackupDir) +
1 +
wcslen(wszfile) +
1) * sizeof(WCHAR));
if (NULL == *ppwszPFXFile)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(*ppwszPFXFile, pwszBackupDir);
wcscat(*ppwszPFXFile, L"\\");
wcscat(*ppwszPFXFile, wszfile);
hr = S_OK;
error:
return(hr);
}
// Return TRUE if pcc is newer than pcc2
BOOL
IsCACertNewer(
IN CERT_CONTEXT const *pcc,
IN DWORD NameId,
IN CERT_CONTEXT const *pcc2,
IN DWORD NameId2)
{
BOOL fNewer = FALSE;
CERT_INFO const *pci = pcc->pCertInfo;
CERT_INFO const *pci2 = pcc2->pCertInfo;
if (MAXDWORD != NameId && MAXDWORD != NameId2)
{
if (CANAMEIDTOICERT(NameId) > CANAMEIDTOICERT(NameId2))
{
fNewer = TRUE;
}
}
else
if (CompareFileTime(&pci->NotAfter, &pci2->NotAfter) > 0)
{
fNewer = TRUE;
}
#if 0
HRESULT hr;
WCHAR *pwszDate = NULL;
WCHAR *pwszDate2 = NULL;
hr = myGMTFileTimeToWszLocalTime(&pci->NotAfter, &pwszDate);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
hr = myGMTFileTimeToWszLocalTime(&pci2->NotAfter, &pwszDate2);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
printf(
"%u.%u %ws is %wsnewer than %u.%u %ws\n",
CANAMEIDTOICERT(NameId),
CANAMEIDTOIKEY(NameId),
pwszDate,
fNewer? L"" : L"NOT ",
CANAMEIDTOICERT(NameId2),
CANAMEIDTOIKEY(NameId2),
pwszDate2);
if (NULL != pwszDate) LocalFree(pwszDate);
if (NULL != pwszDate2) LocalFree(pwszDate2);
#endif
return(fNewer);
}
#if 0
VOID
DumpChainArray(
IN char const *psz,
IN DWORD cCACert,
IN OUT RESTORECHAIN *paRestoreChain)
{
HRESULT hr;
DWORD i;
printf("\n%hs:\n", psz);
for (i = 0; i < cCACert; i++)
{
WCHAR *pwszDate;
hr = myGMTFileTimeToWszLocalTime(
&paRestoreChain[i].pChain->rgpChain[0]->rgpElement[0]->pCertContext->pCertInfo->NotBefore,
&pwszDate);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
printf(
" %u: %u.%u %ws",
i,
CANAMEIDTOICERT(paRestoreChain[i].NameId),
CANAMEIDTOIKEY(paRestoreChain[i].NameId),
pwszDate);
if (NULL != pwszDate) LocalFree(pwszDate);
hr = myGMTFileTimeToWszLocalTime(
&paRestoreChain[i].pChain->rgpChain[0]->rgpElement[0]->pCertContext->pCertInfo->NotAfter,
&pwszDate);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
printf(" -- %ws\n", pwszDate);
if (NULL != pwszDate) LocalFree(pwszDate);
}
printf("\n");
}
#endif
HRESULT
SortCACerts(
IN DWORD cCACert,
IN OUT RESTORECHAIN *paRestoreChain)
{
HRESULT hr;
DWORD i;
DWORD j;
#if 0
DumpChainArray("Start", cCACert, paRestoreChain);
#endif
for (i = 0; i < cCACert; i++)
{
for (j = i + 1; j < cCACert; j++)
{
CERT_CHAIN_CONTEXT const *pChain;
DWORD NameId;
DWORD NameId2;
CERT_CONTEXT const *pcc;
CERT_CONTEXT const *pcc2;
pChain = paRestoreChain[i].pChain;
NameId = paRestoreChain[i].NameId;
NameId2 = paRestoreChain[j].NameId;
pcc = pChain->rgpChain[0]->rgpElement[0]->pCertContext;
pcc2 = paRestoreChain[j].pChain->rgpChain[0]->rgpElement[0]->pCertContext;
#if 0
printf(
"%u(%u.%u) %u(%u.%u): ",
i,
CANAMEIDTOIKEY(NameId),
CANAMEIDTOICERT(NameId),
j,
CANAMEIDTOIKEY(NameId2),
CANAMEIDTOICERT(NameId2));
#endif
if (IsCACertNewer(pcc, NameId, pcc2, NameId2))
{
paRestoreChain[i] = paRestoreChain[j];
paRestoreChain[j].pChain = pChain;
paRestoreChain[j].NameId = NameId;
}
}
}
#if 0
DumpChainArray("End", cCACert, paRestoreChain);
#endif
hr = S_OK;
//error:
return(hr);
}
HRESULT
myDeleteGuidKeys(
IN HCERTSTORE hStorePFX,
IN BOOL fMachineKeySet)
{
HRESULT hr;
CERT_CONTEXT const *pCert = NULL;
CRYPT_KEY_PROV_INFO *pkpi = NULL;
// Look for certificates with keys, and delete all key containers with
// names that look like GUIDs.
while (TRUE)
{
HCRYPTPROV hProv;
pCert = CertEnumCertificatesInStore(hStorePFX, pCert);
if (NULL == pCert)
{
break;
}
if (NULL != pkpi)
{
LocalFree(pkpi);
pkpi = NULL;
}
hr = myRepairCertKeyProviderInfo(pCert, FALSE, &pkpi);
if (S_OK == hr &&
NULL != pkpi->pwszContainerName &&
wcLBRACE == *pkpi->pwszContainerName)
{
if (myCertSrvCryptAcquireContext(
&hProv,
pkpi->pwszContainerName,
pkpi->pwszProvName,
pkpi->dwProvType,
pkpi->dwFlags | CRYPT_DELETEKEYSET,
fMachineKeySet))
{
DBGPRINT((
DBG_SS_CERTLIBI,
"myDeleteGuidKeys(%ws, %ws)\n",
fMachineKeySet? L"Machine" : L"User",
pkpi->pwszContainerName));
}
}
}
hr = S_OK;
//error:
if (NULL != pkpi)
{
LocalFree(pkpi);
}
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
return(hr);
}
HRESULT
myCertServerImportPFX(
IN WCHAR const *pwszBackupDirOrPFXFile,
IN WCHAR const *pwszPassword,
IN BOOL fForceOverWrite,
OPTIONAL OUT WCHAR **ppwszCommonName,
OPTIONAL OUT WCHAR **ppwszPFXFile,
OPTIONAL OUT CERT_CONTEXT const **ppccNewestCA)
{
HRESULT hr;
CRYPT_DATA_BLOB pfx;
HCERTSTORE hStorePFX = NULL;
WCHAR *pwszCommonName = NULL;
WCHAR *pwszSanitizedName = NULL;
RESTORECHAIN *paRestoreChain = NULL;
WCHAR *pwszPFXFile = NULL;
DWORD FileAttr;
BOOL fImpersonating = FALSE;
DWORD cCACert;
DWORD iCert;
pfx.pbData = NULL;
if (NULL != ppwszCommonName)
{
*ppwszCommonName = NULL;
}
if (NULL != ppwszPFXFile)
{
*ppwszPFXFile = NULL;
}
if (NULL != ppccNewestCA)
{
*ppccNewestCA = NULL;
}
if (!ImpersonateSelf(SecurityImpersonation))
{
hr = myHLastError();
_JumpError(hr, error, "ImpersonateSelf");
}
fImpersonating = TRUE;
hr = myEnablePrivilege(SE_RESTORE_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
hr = myEnablePrivilege(SE_BACKUP_NAME, TRUE);
_JumpIfError(hr, error, "myEnablePrivilege");
FileAttr = GetFileAttributes(pwszBackupDirOrPFXFile);
if (MAXDWORD == FileAttr)
{
hr = myHLastError();
_JumpError(hr, error, "GetFileAttributes");
}
if (FILE_ATTRIBUTE_DIRECTORY & FileAttr)
{
hr = FindPFXInBackupDir(pwszBackupDirOrPFXFile, &pwszPFXFile);
_JumpIfError(hr, error, "FindPFXInBackupDir");
}
else
{
hr = myDupString(pwszBackupDirOrPFXFile, &pwszPFXFile);
_JumpIfError(hr, error, "myDupString");
}
hr = DecodeFileW(pwszPFXFile, &pfx.pbData, &pfx.cbData, CRYPT_STRING_ANY);
_JumpIfError(hr, error, "DecodeFileW");
CSASSERT(NULL != pfx.pbData);
if (!PFXIsPFXBlob(&pfx))
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "PFXIsPFXBlob");
}
hStorePFX = myPFXImportCertStore(&pfx, pwszPassword, CRYPT_EXPORTABLE);
if (NULL == hStorePFX)
{
hr = myHLastError();
_JumpError(hr, error, "myPFXImportCertStore");
}
cCACert = 0;
hr = myGetChainArrayFromStore(
hStorePFX,
TRUE, // fCAChain
FALSE, // fUserStore
&pwszCommonName,
&cCACert,
NULL);
_JumpIfError(hr, error, "myGetChainArrayFromStore");
if (0 == cCACert)
{
hr = HRESULT_FROM_WIN32(CRYPT_E_SELF_SIGNED);
_JumpError(hr, error, "myGetChainArrayFromStore <no chain>");
}
paRestoreChain = (RESTORECHAIN *) LocalAlloc(
LMEM_FIXED | LMEM_ZEROINIT,
cCACert * sizeof(paRestoreChain[0]));
if (NULL == paRestoreChain)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
hr = myGetChainArrayFromStore(
hStorePFX,
TRUE, // fCAChain
FALSE, // fUserStore
NULL,
&cCACert,
paRestoreChain);
_JumpIfError(hr, error, "myGetChainArrayFromStore");
hr = SortCACerts(cCACert, paRestoreChain);
_JumpIfError(hr, error, "SortCACerts");
hr = mySanitizeName(pwszCommonName, &pwszSanitizedName);
_JumpIfError(hr, error, "mySanitizeName");
for (iCert = 0; iCert < cCACert; iCert++)
{
CERT_CHAIN_CONTEXT const *pChain = paRestoreChain[iCert].pChain;
DWORD iKey;
CERT_PUBLIC_KEY_INFO *pPublicKeyInfo;
if (1 > pChain->cChain)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "No Chain Context");
}
// Compute iKey by comparing this public key to the public keys
// of all certs in the array already processed.
pPublicKeyInfo = &pChain->rgpChain[0]->rgpElement[0]->pCertContext->pCertInfo->SubjectPublicKeyInfo;
for (iKey = 0; iKey < iCert; iKey++)
{
if (CertComparePublicKeyInfo(
X509_ASN_ENCODING,
pPublicKeyInfo,
&paRestoreChain[iKey].pChain->rgpChain[0]->rgpElement[0]->pCertContext->pCertInfo->SubjectPublicKeyInfo))
{
// by design, CertComparePublicKeyInfo doesn't set last error!
break;
}
}
DBGPRINT((
DBG_SS_CERTLIB,
"Import: %u.%u -- %u.%u\n",
iCert,
iKey,
CANAMEIDTOICERT(paRestoreChain[iCert].NameId),
CANAMEIDTOIKEY(paRestoreChain[iCert].NameId)));
// Retrieve the cert context for the newest CA cert chain in the PFX
// we are importing. We must return a cert context with the new
// key prov info, not the PFX cert context with a GUID key container.
hr = myImportChainAndKeys(
pwszSanitizedName,
iCert,
iKey,
fForceOverWrite,
pChain,
iCert + 1 == cCACert? ppccNewestCA : NULL);
_JumpIfError(hr, error, "myImportChainAndKeys");
}
if (NULL != ppwszCommonName)
{
*ppwszCommonName = pwszCommonName;
pwszCommonName = NULL;
}
if (NULL != ppwszPFXFile)
{
*ppwszPFXFile = pwszPFXFile;
pwszPFXFile = NULL;
}
hr = S_OK;
error:
if (NULL != paRestoreChain)
{
for (iCert = 0; iCert < cCACert; iCert++)
{
if (NULL != paRestoreChain[iCert].pChain)
{
CertFreeCertificateChain(paRestoreChain[iCert].pChain);
}
}
LocalFree(paRestoreChain);
}
if (NULL != pwszPFXFile)
{
LocalFree(pwszPFXFile);
}
if (NULL != pwszCommonName)
{
LocalFree(pwszCommonName);
}
if (NULL != pwszSanitizedName)
{
LocalFree(pwszSanitizedName);
}
if (NULL != hStorePFX)
{
myDeleteGuidKeys(hStorePFX, TRUE);
CertCloseStore(hStorePFX, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != pfx.pbData)
{
LocalFree(pfx.pbData);
}
if (fImpersonating)
{
myEnablePrivilege(SE_RESTORE_NAME, FALSE);
myEnablePrivilege(SE_BACKUP_NAME, FALSE);
RevertToSelf();
}
return(hr);
}