Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4693 lines
112 KiB

//+-------------------------------------------------------------------------n-
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: crl.cpp
//
// Contents: Cert Server CRL processing
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <stdio.h>
#include <esent.h>
#include "cscom.h"
#include "csprop.h"
#include "dbtable.h"
#include "resource.h"
#include "elog.h"
#include "certlog.h"
#include <winldap.h>
#include "csldap.h"
#include "cainfop.h"
#define __dwFILE__ __dwFILE_CERTSRV_CRL_CPP__
HANDLE g_hCRLManualPublishEvent = NULL;
FILETIME g_ftCRLNextPublish;
FILETIME g_ftDeltaCRLNextPublish;
BOOL g_fCRLPublishDisabled = FALSE; // manual publishing always allowed
BOOL g_fDeltaCRLPublishDisabled = FALSE; // controls manual publishing, too
DWORD g_dwCRLFlags = CRLF_DELETE_EXPIRED_CRLS;
LDAP *g_pld = NULL;
typedef struct _CSMEMBLOCK
{
struct _CSMEMBLOCK *pNext;
BYTE *pbFree;
DWORD cbFree;
} CSMEMBLOCK;
#define CBMEMBLOCK 4096
typedef struct _CSCRLELEMENT
{
USHORT usRevocationReason;
USHORT uscbSerialNumber;
BYTE *pbSerialNumber;
FILETIME ftRevocationDate;
} CSCRLELEMENT;
// size the structure just under CBMEMBLOCK to keep it from being just over
// a page size.
#define CCRLELEMENT ((CBMEMBLOCK - 2 * sizeof(DWORD)) / sizeof(CSCRLELEMENT))
typedef struct _CSCRLBLOCK
{
struct _CSCRLBLOCK *pNext;
DWORD cCRLElement;
CSCRLELEMENT aCRLElement[CCRLELEMENT];
} CSCRLBLOCK;
typedef struct _CSCRLREASON
{
struct _CSCRLREASON *pNext;
DWORD RevocationReason;
CERT_EXTENSION ExtReason;
} CSCRLREASON;
typedef struct _CSCRLPERIOD
{
LONG lCRLPeriodCount;
ENUM_PERIOD enumCRLPeriod;
DWORD dwCRLOverlapMinutes;
} CSCRLPERIOD;
#ifdef DBG_CERTSRV_DEBUG_PRINT
# define DPT_DATE 1
# define DPT_DELTA 2
# define DPT_DELTASEC 3
# define DPT_DELTAMS 4
# define DBGPRINTTIME(pfDelta, pszName, Type, ft) \
DbgPrintTime((pfDelta), (pszName), __LINE__, (Type), (ft))
VOID
DbgPrintTime(
OPTIONAL IN BOOL const *pfDelta,
IN char const *pszName,
IN DWORD Line,
IN DWORD Type,
IN FILETIME ft)
{
HRESULT hr;
WCHAR *pwszTime = NULL;
WCHAR awc[1];
LLFILETIME llft;
llft.ft = ft;
if (Type == DPT_DATE)
{
if (0 != llft.ll)
{
hr = myGMTFileTimeToWszLocalTime(&ft, TRUE, &pwszTime);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
}
}
else
{
if (DPT_DELTAMS == Type)
{
llft.ll /= 1000; // milliseconds to seconds
Type = DPT_DELTASEC;
}
if (DPT_DELTASEC == Type)
{
llft.ll *= CVT_BASE; // seconds to FILETIME period
}
llft.ll = -llft.ll; // FILETIME Period must be negative
if (0 != llft.ll)
{
hr = myFileTimePeriodToWszTimePeriod(
&llft.ft,
TRUE, // fExact
&pwszTime);
_PrintIfError(hr, "myFileTimePeriodToWszTimePeriod");
}
}
if (NULL == pwszTime)
{
awc[0] = L'\0';
pwszTime = awc;
}
DBGPRINT((
DBG_SS_CERTSRVI,
"%hs(%d):%hs time(%hs): %lx:%08lx %ws\n",
"crl.cpp",
Line,
NULL == pfDelta? "" : (*pfDelta? " Delta CRL" : " Base CRL"),
pszName,
ft.dwHighDateTime,
ft.dwLowDateTime,
pwszTime));
//error:
if (NULL != pwszTime && awc != pwszTime)
{
LocalFree(pwszTime);
}
}
VOID
CertSrvDbgPrintTime(
IN char const *pszDesc,
IN FILETIME const *pftGMT)
{
HRESULT hr;
WCHAR *pwszTime = NULL;
WCHAR awc[1];
hr = myGMTFileTimeToWszLocalTime(pftGMT, TRUE, &pwszTime);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
if (S_OK != hr)
{
awc[0] = L'\0';
pwszTime = awc;
}
DBGPRINT((DBG_SS_CERTSRV, "%hs: %ws\n", pszDesc, pwszTime));
//error:
if (NULL != pwszTime && awc != pwszTime)
{
LocalFree(pwszTime);
}
}
#else // DBG_CERTSRV_DEBUG_PRINT
# define DBGPRINTTIME(pfDelta, pszName, Type, ft)
#endif // DBG_CERTSRV_DEBUG_PRINT
HRESULT
crlMemBlockAlloc(
IN OUT CSMEMBLOCK **ppBlock,
IN DWORD cb,
OUT BYTE **ppb)
{
HRESULT hr;
CSMEMBLOCK *pBlock = *ppBlock;
*ppb = NULL;
cb = POINTERROUND(cb);
if (NULL == pBlock || cb > pBlock->cbFree)
{
pBlock = (CSMEMBLOCK *) LocalAlloc(LMEM_FIXED, CBMEMBLOCK);
if (NULL == pBlock)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
pBlock->pNext = *ppBlock;
pBlock->pbFree = (BYTE *) Add2Ptr(pBlock, sizeof(CSMEMBLOCK));
pBlock->cbFree = CBMEMBLOCK - sizeof(CSMEMBLOCK);
*ppBlock = pBlock;
}
CSASSERT(cb <= pBlock->cbFree);
*ppb = pBlock->pbFree;
pBlock->pbFree += cb;
pBlock->cbFree -= cb;
hr = S_OK;
error:
return(hr);
}
VOID
crlBlockListFree(
IN OUT CSMEMBLOCK *pBlock)
{
CSMEMBLOCK *pBlockNext;
while (NULL != pBlock)
{
pBlockNext = pBlock->pNext;
LocalFree(pBlock);
pBlock = pBlockNext;
}
}
HRESULT
crlElementAlloc(
IN OUT CSCRLBLOCK **ppBlock,
OUT CSCRLELEMENT **ppCRLElement)
{
HRESULT hr;
CSCRLBLOCK *pBlock = *ppBlock;
*ppCRLElement = NULL;
if (NULL == pBlock ||
ARRAYSIZE(pBlock->aCRLElement) <= pBlock->cCRLElement)
{
pBlock = (CSCRLBLOCK *) LocalAlloc(LMEM_FIXED, sizeof(*pBlock));
if (NULL == pBlock)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
pBlock->pNext = *ppBlock;
pBlock->cCRLElement = 0;
*ppBlock = pBlock;
}
CSASSERT(ARRAYSIZE(pBlock->aCRLElement) > pBlock->cCRLElement);
*ppCRLElement = &pBlock->aCRLElement[pBlock->cCRLElement++];
hr = S_OK;
error:
return(hr);
}
VOID
crlFreeCRLArray(
IN OUT VOID *pvBlockSerial,
IN OUT CRL_ENTRY *paCRL)
{
crlBlockListFree((CSMEMBLOCK *) pvBlockSerial);
if (NULL != paCRL)
{
LocalFree(paCRL);
}
}
HRESULT
crlCreateCRLReason(
IN OUT CSMEMBLOCK **ppBlock,
IN OUT CSCRLREASON **ppReason,
IN DWORD RevocationReason,
OUT DWORD *pcExtension,
OUT CERT_EXTENSION **ppExtension)
{
HRESULT hr;
CSCRLREASON *pReason = *ppReason;
BYTE *pbEncoded = NULL;
DWORD cbEncoded;
for (pReason = *ppReason; NULL != pReason; pReason = pReason->pNext)
{
if (RevocationReason == pReason->RevocationReason)
{
break;
}
}
if (NULL == pReason)
{
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_ENUMERATED,
(const void *) &RevocationReason,
0,
CERTLIB_USE_LOCALALLOC,
&pbEncoded,
&cbEncoded))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
hr = crlMemBlockAlloc(
ppBlock,
sizeof(CSCRLREASON) + cbEncoded,
(BYTE **) &pReason);
_JumpIfError(hr, error, "crlMemBlockAlloc");
pReason->pNext = *ppReason;
pReason->RevocationReason = RevocationReason;
pReason->ExtReason.pszObjId = szOID_CRL_REASON_CODE;
pReason->ExtReason.fCritical = FALSE;
pReason->ExtReason.Value.pbData =
(BYTE *) Add2Ptr(pReason, sizeof(*pReason));
pReason->ExtReason.Value.cbData = cbEncoded;
CopyMemory(pReason->ExtReason.Value.pbData, pbEncoded, cbEncoded);
*ppReason = pReason;
//printf("crlCreateCRLReason: new %x cb %x\n", RevocationReason, cbEncoded);
}
//printf("crlCreateCRLReason: %x\n", RevocationReason);
CSASSERT(NULL != pReason && RevocationReason == pReason->RevocationReason);
*pcExtension = 1;
*ppExtension = &pReason->ExtReason;
hr = S_OK;
error:
if (NULL != pbEncoded)
{
LocalFree(pbEncoded);
}
return(hr);
}
// Convert linked list of CRL blocks to an array.
// If the output array pointer is NULL, just free the list.
HRESULT
ConvertOrFreeCRLList(
IN OUT CSCRLBLOCK **ppBlockCRL, // Freed
IN OUT CSMEMBLOCK **ppBlockReason, // Used to allocate reason extensions
IN DWORD cCRL,
OPTIONAL OUT CRL_ENTRY **paCRL)
{
HRESULT hr;
CSCRLREASON *pReasonList = NULL; // linked list of reason extensions
CSCRLBLOCK *pBlockCRL = *ppBlockCRL;
CRL_ENTRY *aCRL = NULL;
CRL_ENTRY *pCRL;
DWORD i;
if (NULL != paCRL)
{
aCRL = (CRL_ENTRY *) LocalAlloc(LMEM_FIXED, sizeof(aCRL[0]) * cCRL);
if (NULL == aCRL)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
}
pCRL = aCRL;
while (NULL != pBlockCRL)
{
CSCRLBLOCK *pBlockCRLNext;
if (NULL != pCRL)
{
for (i = 0; i < pBlockCRL->cCRLElement; i++)
{
CSCRLELEMENT *pCRLElement = &pBlockCRL->aCRLElement[i];
pCRL->SerialNumber.pbData = pCRLElement->pbSerialNumber;
pCRL->SerialNumber.cbData = pCRLElement->uscbSerialNumber;
pCRL->RevocationDate = pCRLElement->ftRevocationDate;
pCRL->cExtension = 0;
pCRL->rgExtension = NULL;
if (CRL_REASON_UNSPECIFIED != pCRLElement->usRevocationReason)
{
hr = crlCreateCRLReason(
ppBlockReason,
&pReasonList,
pCRLElement->usRevocationReason,
&pCRL->cExtension,
&pCRL->rgExtension);
_JumpIfError(hr, error, "crlCreateCRLReason");
}
pCRL++;
}
}
pBlockCRLNext = pBlockCRL->pNext;
LocalFree(pBlockCRL);
pBlockCRL = pBlockCRLNext;
}
if (NULL != paCRL)
{
CSASSERT(pCRL == &aCRL[cCRL]);
*paCRL = aCRL;
aCRL = NULL;
}
CSASSERT(NULL == pBlockCRL);
hr = S_OK;
error:
*ppBlockCRL = pBlockCRL;
if (NULL != aCRL)
{
LocalFree(aCRL);
}
return(hr);
}
HRESULT
AddCRLElement(
IN OUT CSMEMBLOCK **ppBlockSerial,
IN OUT CSCRLBLOCK **ppBlockCRL,
IN WCHAR const *pwszSerialNumber,
IN FILETIME const *pftRevokedEffectiveWhen,
IN DWORD RevocationReason)
{
HRESULT hr;
CSCRLELEMENT *pCRLElement;
DWORD cbSerial;
BYTE *pbSerial = NULL;
hr = crlElementAlloc(ppBlockCRL, &pCRLElement);
_JumpIfError(hr, error, "crlElementAlloc");
hr = WszToMultiByteInteger(
FALSE,
pwszSerialNumber,
&cbSerial,
&pbSerial);
_JumpIfError(hr, error, "WszToMultiByteInteger");
hr = crlMemBlockAlloc(ppBlockSerial, cbSerial, &pCRLElement->pbSerialNumber);
_JumpIfError(hr, error, "crlMemBlockAlloc");
CopyMemory(pCRLElement->pbSerialNumber, pbSerial, cbSerial);
pCRLElement->ftRevocationDate = *pftRevokedEffectiveWhen;
pCRLElement->usRevocationReason = (USHORT) RevocationReason;
pCRLElement->uscbSerialNumber = (USHORT) cbSerial;
CSASSERT(pCRLElement->usRevocationReason == RevocationReason);
CSASSERT(pCRLElement->uscbSerialNumber == cbSerial);
error:
if (NULL != pbSerial)
{
LocalFree(pbSerial);
}
return(hr);
}
DWORD g_aColCRL[] = {
#define ICOL_DISPOSITION 0
DTI_REQUESTTABLE | DTR_REQUESTDISPOSITION,
#define ICOL_SERIAL 1
DTI_CERTIFICATETABLE | DTC_CERTIFICATESERIALNUMBER,
#define ICOL_EFFECTIVEWHEN 2
DTI_REQUESTTABLE | DTR_REQUESTREVOKEDEFFECTIVEWHEN,
#define ICOL_REASON 3
DTI_REQUESTTABLE | DTR_REQUESTREVOKEDREASON,
};
HRESULT
BuildCRLList(
IN BOOL fDelta,
IN DWORD iKey,
OPTIONAL IN FILETIME const *pftQueryMinimum,
IN FILETIME const *pftThisPublish,
IN FILETIME const *pftLastPublishBase,
IN OUT DWORD *pcCRL,
IN OUT CSCRLBLOCK **ppBlockCRL,
IN OUT CSMEMBLOCK **ppBlockSerial)
{
HRESULT hr;
CERTVIEWRESTRICTION acvr[5];
CERTVIEWRESTRICTION *pcvr;
IEnumCERTDBRESULTROW *pView = NULL;
DWORD celtFetched;
DWORD NameIdMin;
DWORD NameIdMax;
DWORD i;
BOOL fEnd;
CERTDBRESULTROW aResult[10];
BOOL fResultActive = FALSE;
DWORD cCRL = *pcCRL;
CSCRLBLOCK *pBlockCRL = *ppBlockCRL;
CSMEMBLOCK *pBlockSerial = *ppBlockSerial;
DBGPRINTTIME(NULL, "*pftThisPublish", DPT_DATE, *pftThisPublish);
// Set up restrictions as follows:
pcvr = acvr;
// Request.RevokedEffectiveWhen <= *pftThisPublish (indexed column)
pcvr->ColumnIndex = DTI_REQUESTTABLE | DTR_REQUESTREVOKEDEFFECTIVEWHEN;
pcvr->SeekOperator = CVR_SEEK_LE;
pcvr->SortOrder = CVR_SORT_DESCEND;
pcvr->pbValue = (BYTE *) pftThisPublish;
pcvr->cbValue = sizeof(*pftThisPublish);
pcvr++;
// Cert.NotAfter >= *pftLastPublishBase
if (0 == (CRLF_PUBLISH_EXPIRED_CERT_CRLS & g_dwCRLFlags))
{
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_CERTIFICATENOTAFTERDATE;
pcvr->SeekOperator = CVR_SEEK_GE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) pftLastPublishBase;
pcvr->cbValue = sizeof(*pftLastPublishBase);
pcvr++;
}
// NameId >= MAKECANAMEID(iCert == 0, iKey)
NameIdMin = MAKECANAMEID(0, iKey);
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_CERTIFICATEISSUERNAMEID;
pcvr->SeekOperator = CVR_SEEK_GE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &NameIdMin;
pcvr->cbValue = sizeof(NameIdMin);
pcvr++;
// NameId <= MAKECANAMEID(iCert == _16BITMASK, iKey)
NameIdMax = MAKECANAMEID(_16BITMASK, iKey);
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_CERTIFICATEISSUERNAMEID;
pcvr->SeekOperator = CVR_SEEK_LE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &NameIdMax;
pcvr->cbValue = sizeof(NameIdMax);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) > SAFE_SUBTRACT_POINTERS(pcvr, acvr));
if (NULL != pftQueryMinimum)
{
// Request.RevokedWhen >= *pftQueryMinimum
pcvr->ColumnIndex = DTI_REQUESTTABLE | DTR_REQUESTREVOKEDWHEN;
pcvr->SeekOperator = CVR_SEEK_GE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) pftQueryMinimum;
pcvr->cbValue = sizeof(*pftQueryMinimum);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) >= SAFE_SUBTRACT_POINTERS(pcvr, acvr));
}
hr = g_pCertDB->OpenView(
SAFE_SUBTRACT_POINTERS(pcvr, acvr),
acvr,
ARRAYSIZE(g_aColCRL),
g_aColCRL,
0, // no worker thread
&pView);
_JumpIfError(hr, error, "OpenView");
fEnd = FALSE;
while (!fEnd)
{
hr = pView->Next(ARRAYSIZE(aResult), aResult, &celtFetched);
if (S_FALSE == hr)
{
fEnd = TRUE;
if (0 == celtFetched)
{
break;
}
hr = S_OK;
}
_JumpIfError(hr, error, "Next");
fResultActive = TRUE;
CSASSERT(ARRAYSIZE(aResult) >= celtFetched);
for (i = 0; i < celtFetched; i++)
{
DWORD Disposition;
DWORD Reason;
CERTDBRESULTROW *pResult = &aResult[i];
CSASSERT(ARRAYSIZE(g_aColCRL) == pResult->ccol);
CSASSERT(NULL != pResult->acol[ICOL_DISPOSITION].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOL_DISPOSITION].Type));
CSASSERT(sizeof(Disposition) == pResult->acol[ICOL_DISPOSITION].cbValue);
Disposition = *(DWORD *) pResult->acol[ICOL_DISPOSITION].pbValue;
CSASSERT(NULL != pResult->acol[ICOL_SERIAL].pbValue);
CSASSERT(PROPTYPE_STRING == (PROPTYPE_MASK & pResult->acol[ICOL_SERIAL].Type));
CSASSERT(0 < pResult->acol[ICOL_SERIAL].cbValue);
if (NULL == pResult->acol[ICOL_EFFECTIVEWHEN].pbValue)
{
continue;
}
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOL_EFFECTIVEWHEN].cbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOL_EFFECTIVEWHEN].Type));
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOL_REASON].Type));
Reason = CRL_REASON_UNSPECIFIED;
if (NULL != pResult->acol[ICOL_REASON].pbValue)
{
CSASSERT(sizeof(Reason) == pResult->acol[ICOL_REASON].cbValue);
Reason = *(DWORD *) pResult->acol[ICOL_REASON].pbValue;
}
if (NULL == pResult->acol[ICOL_SERIAL].pbValue ||
CRL_REASON_REMOVE_FROM_CRL == Reason)
{
continue;
}
// Add to CRL unless it's:
// not a revoked issued cert &&
// not a root CA cert &&
// not an unrevoked issued cert
if (DB_DISP_REVOKED != Disposition &&
!(DB_DISP_CA_CERT == Disposition && IsRootCA(g_CAType)) &&
!(DB_DISP_ISSUED == Disposition && MAXDWORD == Reason))
{
continue;
}
if (MAXDWORD == Reason)
{
if (!fDelta)
{
continue;
}
Reason = CRL_REASON_REMOVE_FROM_CRL;
}
hr = AddCRLElement(
&pBlockSerial,
&pBlockCRL,
(WCHAR const *) pResult->acol[ICOL_SERIAL].pbValue,
(FILETIME const *) pResult->acol[ICOL_EFFECTIVEWHEN].pbValue,
Reason);
_JumpIfError(hr, error, "AddCRLElement");
CONSOLEPRINT3((
DBG_SS_CERTSRV,
"Cert is %ws: %ws: %d\n",
CRL_REASON_REMOVE_FROM_CRL == Reason?
L"UNREVOKED" : L"Revoked",
pResult->acol[ICOL_SERIAL].pbValue,
Reason));
cCRL++;
}
pView->ReleaseResultRow(celtFetched, aResult);
fResultActive = FALSE;
}
*pcCRL = cCRL;
hr = S_OK;
error:
*ppBlockSerial = pBlockSerial;
*ppBlockCRL = pBlockCRL;
if (NULL != pView)
{
if (fResultActive)
{
pView->ReleaseResultRow(celtFetched, aResult);
}
pView->Release();
}
return(hr);
}
#undef ICOL_DISPOSITION
#undef ICOL_SERIAL
#undef ICOL_EFFECTIVEWHEN
#undef ICOL_REASON
HRESULT
crlBuildCRLArray(
IN BOOL fDelta,
OPTIONAL IN FILETIME const *pftQueryMinimum,
IN FILETIME const *pftThisPublish,
IN FILETIME const *pftLastPublishBase,
IN DWORD iKey,
OUT DWORD *pcCRL,
OUT CRL_ENTRY **paCRL,
OUT VOID **ppvBlock)
{
HRESULT hr;
BOOL fCoInitialized = FALSE;
CSCRLBLOCK *pBlockCRL = NULL;
CSMEMBLOCK *pBlockSerial = NULL;
*pcCRL = 0;
*paCRL = NULL;
*ppvBlock = NULL;
hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitializeEx");
}
fCoInitialized = TRUE;
hr = BuildCRLList(
fDelta,
iKey,
pftQueryMinimum,
pftThisPublish,
pftLastPublishBase,
pcCRL,
&pBlockCRL,
&pBlockSerial);
_JumpIfError(hr, error, "BuildCRLList");
hr = ConvertOrFreeCRLList(&pBlockCRL, &pBlockSerial, *pcCRL, paCRL);
_JumpIfError(hr, error, "ConvertOrFreeCRLList");
*ppvBlock = pBlockSerial;
pBlockSerial = NULL;
error:
if (NULL != pBlockCRL)
{
ConvertOrFreeCRLList(&pBlockCRL, NULL, 0, NULL);
}
if (NULL != pBlockSerial)
{
crlBlockListFree(pBlockSerial);
}
if (fCoInitialized)
{
CoUninitialize();
}
return(hr);
}
HRESULT
crlGetRegCRLNextPublish(
IN BOOL fDelta,
IN WCHAR const *pwszSanitizedName,
IN WCHAR const *pwszRegName,
OUT FILETIME *pftNextPublish)
{
HRESULT hr;
BYTE *pbData = NULL;
DWORD cbData;
DWORD dwType;
hr = myGetCertRegValue(
NULL,
pwszSanitizedName,
NULL,
NULL,
pwszRegName,
&pbData, // free using LocalFree
&cbData,
&dwType);
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
{
hr = S_OK;
goto error;
}
_JumpIfErrorStr(hr, error, "myGetCertRegValue", pwszRegName);
if (REG_BINARY != dwType || sizeof(*pftNextPublish) != cbData)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
goto error;
}
*pftNextPublish = *(FILETIME *) pbData;
DBGPRINTTIME(&fDelta, "*pftNextPublish", DPT_DATE, *pftNextPublish);
error:
if (NULL != pbData)
{
LocalFree(pbData);
}
return(hr);
}
HRESULT
crlSetRegCRLNextPublish(
IN BOOL fDelta,
IN WCHAR const *pwszSanitizedName,
IN WCHAR const *pwszRegName,
IN FILETIME const *pftNextPublish)
{
HRESULT hr;
hr = mySetCertRegValue(
NULL,
pwszSanitizedName,
NULL,
NULL,
pwszRegName,
REG_BINARY,
(BYTE const *) pftNextPublish,
sizeof(*pftNextPublish),
FALSE);
_JumpIfErrorStr(hr, error, "mySetCertRegValue", pwszRegName);
DBGPRINTTIME(&fDelta, "*pftNextPublish", DPT_DATE, *pftNextPublish);
error:
return(hr);
}
// called from CoreInit
// inits process-static data: g_ftCRLNextPublish, etc.
HRESULT
CRLInit(
IN WCHAR const *pwszSanitizedName)
{
HRESULT hr;
DWORD dw;
ZeroMemory(&g_ftCRLNextPublish, sizeof(g_ftCRLNextPublish));
ZeroMemory(&g_ftDeltaCRLNextPublish, sizeof(g_ftDeltaCRLNextPublish));
hr = crlGetRegCRLNextPublish(
FALSE,
pwszSanitizedName,
wszREGCRLNEXTPUBLISH,
&g_ftCRLNextPublish);
_JumpIfError(hr, error, "crlGetRegCRLNextPublish");
hr = crlGetRegCRLNextPublish(
TRUE,
pwszSanitizedName,
wszREGCRLDELTANEXTPUBLISH,
&g_ftDeltaCRLNextPublish);
_JumpIfError(hr, error, "crlGetRegCRLNextPublish");
hr = myGetCertRegDWValue(
pwszSanitizedName,
NULL,
NULL,
wszREGCRLFLAGS,
(DWORD *) &dw);
_PrintIfErrorStr(hr, "myGetCertRegDWValue", wszREGCRLFLAGS);
if (S_OK == hr)
{
g_dwCRLFlags = dw;
}
hr = S_OK;
error:
return(hr);
}
VOID
CRLTerminate()
{
if (NULL != g_pld)
{
ldap_unbind(g_pld);
g_pld = NULL;
}
}
HRESULT
crlGetRegPublishParams(
IN BOOL fDelta,
IN WCHAR const *pwszSanitizedName,
IN WCHAR const *pwszRegCRLPeriodCount,
IN WCHAR const *pwszRegCRLPeriodString,
IN WCHAR const *pwszRegCRLOverlapPeriodCount,
IN WCHAR const *pwszRegCRLOverlapPeriodString,
IN LONG lPeriodCountDefault,
IN WCHAR const *pwszPeriodStringDefault,
OPTIONAL OUT CSCRLPERIOD *pccp,
OUT BOOL *pfCRLPublishDisabled)
{
HRESULT hr;
WCHAR *pwszCRLPeriodString = NULL;
WCHAR *pwszCRLOverlapPeriodString = NULL;
DWORD cbData;
DWORD dwPeriod;
DWORD dwType;
CSCRLPERIOD ccp;
if (NULL == pccp)
{
pccp = &ccp;
}
ZeroMemory(pccp, sizeof(*pccp));
CSASSERT(NULL != pfCRLPublishDisabled);
// get if need lCRLPeriodCount OR enumCRLPeriod
// if any of these fail, skip to error handling below
hr = myGetCertRegDWValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLPeriodCount,
(DWORD *) &pccp->lCRLPeriodCount);
_PrintIfErrorStr(hr, "myGetCertRegDWValue", pwszRegCRLPeriodCount);
if (hr == S_OK)
{
hr = myGetCertRegStrValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLPeriodString,
&pwszCRLPeriodString);
_PrintIfErrorStr(hr, "myGetCertRegDWValue", pwszRegCRLPeriodString);
if (hr == S_OK)
{
hr = myTranslatePeriodUnits(
pwszCRLPeriodString,
pccp->lCRLPeriodCount,
&pccp->enumCRLPeriod,
&pccp->lCRLPeriodCount);
_PrintIfError(hr, "myTranslatePeriodUnits");
}
// don't allow base to be disabled anymore: force defaults to be loaded
if (!fDelta &&
(0 == pccp->lCRLPeriodCount || -1 == pccp->lCRLPeriodCount))
{
hr = E_INVALIDARG;
}
}
if (hr != S_OK)
{
_PrintError(hr, "Error reading CRLPub params. Overwriting with defaults.");
if (CERTLOG_WARNING <= g_dwLogLevel)
{
hr = LogEvent(
EVENTLOG_WARNING_TYPE,
MSG_INVALID_CRL_SETTINGS,
0,
NULL);
_PrintIfError(hr, "LogEvent");
}
// slam default publishing to whatever the caller said
hr = myTranslatePeriodUnits(
pwszPeriodStringDefault,
lPeriodCountDefault,
&pccp->enumCRLPeriod,
&pccp->lCRLPeriodCount);
_JumpIfError(hr, error, "myTranslatePeriodUnits");
// blindly reset defaults
mySetCertRegDWValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLPeriodCount,
pccp->lCRLPeriodCount);
mySetCertRegStrValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLPeriodString,
pwszPeriodStringDefault);
}
*pfCRLPublishDisabled = 0 == pccp->lCRLPeriodCount;
if (&ccp != pccp) // If caller wants the data
{
BOOL fRegistryOverlap = FALSE;
DWORD dwCRLOverlapCount;
ENUM_PERIOD enumCRLOverlap;
LLFILETIME llftDeltaPeriod;
// try and gather overlap values from registry - bail on any failure
hr = myGetCertRegDWValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLOverlapPeriodCount,
&dwCRLOverlapCount);
if (hr == S_OK && 0 != dwCRLOverlapCount) // if not disabled
{
hr = myGetCertRegStrValue(
pwszSanitizedName,
NULL,
NULL,
pwszRegCRLOverlapPeriodString,
&pwszCRLOverlapPeriodString);// free w/ LocalFree
if (hr == S_OK)
{
hr = myTranslatePeriodUnits(
pwszCRLOverlapPeriodString,
dwCRLOverlapCount,
&enumCRLOverlap,
(LONG *) &dwCRLOverlapCount);
// we have enough info to override overlap calculation
if (hr == S_OK)
{
fRegistryOverlap = TRUE;
DBGPRINT((
DBG_SS_CERTSRVI,
"Loaded CRL Overlap values. Overriding overlap calculation with specified values.\n"));
}
}
}
// always possible to revert to calculated value
if (fRegistryOverlap)
{
LLFILETIME llftOverlap;
// convert registry-specified CRL overlap to FILETIME
llftOverlap.ll = 0;
myMakeExprDateTime(
&llftOverlap.ft,
dwCRLOverlapCount,
enumCRLOverlap);
DBGPRINTTIME(&fDelta, "ftdelta1", DPT_DELTA, llftOverlap.ft);
llftOverlap.ll /= CVT_BASE; // now in seconds
// (DELTA sec / 60 secpermin)
pccp->dwCRLOverlapMinutes = (DWORD) (llftOverlap.ll / CVT_MINUTES);
}
// convert CRL period to FILETIME
llftDeltaPeriod.ll = 0;
myMakeExprDateTime(
&llftDeltaPeriod.ft,
pccp->lCRLPeriodCount,
pccp->enumCRLPeriod);
DBGPRINTTIME(&fDelta, "ftdelta2", DPT_DELTA, llftDeltaPeriod.ft);
llftDeltaPeriod.ll /= CVT_BASE; // now in seconds
llftDeltaPeriod.ll /= CVT_MINUTES; // now in minutes
if (!fRegistryOverlap)
{
if (fDelta)
{
// default CRLOverlap for delta CRLs: same as period
pccp->dwCRLOverlapMinutes = llftDeltaPeriod.ft.dwLowDateTime;
}
else
{
// default CRLOverlap for base CRLs: 10% of period
pccp->dwCRLOverlapMinutes = (DWORD) (llftDeltaPeriod.ll / 10);
}
// Clamp computed overlap to less than 12 hours
if (pccp->dwCRLOverlapMinutes > 12 * 60)
{
pccp->dwCRLOverlapMinutes = 12 * 60;
}
}
// Always clamp lower bound: (1.5 * skew) < g_dwCRLOverlapMinutes
// must be at least 1.5x skew
dwCRLOverlapCount = (3 * g_dwClockSkewMinutes) >> 1;
if (pccp->dwCRLOverlapMinutes < dwCRLOverlapCount)
{
pccp->dwCRLOverlapMinutes = dwCRLOverlapCount;
}
// Always clamp upper bound: must be no more than CRL period
if (pccp->dwCRLOverlapMinutes > llftDeltaPeriod.ft.dwLowDateTime)
{
pccp->dwCRLOverlapMinutes = llftDeltaPeriod.ft.dwLowDateTime;
}
}
hr = S_OK;
error:
if (NULL != pwszCRLPeriodString)
{
LocalFree(pwszCRLPeriodString);
}
if (NULL != pwszCRLOverlapPeriodString)
{
LocalFree(pwszCRLOverlapPeriodString);
}
return(hr);
}
// Reload publication params during each CRL publication
HRESULT
crlGetRegCRLPublishParams(
IN WCHAR const *pwszSanitizedName,
OPTIONAL OUT CSCRLPERIOD *pccpBase,
OPTIONAL OUT CSCRLPERIOD *pccpDelta)
{
HRESULT hr;
hr = crlGetRegPublishParams(
FALSE,
pwszSanitizedName,
wszREGCRLPERIODCOUNT,
wszREGCRLPERIODSTRING,
wszREGCRLOVERLAPPERIODCOUNT,
wszREGCRLOVERLAPPERIODSTRING,
dwCRLPERIODCOUNTDEFAULT, // default period
wszCRLPERIODSTRINGDEFAULT, // default period
pccpBase,
&g_fCRLPublishDisabled);
_JumpIfError(hr, error, "crlGetRegPublishParams");
hr = crlGetRegPublishParams(
TRUE,
pwszSanitizedName,
wszREGCRLDELTAPERIODCOUNT,
wszREGCRLDELTAPERIODSTRING,
wszREGCRLDELTAOVERLAPPERIODCOUNT,
wszREGCRLDELTAOVERLAPPERIODSTRING,
dwCRLDELTAPERIODCOUNTDEFAULT, // default period
wszCRLDELTAPERIODSTRINGDEFAULT, // default period
pccpDelta,
&g_fDeltaCRLPublishDisabled);
_JumpIfError(hr, error, "crlGetRegPublishParams");
error:
return(hr);
}
#define CERTSRV_CRLPUB_RETRY_COUNT_DEFAULT 10
#define CERTSRV_CRLPUB_RETRY_SECONDS (10 * CVT_MINUTES)
VOID
crlComputeTimeOutSub(
OPTIONAL IN BOOL *pfDelta,
IN FILETIME const *pftFirst,
IN FILETIME const *pftLast,
OUT DWORD *pdwMSTimeOut)
{
LLFILETIME llft;
// llft.ll = *pftLast - *pftFirst;
llft.ll = mySubtractFileTimes(pftLast, pftFirst);
DBGPRINTTIME(pfDelta, "*pftFirst", DPT_DATE, *pftFirst);
DBGPRINTTIME(pfDelta, "*pftLast", DPT_DATE, *pftLast);
llft.ll /= (CVT_BASE / 1000); // convert 100ns to msecs
DBGPRINTTIME(pfDelta, "llft", DPT_DELTAMS, llft.ft);
if (0 > llft.ll || MAXLONG < llft.ll)
{
// wait as long as we can without going infinite
llft.ll = MAXLONG;
}
*pdwMSTimeOut = llft.ft.dwLowDateTime;
}
VOID
crlComputeTimeOutEx(
IN BOOL fDelta,
IN FILETIME const *pftFirst,
IN FILETIME const *pftLast,
OUT DWORD *pdwMSTimeOut)
{
crlComputeTimeOutSub(&fDelta, pftFirst, pftLast, pdwMSTimeOut);
}
VOID
CRLComputeTimeOut(
IN FILETIME const *pftFirst,
IN FILETIME const *pftLast,
OUT DWORD *pdwMSTimeOut)
{
crlComputeTimeOutSub(NULL, pftFirst, pftLast, pdwMSTimeOut);
}
#ifdef DBG_CERTSRV_DEBUG_PRINT
VOID
DbgPrintRemainTime(
IN BOOL fDelta,
IN FILETIME const *pftCurrent,
IN FILETIME const *pftCRLNextPublish)
{
HRESULT hr;
LLFILETIME llftDelta;
WCHAR *pwszTime = NULL;
WCHAR awc[1];
llftDelta.ll = mySubtractFileTimes(pftCRLNextPublish, pftCurrent);
DBGPRINTTIME(&fDelta, "delta", DPT_DELTA, llftDelta.ft);
llftDelta.ll = -llftDelta.ll;
hr = myFileTimePeriodToWszTimePeriod(
&llftDelta.ft,
TRUE, // fExact
&pwszTime);
_PrintIfError(hr, "myFileTimePeriodToWszTimePeriod");
if (S_OK != hr)
{
awc[0] = L'\0';
pwszTime = awc;
}
DBGPRINT((
DBG_SS_CERTSRV,
"CRLPubWakeupEvent(tid=%d): Next %hs CRL: %ws\n",
GetCurrentThreadId(),
fDelta? "Delta" : "Base",
pwszTime));
if (NULL != pwszTime && awc != pwszTime)
{
LocalFree(pwszTime);
}
}
#endif // DBG_CERTSRV_DEBUG_PRINT
DWORD g_aColExpiredCRL[] = {
#define ICOLEXP_ROWID 0
DTI_CRLTABLE | DTL_ROWID,
#define ICOLEXP_MINBASE 1
DTI_CRLTABLE | DTL_MINBASE,
#define ICOLEXP_CRLNEXTUPDATE 2
DTI_CRLTABLE | DTL_NEXTUPDATEDATE,
};
HRESULT
crlDeleteExpiredCRLs(
IN FILETIME const *pftCurrent,
IN FILETIME const *pftQueryDeltaDelete,
IN DWORD RowIdBase)
{
HRESULT hr;
CERTVIEWRESTRICTION acvr[1];
CERTVIEWRESTRICTION *pcvr;
IEnumCERTDBRESULTROW *pView = NULL;
BOOL fResultActive = FALSE;
CERTDBRESULTROW aResult[1];
CERTDBRESULTROW *pResult;
DWORD celtFetched;
if (CRLF_DELETE_EXPIRED_CRLS & g_dwCRLFlags)
{
DBGPRINTTIME(NULL, "DeleteCRL:*pftCurrent", DPT_DATE, *pftCurrent);
DBGPRINTTIME(NULL, "DeleteCRL:*pftQueryDeltaDelete", DPT_DATE, *pftQueryDeltaDelete);
// Set up restrictions as follows:
pcvr = acvr;
// CRL Expiration < ftCurrent (indexed column)
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_NEXTPUBLISHDATE;
pcvr->SeekOperator = CVR_SEEK_LT;
pcvr->SortOrder = CVR_SORT_ASCEND; // Oldest propagated CRL first
pcvr->pbValue = (BYTE *) pftCurrent;
pcvr->cbValue = sizeof(*pftCurrent);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) == SAFE_SUBTRACT_POINTERS(pcvr, acvr));
hr = g_pCertDB->OpenView(
ARRAYSIZE(acvr),
acvr,
ARRAYSIZE(g_aColExpiredCRL),
g_aColExpiredCRL,
0, // no worker thread
&pView);
_JumpIfError(hr, error, "OpenView");
while (TRUE)
{
DWORD RowId;
DWORD MinBase;
FILETIME ftNextUpdate;
BOOL fDelete;
hr = pView->Next(ARRAYSIZE(aResult), aResult, &celtFetched);
if (S_FALSE == hr)
{
if (0 == celtFetched)
{
break;
}
}
_JumpIfError(hr, error, "Next");
fResultActive = TRUE;
CSASSERT(ARRAYSIZE(aResult) == celtFetched);
pResult = &aResult[0];
CSASSERT(ARRAYSIZE(g_aColExpiredCRL) == pResult->ccol);
CSASSERT(NULL != pResult->acol[ICOLEXP_ROWID].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOLEXP_ROWID].Type));
CSASSERT(sizeof(RowId) == pResult->acol[ICOLEXP_ROWID].cbValue);
RowId = *(DWORD *) pResult->acol[ICOLEXP_ROWID].pbValue;
CSASSERT(NULL != pResult->acol[ICOLEXP_MINBASE].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOLEXP_MINBASE].Type));
CSASSERT(sizeof(MinBase) == pResult->acol[ICOLEXP_MINBASE].cbValue);
MinBase = *(DWORD *) pResult->acol[ICOLEXP_MINBASE].pbValue;
CSASSERT(NULL != pResult->acol[ICOLEXP_CRLNEXTUPDATE].pbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOLEXP_CRLNEXTUPDATE].Type));
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOLEXP_CRLNEXTUPDATE].cbValue);
ftNextUpdate = *(FILETIME *) pResult->acol[ICOLEXP_CRLNEXTUPDATE].pbValue;
pView->ReleaseResultRow(celtFetched, aResult);
fResultActive = FALSE;
CSASSERT(0 != RowId);
// Delete the CRL row if it is not the current Base CRL and the
// row represents a CRL that expired prior to the current Base CRL.
fDelete = FALSE;
if (RowIdBase != RowId &&
0 < CompareFileTime(pftQueryDeltaDelete, &ftNextUpdate))
{
fDelete = TRUE;
}
DBGPRINTTIME(NULL, "DeleteCRL:ftNextUpdate", DPT_DATE, ftNextUpdate);
DBGPRINT((
DBG_SS_CERTSRVI,
"crlDeleteExpiredCRLs(RowId=%x) %ws\n",
RowId,
fDelete? L"DELETE" : L"SKIP"));
if (fDelete)
{
ICertDBRow *prow;
hr = g_pCertDB->OpenRow(
PROPOPEN_DELETE | PROPTABLE_CRL,
RowId,
NULL,
&prow);
_JumpIfError(hr, error, "OpenRow");
hr = prow->Delete();
_PrintIfError(hr, "Delete");
if (S_OK == hr)
{
hr = prow->CommitTransaction(TRUE);
_PrintIfError(hr, "CommitTransaction");
}
if (S_OK != hr)
{
HRESULT hr2 = prow->CommitTransaction(FALSE);
_PrintIfError(hr2, "CommitTransaction");
}
prow->Release();
}
}
}
hr = S_OK;
error:
if (NULL != pView)
{
if (fResultActive)
{
pView->ReleaseResultRow(celtFetched, aResult);
}
pView->Release();
}
return(hr);
}
#undef ICOLEXP_ROWID
#undef ICOLEXP_MINBASE
#undef ICOLEXP_CRLNEXTUPDATE
///////////////////////////////////////////////////
// CRLPubWakeupEvent is the handler for wakeup notifications.
//
// This function is called at miscellaneous times and
// determines whether or not it is time to rebuild the
// CRL to be published.
//
// It then calls CRLPublishCRLs and advises it as to whether to
// rebuild or not.
//
// Its final task is to recalculate the next wakeup time, which
// depends on current time, if the exit module needs to be retried,
// or whether CRL publishing is disabled.
HRESULT
CRLPubWakeupEvent(
OUT DWORD *pdwMSTimeOut)
{
HRESULT hr;
HRESULT hrPublish;
FILETIME ftZero;
FILETIME ftCurrent;
BOOL fBaseTrigger = TRUE;
BOOL fRebuildCRL = FALSE;
BOOL fForceRepublish = FALSE;
BOOL fShadowDelta = FALSE;
BOOL fSetRetryTimer = FALSE;
DWORD dwMSTimeOut = CERTSRV_CRLPUB_RETRY_SECONDS * 1000;
DWORD State = 0;
static BOOL s_fFirstWakeup = TRUE;
CSASSERT(NULL != pdwMSTimeOut);
// if anything goes wrong, call us again after a pause
hr = CertSrvEnterServer(&State);
_JumpIfError(hr, error, "CertSrvEnterServer");
__try
{
BOOL fCRLPublishDisabledOld = g_fCRLPublishDisabled;
BOOL fDeltaCRLPublishDisabledOld = g_fDeltaCRLPublishDisabled;
// Recalc Timeout
GetSystemTimeAsFileTime(&ftCurrent);
#ifdef DBG_CERTSRV_DEBUG_PRINT
{
WCHAR *pwszNow = NULL;
myGMTFileTimeToWszLocalTime(&ftCurrent, TRUE, &pwszNow);
DBGPRINT((DBG_SS_CERTSRV, "CRLPubWakeupEvent(%ws)\n", pwszNow));
if (NULL != pwszNow)
{
LocalFree(pwszNow);
}
}
#endif // DBG_CERTSRV_DEBUG_PRINT
// get current publish params
hr = crlGetRegCRLPublishParams(g_wszSanitizedName, NULL, NULL);
_LeaveIfError(hr, "crlGetRegCRLPublishParams");
if (s_fFirstWakeup)
{
s_fFirstWakeup = FALSE;
if (g_fDBRecovered)
{
fForceRepublish = TRUE;
}
}
else
{
if (!g_fCRLPublishDisabled &&
(fCRLPublishDisabledOld ||
g_fDeltaCRLPublishDisabled != fDeltaCRLPublishDisabledOld))
{
fRebuildCRL = TRUE; // state change: force new CRLs
// If delta CRLs were just now disabled, make one attempt to
// publish shadow deltas; force clients to fetch a new base CRL.
if (!fDeltaCRLPublishDisabledOld && g_fDeltaCRLPublishDisabled)
{
fShadowDelta = TRUE; // force shadow delta
}
}
}
// if "not yet ready"
if (0 < CompareFileTime(&g_ftCRLNextPublish, &ftCurrent))
{
fBaseTrigger = FALSE;
#ifdef DBG_CERTSRV_DEBUG_PRINT
// give next pub status
DbgPrintRemainTime(FALSE, &ftCurrent, &g_ftCRLNextPublish);
#endif // DBG_CERTSRV_DEBUG_PRINT
}
// if "not yet ready"
if (!fBaseTrigger &&
(g_fDeltaCRLPublishDisabled ||
0 < CompareFileTime(&g_ftDeltaCRLNextPublish, &ftCurrent)))
{
#ifdef DBG_CERTSRV_DEBUG_PRINT
// give next pub status
if (!g_fDeltaCRLPublishDisabled)
{
DbgPrintRemainTime(TRUE, &ftCurrent, &g_ftDeltaCRLNextPublish);
}
#endif // DBG_CERTSRV_DEBUG_PRINT
}
else // "ready to publish" trigger
{
if (!g_fCRLPublishDisabled) // is publishing enabled?
{
fRebuildCRL = TRUE; // ENABLED, ready to go!
}
else
{
DBGPRINT((
DBG_SS_CERTSRV,
"CRLPubWakeupEvent(tid=%d): Publishing disabled\n",
GetCurrentThreadId() ));
}
}
ftZero.dwLowDateTime = 0;
ftZero.dwHighDateTime = 0;
while (TRUE)
{
hr = CRLPublishCRLs(
fRebuildCRL,
fForceRepublish,
NULL, // pwszUserName
!fForceRepublish && // fDeltaOnly
!fBaseTrigger &&
!g_fDeltaCRLPublishDisabled &&
!fDeltaCRLPublishDisabledOld,
fShadowDelta,
ftZero,
&fSetRetryTimer,
&hrPublish);
if (S_OK == hr)
{
break;
}
_PrintError(hr, "CRLPublishCRLs");
if (!fForceRepublish || fRebuildCRL)
{
_leave; // give up
}
// We failed to republish existing CRLs after a database restore
// and recovery; generate new base and delta CRLs and publish them.
fRebuildCRL = TRUE;
}
_PrintIfError(hrPublish, "CRLPublishCRLs(hrPublish)");
// if we called CRLPublishCRLs, clear the manual event it'll trigger
ResetEvent(g_hCRLManualPublishEvent);
// how many ms until next publish? set dwMSTimeOut
if (g_fCRLPublishDisabled)
{
// if disabled, don't set timeout
dwMSTimeOut = INFINITE;
CONSOLEPRINT1((
DBG_SS_CERTSRV,
"CRL Publishing Disabled, TimeOut=INFINITE (%d ms)\n",
dwMSTimeOut));
}
else
{
DWORD dwMSTimeOutDelta;
WCHAR *pwszCRLType = NULL;
crlComputeTimeOutEx(
FALSE,
&ftCurrent,
&g_ftCRLNextPublish,
&dwMSTimeOut);
if (g_fDeltaCRLPublishDisabled)
{
pwszCRLType = L"Base";
}
else
{
crlComputeTimeOutEx(
TRUE,
&ftCurrent,
&g_ftDeltaCRLNextPublish,
&dwMSTimeOutDelta);
if (dwMSTimeOut > dwMSTimeOutDelta)
{
dwMSTimeOut = dwMSTimeOutDelta;
}
pwszCRLType = L"Base + Delta";
}
if (NULL != pwszCRLType)
{
LONGLONG ll;
WCHAR *pwszTimePeriod = NULL;
WCHAR awc[1];
ll = dwMSTimeOut;
ll *= CVT_BASE / 1000; // milliseconds to FILETIME Period
ll = -ll; // FILETIME Period must be negative
hr = myFileTimePeriodToWszTimePeriod(
(FILETIME const *) &ll,
TRUE, // fExact
&pwszTimePeriod);
_PrintIfError(hr, "myFileTimePeriodToWszTimePeriod");
if (S_OK != hr)
{
awc[0] = L'\0';
pwszTimePeriod = awc;
}
CONSOLEPRINT3((
DBG_SS_CERTSRV,
"%ws CRL Publishing Enabled, TimeOut=%ds, %ws\n",
pwszCRLType,
dwMSTimeOut/1000,
pwszTimePeriod));
if (NULL != pwszTimePeriod && awc != pwszTimePeriod)
{
LocalFree(pwszTimePeriod);
}
}
}
// if we need to retry, wait no longer than the retry period
if (fSetRetryTimer)
{
if (dwMSTimeOut > CERTSRV_CRLPUB_RETRY_SECONDS * 1000)
{
dwMSTimeOut = CERTSRV_CRLPUB_RETRY_SECONDS * 1000;
CONSOLEPRINT1((
DBG_SS_CERTSRV,
"CRL Publishing periodic retry, TimeOut=%ds\n",
dwMSTimeOut/1000));
}
}
hr = S_OK;
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
_PrintError(hr, "Exception");
}
error:
*pdwMSTimeOut = dwMSTimeOut;
CertSrvExitServer(State);
return(hr);
}
HRESULT
WriteToLockedFile(
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN LPCWSTR szFileDir,
IN LPCWSTR szFile)
{
HRESULT hr;
WCHAR wszTmpPrepFile[MAX_PATH];
WCHAR wszTmpInUseFile[MAX_PATH];
BYTE *pbData = NULL;
DWORD cbData;
// According to JohnL, the best way to do this is to gen a temp
// file name, rename the existing file to that, then delete it.
//
// Logic:
// create unique preparation filename
// write new data to prep file
// create unique destination filename for old file (possibly locked)
// move old file to destination filename
// move prep file to (vacated) file name
// delete old file from destination filename
hr = DecodeFileW(szFile, &pbData, &cbData, CRYPT_STRING_BINARY);
if (S_OK == hr &&
cbEncoded == cbData &&
0 == memcmp(pbData, pbEncoded, cbData))
{
CSASSERT(S_OK == hr);
goto error; // already written, do nothing
}
// create a prep file
if (0 == GetTempFileName(szFileDir, L"pre", 0, wszTmpPrepFile))
{
hr = myHLastError();
_JumpError(hr, error, "GetTempFileName");
}
// write file to prep area
hr = EncodeToFileW(
wszTmpPrepFile,
pbEncoded,
cbEncoded,
DECF_FORCEOVERWRITE | CRYPT_STRING_BINARY);
_JumpIfError(hr, error, "EncodeToFileW");
if (0 == GetTempFileName(szFileDir, L"crl", 0, wszTmpInUseFile))
{
hr = myHLastError();
_JumpError(hr, error, "GetTempFileName");
}
// move old to "in use" file (empty file already exists from
// GetTempFileName call) may not exist, so don't bother checking status
MoveFileEx(
szFile,
wszTmpInUseFile,
MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING);
// move prepared file to current file
if (!MoveFileEx(wszTmpPrepFile, szFile, MOVEFILE_WRITE_THROUGH))
{
hr = myHLastError();
_JumpError(hr, error, "MoveFileEx");
}
// The "in use" file may not exist, so don't bother checking status.
DeleteFile(wszTmpInUseFile);
hr = S_OK;
error:
if (NULL != pbData)
{
LocalFree(pbData);
}
return(hr);
}
WCHAR const g_wszPropCRLNumber[] = wszPROPCRLNUMBER;
WCHAR const g_wszPropCRLMinBase[] = wszPROPCRLMINBASE;
WCHAR const g_wszPropCRLNameId[] = wszPROPCRLNAMEID;
WCHAR const g_wszPropCRLCount[] = wszPROPCRLCOUNT;
WCHAR const g_wszPropCRLThisUpdateDate[] = wszPROPCRLTHISUPDATE;
WCHAR const g_wszPropCRLNextUpdateDate[] = wszPROPCRLNEXTUPDATE;
WCHAR const g_wszPropCRLThisPublishDate[] = wszPROPCRLTHISPUBLISH;
WCHAR const g_wszPropCRLNextPublishDate[] = wszPROPCRLNEXTPUBLISH;
WCHAR const g_wszPropCRLEffectiveDate[] = wszPROPCRLEFFECTIVE;
WCHAR const g_wszPropCRLPropagationCompleteDate[] = wszPROPCRLPROPAGATIONCOMPLETE;
WCHAR const g_wszPropCRLLastPublished[] = wszPROPCRLLASTPUBLISHED;
WCHAR const g_wszPropCRLPublishAttempts[] = wszPROPCRLPUBLISHATTEMPTS;
WCHAR const g_wszPropCRLPublishFlags[] = wszPROPCRLPUBLISHFLAGS;
WCHAR const g_wszPropCRLPublishStatusCode[] = wszPROPCRLPUBLISHSTATUSCODE;
WCHAR const g_wszPropCRLPublishError[] = wszPROPCRLPUBLISHERROR;
WCHAR const g_wszPropCRLRawCRL[] = wszPROPCRLRAWCRL;
HRESULT
crlWriteCRLToDB(
IN DWORD CRLNumber,
IN DWORD CRLMinBase, // 0 implies base CRL
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fShadowDelta, // empty delta CRL with new MinBaseCRL
IN DWORD CRLNameId,
IN DWORD CRLCount,
IN FILETIME const *pftThisUpdate,
IN FILETIME const *pftNextUpdate,
IN FILETIME const *pftThisPublish,
IN FILETIME const *pftNextPublish,
OPTIONAL IN FILETIME const *pftQuery,
IN FILETIME const *pftPropagationComplete,
OPTIONAL IN BYTE const *pbCRL,
IN DWORD cbCRL,
OUT DWORD *pdwRowId)
{
HRESULT hr;
ICertDBRow *prow = NULL;
DWORD CRLPublishFlags;
BOOL fCommitted = FALSE;
*pdwRowId = 0;
// Create a new CRL table entry
hr = g_pCertDB->OpenRow(
PROPTABLE_CRL,
0,
NULL,
&prow);
_JumpIfError(hr, error, "OpenRow");
prow->GetRowId(pdwRowId);
hr = prow->SetProperty(
g_wszPropCRLNumber,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLNumber),
(BYTE const *) &CRLNumber);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLMinBase,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLMinBase),
(BYTE const *) &CRLMinBase);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLNameId,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLNameId),
(BYTE const *) &CRLNameId);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLCount,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLCount),
(BYTE const *) &CRLCount);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLThisUpdateDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftThisUpdate),
(BYTE const *) pftThisUpdate);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLNextUpdateDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftNextUpdate),
(BYTE const *) pftNextUpdate);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLThisPublishDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftThisPublish),
(BYTE const *) pftThisPublish);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLNextPublishDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftNextPublish),
(BYTE const *) pftNextPublish);
_JumpIfError(hr, error, "SetProperty");
if (NULL != pftQuery)
{
hr = prow->SetProperty(
g_wszPropCRLEffectiveDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftQuery),
(BYTE const *) pftQuery);
_JumpIfError(hr, error, "SetProperty");
}
hr = prow->SetProperty(
g_wszPropCRLPropagationCompleteDate,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftPropagationComplete),
(BYTE const *) pftPropagationComplete);
_JumpIfError(hr, error, "SetProperty");
CRLPublishFlags = 0 == CRLMinBase? CPF_BASE : CPF_DELTA;
if (fShadowDelta)
{
CRLPublishFlags |= CPF_SHADOW;
}
if (NULL != pwszUserName)
{
CRLPublishFlags |= CPF_MANUAL;
}
hr = prow->SetProperty(
g_wszPropCRLPublishFlags,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLPublishFlags),
(BYTE const *) &CRLPublishFlags);
_JumpIfError(hr, error, "SetProperty");
hr = prow->SetProperty(
g_wszPropCRLRawCRL,
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CRL,
cbCRL,
pbCRL);
_JumpIfError(hr, error, "SetProperty");
hr = prow->CommitTransaction(TRUE);
_JumpIfError(hr, error, "CommitTransaction");
fCommitted = TRUE;
error:
if (NULL != prow)
{
if (S_OK != hr && !fCommitted)
{
HRESULT hr2 = prow->CommitTransaction(FALSE);
_PrintIfError(hr2, "CommitTransaction");
}
prow->Release();
}
return(hr);
}
HRESULT
crlCombineCRLError(
IN ICertDBRow *prow,
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
OPTIONAL IN WCHAR const *pwszCRLError,
OUT WCHAR **ppwszCRLErrorNew)
{
HRESULT hr;
WCHAR *pwszCRLErrorOld = NULL;
WCHAR *pwszCRLErrorNew = NULL;
WCHAR *pwsz;
DWORD cwc;
DWORD cwc2;
*ppwszCRLErrorNew = NULL;
hr = PKCSGetProperty(
prow,
g_wszPropCRLPublishError,
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CRL,
NULL,
(BYTE **) &pwszCRLErrorOld);
_PrintIfError2(hr, "PKCSGetProperty", CERTSRV_E_PROPERTY_EMPTY);
cwc = 0;
if (NULL != pwszCRLErrorOld)
{
pwsz = wcsstr(pwszCRLErrorOld, L"\n\n");
if (NULL == pwsz)
{
pwsz = pwszCRLErrorOld;
}
*pwsz = L'\0';
cwc = wcslen(pwszCRLErrorOld);
if (0 != cwc)
{
cwc++; // newline separator
}
}
if (NULL != pwszUserName)
{
cwc2 = wcslen(g_pwszPublishedBy) + wcslen(pwszUserName);
cwc += cwc2;
}
else
{
cwc++;
}
cwc += 2; // double newline separator
if (NULL != pwszCRLError)
{
cwc += wcslen(pwszCRLError);
}
pwszCRLErrorNew = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(cwc + 1) * sizeof(WCHAR));
if (NULL == pwszCRLErrorNew)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
*pwszCRLErrorNew = L'\0';
if (NULL != pwszCRLErrorOld && L'\0' != *pwszCRLErrorOld)
{
wcscpy(pwszCRLErrorNew, pwszCRLErrorOld);
wcscat(pwszCRLErrorNew, L"\n");
}
if (NULL != pwszUserName)
{
pwsz = &pwszCRLErrorNew[wcslen(pwszCRLErrorNew)];
_snwprintf(pwsz, cwc2, g_pwszPublishedBy, pwszUserName);
}
else
{
wcscat(pwszCRLErrorNew, L"-");
}
wcscat(pwszCRLErrorNew, L"\n\n"); // double newline separator
if (NULL != pwszCRLError)
{
wcscat(pwszCRLErrorNew, pwszCRLError);
}
CSASSERT(wcslen(pwszCRLErrorNew) <= cwc);
CSASSERT(
wcslen(pwszCRLErrorNew) +
(NULL != pwszUserName? wcslen(L"%ws") : 0) == cwc);
*ppwszCRLErrorNew = pwszCRLErrorNew;
pwszCRLErrorNew = NULL;
hr = S_OK;
error:
if (NULL != pwszCRLErrorOld)
{
LocalFree(pwszCRLErrorOld);
}
if (NULL != pwszCRLErrorNew)
{
LocalFree(pwszCRLErrorNew);
}
return(hr);
}
HRESULT
crlUpdateCRLPublishStateInDB(
IN DWORD RowId,
IN FILETIME const *pftCurrent,
IN HRESULT hrCRLPublish,
IN DWORD CRLPublishFlags,
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
OPTIONAL IN WCHAR const *pwszCRLError)
{
HRESULT hr;
ICertDBRow *prow = NULL;
WCHAR *pwszCRLErrorNew = NULL;
DWORD cb;
DWORD dw;
BOOL fCommitted = FALSE;
hr = g_pCertDB->OpenRow(
PROPTABLE_CRL,
RowId,
NULL,
&prow);
_JumpIfError(hr, error, "OpenRow");
hr = prow->SetProperty(
g_wszPropCRLLastPublished,
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(*pftCurrent),
(BYTE const *) pftCurrent);
_JumpIfError(hr, error, "SetProperty");
cb = sizeof(dw);
hr = prow->GetProperty(
g_wszPropCRLPublishAttempts,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
&cb,
(BYTE *) &dw);
if (S_OK != hr)
{
dw = 0;
}
dw++;
hr = prow->SetProperty(
g_wszPropCRLPublishAttempts,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(dw),
(BYTE const *) &dw);
_JumpIfError(hr, error, "SetProperty");
cb = sizeof(dw);
hr = prow->GetProperty(
g_wszPropCRLPublishFlags,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
&cb,
(BYTE *) &dw);
if (S_OK != hr)
{
dw = 0;
}
CRLPublishFlags |= (CPF_BASE | CPF_DELTA | CPF_SHADOW | CPF_MANUAL) & dw;
if (S_OK == hrCRLPublish)
{
CRLPublishFlags |= CPF_COMPLETE;
}
hr = prow->SetProperty(
g_wszPropCRLPublishFlags,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(CRLPublishFlags),
(BYTE const *) &CRLPublishFlags);
_JumpIfError(hr, error, "SetProperty");
// Always set error string property to clear out previous errors.
hr = prow->SetProperty(
g_wszPropCRLPublishStatusCode,
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CRL,
sizeof(hrCRLPublish),
(BYTE const *) &hrCRLPublish);
_JumpIfError(hr, error, "SetProperty");
hr = crlCombineCRLError(prow, pwszUserName, pwszCRLError, &pwszCRLErrorNew);
_JumpIfError(hr, error, "crlCombineCRLError");
hr = prow->SetProperty(
g_wszPropCRLPublishError,
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CRL,
NULL == pwszCRLErrorNew? 0 : MAXDWORD,
(BYTE const *) pwszCRLErrorNew);
_JumpIfError(hr, error, "SetProperty");
hr = prow->CommitTransaction(TRUE);
_JumpIfError(hr, error, "CommitTransaction");
fCommitted = TRUE;
error:
if (NULL != prow)
{
if (S_OK != hr && !fCommitted)
{
HRESULT hr2 = prow->CommitTransaction(FALSE);
_PrintIfError(hr2, "CommitTransaction");
}
prow->Release();
}
if (NULL != pwszCRLErrorNew)
{
LocalFree(pwszCRLErrorNew);
}
return(hr);
}
HRESULT
WriteCRLToDSAttribute(
IN WCHAR const *pwszCRLDN,
IN BOOL fDelta,
IN BYTE const *pbCRL,
IN DWORD cbCRL,
OUT WCHAR **ppwszError)
{
HRESULT hr;
DWORD ldaperr;
BOOL fRebind = FALSE;
LDAPMod crlmod;
struct berval crlberval;
struct berval *crlVals[2];
LDAPMod *mods[2];
while (TRUE)
{
if (NULL == g_pld)
{
hr = myRobustLdapBind(&g_pld, FALSE);
_JumpIfError(hr, error, "myRobustLdapBind");
}
mods[0] = &crlmod;
mods[1] = NULL;
crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
crlmod.mod_type = fDelta? wszDSDELTACRLATTRIBUTE : wszDSBASECRLATTRIBUTE;
crlmod.mod_bvalues = crlVals;
crlVals[0] = &crlberval;
crlVals[1] = NULL;
crlberval.bv_len = cbCRL;
crlberval.bv_val = (char *) pbCRL;
ldaperr = ldap_modify_ext_s(
g_pld,
const_cast<WCHAR *>(pwszCRLDN),
mods,
NULL,
NULL);
hr = myHLdapError(g_pld, ldaperr, ppwszError);
_PrintIfErrorStr(hr, "ldap_modify_ext_s", pwszCRLDN);
if (fRebind || S_OK == hr)
{
break;
}
if (!myLdapRebindRequired(ldaperr, g_pld))
{
_JumpErrorStr(hr, error, "ldap_modify_ext_s", pwszCRLDN);
}
fRebind = TRUE;
if (NULL != g_pld)
{
ldap_unbind(g_pld);
g_pld = NULL;
}
}
error:
return(hr);
}
HRESULT
crlParseURLPrefix(
IN WCHAR const *pwszIn,
IN DWORD cwcPrefix,
OUT WCHAR *pwcPrefix,
OUT WCHAR const **ppwszOut)
{
HRESULT hr;
WCHAR const *pwsz;
CSASSERT(6 <= cwcPrefix);
wcscpy(pwcPrefix, L"file:");
*ppwszOut = pwszIn;
if (L'\\' != pwszIn[0] || L'\\' != pwszIn[1])
{
pwsz = wcschr(pwszIn, L':');
if (NULL != pwsz)
{
DWORD cwc;
pwsz++;
cwc = SAFE_SUBTRACT_POINTERS(pwsz, pwszIn);
if (2 < cwc && cwc < cwcPrefix)
{
CopyMemory(pwcPrefix, pwszIn, cwc * sizeof(WCHAR));
pwcPrefix[cwc] = L'\0';
if (0 == lstrcmpi(pwcPrefix, L"file:") &&
L'/' == pwsz[0] &&
L'/' == pwsz[1])
{
pwsz += 2;
}
*ppwszOut = pwsz;
}
}
}
hr = S_OK;
//error:
return(hr);
}
VOID
crlLogError(
IN BOOL fDelta,
IN BOOL fLdapURL,
IN DWORD iKey,
IN WCHAR const *pwszURL,
IN WCHAR const *pwszError,
IN HRESULT hrPublish)
{
HRESULT hr;
WCHAR const *apwsz[6];
WORD cpwsz;
WCHAR wszKey[11 + 1];
WCHAR awchr[cwcHRESULTSTRING];
WCHAR const *pwszMessageText = NULL;
WCHAR *pwszHostName = NULL;
DWORD LogMsg;
if (fLdapURL && NULL != g_pld)
{
myLdapGetDSHostName(g_pld, &pwszHostName);
}
wsprintf(wszKey, L"%u", iKey);
pwszMessageText = myGetErrorMessageText(hrPublish, TRUE);
if (NULL == pwszMessageText)
{
pwszMessageText = myHResultToStringRaw(awchr, hrPublish);
}
cpwsz = 0;
apwsz[cpwsz++] = wszKey;
apwsz[cpwsz++] = pwszURL;
apwsz[cpwsz++] = pwszMessageText;
LogMsg = fDelta?
MSG_E_DELTA_CRL_PUBLICATION : MSG_E_BASE_CRL_PUBLICATION;
if (NULL != pwszHostName)
{
LogMsg = fDelta?
MSG_E_DELTA_CRL_PUBLICATION_HOST_NAME :
MSG_E_BASE_CRL_PUBLICATION_HOST_NAME;
}
else
{
pwszHostName = L"";
}
apwsz[cpwsz++] = pwszHostName;
apwsz[cpwsz++] = NULL != pwszError? L"\n" : L"";
apwsz[cpwsz++] = NULL != pwszError? pwszError : L"";
CSASSERT(ARRAYSIZE(apwsz) >= cpwsz);
if (CERTLOG_ERROR <= g_dwLogLevel)
{
hr = LogEvent(EVENTLOG_ERROR_TYPE, LogMsg, cpwsz, apwsz);
_PrintIfError(hr, "LogEvent");
}
//error:
if (NULL != pwszMessageText && awchr != pwszMessageText)
{
LocalFree(const_cast<WCHAR *>(pwszMessageText));
}
}
HRESULT
crlWriteCRLToURL(
IN BOOL fDelta,
IN BOOL iKey,
IN WCHAR const *pwszURL,
IN BYTE const *pbCRL,
IN DWORD cbCRL,
OUT DWORD *pPublishFlags)
{
HRESULT hr;
WCHAR *pwszDup = NULL;
WCHAR const *pwsz2;
WCHAR *pwszT;
WCHAR awcPrefix[6]; // file:/ftp:/http:/ldap: and trailing '\0'
DWORD ErrorFlags;
WCHAR *pwszError = NULL;
*pPublishFlags = 0;
ErrorFlags = CPF_BADURL_ERROR;
hr = crlParseURLPrefix(
pwszURL,
ARRAYSIZE(awcPrefix),
awcPrefix,
&pwsz2);
_JumpIfError(hr, error, "crlParseURLPrefix");
DBGPRINT((
DBG_SS_CERTSRV,
"crlWriteCRLToURL: \"%ws\" %ws\n",
awcPrefix,
pwsz2));
if (0 == lstrcmpi(awcPrefix, L"file:"))
{
ErrorFlags = CPF_FILE_ERROR;
hr = myDupString(pwsz2, &pwszDup);
_JumpIfError(hr, error, "myDupString");
pwszT = wcsrchr(pwszDup, L'\\');
if (NULL != pwszT)
{
*pwszT = L'\0'; // for dir path, remove "\filename.crl"
}
// tricky
hr = WriteToLockedFile(pbCRL, cbCRL, pwszDup, pwsz2);
_JumpIfError(hr, error, "WriteToLockedFile");
}
else if (0 == lstrcmpi(awcPrefix, L"ftp:"))
{
ErrorFlags = CPF_FTP_ERROR;
hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
_JumpError(hr, error, "Publish to ftp:");
}
else if (0 == lstrcmpi(awcPrefix, L"http:"))
{
ErrorFlags = CPF_HTTP_ERROR;
hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
_JumpError(hr, error, "Publish to http:");
}
else if (0 == lstrcmpi(awcPrefix, L"ldap:"))
{
ErrorFlags = CPF_LDAP_ERROR;
while (L'/' == *pwsz2)
{
pwsz2++;
}
hr = myDupString(pwsz2, &pwszDup);
_JumpIfError(hr, error, "myDupString");
pwszT = wcschr(pwszDup, L'?');
if (NULL != pwszT)
{
*pwszT = L'\0';
}
hr = WriteCRLToDSAttribute(pwszDup, fDelta, pbCRL, cbCRL, &pwszError);
_JumpIfError(hr, error, "WriteCRLToDSAttribute");
}
else
{
ErrorFlags = CPF_BADURL_ERROR;
hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
_JumpError(hr, error, "Publish to unknown URL type");
}
CSASSERT(S_OK == hr);
error:
if (S_OK != hr)
{
*pPublishFlags = ErrorFlags;
crlLogError(
fDelta,
CPF_LDAP_ERROR == ErrorFlags,
iKey,
pwszURL,
pwszError,
hr);
}
if (NULL != pwszError)
{
LocalFree(pwszError);
}
if (NULL != pwszDup)
{
LocalFree(pwszDup);
}
return(hr);
}
HRESULT
crlWriteCRLToURLList(
IN BOOL fDelta,
IN DWORD iKey,
IN WCHAR const * const *papwszURLs,
IN BYTE const *pbCRL,
IN DWORD cbCRL,
IN OUT DWORD *pCRLPublishFlags,
OUT WCHAR **ppwszCRLError)
{
HRESULT hr = S_OK;
HRESULT hr2;
WCHAR *pwszCRLError = NULL;
DWORD PublishFlags;
*ppwszCRLError = NULL;
// publish this CRL in multiple places
if (NULL != papwszURLs)
{
WCHAR const * const *ppwsz;
for (ppwsz = papwszURLs; NULL != *ppwsz; ppwsz++)
{
PublishFlags = 0;
hr2 = crlWriteCRLToURL(
fDelta,
iKey,
*ppwsz,
pbCRL,
cbCRL,
&PublishFlags);
*pCRLPublishFlags |= PublishFlags;
if (S_OK != hr2)
{
DWORD cwc;
WCHAR *pwsz;
if (S_OK == hr)
{
hr = hr2; // Save first error
}
_PrintError(hr2, "crlWriteCRLToURL");
cwc = wcslen(*ppwsz) + 1;
if (NULL != pwszCRLError)
{
cwc += wcslen(pwszCRLError) + 1;
}
pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
if (NULL == pwsz)
{
hr2 = E_OUTOFMEMORY;
_PrintError(hr2, "LocalAlloc");
if (S_OK == hr)
{
hr = hr2; // Save first error
}
}
else
{
pwsz[0] = L'\0';
if (NULL != pwszCRLError)
{
wcscpy(pwsz, pwszCRLError);
wcscat(pwsz, L"\n");
LocalFree(pwszCRLError);
}
wcscat(pwsz, *ppwsz);
pwszCRLError = pwsz;
}
}
}
}
*ppwszCRLError = pwszCRLError;
pwszCRLError = NULL;
//error:
if (NULL != pwszCRLError)
{
LocalFree(pwszCRLError);
}
return(hr);
}
HRESULT
crlIsDeltaCRL(
IN CRL_CONTEXT const *pCRL,
OUT BOOL *pfIsDeltaCRL)
{
HRESULT hr;
CERT_EXTENSION *pExt;
*pfIsDeltaCRL = FALSE;
pExt = CertFindExtension(
szOID_DELTA_CRL_INDICATOR,
pCRL->pCrlInfo->cExtension,
pCRL->pCrlInfo->rgExtension);
if (NULL != pExt)
{
*pfIsDeltaCRL = TRUE;
}
hr = S_OK;
//error:
return(hr);
}
HRESULT
crlWriteCRLToCAStore(
IN BOOL fDelta,
IN DWORD iKey,
IN BYTE const *pbCRL,
IN DWORD cbCRL,
IN CERT_CONTEXT const *pccCA)
{
HRESULT hr;
HCERTSTORE hStore = NULL;
CRL_CONTEXT const *pCRLStore = NULL;
CRL_CONTEXT const *pCRLNew = NULL;
BOOL fFound = FALSE;
hStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING,
NULL, // hProv
CERT_SYSTEM_STORE_LOCAL_MACHINE,
wszCA_CERTSTORE);
if (NULL == hStore)
{
hr = myHLastError();
_JumpError(hr, error, "CertOpenStore");
}
while (TRUE)
{
DWORD dwCryptFlags;
BOOL fIsDeltaCRL;
CRL_CONTEXT const *pCRL;
dwCryptFlags = CERT_STORE_SIGNATURE_FLAG;
pCRLStore = CertGetCRLFromStore(
hStore,
pccCA,
pCRLStore,
&dwCryptFlags);
if (NULL == pCRLStore)
{
break;
}
// delete this CRL from the store ONLY if the CRL signature matches
// this CA context's public key
if (0 != dwCryptFlags)
{
continue; // no match -- skip
}
hr = crlIsDeltaCRL(pCRLStore, &fIsDeltaCRL);
_JumpIfError(hr, error, "crlIsDeltaCRL");
if (fIsDeltaCRL)
{
if (!fDelta)
{
continue; // no match -- skip Delta CRLs
}
}
else
{
if (fDelta)
{
continue; // no match -- skip Base CRLs
}
}
// See if it has already been published
if (cbCRL == pCRLStore->cbCrlEncoded &&
0 == memcmp(pbCRL, pCRLStore->pbCrlEncoded, cbCRL))
{
fFound = TRUE;
continue; // exact match -- already published
}
pCRL = CertDuplicateCRLContext(pCRLStore);
if (!CertDeleteCRLFromStore(pCRL))
{
hr = myHLastError();
_JumpError(hr, error, "CertDeleteCRLFromStore");
}
}
if (!fFound)
{
pCRLNew = CertCreateCRLContext(X509_ASN_ENCODING, pbCRL, cbCRL);
if (NULL == pCRLNew)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCRLContext");
}
if (!CertAddCRLContextToStore(
hStore,
pCRLNew,
CERT_STORE_ADD_ALWAYS,
NULL))
{
hr = myHLastError();
_JumpError(hr, error, "CertAddCRLContextToStore");
}
}
hr = S_OK;
error:
if (S_OK != hr)
{
crlLogError(fDelta, FALSE, iKey, g_pwszIntermediateCAStore, NULL, hr);
}
if (NULL != pCRLNew)
{
CertFreeCRLContext(pCRLNew);
}
if (NULL != pCRLStore)
{
CertFreeCRLContext(pCRLStore);
}
if (NULL != hStore)
{
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
return(hr);
}
HRESULT
crlPublishGeneratedCRL(
IN DWORD RowId,
IN FILETIME const *pftCurrent,
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fDelta,
IN DWORD iKey,
IN BYTE const *pbCRL,
IN DWORD cbCRL,
IN CACTX const *pCAContext,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrCRLPublish)
{
HRESULT hr;
HRESULT hrCRLPublish;
DWORD CRLPublishFlags;
WCHAR *pwszCRLError = NULL;
*pfRetryNeeded = FALSE;
hrCRLPublish = S_OK;
CRLPublishFlags = 0;
hr = crlWriteCRLToCAStore(fDelta, iKey, pbCRL, cbCRL, pCAContext->pccCA);
if (S_OK != hr)
{
_PrintError(hr, "crlWriteCRLToCAStore");
hrCRLPublish = hr;
CRLPublishFlags |= CPF_CASTORE_ERROR;
}
hr = crlWriteCRLToURLList(
fDelta,
iKey,
fDelta?
pCAContext->papwszDeltaCRLFiles :
pCAContext->papwszCRLFiles,
pbCRL,
cbCRL,
&CRLPublishFlags,
&pwszCRLError);
if (S_OK != hr)
{
_PrintError(hr, "crlWriteCRLToURLList");
if (S_OK == hrCRLPublish)
{
hrCRLPublish = hr; // save first error
}
}
if (S_OK != hrCRLPublish)
{
*pfRetryNeeded = TRUE;
}
hr = crlUpdateCRLPublishStateInDB(
RowId,
pftCurrent,
hrCRLPublish,
CRLPublishFlags,
pwszUserName,
pwszCRLError);
_JumpIfError(hr, error, "crlUpdateCRLPublishStateInDB");
error:
*phrCRLPublish = hrCRLPublish;
if (NULL != pwszCRLError)
{
LocalFree(pwszCRLError);
}
return(hr);
}
HRESULT
crlSignAndSaveCRL(
IN DWORD CRLNumber,
IN DWORD CRLNumberBaseMin, // 0 implies Base CRL; else Delta CRL
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fShadowDelta, // empty delta CRL with new MinBaseCRL
IN CACTX const *pCAContext,
IN DWORD cCRL,
IN CRL_ENTRY *aCRL,
IN FILETIME const *pftCurrent,
IN FILETIME const *pftThisUpdate, // includes skew
IN FILETIME const *pftNextUpdate, // includes skew & overlap
IN FILETIME const *pftThisPublish,
IN FILETIME const *pftNextPublish,
OPTIONAL IN FILETIME const *pftQuery,
IN FILETIME const *pftPropagationComplete,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrCRLPublish)
{
HRESULT hr;
CRL_INFO CRLInfo;
DWORD i;
DWORD cb;
DWORD cbCRL;
BYTE *pbCrlEncoded = NULL;
BYTE *pbCRL = NULL;
#define CCRLEXT 6
CERT_EXTENSION aext[CCRLEXT];
BYTE *apbFree[CCRLEXT];
DWORD cpbFree = 0;
DWORD RowId;
*pfRetryNeeded = FALSE;
*phrCRLPublish = S_OK;
ZeroMemory(&CRLInfo, sizeof(CRLInfo));
CRLInfo.dwVersion = CRL_V2;
CRLInfo.SignatureAlgorithm.pszObjId = pCAContext->pszObjIdSignatureAlgorithm;
CRLInfo.Issuer.pbData = pCAContext->pccCA->pCertInfo->Subject.pbData;
CRLInfo.Issuer.cbData = pCAContext->pccCA->pCertInfo->Subject.cbData;
CRLInfo.ThisUpdate = *pftThisUpdate;
CRLInfo.NextUpdate = *pftNextUpdate;
CRLInfo.cCRLEntry = cCRL;
CRLInfo.rgCRLEntry = aCRL;
CRLInfo.cExtension = 0;
CRLInfo.rgExtension = aext;
ZeroMemory(aext, sizeof(aext));
if (NULL != pCAContext->KeyAuthority2CRL.pbData)
{
aext[CRLInfo.cExtension].pszObjId = szOID_AUTHORITY_KEY_IDENTIFIER2;
if (EDITF_ENABLEAKICRITICAL & g_CRLEditFlags)
{
aext[CRLInfo.cExtension].fCritical = TRUE;
}
aext[CRLInfo.cExtension].Value = pCAContext->KeyAuthority2CRL;
CRLInfo.cExtension++;
}
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
&pCAContext->NameId,
0,
CERTLIB_USE_LOCALALLOC,
&aext[CRLInfo.cExtension].Value.pbData,
&aext[CRLInfo.cExtension].Value.cbData))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
aext[CRLInfo.cExtension].pszObjId = szOID_CERTSRV_CA_VERSION;
apbFree[cpbFree++] = aext[CRLInfo.cExtension].Value.pbData,
CRLInfo.cExtension++;
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
&CRLNumber,
0,
CERTLIB_USE_LOCALALLOC,
&aext[CRLInfo.cExtension].Value.pbData,
&aext[CRLInfo.cExtension].Value.cbData))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
aext[CRLInfo.cExtension].pszObjId = szOID_CRL_NUMBER;
apbFree[cpbFree++] = aext[CRLInfo.cExtension].Value.pbData;
if ((CRLF_CRLNUMBER_CRITICAL & g_dwCRLFlags) && 0 == CRLNumberBaseMin)
{
aext[CRLInfo.cExtension].fCritical = TRUE;
}
CRLInfo.cExtension++;
// NextPublish is the earliest the client should look for a newer CRL.
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CHOICE_OF_TIME,
pftNextPublish,
0,
CERTLIB_USE_LOCALALLOC,
&aext[CRLInfo.cExtension].Value.pbData,
&aext[CRLInfo.cExtension].Value.cbData))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
aext[CRLInfo.cExtension].pszObjId = szOID_CRL_NEXT_PUBLISH;
apbFree[cpbFree++] = aext[CRLInfo.cExtension].Value.pbData,
CRLInfo.cExtension++;
if (0 != CRLNumberBaseMin) // if Delta CRL
{
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
&CRLNumberBaseMin,
0,
CERTLIB_USE_LOCALALLOC,
&aext[CRLInfo.cExtension].Value.pbData,
&aext[CRLInfo.cExtension].Value.cbData))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
aext[CRLInfo.cExtension].pszObjId = szOID_DELTA_CRL_INDICATOR;
aext[CRLInfo.cExtension].fCritical = TRUE;
apbFree[cpbFree++] = aext[CRLInfo.cExtension].Value.pbData,
CRLInfo.cExtension++;
// Add a CDP to base and delta CRLs to make it easier to manually
// publish an off-line CA's CRLs to the correct DS location.
if (NULL != pCAContext->CDPCRLDelta.pbData)
{
aext[CRLInfo.cExtension].pszObjId = szOID_CRL_SELF_CDP;
aext[CRLInfo.cExtension].Value = pCAContext->CDPCRLDelta;
CRLInfo.cExtension++;
}
}
else
{
// else if Base CRL (and if delta CRLs are enabled)
if (!g_fDeltaCRLPublishDisabled &&
NULL != pCAContext->CDPCRLFreshest.pbData)
{
aext[CRLInfo.cExtension].pszObjId = szOID_FRESHEST_CRL;
aext[CRLInfo.cExtension].Value = pCAContext->CDPCRLFreshest;
CRLInfo.cExtension++;
}
// Add a CDP to base and delta CRLs to make it easier to manually
// publish an off-line CA's CRLs to the correct DS location.
if (NULL != pCAContext->CDPCRLBase.pbData)
{
aext[CRLInfo.cExtension].pszObjId = szOID_CRL_SELF_CDP;
aext[CRLInfo.cExtension].Value = pCAContext->CDPCRLBase;
CRLInfo.cExtension++;
}
}
CSASSERT(ARRAYSIZE(aext) >= CRLInfo.cExtension);
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CERT_CRL_TO_BE_SIGNED,
&CRLInfo,
0,
CERTLIB_USE_LOCALALLOC,
&pbCrlEncoded, // pbEncoded
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
hr = myEncodeSignedContent(
pCAContext->hProvCA,
X509_ASN_ENCODING,
pCAContext->pszObjIdSignatureAlgorithm,
pbCrlEncoded,
cb,
CERTLIB_USE_LOCALALLOC,
&pbCRL,
&cbCRL); // use LocalAlloc*
_JumpIfError(hr, error, "myEncodeSignedContent");
hr = crlWriteCRLToDB(
CRLNumber, // CRLNumber
CRLNumberBaseMin, // CRLMinBase: 0 implies Base CRL
pwszUserName,
fShadowDelta,
pCAContext->NameId, // CRLNameId
cCRL, // CRLCount
&CRLInfo.ThisUpdate, // pftThisUpdate
&CRLInfo.NextUpdate, // pftNextUpdate
pftThisPublish, // pftThisPublish
pftNextPublish, // pftNextPublish
pftQuery,
pftPropagationComplete,
pbCRL, // pbCRL
cbCRL, // cbCRL
&RowId);
_JumpIfError(hr, error, "crlWriteCRLToDB");
hr = crlPublishGeneratedCRL(
RowId,
pftCurrent,
pwszUserName,
0 != CRLNumberBaseMin, // fDelta
pCAContext->iKey,
pbCRL, // pbCRL
cbCRL, // cbCRL
pCAContext,
pfRetryNeeded,
phrCRLPublish);
_JumpIfError(hr, error, "crlPublishGeneratedCRL");
error:
CSASSERT(ARRAYSIZE(aext) >= CRLInfo.cExtension);
CSASSERT(ARRAYSIZE(apbFree) >= cpbFree);
for (i = 0; i < cpbFree; i++)
{
CSASSERT(NULL != apbFree[i]);
LocalFree(apbFree[i]);
}
if (NULL != pbCrlEncoded)
{
LocalFree(pbCrlEncoded);
}
if (NULL != pbCRL)
{
LocalFree(pbCRL);
}
return(myHError(hr));
}
///////////////////////////////////////////////////
// crlPublishCRLFromCAContext is called to build and save one CRL.
//
HRESULT
crlPublishCRLFromCAContext(
IN DWORD CRLNumber,
IN DWORD CRLNumberBaseMin, // 0 implies Base CRL; else Delta CRL
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fShadowDelta, // empty delta CRL with new MinBaseCRL
IN CACTX const *pCAContext,
IN FILETIME const *pftCurrent,
IN FILETIME ftThisUpdate, // clamped by CA cert
IN OUT FILETIME *pftNextUpdate, // clamped by CA cert
OPTIONAL OUT BOOL *pfClamped,
OPTIONAL IN FILETIME const *pftQuery,
IN FILETIME const *pftThisPublish,
IN FILETIME const *pftNextPublish,
IN FILETIME const *pftLastPublishBase,
IN FILETIME const *pftPropagationComplete,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrPublish)
{
HRESULT hr;
DWORD cCRL = 0;
CRL_ENTRY *aCRL = NULL;
VOID *pvBlockSerial = NULL;
CERT_INFO const *pCertInfo = pCAContext->pccCA->pCertInfo;
*pfRetryNeeded = FALSE;
*phrPublish = S_OK;
__try
{
if (!fShadowDelta)
{
hr = crlBuildCRLArray(
0 != CRLNumberBaseMin, // fDelta
pftQuery,
pftThisPublish,
pftLastPublishBase,
pCAContext->iKey,
&cCRL,
&aCRL,
&pvBlockSerial);
_JumpIfError(hr, error, "crlBuildCRLArray");
}
// Ensure it is not before the CA certificate's start date.
if (0 > CompareFileTime(&ftThisUpdate, &pCertInfo->NotBefore))
{
// clamp
ftThisUpdate = pCertInfo->NotBefore;
}
// Ensure it is not after the CA certificate's end date.
if (NULL != pfClamped)
{
//init to FALSE
*pfClamped = FALSE;
}
if (0 == (CRLF_PUBLISH_EXPIRED_CERT_CRLS & g_dwCRLFlags) &&
0 < CompareFileTime(pftNextUpdate, &pCertInfo->NotAfter))
{
// clamp
*pftNextUpdate = pCertInfo->NotAfter;
if (NULL != pfClamped)
{
*pfClamped = TRUE;
}
}
#ifdef DBG_CERTSRV_DEBUG_PRINT
{
WCHAR *pwszNow = NULL;
WCHAR *pwszQuery = NULL;
WCHAR *pwszThisUpdate = NULL;
WCHAR *pwszNextUpdate = NULL;
WCHAR const *pwszCRLType = 0 == CRLNumberBaseMin? L"Base" : L"Delta";
myGMTFileTimeToWszLocalTime(pftThisPublish, TRUE, &pwszNow);
if (NULL != pftQuery)
{
myGMTFileTimeToWszLocalTime(pftQuery, TRUE, &pwszQuery);
}
myGMTFileTimeToWszLocalTime(&ftThisUpdate, TRUE, &pwszThisUpdate);
myGMTFileTimeToWszLocalTime(pftNextUpdate, TRUE, &pwszNextUpdate);
DBGPRINT((
DBG_SS_ERROR | DBG_SS_CERTSRV,
"crlPublishCRLFromCAContext(tid=%d, CA Version=%u.%u): %ws CRL %u,%hs %u\n"
" %ws CRL Publishing now(%ws)\n"
" %ws CRL Query(%ws)\n"
" %ws CRL ThisUpdate(%ws)\n"
" %ws CRL NextUpdate(%ws)\n",
GetCurrentThreadId(),
pCAContext->iCert,
pCAContext->iKey,
pwszCRLType,
CRLNumber,
0 == CRLNumberBaseMin? "" : " Min Base",
CRLNumberBaseMin,
pwszCRLType,
pwszNow,
pwszCRLType,
NULL != pftQuery? pwszQuery : L"None",
pwszCRLType,
pwszThisUpdate,
pwszCRLType,
pwszNextUpdate));
if (NULL != pwszNow)
{
LocalFree(pwszNow);
}
if (NULL != pwszQuery)
{
LocalFree(pwszQuery);
}
if (NULL != pwszThisUpdate)
{
LocalFree(pwszThisUpdate);
}
if (NULL != pwszNextUpdate)
{
LocalFree(pwszNextUpdate);
}
}
#endif //DBG_CERTSRV_DEBUG_PRINT
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlSignAndSaveCRL(
CRLNumber,
CRLNumberBaseMin,
pwszUserName,
fShadowDelta,
pCAContext,
cCRL,
aCRL,
pftCurrent,
&ftThisUpdate,
pftNextUpdate,
pftThisPublish, // - no skew or overlap
pftNextPublish, // no skew
pftQuery,
pftPropagationComplete,
pfRetryNeeded,
phrPublish);
_JumpIfError(hr, error, "crlSignAndSaveCRL");
CONSOLEPRINT4((
DBG_SS_CERTSRV,
"Published %hs CRL #%u for key %u.%u\n",
0 == CRLNumberBaseMin? "Base" : "Delta",
CRLNumber,
pCAContext->iCert,
pCAContext->iKey));
CSASSERT(S_OK == hr);
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
error:
crlFreeCRLArray(pvBlockSerial, aCRL);
CSASSERT(S_OK == hr || FAILED(hr));
return(hr);
}
DWORD g_aColCRLNumber[] = {
#define ICOL_CRLNUMBER 0
DTI_CRLTABLE | DTL_NUMBER,
};
HRESULT
crlGetNextCRLNumber(
OUT DWORD *pdwCRLNumber)
{
HRESULT hr;
CERTVIEWRESTRICTION acvr[1];
CERTVIEWRESTRICTION *pcvr;
IEnumCERTDBRESULTROW *pView = NULL;
DWORD Zero = 0;
CERTDBRESULTROW aResult[1];
CERTDBRESULTROW *pResult;
DWORD celtFetched;
BOOL fResultActive = FALSE;
*pdwCRLNumber = 1;
// Set up restrictions as follows:
pcvr = acvr;
// CRLNumber > 0 (indexed column)
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_NUMBER;
pcvr->SeekOperator = CVR_SEEK_GT;
pcvr->SortOrder = CVR_SORT_DESCEND; // highest CRL Number first
pcvr->pbValue = (BYTE *) &Zero;
pcvr->cbValue = sizeof(Zero);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) == SAFE_SUBTRACT_POINTERS(pcvr, acvr));
hr = g_pCertDB->OpenView(
ARRAYSIZE(acvr),
acvr,
ARRAYSIZE(g_aColCRLNumber),
g_aColCRLNumber,
0, // no worker thread
&pView);
_JumpIfError(hr, error, "OpenView");
hr = pView->Next(ARRAYSIZE(aResult), aResult, &celtFetched);
if (S_FALSE == hr)
{
if (0 == celtFetched)
{
hr = S_OK;
goto error;
}
}
_JumpIfError(hr, error, "Next");
fResultActive = TRUE;
CSASSERT(ARRAYSIZE(aResult) == celtFetched);
pResult = &aResult[0];
CSASSERT(ARRAYSIZE(g_aColCRLNumber) == pResult->ccol);
CSASSERT(NULL != pResult->acol[ICOL_CRLNUMBER].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOL_CRLNUMBER].Type));
CSASSERT(sizeof(*pdwCRLNumber) == pResult->acol[ICOL_CRLNUMBER].cbValue);
*pdwCRLNumber = 1 + *(DWORD *) pResult->acol[ICOL_CRLNUMBER].pbValue;
hr = S_OK;
error:
if (NULL != pView)
{
if (fResultActive)
{
pView->ReleaseResultRow(celtFetched, aResult);
}
pView->Release();
}
DBGPRINT((
DBG_SS_CERTSRVI,
"crlGetNextCRLNumber -> %u\n",
*pdwCRLNumber));
return(hr);
}
#undef ICOL_CRLNUMBER
//+--------------------------------------------------------------------------
// crlGetBaseCRLInfo -- get database column data for the most recent Base CRL
//
//---------------------------------------------------------------------------
DWORD g_aColBaseCRLInfo[] = {
#define ICOLBI_CRLNUMBER 0
DTI_CRLTABLE | DTL_NUMBER,
#define ICOLBI_CRLTHISUPDATE 1
DTI_CRLTABLE | DTL_THISUPDATEDATE,
#define ICOLBI_CRLNEXTUPDATE 2
DTI_CRLTABLE | DTL_NEXTUPDATEDATE,
#define ICOLBI_CRLNAMEID 3
DTI_CRLTABLE | DTL_NAMEID,
};
HRESULT
crlGetBaseCRLInfo(
IN FILETIME const *pftCurrent,
IN BOOL fOldestUnexpiredBase, // else newest propagated CRL
OUT DWORD *pdwRowId,
OUT DWORD *pdwCRLNumber,
OUT FILETIME *pftThisUpdate)
{
HRESULT hr;
CERTVIEWRESTRICTION acvr[2];
CERTVIEWRESTRICTION *pcvr;
IEnumCERTDBRESULTROW *pView = NULL;
DWORD Zero = 0;
CERTDBRESULTROW aResult[1];
CERTDBRESULTROW *pResult;
DWORD celtFetched;
BOOL fResultActive = FALSE;
BOOL fSaveCRLInfo;
DWORD RowId = 0;
DWORD CRLNumber;
FILETIME ftThisUpdate;
FILETIME ftNextUpdate;
*pdwRowId = 0;
*pdwCRLNumber = 0;
pftThisUpdate->dwHighDateTime = 0;
pftThisUpdate->dwLowDateTime = 0;
if (CRLF_DELTA_USE_OLDEST_UNEXPIRED_BASE & g_dwCRLFlags)
{
fOldestUnexpiredBase = TRUE;
}
// Set up restrictions as follows:
pcvr = acvr;
if (fOldestUnexpiredBase)
{
// NextUpdate >= now
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_NEXTUPDATEDATE;
pcvr->SeekOperator = CVR_SEEK_GE;
}
else // else newest propagated CRL
{
// PropagationComplete < now
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_PROPAGATIONCOMPLETEDATE;
pcvr->SeekOperator = CVR_SEEK_LT;
}
pcvr->SortOrder = CVR_SORT_DESCEND; // Newest CRL first
pcvr->pbValue = (BYTE *) pftCurrent;
pcvr->cbValue = sizeof(*pftCurrent);
pcvr++;
// CRL Minimum Base == 0 (to eliminate delta CRLs)
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_MINBASE;
pcvr->SeekOperator = CVR_SEEK_EQ;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &Zero;
pcvr->cbValue = sizeof(Zero);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) == SAFE_SUBTRACT_POINTERS(pcvr, acvr));
hr = g_pCertDB->OpenView(
ARRAYSIZE(acvr),
acvr,
ARRAYSIZE(g_aColBaseCRLInfo),
g_aColBaseCRLInfo,
0, // no worker thread
&pView);
_JumpIfError(hr, error, "OpenView");
while (0 == RowId || fOldestUnexpiredBase)
{
hr = pView->Next(ARRAYSIZE(aResult), aResult, &celtFetched);
if (S_FALSE == hr)
{
CSASSERT(0 == celtFetched);
if (0 != RowId)
{
break;
}
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
_JumpIfError(hr, error, "Next: no matching base CRL");
fResultActive = TRUE;
CSASSERT(ARRAYSIZE(aResult) == celtFetched);
pResult = &aResult[0];
CSASSERT(ARRAYSIZE(g_aColBaseCRLInfo) == pResult->ccol);
CSASSERT(NULL != pResult->acol[ICOLBI_CRLNUMBER].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOLBI_CRLNUMBER].Type));
CSASSERT(sizeof(DWORD) == pResult->acol[ICOLBI_CRLNUMBER].cbValue);
CSASSERT(NULL != pResult->acol[ICOLBI_CRLTHISUPDATE].pbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOLBI_CRLTHISUPDATE].Type));
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOLBI_CRLTHISUPDATE].cbValue);
CSASSERT(NULL != pResult->acol[ICOLBI_CRLNEXTUPDATE].pbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOLBI_CRLNEXTUPDATE].Type));
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOLBI_CRLNEXTUPDATE].cbValue);
DBGPRINT((DBG_SS_CERTSRVI, "Query:RowId: %u\n", pResult->rowid));
DBGPRINT((DBG_SS_CERTSRVI, "Query:CRLNumber: %u\n", *(DWORD *) pResult->acol[ICOLBI_CRLNUMBER].pbValue));
DBGPRINT((DBG_SS_CERTSRVI, "Query:NameId: 0x%x\n", *(DWORD *) pResult->acol[ICOLBI_CRLNAMEID].pbValue));
DBGPRINTTIME(NULL, "Query:ThisUpdate", DPT_DATE, *(FILETIME *) pResult->acol[ICOLBI_CRLNEXTUPDATE].pbValue);
DBGPRINTTIME(NULL, "Query:NextUpdate", DPT_DATE, *(FILETIME *) pResult->acol[ICOLBI_CRLTHISUPDATE].pbValue);
if (0 == RowId)
{
// save first matching row info
fSaveCRLInfo = TRUE;
}
else
{
// save row info, if looking for
// oldest unexpired base & this CRL expires before the saved CRL
// +1 if first > second -- saved > this
CSASSERT(fOldestUnexpiredBase);
fSaveCRLInfo = 0 < CompareFileTime(
&ftNextUpdate,
(FILETIME *) pResult->acol[ICOLBI_CRLNEXTUPDATE].pbValue);
}
if (fSaveCRLInfo)
{
CRLNumber = *(DWORD *) pResult->acol[ICOLBI_CRLNUMBER].pbValue;
ftThisUpdate = *(FILETIME *) pResult->acol[ICOLBI_CRLTHISUPDATE].pbValue;
ftNextUpdate = *(FILETIME *) pResult->acol[ICOLBI_CRLNEXTUPDATE].pbValue;
RowId = pResult->rowid;
DBGPRINT((
DBG_SS_CERTSRVI,
"Query: SAVED RowId=%u CRLNumber=%u\n",
pResult->rowid,
CRLNumber));
DBGPRINTTIME(NULL, "ftThisUpdate", DPT_DATE, ftThisUpdate);
}
pView->ReleaseResultRow(celtFetched, aResult);
fResultActive = FALSE;
}
*pdwRowId = RowId;
*pdwCRLNumber = CRLNumber;
*pftThisUpdate = ftThisUpdate;
DBGPRINTTIME(NULL, "*pftThisUpdate", DPT_DATE, *pftThisUpdate);
DBGPRINTTIME(NULL, "ftNextUpdate", DPT_DATE, ftNextUpdate);
hr = S_OK;
error:
if (NULL != pView)
{
if (fResultActive)
{
pView->ReleaseResultRow(celtFetched, aResult);
}
pView->Release();
}
DBGPRINT((
DBG_SS_CERTSRV,
"crlGetBaseCRLInfo -> RowId=%u, CRL=%u\n",
*pdwRowId,
*pdwCRLNumber));
return(hr);
}
#undef ICOLBI_CRLNUMBER
#undef ICOLBI_CRLTHISUPDATE
#undef ICOLBI_CRLNEXTUPDATE
#undef ICOLBI_CRLNAMEID
DWORD g_aColRepublishCRLInfo[] = {
#define ICOLRI_CRLNUMBER 0
DTI_CRLTABLE | DTL_NUMBER,
#define ICOLRI_CRLNAMEID 1
DTI_CRLTABLE | DTL_NAMEID,
#define ICOLRI_CRLPUBLISHFLAGS 2
DTI_CRLTABLE | DTL_PUBLISHFLAGS,
#define ICOLRI_CRLTHISUPDATE 3
DTI_CRLTABLE | DTL_THISUPDATEDATE,
#define ICOLRI_CRLNEXTUPDATE 4
DTI_CRLTABLE | DTL_NEXTUPDATEDATE,
#define ICOLRI_CRLRAWCRL 5
DTI_CRLTABLE | DTL_RAWCRL,
};
HRESULT
crlGetRowIdAndCRL(
IN BOOL fDelta,
IN CACTX *pCAContext,
OUT DWORD *pdwRowId,
OUT DWORD *pcbCRL,
OPTIONAL OUT BYTE **ppbCRL,
OPTIONAL OUT DWORD *pdwCRLPublishFlags)
{
HRESULT hr;
CERTVIEWRESTRICTION acvr[4];
CERTVIEWRESTRICTION *pcvr;
IEnumCERTDBRESULTROW *pView = NULL;
DWORD Zero = 0;
DWORD NameIdMin;
DWORD NameIdMax;
CERTDBRESULTROW aResult[1];
CERTDBRESULTROW *pResult;
DWORD celtFetched;
BOOL fResultActive = FALSE;
FILETIME ftCurrent;
DWORD RowId = 0;
BYTE *pbCRL = NULL;
DWORD cbCRL;
*pdwRowId = 0;
*pcbCRL = 0;
if (NULL != ppbCRL)
{
*ppbCRL = NULL;
}
if (NULL != pdwCRLPublishFlags)
{
*pdwCRLPublishFlags = 0;
}
GetSystemTimeAsFileTime(&ftCurrent);
DBGPRINT((
DBG_SS_CERTSRVI,
"crlGetRowIdAndCRL(%ws, NameId=%x)\n",
fDelta? L"Delta" : L"Base",
pCAContext->NameId));
// Set up restrictions as follows:
pcvr = acvr;
// RowId > 0
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_ROWID;
pcvr->SeekOperator = CVR_SEEK_GE;
pcvr->SortOrder = CVR_SORT_DESCEND; // Newest CRL first
pcvr->pbValue = (BYTE *) &Zero;
pcvr->cbValue = sizeof(Zero);
pcvr++;
if (fDelta)
{
// CRL Minimum Base > 0 (to eliminate base CRLs)
pcvr->SeekOperator = CVR_SEEK_GT;
}
else
{
// CRL Minimum Base == 0 (to eliminate delta CRLs)
pcvr->SeekOperator = CVR_SEEK_EQ;
}
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_MINBASE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &Zero;
pcvr->cbValue = sizeof(Zero);
pcvr++;
// NameId >= MAKECANAMEID(iCert == 0, pCAContext->iKey)
NameIdMin = MAKECANAMEID(0, pCAContext->iKey);
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_NAMEID;
pcvr->SeekOperator = CVR_SEEK_GE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &NameIdMin;
pcvr->cbValue = sizeof(NameIdMin);
pcvr++;
// NameId <= MAKECANAMEID(iCert == _16BITMASK, pCAContext->iKey)
NameIdMax = MAKECANAMEID(_16BITMASK, pCAContext->iKey);
pcvr->ColumnIndex = DTI_CRLTABLE | DTL_NAMEID;
pcvr->SeekOperator = CVR_SEEK_LE;
pcvr->SortOrder = CVR_SORT_NONE;
pcvr->pbValue = (BYTE *) &NameIdMax;
pcvr->cbValue = sizeof(NameIdMax);
pcvr++;
CSASSERT(ARRAYSIZE(acvr) == SAFE_SUBTRACT_POINTERS(pcvr, acvr));
hr = g_pCertDB->OpenView(
ARRAYSIZE(acvr),
acvr,
((NULL != ppbCRL) ?
(DWORD) ARRAYSIZE(g_aColRepublishCRLInfo) :
(DWORD) ARRAYSIZE(g_aColRepublishCRLInfo) - 1 ), // explicitly describe expected return value
g_aColRepublishCRLInfo,
0, // no worker thread
&pView);
_JumpIfError(hr, error, "OpenView");
while (0 == RowId)
{
hr = pView->Next(ARRAYSIZE(aResult), aResult, &celtFetched);
if (S_FALSE == hr)
{
CSASSERT(0 == celtFetched);
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
_JumpIfErrorStr(
hr,
error,
"Next: no matching CRL",
fDelta? L"delta" : L"base");
fResultActive = TRUE;
CSASSERT(ARRAYSIZE(aResult) == celtFetched);
pResult = &aResult[0];
CSASSERT(ARRAYSIZE(g_aColRepublishCRLInfo) == pResult->ccol);
// verify CRLNumber data & schema
CSASSERT(NULL != pResult->acol[ICOLRI_CRLNUMBER].pbValue);
CSASSERT(PROPTYPE_LONG == (PROPTYPE_MASK & pResult->acol[ICOLRI_CRLNUMBER].Type));
CSASSERT(sizeof(DWORD) == pResult->acol[ICOLRI_CRLNUMBER].cbValue);
// verify ThisUpdate data & schema
CSASSERT(NULL != pResult->acol[ICOLRI_CRLTHISUPDATE].pbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOLRI_CRLTHISUPDATE].Type));
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOLRI_CRLTHISUPDATE].cbValue);
// verify NextUpdate data & schema
CSASSERT(NULL != pResult->acol[ICOLRI_CRLNEXTUPDATE].pbValue);
CSASSERT(PROPTYPE_DATE == (PROPTYPE_MASK & pResult->acol[ICOLRI_CRLNEXTUPDATE].Type));
CSASSERT(sizeof(FILETIME) == pResult->acol[ICOLRI_CRLNEXTUPDATE].cbValue);
// verify RawCRL data & schema
if (NULL != ppbCRL)
{
CSASSERT(NULL != pResult->acol[ICOLRI_CRLRAWCRL].pbValue);
CSASSERT(PROPTYPE_BINARY == (PROPTYPE_MASK & pResult->acol[ICOLRI_CRLRAWCRL].Type));
}
// DBGPRINT query results
DBGPRINT((DBG_SS_CERTSRVI, "Query:RowId: %u\n", pResult->rowid));
DBGPRINT((DBG_SS_CERTSRVI, "Query:CRLNumber: %u\n", *(DWORD *) pResult->acol[ICOLRI_CRLNUMBER].pbValue));
DBGPRINT((DBG_SS_CERTSRVI, "Query:NameId: 0x%x\n", *(DWORD *) pResult->acol[ICOLRI_CRLNAMEID].pbValue));
DBGPRINTTIME(NULL, "Query:ThisUpdate", DPT_DATE, *(FILETIME *) pResult->acol[ICOLRI_CRLNEXTUPDATE].pbValue);
DBGPRINTTIME(NULL, "Query:NextUpdate", DPT_DATE, *(FILETIME *) pResult->acol[ICOLRI_CRLTHISUPDATE].pbValue);
if (NULL != ppbCRL)
{
DBGPRINT((DBG_SS_CERTSRVI, "Query:RawCRL: cb=%x\n", pResult->acol[ICOLRI_CRLRAWCRL].cbValue));
}
if (NULL != pResult->acol[ICOLRI_CRLPUBLISHFLAGS].pbValue)
{
DBGPRINT((
DBG_SS_CERTSRVI,
"Query:PublishFlags: f=%x\n",
*(DWORD *) pResult->acol[ICOLRI_CRLPUBLISHFLAGS].pbValue));
}
if (0 < CompareFileTime(
(FILETIME *) pResult->acol[ICOLRI_CRLTHISUPDATE].pbValue,
&ftCurrent))
{
_PrintError(E_INVALIDARG, "ThisUpdate in future");
}
if (0 > CompareFileTime(
(FILETIME *) pResult->acol[ICOLRI_CRLNEXTUPDATE].pbValue,
&ftCurrent))
{
hr = E_INVALIDARG;
_JumpError(hr, error, "NextUpdate in past");
}
CSASSERT(0 != pResult->rowid);
CSASSERT(NULL == pbCRL);
RowId = pResult->rowid;
if (NULL != ppbCRL)
{
cbCRL = pResult->acol[ICOLRI_CRLRAWCRL].cbValue;
pbCRL = (BYTE *) LocalAlloc(LMEM_FIXED, cbCRL);
if (NULL == pbCRL)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(
pbCRL,
pResult->acol[ICOLRI_CRLRAWCRL].pbValue,
cbCRL);
}
if (NULL != pdwCRLPublishFlags &&
NULL != pResult->acol[ICOLRI_CRLPUBLISHFLAGS].pbValue)
{
*pdwCRLPublishFlags = *(DWORD *) pResult->acol[ICOLRI_CRLPUBLISHFLAGS].pbValue;
}
DBGPRINT((DBG_SS_CERTSRVI, "Query:RowId: SAVED %u\n", pResult->rowid));
pView->ReleaseResultRow(celtFetched, aResult);
fResultActive = FALSE;
}
*pdwRowId = RowId;
if (NULL != ppbCRL)
{
*pcbCRL = cbCRL;
*ppbCRL = pbCRL;
pbCRL = NULL;
}
hr = S_OK;
error:
if (NULL != pbCRL)
{
LocalFree(pbCRL);
}
if (NULL != pView)
{
if (fResultActive)
{
pView->ReleaseResultRow(celtFetched, aResult);
}
pView->Release();
}
DBGPRINT((
DBG_SS_CERTSRVI,
"crlGetRowIdAndCRL(%ws) -> RowId=%u, cbCRL=%x, hr=%x\n",
fDelta? L"Delta" : L"Base",
*pdwRowId,
*pcbCRL,
hr));
return(hr);
}
#undef ICOLRI_CRLNUMBER
#undef ICOLRI_CRLNAMEID
#undef ICOLRI_CRLRAWCRL
#undef ICOLRI_CRLPUBLISHFLAGS
#undef ICOLRI_CRLTHISUPDATEDATE
#undef ICOLRI_CRLNEXTUPDATEDATE
HRESULT
crlRepublishCRLFromCAContext(
IN FILETIME const *pftCurrent,
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fDelta,
IN CACTX *pCAContext,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrPublish)
{
HRESULT hr;
DWORD cbCRL;
BYTE *pbCRL = NULL;
DWORD RowId;
*pfRetryNeeded = FALSE;
*phrPublish = S_OK;
hr = crlGetRowIdAndCRL(fDelta, pCAContext, &RowId, &cbCRL, &pbCRL, NULL);
_JumpIfError(hr, error, "crlGetRowIdAndCRL");
hr = crlPublishGeneratedCRL(
RowId,
pftCurrent,
pwszUserName,
fDelta,
pCAContext->iKey,
pbCRL,
cbCRL,
pCAContext,
pfRetryNeeded,
phrPublish);
_JumpIfError(hr, error, "crlPublishGeneratedCRL");
error:
if (NULL != pbCRL)
{
LocalFree(pbCRL);
}
return(hr);
}
HRESULT
crlRepublishExistingCRLs(
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fDeltaOnly,
IN BOOL fShadowDelta,
IN FILETIME const *pftCurrent,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrPublish)
{
HRESULT hr;
HRESULT hrPublish;
BOOL fRetryNeeded;
DWORD i;
*pfRetryNeeded = FALSE;
*phrPublish = S_OK;
// Walk global CA Context array from the back, and republish CRLs for
// each unique CA key. This causes the most current CRL to be published
// first, and the most current CA Cert context to be used to publish a CRL
// that covers multiple CA Certs due to key reuse.
for (i = g_cCACerts; i > 0; i--)
{
CACTX *pCAContext = &g_aCAContext[i - 1];
PKCSVerifyCAState(pCAContext);
if (CTXF_SKIPCRL & pCAContext->Flags)
{
continue;
}
if (!fDeltaOnly)
{
// Publish the most recent existing Base CRL
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlRepublishCRLFromCAContext(
pftCurrent,
pwszUserName,
FALSE, // fDelta
pCAContext,
&fRetryNeeded,
&hrPublish);
_JumpIfError(hr, error, "crlRepublishCRLFromCAContext");
if (fRetryNeeded)
{
*pfRetryNeeded = TRUE;
}
if (S_OK == *phrPublish)
{
*phrPublish = hrPublish;
}
}
if (!g_fDeltaCRLPublishDisabled || fShadowDelta)
{
// Publish the most recent existing Delta CRL
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlRepublishCRLFromCAContext(
pftCurrent,
pwszUserName,
TRUE, // fDelta
pCAContext,
&fRetryNeeded,
&hrPublish);
_JumpIfError(hr, error, "crlRepublishCRLFromCAContext");
if (fRetryNeeded)
{
*pfRetryNeeded = TRUE;
}
if (S_OK == *phrPublish)
{
*phrPublish = hrPublish;
}
}
}
hr = S_OK;
error:
return(hr);
}
HRESULT
crlComputeCRLTimes(
IN BOOL fDelta,
IN CSCRLPERIOD const *pccp,
IN FILETIME const *pftCurrent,
OUT FILETIME *pftThisUpdate, // ftCurrent - clock skew
IN OUT FILETIME *pftNextUpdate, // ftCurrent + period + overlap + skew
OUT FILETIME *pftNextPublish, // ftCurrent + CRL period
OUT FILETIME *pftPropagationComplete) // ftCurrent + overlap
{
HRESULT hr;
LONGLONG lldelta;
if (0 == pftNextUpdate->dwHighDateTime &&
0 == pftNextUpdate->dwLowDateTime)
{
// Calculate expiration date for this CRL:
// ftCurrent + CRL period
DBGPRINTTIME(&fDelta, "*pftCurrent", DPT_DATE, *pftCurrent);
*pftNextUpdate = *pftCurrent;
DBGPRINT((
DBG_SS_CERTSRVI,
"+ count=%d, enum=%d\n",
pccp->lCRLPeriodCount,
pccp->enumCRLPeriod));
myMakeExprDateTime(
pftNextUpdate,
pccp->lCRLPeriodCount,
pccp->enumCRLPeriod);
DBGPRINTTIME(&fDelta, "*pftNextUpdate", DPT_DATE, *pftNextUpdate);
}
if (0 > CompareFileTime(pftNextUpdate, pftCurrent))
{
hr = E_INVALIDARG;
_JumpError(hr, error, "*pftNextUpdate in past");
}
*pftThisUpdate = *pftCurrent;
*pftNextPublish = *pftNextUpdate; // unmodified expiration time
// Subtract clock skew from the current time for ftThisUpdate time.
lldelta = g_dwClockSkewMinutes * CVT_MINUTES;
myAddToFileTime(pftThisUpdate, -lldelta * CVT_BASE);
// Add clock skew to ftNextUpdate,
// Add propogation overlap to ftNextUpdate.
lldelta += pccp->dwCRLOverlapMinutes * CVT_MINUTES;
myAddToFileTime(pftNextUpdate, lldelta * CVT_BASE);
*pftPropagationComplete = *pftCurrent;
lldelta = pccp->dwCRLOverlapMinutes * CVT_MINUTES;
myAddToFileTime(pftPropagationComplete, lldelta * CVT_BASE);
DBGPRINTTIME(&fDelta, "*pftCurrent", DPT_DATE, *pftCurrent);
DBGPRINTTIME(&fDelta, "*pftThisUpdate", DPT_DATE, *pftThisUpdate);
DBGPRINTTIME(&fDelta, "*pftNextUpdate", DPT_DATE, *pftNextUpdate);
DBGPRINTTIME(&fDelta, "*pftNextPublish", DPT_DATE, *pftNextPublish);
DBGPRINTTIME(&fDelta, "*pftPropagationComplete", DPT_DATE, *pftPropagationComplete);
hr = S_OK;
error:
return(hr);
}
HRESULT
crlGenerateAndPublishCRLs(
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fDeltaOnly, // else base (and delta, if enabled)
IN BOOL fShadowDelta, // empty delta CRL with new MinBaseCRL
IN FILETIME const *pftCurrent,
IN FILETIME ftNextUpdateBase,
OUT DWORD *pdwRowIdBase,
OUT FILETIME *pftQueryDeltaDelete,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrPublish)
{
HRESULT hr;
HRESULT hrPublish;
HKEY hkeyBase = NULL;
HKEY hkeyCA = NULL;
BOOL fClamped = FALSE;
DWORD CRLNumber;
DWORD CRLNumberBaseMin = 0;
DWORD i;
BOOL fRetryNeeded;
FILETIME ftNextUpdateDelta;
FILETIME ftThisUpdate;
FILETIME ftQueryDelta;
FILETIME *pftQueryDelta = &ftQueryDelta;
FILETIME ftLastPublishBase;
FILETIME ftNextPublishBase;
FILETIME ftNextUpdateBaseClamped = ftNextUpdateBase; // if clamped
FILETIME ftNextPublishDelta;
FILETIME ftPropagationCompleteBase;
FILETIME ftPropagationCompleteDelta;
CSCRLPERIOD ccpBase;
CSCRLPERIOD ccpDelta;
*pfRetryNeeded = FALSE;
pftQueryDeltaDelete->dwHighDateTime = 0;
pftQueryDeltaDelete->dwLowDateTime = 0;
*phrPublish = S_OK;
hr = crlGetNextCRLNumber(&CRLNumber);
_JumpIfError(hr, error, "crlGetNextCRLNumber");
hr = crlGetRegCRLPublishParams(
g_wszSanitizedName,
&ccpBase,
&ccpDelta);
_JumpIfError(hr, error, "crlGetRegCRLPublishParams");
// in manual publish case, 0 implies use default publish period
if (fDeltaOnly)
{
ftNextUpdateDelta = ftNextUpdateBase;
ZeroMemory(&ftNextUpdateBase, sizeof(ftNextUpdateBase));
}
else
{
ZeroMemory(&ftNextUpdateDelta, sizeof(ftNextUpdateDelta));
}
hr = crlComputeCRLTimes(
FALSE, // fDelta
&ccpBase, // IN
pftCurrent, // IN
&ftThisUpdate, // OUT includes skew
&ftNextUpdateBase, // INOUT includes overlap, skew
&ftNextPublishBase, // OUT unmodified expire time
&ftPropagationCompleteBase); // OUT includes overlap
_JumpIfError(hr, error, "crlComputeCRLTimes");
hr = crlComputeCRLTimes(
TRUE, // fDelta
fShadowDelta? &ccpBase : &ccpDelta, // IN
pftCurrent, // IN
&ftThisUpdate, // OUT includes skew
&ftNextUpdateDelta, // INOUT includes overlap, skew
&ftNextPublishDelta, // OUT unmodified expire time
&ftPropagationCompleteDelta); // OUT includes overlap
_JumpIfError(hr, error, "crlComputeCRLTimes");
// Set ftLastPublishBase to *pftCurrent minus lifetime of this base CRL,
// which is an educated guess for the ftThisPublish value for the last
// CRL issued.
ftLastPublishBase = *pftCurrent;
myAddToFileTime(
&ftLastPublishBase,
-mySubtractFileTimes(&ftNextPublishBase, pftCurrent));
// Clamp delta CRL to not end after base CRL.
if (0 < CompareFileTime(&ftNextPublishDelta, &ftNextPublishBase))
{
ftNextPublishDelta = ftNextPublishBase;
DBGPRINTTIME(NULL, "ftNextPublishDelta", DPT_DATE, ftNextPublishDelta);
}
if (0 < CompareFileTime(&ftNextUpdateDelta, &ftNextUpdateBase))
{
ftNextUpdateDelta = ftNextUpdateBase;
DBGPRINTTIME(NULL, "ftNextUpdateDelta", DPT_DATE, ftNextUpdateDelta);
}
if (0 < CompareFileTime(&ftPropagationCompleteDelta, &ftPropagationCompleteBase))
{
ftPropagationCompleteDelta = ftPropagationCompleteBase;
DBGPRINTTIME(NULL, "ftPropagationCompleteDelta", DPT_DATE, ftPropagationCompleteDelta);
}
if (!g_fDeltaCRLPublishDisabled || fShadowDelta)
{
hr = crlGetBaseCRLInfo(
pftCurrent,
FALSE, // try newest propagated CRL
pdwRowIdBase,
&CRLNumberBaseMin,
&ftQueryDelta);
_PrintIfError(hr, "crlGetBaseCRLInfo");
if (S_OK != hr)
{
hr = crlGetBaseCRLInfo(
pftCurrent,
TRUE, // try oldest unexpired CRL
pdwRowIdBase,
&CRLNumberBaseMin,
&ftQueryDelta);
_PrintIfError(hr, "crlGetBaseCRLInfo");
if (S_OK != hr)
{
CRLNumberBaseMin = 1;
if (!fDeltaOnly && 1 == CRLNumber)
{
ftQueryDelta = *pftCurrent; // empty CRL
}
else
{
pftQueryDelta = NULL; // full CRL
}
}
}
if (S_OK == hr)
{
// Delete old CRLs that expired at least one base CRL period prior
// to the "minimum" base crl ThisUpdate date found in the database.
*pftQueryDeltaDelete = ftQueryDelta;
myAddToFileTime(
pftQueryDeltaDelete,
-mySubtractFileTimes(&ftNextUpdateBase, &ftThisUpdate));
}
if (fShadowDelta)
{
CRLNumberBaseMin = CRLNumber;
}
CSASSERT(0 != CRLNumberBaseMin);
}
// Walk global CA Context array from the back, and generate a CRL for
// each unique CA key. This causes the most current CRL to be built
// first, and the most current CA Cert to be used to build a CRL that
// covers multiple CA Certs due to key reuse.
for (i = g_cCACerts; i > 0; i--)
{
CACTX *pCAContext = &g_aCAContext[i - 1];
PKCSVerifyCAState(pCAContext);
if (CTXF_SKIPCRL & pCAContext->Flags)
{
continue;
}
if (!fDeltaOnly)
{
// Publish a new Base CRL
// make a local copy in case clamped
FILETIME ftNextUpdateBaseTemp = ftNextUpdateBase;
fClamped = FALSE;
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlPublishCRLFromCAContext(
CRLNumber,
0, // CRLNumberBaseMin
pwszUserName,
FALSE, // fShadowDelta
pCAContext,
pftCurrent,
ftThisUpdate,
&ftNextUpdateBaseTemp,
&fClamped,
NULL,
pftCurrent,
&ftNextPublishBase,
&ftLastPublishBase,
&ftPropagationCompleteBase,
&fRetryNeeded,
&hrPublish);
_JumpIfError(hr, error, "crlPublishCRLFromCAContext");
if (fRetryNeeded)
{
*pfRetryNeeded = TRUE;
}
if (S_OK == *phrPublish)
{
*phrPublish = hrPublish;
}
{
CertSrv::CAuditEvent event(SE_AUDITID_CERTSRV_AUTOPUBLISHCRL, g_dwAuditFilter);
hr = event.AddData(true); // %1 base crl?
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(CRLNumber); // %2 CRL#
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(pCAContext->pwszKeyContainerName); // %3 key container
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(ftNextPublishBase); // %4 next publish
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData((LPCWSTR*)pCAContext->papwszCRLFiles); //%5 URLs
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.Report();
_JumpIfError(hr, error, "CAuditEvent::Report");
}
if (i == g_cCACerts && fClamped)
{
// new next publish clamps with CA expiration, only update
// the current crl with new one for later reg save
ftNextUpdateBaseClamped = ftNextUpdateBaseTemp;
}
}
if (!g_fDeltaCRLPublishDisabled || fShadowDelta)
{
// Publish a new Delta CRL
FILETIME ftNextUpdateDeltaTemp = ftNextUpdateDelta;
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlPublishCRLFromCAContext(
CRLNumber,
CRLNumberBaseMin,
pwszUserName,
fShadowDelta,
pCAContext,
pftCurrent,
ftThisUpdate,
&ftNextUpdateDeltaTemp,
NULL,
pftQueryDelta,
pftCurrent,
&ftNextPublishDelta,
&ftLastPublishBase, // Base!
&ftPropagationCompleteDelta,
&fRetryNeeded,
&hrPublish);
_JumpIfError(hr, error, "crlPublishCRLFromCAContext");
if (fRetryNeeded)
{
*pfRetryNeeded = TRUE;
}
if (S_OK == *phrPublish)
{
*phrPublish = hrPublish;
}
{
CertSrv::CAuditEvent event(SE_AUDITID_CERTSRV_AUTOPUBLISHCRL, g_dwAuditFilter);
hr = event.AddData(false); // %1 base crl?
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(CRLNumber); // %2 CRL#
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(pCAContext->pwszKeyContainerName); // %3 key container
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData(ftNextPublishDelta); // %4 next publish
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData((LPCWSTR*)pCAContext->papwszDeltaCRLFiles); // %5 URLs
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.Report();
_JumpIfError(hr, error, "CAuditEvent::Report");
}
}
}
// update the registry and global variables
if (!fDeltaOnly)
{
if (!fClamped)
{
g_ftCRLNextPublish = ftNextPublishBase;
}
else
{
g_ftCRLNextPublish = ftNextUpdateBaseClamped;
}
hr = crlSetRegCRLNextPublish(
FALSE,
g_wszSanitizedName,
wszREGCRLNEXTPUBLISH,
&g_ftCRLNextPublish);
_JumpIfError(hr, error, "crlSetRegCRLNextPublish");
}
g_ftDeltaCRLNextPublish = ftNextPublishDelta;
if (!g_fDeltaCRLPublishDisabled)
{
hr = crlSetRegCRLNextPublish(
TRUE,
g_wszSanitizedName,
wszREGCRLDELTANEXTPUBLISH,
&g_ftDeltaCRLNextPublish);
_JumpIfError(hr, error, "crlSetRegCRLNextPublish");
}
hr = S_OK;
error:
if (NULL != hkeyCA)
{
RegCloseKey(hkeyCA);
}
if (NULL != hkeyBase)
{
RegCloseKey(hkeyBase);
}
return(hr);
}
///////////////////////////////////////////////////
// CRLPublishCRLs is called to publish a set of CRLs.
//
// if fRebuildCRL is TRUE, the CRLs are rebuilt from the database.
// otherwise, the exit module is re-notified of the CRLs.
// For consistency, if the exit module returns ERROR_RETRY, this
// function will write the retry bit into the registry which will
// trigger the Wakeup function, which then recalculates when the
// next publish should happen.
//
// pfRetryNeeded is an OUT param that notifies the autopublish routine if
// a retry is immediately necessary following a rebuilt CRL. In this
// case the registry would not be changed and the registry trigger
// would not fire.
//
// (Current_time - skew) is used as ThisUpdate
// (ftNextUpdate+skew+Overlap) is used as NextUpdate
// (ftNextUpdate) is next wakeup/publish time
//
// There are registry values to specify the overlap.
// HLKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CA Name>:
// CRLOverlapPeriod REG_SZ = Hours (or Minutes)
// CRLOverlapUnits REG_DWORD = 0 (0) -- DISABLED
//
// If the above registry values are set and valid, the registry overlap period
// is calculated as:
// max(Registry CRL Overlap Period, 1.5 * Registry clock skew minutes)
//
// If they are not present or invalid, the overlap period is calculated as:
// max(
// min(Registry CRL Period / 10, 12 hours),
// 1.5 * Registry clock skew minutes) +
// Registry clock skew minutes
//
// ThisUpdate is calculated as:
// max(Current Time - Registry clock skew minutes, CA cert NotBefore date)
//
// NextUpdate is calculated as:
// min(
// Current Time +
// Registry CRL period +
// calculated overlap period +
// Registry clock skew minutes,
// CA cert NotAfter date)
//
// The Next CRL publication time is calculated as:
// Current Time + Registry CRL period
//
// This function sets g_hCRLManualPublishEvent. Automatic publishing
// is personally responsible for clearing this event if it calls us.
HRESULT
CRLPublishCRLs(
IN BOOL fRebuildCRL, // else republish only
IN BOOL fForceRepublish, // else check registry retry count
OPTIONAL IN WCHAR const *pwszUserName, // else timer thread
IN BOOL fDeltaOnly, // else base (and delta, if enabled)
IN BOOL fShadowDelta, // empty delta CRL with new MinBaseCRL
IN FILETIME ftNextUpdateBase,
OUT BOOL *pfRetryNeeded,
OUT HRESULT *phrPublish)
{
HRESULT hr;
BOOL fRetryNeeded = FALSE;
BOOL fExitNotify = FALSE;
BOOL fCoInitialized = FALSE;
DWORD RowIdBase = 0;
FILETIME ftQueryDeltaDelete = { 0, 0 };
DWORD dwPreviousAttempts;
DWORD dwCurrentAttempts;
static BOOL s_fSkipRetry = FALSE;
*pfRetryNeeded = FALSE;
*phrPublish = S_OK;
if (fDeltaOnly && g_fDeltaCRLPublishDisabled && !fShadowDelta)
{
hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DISABLED);
_JumpError(hr, error, "g_fDeltaCRLPublishDisabled");
}
// retrieve initial retry value (optional registry value)
hr = myGetCertRegDWValue(
g_wszSanitizedName,
NULL,
NULL,
wszREGCRLATTEMPTREPUBLISH,
&dwPreviousAttempts);
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
{
dwPreviousAttempts = 0; // assume no previous failed publish attempts
hr = S_OK;
}
_JumpIfErrorStr(
hr,
error,
"myGetCertRegDWValue",
wszREGCRLATTEMPTREPUBLISH);
dwCurrentAttempts = dwPreviousAttempts;
DBGPRINT((
DBG_SS_CERTSRV,
"CRLPublishCRLs(fRebuildCRL=%u, fForceRepublish=%u, User=%ws)\n",
fRebuildCRL,
fForceRepublish,
pwszUserName));
DBGPRINT((
DBG_SS_CERTSRV,
"CRLPublishCRLs(fDeltaOnly=%u, fShadowDelta=%u, dwPreviousAttempts=%u)\n",
fDeltaOnly,
fShadowDelta,
dwPreviousAttempts));
if (0 != dwPreviousAttempts && NULL == pwszUserName && s_fSkipRetry)
{
fRetryNeeded = TRUE;
}
else
{
FILETIME ftCurrent;
GetSystemTimeAsFileTime(&ftCurrent);
// generate CRLs if necessary
if (fRebuildCRL)
{
hr = crlGenerateAndPublishCRLs(
pwszUserName,
fDeltaOnly,
fShadowDelta,
&ftCurrent,
ftNextUpdateBase,
&RowIdBase,
&ftQueryDeltaDelete,
&fRetryNeeded,
phrPublish);
_JumpIfError(hr, error, "crlGenerateAndPublishCRLs");
fExitNotify = TRUE;
dwCurrentAttempts = 1;
}
else
if (fForceRepublish ||
(0 < dwPreviousAttempts &&
CERTSRV_CRLPUB_RETRY_COUNT_DEFAULT > dwPreviousAttempts))
{
// If the timer thread is auto-republishing due to previously
// failed publish attempts, retry base CRLs, too, because we
// can't tell if the retry is due to a base or delta CRL error.
if (NULL == pwszUserName)
{
fDeltaOnly = FALSE;
}
hr = crlRepublishExistingCRLs(
pwszUserName,
fDeltaOnly,
fShadowDelta,
&ftCurrent,
&fRetryNeeded,
phrPublish);
_JumpIfError(hr, error, "crlRepublishCRLs");
fExitNotify = TRUE;
dwCurrentAttempts++;
}
if (fExitNotify && g_fEnableExit)
{
hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitializeEx");
}
fCoInitialized = TRUE;
// make sure exit module(s) get notified for publish and republish
// in case of earlier exit module publish failure.
hr = ExitNotify(EXITEVENT_CRLISSUED, 0, MAXDWORD);
_PrintIfError(hr, "ExitNotify");
if ((HRESULT) ERROR_RETRY == hr ||
HRESULT_FROM_WIN32(ERROR_RETRY) == hr)
{
fRetryNeeded = TRUE;
if (S_OK == *phrPublish)
{
*phrPublish = HRESULT_FROM_WIN32(ERROR_RETRY);
}
}
CONSOLEPRINT0((DBG_SS_CERTSRV, "Issued CRL Exit Event\n"));
}
// If new or existing CRLs successfully published, reset count to 0
if (fExitNotify && !fRetryNeeded)
{
dwCurrentAttempts = 0;
if (CERTLOG_VERBOSE <= g_dwLogLevel)
{
WCHAR *pwszHostName = NULL;
DWORD LogMsg;
WORD cpwsz = 0;
if (NULL != g_pld)
{
myLdapGetDSHostName(g_pld, &pwszHostName);
}
LogMsg = fDeltaOnly?
MSG_DELTA_CRLS_PUBLISHED :
(g_fDeltaCRLPublishDisabled?
MSG_BASE_CRLS_PUBLISHED :
MSG_BASE_AND_DELTA_CRLS_PUBLISHED);
if (NULL != pwszHostName)
{
LogMsg = fDeltaOnly?
MSG_DELTA_CRLS_PUBLISHED_HOST_NAME :
(g_fDeltaCRLPublishDisabled?
MSG_BASE_CRLS_PUBLISHED_HOST_NAME :
MSG_BASE_AND_DELTA_CRLS_PUBLISHED_HOST_NAME);
}
hr = LogEvent(
EVENTLOG_INFORMATION_TYPE,
LogMsg,
NULL == pwszHostName? 0 : 1, // cStrings
(WCHAR const **) &pwszHostName); // apwszStrings
_PrintIfError(hr, "LogEvent");
}
}
// If the retry count has changed, update the registry.
if (dwCurrentAttempts != dwPreviousAttempts)
{
DBGPRINT((
DBG_SS_CERTSRV,
"CRLPublishCRLs(Attempts: %u --> %u)\n",
dwPreviousAttempts,
dwCurrentAttempts));
hr = mySetCertRegDWValue(
g_wszSanitizedName,
NULL,
NULL,
wszREGCRLATTEMPTREPUBLISH,
dwCurrentAttempts);
_JumpIfErrorStr(
hr,
error,
"mySetCertRegDWValue",
wszREGCRLATTEMPTREPUBLISH);
// If we tried unsuccessfully too many times to publish these CRLs,
// and we're about to give up until a new set is generated, log an
// event saying so.
if (fExitNotify &&
CERTSRV_CRLPUB_RETRY_COUNT_DEFAULT == dwCurrentAttempts &&
CERTLOG_ERROR <= g_dwLogLevel)
{
WCHAR wszAttempts[11 + 1];
WCHAR const *pwsz = wszAttempts;
wsprintf(wszAttempts, L"%u", dwCurrentAttempts);
hr = LogEvent(
EVENTLOG_ERROR_TYPE,
MSG_E_CRL_PUBLICATION_TOO_MANY_RETRIES,
1, // cStrings
&pwsz); // apwszStrings
_PrintIfError(hr, "LogEvent");
}
}
if (fRebuildCRL)
{
// Delete old CRLs only if new CRLs built & published successfully.
if (!fRetryNeeded)
{
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
hr = crlDeleteExpiredCRLs(
&ftCurrent,
&ftQueryDeltaDelete,
RowIdBase);
_PrintIfError(hr, "crlDeleteExpiredCRLs");
}
// Clear force CRL flag only when we build new CRLs.
hr = SetSetupStatus(g_wszSanitizedName, SETUP_FORCECRL_FLAG, FALSE);
_PrintIfError(hr, "SetSetupStatus");
}
}
s_fSkipRetry = NULL != pwszUserName;
if (fRebuildCRL || fRetryNeeded)
{
// If we are doing ANYTHING that will affect automatic wakeup, trigger
// our publish event.
// NOTE: do this last or else state might not be updated
SetEvent(g_hCRLManualPublishEvent);
}
hr = S_OK;
error:
*pfRetryNeeded = fRetryNeeded;
if (fCoInitialized)
{
CoUninitialize();
}
return(hr);
}
HRESULT
CRLGetCRL(
IN DWORD iCertArg,
IN BOOL fDelta,
OPTIONAL OUT CRL_CONTEXT const **ppCRL,
OPTIONAL OUT DWORD *pdwCRLPublishFlags)
{
HRESULT hr;
DWORD State;
DWORD iCert;
DWORD iCRL;
CACTX *pCAContext;
DWORD dwRowId;
BYTE *pbCRL = NULL;
DWORD cbCRL;
if (NULL != ppCRL)
{
*ppCRL = NULL;
}
hr = PKCSMapCRLIndex(iCertArg, &iCert, &iCRL, &State);
_JumpIfError(hr, error, "PKCSMapCRLIndex");
if (MAXDWORD != iCertArg && CA_DISP_VALID != State)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "No CRL for this Cert");
}
// Now we know iCert is a valid Cert Index:
hr = crlGetRowIdAndCRL(
fDelta,
&g_aCAContext[iCert],
&dwRowId,
&cbCRL,
&pbCRL,
pdwCRLPublishFlags);
_JumpIfError(hr, error, "crlGetRowIdAndCRL");
if (NULL != ppCRL)
{
*ppCRL = CertCreateCRLContext(X509_ASN_ENCODING, pbCRL, cbCRL);
if (NULL == *ppCRL)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCRLContext");
}
}
hr = S_OK;
error:
if (NULL != pbCRL)
{
LocalFree(pbCRL);
}
return(hr);
}