|
|
//+--------------------------------------------------------------------------
//
// 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); }
|