mirror of https://github.com/tongzx/nt5src
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
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);
|
|
}
|