//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: backup.cpp // // Contents: Cert Server wrapper routines // //--------------------------------------------------------------------------- #include #pragma hdrstop #include "certdb.h" #include "cscsp.h" #include #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(*ppwszDBDir)); *ppwszDBDir = NULL; } if (NULL != *ppwszLogDir) { LocalFree(const_cast(*ppwszLogDir)); *ppwszLogDir = NULL; } if (NULL != *ppwszSystemDir) { LocalFree(const_cast(*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(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 "); } 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); }