//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        core.cpp
//
// Contents:    Cert Server Core implementation
//
// History:     25-Jul-96       vich created
//
//---------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <stdio.h>
#include <winldap.h>
#include <ntdsapi.h>
#include <dsgetdc.h>
#include <lm.h>

#include "cscom.h"
#include "csprop.h"
#include "cspolicy.h"
#include "ciinit.h"
#include "csdisp.h"
#include "csldap.h"
#include "cainfop.h"
#include "elog.h"
#include "certlog.h"
#include "resource.h"

#define __dwFILE__	__dwFILE_CERTSRV_CORE_CPP__


#if DBG_COMTEST
#define DBG_COMTEST_CONST
#else
#define DBG_COMTEST_CONST	const
#endif


DBG_COMTEST_CONST BOOL fComTest = FALSE;

SERVERCALLBACKS ServerCallBacks = {
    PropCIGetProperty,
    PropCISetProperty,
    PropCIGetExtension,
    PropCISetExtension,
    PropCIEnumSetup,
    PropCIEnumNext,
    PropCIEnumClose,
};


HINSTANCE g_hInstance;
WCHAR g_wszSharedFolder[MAX_PATH];
WCHAR g_wszSanitizedName[MAX_PATH];
WCHAR *g_pwszSanitizedDSName;
WCHAR g_wszCommonName[MAX_PATH];
WCHAR g_wszParentConfig[MAX_PATH];

WCHAR *g_pwszzSubjectTemplate = NULL;
WCHAR *g_pwszServerName = NULL;

DWORD g_dwClockSkewMinutes = CCLOCKSKEWMINUTESDEFAULT;
DWORD g_dwLogLevel = CERTLOG_WARNING;
DWORD g_dwHighSerial = 0;
DWORD g_cbMaxIncomingMessageSize = MAXINCOMINGMESSAGESIZEDEFAULT;

WCHAR const g_wszRegValidityPeriodString[] = wszREGVALIDITYPERIODSTRING;
WCHAR const g_wszRegValidityPeriodCount[] = wszREGVALIDITYPERIODCOUNT;

WCHAR const g_wszRegCAXchgValidityPeriodString[] = wszREGCAXCHGVALIDITYPERIODSTRING;
WCHAR const g_wszRegCAXchgValidityPeriodCount[] = wszREGCAXCHGVALIDITYPERIODCOUNT;
WCHAR const g_wszRegCAXchgOverlapPeriodString[] = wszREGCAXCHGOVERLAPPERIODSTRING;
WCHAR const g_wszRegCAXchgOverlapPeriodCount[] = wszREGCAXCHGOVERLAPPERIODCOUNT;
WCHAR const g_wszRegCAXchgCertHash[] = wszREGCAXCHGCERTHASH;

WCHAR const g_wszRegSubjectTemplate[] = wszREGSUBJECTTEMPLATE;
WCHAR const g_wszRegKeyConfigPath[] = wszREGKEYCONFIGPATH;
WCHAR const g_wszRegDirectory[] = wszREGDIRECTORY;
WCHAR const g_wszRegActive[] = wszREGACTIVE;
WCHAR const g_wszRegEnabled[] = wszREGENABLED;
WCHAR const g_wszRegPolicyFlags[] = wszREGPOLICYFLAGS;
WCHAR const g_wszCertSrvServiceName[] = wszSERVICE_NAME;
WCHAR const g_wszRegCertEnrollCompatible[] = wszREGCERTENROLLCOMPATIBLE;
WCHAR const g_wszRegEnforceX500NameLengths[] = wszREGENFORCEX500NAMELENGTHS;
WCHAR const g_wszRegForceTeletex[] = wszREGFORCETELETEX;
WCHAR const g_wszRegClockSkewMinutes[] = wszREGCLOCKSKEWMINUTES;
WCHAR const g_wszRegLogLevel[] = wszREGLOGLEVEL;
WCHAR const g_wszRegHighSerial[] = wszREGHIGHSERIAL;
WCHAR const g_wszRegMaxIncomingMessageSize[] = wszREGMAXINCOMINGMESSAGESIZE;

BOOL g_fCertEnrollCompatible = TRUE;
BOOL g_fEnforceRDNNameLengths = TRUE;
DWORD g_KRAFlags = 0;
DWORD g_CRLEditFlags = EDITF_ENABLEAKIKEYID |
			EDITF_ENABLEAKIISSUERNAME |
			EDITF_ENABLEAKIISSUERSERIAL |
			EDITF_ENABLEAKICRITICAL;
ENUM_FORCETELETEX g_fForceTeletex = ENUM_TELETEX_AUTO;
ENUM_CATYPES g_CAType = ENUM_UNKNOWN_CA;

BOOL g_fUseDS = FALSE;
BOOL g_fcritsecDSCache = FALSE;
BOOL g_fServerUpgraded = FALSE;
CRITICAL_SECTION g_critsecDSCache;
BOOL g_fLockICertRequest = FALSE;

//+--------------------------------------------------------------------------
// Name properties:

WCHAR const g_wszPropDistinguishedName[] = wszPROPDISTINGUISHEDNAME;
WCHAR const g_wszPropRawName[] = wszPROPRAWNAME;

WCHAR const g_wszPropCountry[] = wszPROPCOUNTRY;
WCHAR const g_wszPropOrganization[] = wszPROPORGANIZATION;
WCHAR const g_wszPropOrgUnit[] = wszPROPORGUNIT;
WCHAR const g_wszPropCommonName[] = wszPROPCOMMONNAME;
WCHAR const g_wszPropLocality[] = wszPROPLOCALITY;
WCHAR const g_wszPropState[] = wszPROPSTATE;
WCHAR const g_wszPropTitle[] = wszPROPTITLE;
WCHAR const g_wszPropGivenName[] = wszPROPGIVENNAME;
WCHAR const g_wszPropInitials[] = wszPROPINITIALS;
WCHAR const g_wszPropSurName[] = wszPROPSURNAME;
WCHAR const g_wszPropDomainComponent[] = wszPROPDOMAINCOMPONENT;
WCHAR const g_wszPropEMail[] = wszPROPEMAIL;
WCHAR const g_wszPropStreetAddress[] = wszPROPSTREETADDRESS;
WCHAR const g_wszPropUnstructuredAddress[] = wszPROPUNSTRUCTUREDADDRESS;
WCHAR const g_wszPropUnstructuredName[] = wszPROPUNSTRUCTUREDNAME;
WCHAR const g_wszPropDeviceSerialNumber[] = wszPROPDEVICESERIALNUMBER;


//+--------------------------------------------------------------------------
// Subject Name properties:

WCHAR const g_wszPropSubjectDot[] = wszPROPSUBJECTDOT;
WCHAR const g_wszPropSubjectDistinguishedName[] = wszPROPSUBJECTDISTINGUISHEDNAME;
WCHAR const g_wszPropSubjectRawName[] = wszPROPSUBJECTRAWNAME;

WCHAR const g_wszPropSubjectCountry[] = wszPROPSUBJECTCOUNTRY;
WCHAR const g_wszPropSubjectOrganization[] = wszPROPSUBJECTORGANIZATION;
WCHAR const g_wszPropSubjectOrgUnit[] = wszPROPSUBJECTORGUNIT;
WCHAR const g_wszPropSubjectCommonName[] = wszPROPSUBJECTCOMMONNAME;
WCHAR const g_wszPropSubjectLocality[] = wszPROPSUBJECTLOCALITY;
WCHAR const g_wszPropSubjectState[] = wszPROPSUBJECTSTATE;
WCHAR const g_wszPropSubjectTitle[] = wszPROPSUBJECTTITLE;
WCHAR const g_wszPropSubjectGivenName[] = wszPROPSUBJECTGIVENNAME;
WCHAR const g_wszPropSubjectInitials[] = wszPROPSUBJECTINITIALS;
WCHAR const g_wszPropSubjectSurName[] = wszPROPSUBJECTSURNAME;
WCHAR const g_wszPropSubjectDomainComponent[] = wszPROPSUBJECTDOMAINCOMPONENT;
WCHAR const g_wszPropSubjectEMail[] = wszPROPSUBJECTEMAIL;
WCHAR const g_wszPropSubjectStreetAddress[] = wszPROPSUBJECTSTREETADDRESS;
WCHAR const g_wszPropSubjectUnstructuredAddress[] = wszPROPSUBJECTUNSTRUCTUREDADDRESS;
WCHAR const g_wszPropSubjectUnstructuredName[] = wszPROPSUBJECTUNSTRUCTUREDNAME;
WCHAR const g_wszPropSubjectDeviceSerialNumber[] = wszPROPSUBJECTDEVICESERIALNUMBER;


//+--------------------------------------------------------------------------
// Issuer Name properties:

WCHAR const g_wszPropIssuerDot[] = wszPROPISSUERDOT;
WCHAR const g_wszPropIssuerDistinguishedName[] = wszPROPISSUERDISTINGUISHEDNAME;
WCHAR const g_wszPropIssuerRawName[] = wszPROPISSUERRAWNAME;

WCHAR const g_wszPropIssuerCountry[] = wszPROPISSUERCOUNTRY;
WCHAR const g_wszPropIssuerOrganization[] = wszPROPISSUERORGANIZATION;
WCHAR const g_wszPropIssuerOrgUnit[] = wszPROPISSUERORGUNIT;
WCHAR const g_wszPropIssuerCommonName[] = wszPROPISSUERCOMMONNAME;
WCHAR const g_wszPropIssuerLocality[] = wszPROPISSUERLOCALITY;
WCHAR const g_wszPropIssuerState[] = wszPROPISSUERSTATE;
WCHAR const g_wszPropIssuerTitle[] = wszPROPISSUERTITLE;
WCHAR const g_wszPropIssuerGivenName[] = wszPROPISSUERGIVENNAME;
WCHAR const g_wszPropIssuerInitials[] = wszPROPISSUERINITIALS;
WCHAR const g_wszPropIssuerSurName[] = wszPROPISSUERSURNAME;
WCHAR const g_wszPropIssuerDomainComponent[] = wszPROPISSUERDOMAINCOMPONENT;
WCHAR const g_wszPropIssuerEMail[] = wszPROPISSUEREMAIL;
WCHAR const g_wszPropIssuerStreetAddress[] = wszPROPISSUERSTREETADDRESS;
WCHAR const g_wszPropIssuerUnstructuredAddress[] = wszPROPISSUERUNSTRUCTUREDADDRESS;
WCHAR const g_wszPropIssuerUnstructuredName[] = wszPROPISSUERUNSTRUCTUREDNAME;
WCHAR const g_wszPropIssuerDeviceSerialNumber[] = wszPROPISSUERDEVICESERIALNUMBER;


//+--------------------------------------------------------------------------
// Request properties:

WCHAR const g_wszPropRequestRequestID[] = wszPROPREQUESTREQUESTID;
WCHAR const g_wszPropRequestRawRequest[] = wszPROPREQUESTRAWREQUEST;
WCHAR const g_wszPropRequestRawArchivedKey[] = wszPROPREQUESTRAWARCHIVEDKEY;
WCHAR const g_wszPropRequestKeyRecoveryHashes[] = wszPROPREQUESTKEYRECOVERYHASHES;
WCHAR const g_wszPropRequestRawOldCertificate[] = wszPROPREQUESTRAWOLDCERTIFICATE;
WCHAR const g_wszPropRequestAttributes[] = wszPROPREQUESTATTRIBUTES;
WCHAR const g_wszPropRequestType[] = wszPROPREQUESTTYPE;
WCHAR const g_wszPropRequestFlags[] = wszPROPREQUESTFLAGS;
WCHAR const g_wszPropRequestStatusCode[] = wszPROPREQUESTSTATUSCODE;
WCHAR const g_wszPropRequestDisposition[] = wszPROPREQUESTDISPOSITION;
WCHAR const g_wszPropRequestDispositionMessage[] = wszPROPREQUESTDISPOSITIONMESSAGE;
WCHAR const g_wszPropRequestSubmittedWhen[] = wszPROPREQUESTSUBMITTEDWHEN;
WCHAR const g_wszPropRequestResolvedWhen[] = wszPROPREQUESTRESOLVEDWHEN;
WCHAR const g_wszPropRequestRevokedWhen[] = wszPROPREQUESTREVOKEDWHEN;
WCHAR const g_wszPropRequestRevokedEffectiveWhen[] = wszPROPREQUESTREVOKEDEFFECTIVEWHEN;
WCHAR const g_wszPropRequestRevokedReason[] = wszPROPREQUESTREVOKEDREASON;
WCHAR const g_wszPropRequesterName[] = wszPROPREQUESTERNAME;
WCHAR const g_wszPropCallerName[] = wszPROPCALLERNAME;
WCHAR const g_wszPropRequestOSVersion[] = wszPROPREQUESTOSVERSION;
WCHAR const g_wszPropRequestCSPProvider[] = wszPROPREQUESTCSPPROVIDER;
//+--------------------------------------------------------------------------
// Request attribute properties:

WCHAR const g_wszPropChallenge[] = wszPROPCHALLENGE;
WCHAR const g_wszPropExpectedChallenge[] = wszPROPEXPECTEDCHALLENGE;


//+--------------------------------------------------------------------------
// Certificate properties:

WCHAR const g_wszPropCertificateRequestID[] = wszPROPCERTIFICATEREQUESTID;
WCHAR const g_wszPropRawCertificate[] = wszPROPRAWCERTIFICATE;
WCHAR const g_wszPropCertificateHash[] = wszPROPCERTIFICATEHASH;
WCHAR const g_wszPropCertificateSerialNumber[] = wszPROPCERTIFICATESERIALNUMBER;
WCHAR const g_wszPropCertificateIssuerNameID[] = wszPROPCERTIFICATEISSUERNAMEID;
WCHAR const g_wszPropCertificateNotBeforeDate[] = wszPROPCERTIFICATENOTBEFOREDATE;
WCHAR const g_wszPropCertificateNotAfterDate[] = wszPROPCERTIFICATENOTAFTERDATE;
WCHAR const g_wszPropCertificateSubjectKeyIdentifier[] = wszPROPCERTIFICATESUBJECTKEYIDENTIFIER;
WCHAR const g_wszPropCertificateRawPublicKey[] = wszPROPCERTIFICATERAWPUBLICKEY;
WCHAR const g_wszPropCertificatePublicKeyLength[] = wszPROPCERTIFICATEPUBLICKEYLENGTH;
WCHAR const g_wszPropCertificatePublicKeyAlgorithm[] = wszPROPCERTIFICATEPUBLICKEYALGORITHM;
WCHAR const g_wszPropCertificateRawPublicKeyAlgorithmParameters[] = wszPROPCERTIFICATERAWPUBLICKEYALGORITHMPARAMETERS;


// Strings loaded from the resource file:

WCHAR const *g_pwszRequestedBy;
WCHAR const *g_pwszDeniedBy;
WCHAR const *g_pwszPublishedBy;
WCHAR const *g_pwszPolicyDeniedRequest;
WCHAR const *g_pwszIssued;
WCHAR const *g_pwszUnderSubmission;
WCHAR const *g_pwszRequestProcessingError;
WCHAR const *g_pwszRequestParsingError;

WCHAR const *g_pwszRevokedBy;
WCHAR const *g_pwszUnrevokedBy;
WCHAR const *g_pwszResubmittedBy;
WCHAR const *g_pwszPrintfCertRequestDisposition;
WCHAR const *g_pwszUnknownSubject;
WCHAR const *g_pwszIntermediateCAStore;
WCHAR const *g_pwszPublishError;
WCHAR const *g_pwszYes;
WCHAR const *g_pwszNo;

LPWSTR g_wszzSecuredAttributes = NULL;

LPCWSTR g_wszzSecuredAttributesDefault = wszzDEFAULTSIGNEDATTRIBUTES; 


typedef struct _STRINGINITMAP
{
    int		  idResource;
    WCHAR const **ppwszResource;
} STRINGINITMAP;

STRINGINITMAP g_aStringInitStrings[] = {
    { IDS_REVOKEDBY,			&g_pwszRevokedBy },
    { IDS_UNREVOKEDBY,			&g_pwszUnrevokedBy },
    { IDS_RESUBMITTEDBY,		&g_pwszResubmittedBy },
    { IDS_REQUESTEDBY,			&g_pwszRequestedBy },
    { IDS_DENIEDBY,			&g_pwszDeniedBy },
    { IDS_PUBLISHEDBY,			&g_pwszPublishedBy },
    { IDS_POLICYDENIED,			&g_pwszPolicyDeniedRequest },
    { IDS_ISSUED,			&g_pwszIssued },
    { IDS_REQUESTPROCESSERROR,		&g_pwszRequestProcessingError },
    { IDS_REQUESTPARSEERROR,		&g_pwszRequestParsingError },
    { IDS_UNDERSUBMISSION,		&g_pwszUnderSubmission },
    { IDS_PRINTFCERTREQUESTDISPOSITION, &g_pwszPrintfCertRequestDisposition },
    { IDS_UNKNOWNSUBJECT, 		&g_pwszUnknownSubject },
    { IDS_INTERMEDIATECASTORE,		&g_pwszIntermediateCAStore },
    { IDS_PUBLISHERROR,			&g_pwszPublishError },
    { IDS_YES,  &g_pwszYes},
    { IDS_NO,   &g_pwszNo},
    { IDS_ALLOW,&g_pwszAuditResources[0]}, 
    { IDS_DENY, &g_pwszAuditResources[1]},
    { IDS_CAADMIN, &g_pwszAuditResources[2]},
    { IDS_OFFICER, &g_pwszAuditResources[3]},
    { IDS_READ, &g_pwszAuditResources[4]},
    { IDS_ENROLL, &g_pwszAuditResources[5]},
};


typedef struct _DSCACHE
{
    _DSCACHE *pdscNext;
    WCHAR    *pwszDomain;
    LDAP     *pld;
} DSCACHE;

DSCACHE *g_DSCache = NULL;
HANDLE g_hDS = NULL;


DWORD
coreDSUnbindWorker(
    OPTIONAL IN OUT VOID *pvparms)
{
    HANDLE hDS = (HANDLE) pvparms;

    DsUnBind(&hDS);
    return(0);
}


VOID
coreDSUnBind(
    IN BOOL fSynchronous)
{
    HRESULT hr;
    HANDLE hDS = g_hDS;
    HANDLE hThread = NULL;
    DWORD ThreadId;

    if (NULL != hDS)
    {
	g_hDS = NULL;
	if (fSynchronous)
	{
	    coreDSUnbindWorker(hDS);
	}
	else
	{
	    hThread = CreateThread(
			    NULL,	// lpThreadAttributes (Security Attr)
			    0,		// dwStackSize
			    coreDSUnbindWorker,
			    hDS,	// lpParameter
			    0,          // dwCreationFlags
			    &ThreadId);
	    if (NULL == hThread)
	    {
		hr = myHLastError();
		_JumpError(hr, error, "CreateThread");
	    }
	}
    }

error:
    if (NULL != hThread)
    {
        CloseHandle(hThread);
    }
}


HRESULT
myAddDomainName(
    IN WCHAR const *pwszSamName,
    OUT WCHAR **ppwszSamName,		// *ppwszSamName is NULL if unchanged
    OUT WCHAR const **ppwszUserName)
{
    HRESULT hr;
    WCHAR const *pwszUserName;
    WCHAR wszDomain[MAX_PATH];

    *ppwszSamName = NULL;
    *ppwszUserName = NULL;

    if (L'\0' == *pwszSamName)
    {
	hr = E_ACCESSDENIED;	// can't have a zero length name
	_JumpError(hr, error, "zero length name");
    }

    // See if it includes a domain name.

    pwszUserName = wcschr(pwszSamName, L'\\');
    if (NULL == pwszUserName)
    {
	DWORD cwc = ARRAYSIZE(wszDomain);
	WCHAR *pwsz;

        // There was no domain portion, so assume part of the current domain.

        if (GetUserNameEx(NameSamCompatible, wszDomain, &cwc))
        {
            // Fix NULL termination bug

            if (0 != cwc)
            {
		cwc--;
            }
	    wszDomain[cwc] = L'\0';
            pwsz = wcschr(wszDomain, L'\\');
            if (NULL != pwsz)
            {
                pwsz++;
                wcsncpy(pwsz, pwszSamName, ARRAYSIZE(wszDomain) - cwc);

		hr = myDupString(wszDomain, ppwszSamName);
		_JumpIfError(hr, error, "myDupString");

		pwszSamName = *ppwszSamName;
            }          
        }
    }
    pwszUserName = wcschr(pwszSamName, L'\\');
    if (NULL == pwszUserName)
    {
        pwszUserName = pwszSamName;
    }
    else
    {
        pwszUserName++;
    }
    *ppwszUserName = pwszUserName;
    hr = S_OK;

error:
    return(hr);
}


HRESULT
coreGetDNFromSamName(
    IN WCHAR const *pwszSamName,
    OUT WCHAR **ppwszDN)
{
    HRESULT hr;
    BOOL fRediscover = FALSE;
    DS_NAME_RESULTW *pNameResults = NULL;

    CSASSERT(NULL != ppwszDN);
    *ppwszDN = NULL;

    while (TRUE)
    {
	if (fRediscover)
	{
	    coreDSUnBind(FALSE);
	}
	if (NULL == g_hDS)
	{
	    hr = DsBind(NULL, NULL, &g_hDS);
	    if (S_OK != hr)
	    {
		hr = myHError(hr);
		_JumpError(hr, error, "DsBind");
	    }
	}

	// Got a connection.  Crack the name:

	hr = DsCrackNames(
		    g_hDS,
		    DS_NAME_NO_FLAGS,
		    DS_NT4_ACCOUNT_NAME,
		    DS_FQDN_1779_NAME,
		    1,			// one name
		    &pwszSamName,		// one name (IN)
		    &pNameResults);		// OUT
	if (S_OK != hr)
	{
	    hr = myHError(hr);
	    if (!fRediscover)
	    {
		_PrintError(hr, "DsCrackNames");
		fRediscover = TRUE;
		continue;
	    }
	    _JumpError(hr, error, "DsCrackNames");
	}
	if (1 > pNameResults->cItems ||
	    DS_NAME_NO_ERROR != pNameResults->rItems[0].status)
	{
	    hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
	    if (!fRediscover)
	    {
		_PrintError(hr, "DsCrackNames result");
		fRediscover = TRUE;
		continue;
	    }
	    _JumpError(hr, error, "DsCrackNames result");
	}
	break;
    }
    hr = myDupString(pNameResults->rItems[0].pName, ppwszDN);
    _JumpIfError(hr, error, "myDupString");

error:
    if (NULL != pNameResults)
    {
	DsFreeNameResult(pNameResults);
    }
    return(hr);
}



HRESULT
coreGetComContextUserDNFromSamName(
    IN BOOL fDeleteUserDNOnly,
    OPTIONAL IN WCHAR const *pwszSamName,
    IN LONG Context,
    IN DWORD dwComContextIndex,
    OPTIONAL OUT WCHAR const **ppwszDN)		// do NOT free!
{
    HRESULT hr;
    CERTSRV_COM_CONTEXT *pComContext;

    hr = ComGetClientInfo(Context, dwComContextIndex, &pComContext);
    _JumpIfError(hr, error, "ComGetClientInfo");

    if (fDeleteUserDNOnly)
    {
	if (NULL != pComContext->pwszUserDN)
	{
	    LocalFree(pComContext->pwszUserDN);
	    pComContext->pwszUserDN = NULL;
	}
    }
    else
    {
	if (NULL == pComContext->pwszUserDN)
	{
	    hr = coreGetDNFromSamName(pwszSamName, &pComContext->pwszUserDN);
	    _JumpIfError(hr, error, "coreGetDNFromSamName");
	}
    }
    if (NULL != ppwszDN)
    {
	*ppwszDN = pComContext->pwszUserDN;
    }
    hr = S_OK;

error:
    return(hr);
}


HRESULT
CoreSetComContextUserDN(
    IN DWORD dwRequestId,
    IN LONG Context,
    IN DWORD dwComContextIndex,
    OPTIONAL OUT WCHAR const **ppwszDN)		// do NOT free!
{
    HRESULT hr;
    ICertDBRow *prow = NULL;
    WCHAR *pwszSamName = NULL;
    WCHAR *pwszSamNamePatched = NULL;
    WCHAR const *pwszUserName;

    hr = g_pCertDB->OpenRow(
		    PROPOPEN_READONLY | PROPTABLE_REQCERT,
		    dwRequestId,
		    NULL,
		    &prow);
    _JumpIfError(hr, error, "OpenRow");

    hr = PKCSGetProperty(
		prow,
		g_wszPropRequesterName,
		PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		NULL,
		(BYTE **) &pwszSamName);
    _JumpIfError(hr, error, "PKCSGetProperty");

    hr = myAddDomainName(pwszSamName, &pwszSamNamePatched, &pwszUserName);
    _JumpIfError(hr, error, "myAddDomainName");

    hr = coreGetComContextUserDNFromSamName(
		FALSE,		// fDeleteUserDNOnly
		NULL != pwszSamNamePatched? pwszSamNamePatched : pwszSamName,
		Context,
		dwComContextIndex,
		ppwszDN);
    _JumpIfError(hr, error, "coreGetComContextUserDNFromSamName");

error:
    if (NULL != pwszSamName)
    {
	LocalFree(pwszSamName);
    }
    if (NULL != pwszSamNamePatched)
    {
	LocalFree(pwszSamNamePatched);
    }
    if (NULL != prow)
    {
        prow->Release();
    }
    return(hr);
}


VOID
CoreTerminate(VOID)
{
    VOID coreFreeDSCache();

    coreFreeDSCache();
    DBShutDown(FALSE);
    ComShutDown();
    PKCSTerminate();
    CRLTerminate();
    if (NULL != g_pwszServerName)
    {
	LocalFree(g_pwszServerName);
	g_pwszServerName = NULL;
    }
    if (NULL != g_pwszzSubjectTemplate)
    {
	LocalFree(g_pwszzSubjectTemplate);
	g_pwszzSubjectTemplate = NULL;
    }
    if (NULL != g_pwszSanitizedDSName)
    {
	LocalFree(g_pwszSanitizedDSName);
	g_pwszSanitizedDSName = NULL;
    }

    // free only if it points to memory that isn't the default static buffer

    if (NULL != g_wszzSecuredAttributes &&
	g_wszzSecuredAttributes != g_wszzSecuredAttributesDefault)
    {
        LocalFree(g_wszzSecuredAttributes);
        g_wszzSecuredAttributes = NULL;
    }
    if (g_fcritsecDSCache)
    {
	DeleteCriticalSection(&g_critsecDSCache);
	g_fcritsecDSCache = FALSE;
    }
}


DWORD g_PolicyFlags;


HRESULT
CoreSetDisposition(
    IN ICertDBRow *prow,
    IN DWORD Disposition)
{
    HRESULT hr;

    hr = prow->SetProperty(
		    g_wszPropRequestDisposition,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    sizeof(Disposition),
		    (BYTE const *) &Disposition);
    _JumpIfError(hr, error, "SetProperty(disposition)");

error:
    return(hr);
}


DWORD
coreRegGetTimePeriod(
    IN HKEY hkeyCN,
    IN WCHAR const *pwszRegPeriodCount,
    IN WCHAR const *pwszRegPeriodString,
    OUT enum ENUM_PERIOD *penumPeriod,
    OUT LONG *plCount)
{
    HRESULT hr;
    LONG lCount;
    DWORD dwType;
    DWORD cbValue;
    
    cbValue = sizeof(lCount);
    hr = RegQueryValueEx(
		hkeyCN,
		pwszRegPeriodCount,
		NULL,		// lpdwReserved
		&dwType,
		(BYTE *) &lCount,
		&cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(lCount) == cbValue)
    {
        WCHAR awcPeriod[10];
        
        cbValue = sizeof(awcPeriod);
        hr = RegQueryValueEx(
		    hkeyCN,
		    pwszRegPeriodString,
		    NULL,		// lpdwReserved
		    &dwType,
		    (BYTE *) &awcPeriod,
		    &cbValue);
        if (S_OK != hr)
        {
            hr = myHError(hr);
            if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr)
            {
                hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            }
            else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
            {
                hr = S_OK;
            }
            _JumpIfError(hr, error, "RegQueryValueEx");
        }
        else
        {
            
            if (REG_SZ != dwType || sizeof(awcPeriod) <= cbValue)
            {
                hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
                _JumpIfErrorStr(hr, error, "time period string", pwszRegPeriodString);
            }
            hr = myTranslatePeriodUnits(
				awcPeriod,
				lCount,
				penumPeriod,
				plCount);
            _JumpIfError(hr, error, "myTranslatePeriodUnits");
        }
    }
error:
    return(hr);
}


// Converts a REG_SZ Subject template into a double null terminated REG_MULTI_SZ type string

DWORD
coreConvertSubjectTemplate(
    OUT WCHAR* pwszz,
    IN WCHAR* pwszTemplate,
    IN DWORD cwc)
{
    HRESULT hr;
    WCHAR *pwszToken;
    WCHAR *pwszRemain = pwszTemplate;
    WCHAR *pwszzNew = pwszz;
    DWORD cwszzNew = 0;
    BOOL fSplit;

    while (TRUE)
    {
        pwszToken = PKCSSplitToken(&pwszRemain, wszNAMESEPARATORDEFAULT, &fSplit);
        if (NULL == pwszToken)
        {
            *pwszzNew = L'\0';
            break;
        }
        cwszzNew += (1 + wcslen(pwszToken)) * sizeof(WCHAR);
        if (cwszzNew > cwc)
        {
            hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
	    _JumpError(hr, error, "overflow");
        }
        wcscpy(pwszzNew, pwszToken);
        pwszzNew = wcschr(pwszzNew, L'\0');
        pwszzNew++;
    }
    hr = S_OK;

error:
    return(hr);
}
			
			

HRESULT
CoreInit(VOID)
{
    HRESULT hr;
    HKEY hkeyConfig = NULL;
    HKEY hkeyCN = NULL;
    BYTE abbuf[MAX_PATH * sizeof(TCHAR)];
    WCHAR awcTemplate[MAX_PATH];
    DWORD cbbuf;
    DWORD cchbuf;
    DWORD dwType;
    WCHAR *pwsz;
    DWORD dw;
    BOOL fLogError = TRUE;
    DWORD LogMsg = MSG_BAD_REGISTRY;
    WCHAR const *pwszLog = NULL;
    int i;
    DWORD cbValue;
    DWORD dwEnabled;
    CAuditEvent AuditSettings;
    
    __try
    {
	InitializeCriticalSection(&g_critsecDSCache);
	g_fcritsecDSCache = TRUE;
	hr = S_OK;
    }
    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _JumpIfError(hr, error, "InitializeCriticalSection");

    hr = myGetMachineDnsName(&g_pwszServerName);
    _JumpIfError(hr, error, "myGetMachineDnsName");
    
    for (i = 0; i < ARRAYSIZE(g_aStringInitStrings); i++)
    {
        WCHAR const *pwszT;
        
        pwszT = myLoadResourceString(g_aStringInitStrings[i].idResource);
        if (NULL == pwszT)
        {
            hr = myHLastError();
            _JumpError(hr, error, "myLoadResourceString");
        }
        *g_aStringInitStrings[i].ppwszResource = pwszT;
    }
    
    hr = RegOpenKeyEx(
        HKEY_LOCAL_MACHINE,
        g_wszRegKeyConfigPath,
        0,		// dwReserved
        KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
        &hkeyConfig);
    _JumpIfError(hr, error, "RegOpenKeyEx(Config)");
    
    cbbuf = sizeof(abbuf);
    hr = RegQueryValueEx(
        hkeyConfig,
        g_wszRegDirectory,
        NULL,		// lpdwReserved
        &dwType,
        abbuf,
        &cbbuf);
    if (S_OK != hr)
    {
        hr = myHError(hr);
    }
    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr)
    {
        _JumpIfError(hr, error, "RegQueryValueEx(Base)");
        
        if (REG_SZ != dwType)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            _JumpError(hr, error, "RegQueryValueEx(Base)");
        }
        if (sizeof(abbuf) < cbbuf)
        {
            hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
            _JumpError(hr, error, "RegQueryValueEx(Base)");
        }
        CopyMemory(g_wszSharedFolder, abbuf, cbbuf);
    }
    DBGPRINT((DBG_SS_CERTSRVI, "Shared Folder = '%ws'\n", g_wszSharedFolder));
    
    // Find out the name of the active CA(s)
    
    g_wszSanitizedName[0] = L'\0';
    cbbuf = sizeof(g_wszSanitizedName);
    hr = RegQueryValueEx(
        hkeyConfig,
        g_wszRegActive,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) g_wszSanitizedName,
        &cbbuf);
    if ((HRESULT) ERROR_FILE_NOT_FOUND == hr)
    {
#define szForgotSetup "\n\nDid you forget to setup the Cert Server?\n\n\n"

	CONSOLEPRINT0((MAXDWORD, szForgotSetup));
    }
    _JumpIfError(hr, error, "RegQueryValueEx(Base)");
    
    if (REG_SZ != dwType && REG_MULTI_SZ != dwType)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        _JumpError(hr, error, "RegQueryValueEx: value type");
    }
    g_wszSanitizedName[cbbuf / sizeof(WCHAR)] = L'\0';
    if (REG_MULTI_SZ == dwType)
    {
        i = wcslen(g_wszSanitizedName);
        if (L'\0' != g_wszSanitizedName[i + 1])
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            _JumpError(hr, error, "RegQueryValueEx: multiple Active CAs");
        }
    }
    DBGPRINT((DBG_SS_CERTSRVI, "Active CA (Sanitized Name) = '%ws'\n", g_wszSanitizedName));
    
    pwszLog = g_wszSanitizedName;

    hr = mySanitizedNameToDSName(g_wszSanitizedName, &g_pwszSanitizedDSName);
    _JumpIfError(hr, error, "mySanitizedNameToDSName");
    
    hr = RegOpenKeyEx(
        hkeyConfig,
        g_wszSanitizedName,
        0,		// dwReserved
        KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
        &hkeyCN);
    if (S_OK != hr)
    {
        hr = myHError(hr);
        _JumpError(hr, error, "RegOpenKeyEx");
    }
    
    cbValue = sizeof(g_wszCommonName) - 2 * sizeof(WCHAR);
    hr = RegQueryValueEx(
        hkeyCN,
        wszREGCOMMONNAME,
        NULL,
        &dwType,
        (BYTE *)g_wszCommonName,
        &cbValue);
    
    if (S_OK != hr)
    {
        hr = myHError(hr);
        _JumpError(hr, error, "RegOpenKeyEx");
    }
    if (REG_SZ != dwType)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        _JumpError(hr, error, "Couldn't find CA common name");
    }
    g_wszCommonName[cbValue / sizeof(WCHAR)] = L'\0';

    pwszLog = g_wszCommonName;
    
    cbValue = sizeof(dwEnabled);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegEnabled,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dwEnabled,
        &cbValue);
    if (S_OK != hr)
    {
        hr = myHError(hr);
        _JumpError(hr, error, "RegQueryValueEx");
    }
    if (REG_DWORD == dwType &&
        sizeof(dwEnabled) == cbValue &&
        0 == dwEnabled)
    {
        DBGPRINT((DBG_SS_CERTSRVI, "CN = '%ws' DISABLED!\n", g_wszSanitizedName));
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        _JumpError(hr, error, "RegQueryValueEx: Active CA DISABLED!");
    }
    DBGPRINT((DBG_SS_CERTSRVI, "CN = '%ws': Enabled\n", g_wszSanitizedName));
    
    // to check machine setup status
    hr = GetSetupStatus(NULL, &dw);
    _JumpIfError(hr, error, "GetSetupStatus");
    
    if (!(SETUP_SERVER_FLAG & dw))
    {
        hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
        _JumpError(hr, error, "Server installation was not complete");
    }
    if (SETUP_SERVER_UPGRADED_FLAG & dw)
    {
	g_fServerUpgraded = TRUE;
	DBGPRINT((
	    DBG_SS_CERTSRV,
	    "CoreInit: read SETUP_SERVER_UPGRADED_FLAG\n"));
    }
    
    // check per ca
    hr = GetSetupStatus(g_wszSanitizedName, &dw);
    _JumpIfError(hr, error, "GetSetupStatus");
    
    if (SETUP_SUSPEND_FLAG & dw)
    {
        LogMsg = MSG_E_INCOMPLETE_HIERARCHY;
        hr = HRESULT_FROM_WIN32(ERROR_INSTALL_SUSPEND);
        _JumpError(hr, error, "Hierarchy setup incomplete");
    }
    if (!(SETUP_SERVER_FLAG & dw))
    {
        hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
        _JumpError(hr, error, "Server installation was not complete");
    }
    if (SETUP_FORCECRL_FLAG & dw)
    {
	// Don't clear SETUP_FORCECRL_FLAG until CRLs successfully generated

	hr = myDeleteCertRegValue(
			    g_wszSanitizedName,
			    NULL,
			    NULL,
			    wszREGCRLNEXTPUBLISH);
	_PrintIfErrorStr2(
		    hr,
		    "myDeleteCertRegValue",
		    wszREGCRLNEXTPUBLISH,
		    HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
    }

    // update the CA DS object with the server type flags

    if (SETUP_UPDATE_CAOBJECT_SVRTYPE & dw)
    {
        hr = SetCAObjectFlags(
			g_fAdvancedServer? CA_FLAG_CA_SERVERTYPE_ADVANCED : 0);
        _PrintIfError(hr, "SetCAObjectFlags");
        if (S_OK == hr)
        {
            hr = SetSetupStatus(
			g_wszSanitizedName, 
			SETUP_UPDATE_CAOBJECT_SVRTYPE, 
			FALSE);
            _PrintIfError(hr, "SetSetupStatus");
        }
    }
    
    cbValue = sizeof(g_PolicyFlags);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegPolicyFlags,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &g_PolicyFlags,
        &cbValue);
    if (S_OK != hr ||
        REG_DWORD != dwType ||
        sizeof(g_PolicyFlags) != cbValue)
    {
        g_PolicyFlags = 0;
    }
    
    cbValue = sizeof(awcTemplate);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegSubjectTemplate,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) awcTemplate,
        &cbValue);
    if (S_OK == hr &&
        (REG_SZ == dwType || REG_MULTI_SZ == dwType) &&
        sizeof(WCHAR) < cbValue &&
        L'\0' != awcTemplate[0])
    {
        if (L'\0' != awcTemplate[cbValue/sizeof(WCHAR) - 1] ||
            (REG_MULTI_SZ == dwType &&
            L'\0' != awcTemplate[cbValue/sizeof(WCHAR) - 2]) ||
            sizeof(awcTemplate) < cbValue)
        {
            LogMsg = MSG_E_REG_BAD_SUBJECT_TEMPLATE;
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            _JumpError(hr, error, "Bad Subject Template length/termination");
        }
        
        pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cbValue + sizeof(WCHAR));
        if (NULL != pwsz)
        {
            if (dwType == REG_MULTI_SZ)
            {
                CopyMemory(pwsz, awcTemplate, cbValue);
            }
            else
            {
                hr = coreConvertSubjectTemplate(pwsz, awcTemplate, cbValue);
                if (S_OK != hr)
                {
                    LocalFree(pwsz);
                }
                _JumpIfError(hr, error, "coreConvertSubjectTemplate");
            }
            g_pwszzSubjectTemplate = pwsz;
        }
    }
    
    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegCertEnrollCompatible,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr &&
        REG_DWORD == dwType &&
        sizeof(dw) == cbValue)
    {
        g_fCertEnrollCompatible = dw? TRUE : FALSE;
    }
    
    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegEnforceX500NameLengths,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr &&
        REG_DWORD == dwType &&
        sizeof(dw) == cbValue)
    {
        g_fEnforceRDNNameLengths = dw? TRUE : FALSE;
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        wszREGCRLEDITFLAGS,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr &&
        REG_DWORD == dwType &&
        sizeof(dw) == cbValue)
    {
	g_CRLEditFlags = dw;
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
	wszREGKRAFLAGS,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr &&
        REG_DWORD == dwType &&
        sizeof(dw) == cbValue)
    {
	g_KRAFlags = dw;
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
	wszREGKRACERTCOUNT,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr &&
        REG_DWORD == dwType &&
        sizeof(dw) == cbValue)
    {
	g_cKRACertsRoundRobin = dw;
    }
    
    hr = coreRegGetTimePeriod(
        hkeyCN,
        g_wszRegValidityPeriodCount,
        g_wszRegValidityPeriodString,
        &g_enumValidityPeriod,
        &g_lValidityPeriodCount);
    if (S_OK != hr)
    {
        LogMsg = MSG_E_REG_BAD_CERT_PERIOD;
        _JumpError(hr, error, "Bad Registry ValidityPeriod");
    }

    hr = coreRegGetTimePeriod(
        hkeyCN,
        g_wszRegCAXchgValidityPeriodCount,
        g_wszRegCAXchgValidityPeriodString,
        &g_enumCAXchgValidityPeriod,
        &g_lCAXchgValidityPeriodCount);
    _PrintIfError(hr, "Bad Registry CA Xchg Validity Period");

    hr = coreRegGetTimePeriod(
        hkeyCN,
        g_wszRegCAXchgOverlapPeriodCount,
        g_wszRegCAXchgOverlapPeriodString,
        &g_enumCAXchgOverlapPeriod,
        &g_lCAXchgOverlapPeriodCount);
    _PrintIfError(hr, "Bad Registry CA Xchg Overlap Period");
    
    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegForceTeletex,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
	switch (ENUM_TELETEX_MASK & dw)
        {
	    case ENUM_TELETEX_OFF:
	    case ENUM_TELETEX_ON:
		g_fForceTeletex =
		    (enum ENUM_FORCETELETEX) (ENUM_TELETEX_MASK & dw);
		break;
            
	    default:
		g_fForceTeletex = ENUM_TELETEX_AUTO;
		break;
        }
        if (ENUM_TELETEX_UTF8 & dw)
	{
	    *(DWORD *) &g_fForceTeletex |= ENUM_TELETEX_UTF8;
	}
    }
    
    cbValue = sizeof(g_CAType);
    hr = RegQueryValueEx(
		    hkeyCN,
		    wszREGCATYPE,
		    NULL,
		    &dwType,
		    (BYTE *) &g_CAType,
		    &cbValue);
    _JumpIfError(hr, error, "RegQueryValueEx");

    cbValue = sizeof(g_fUseDS);
    hr = RegQueryValueEx(
		    hkeyCN,
		    wszREGCAUSEDS,
		    NULL,
		    &dwType,
		    (BYTE *) &g_fUseDS,
		    &cbValue);
    _JumpIfError(hr, error, "RegQueryValueEx");


    cbValue = sizeof(g_wszParentConfig) - 2 * sizeof(WCHAR);
    hr = RegQueryValueEx(
		    hkeyCN,
		    wszREGPARENTCAMACHINE,
		    NULL,
		    &dwType,
		    (BYTE *) g_wszParentConfig,
		    &cbValue);
    if (S_OK == hr && REG_SZ == dwType)
    {
	g_wszParentConfig[cbValue / sizeof(WCHAR)] = L'\0';
	pwsz = &g_wszParentConfig[wcslen(g_wszParentConfig)];

	*pwsz++ = L'\\';
	*pwsz = L'\0';

	cbValue =
	    sizeof(g_wszParentConfig) - 
	    (SAFE_SUBTRACT_POINTERS(pwsz, g_wszParentConfig) + 1) *
		sizeof(WCHAR);
	hr = RegQueryValueEx(
		    hkeyCN,
		    wszREGPARENTCANAME,
		    NULL,
		    &dwType,
		    (BYTE *) pwsz,
		    &cbValue);
	if (S_OK == hr && REG_SZ == dwType)
	{
	    pwsz[cbValue / sizeof(WCHAR)] = L'\0';
	}
	else
	{
	    g_wszParentConfig[0] = L'\0';
	}
    }
    else
    {
	g_wszParentConfig[0] = L'\0';
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegClockSkewMinutes,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
        g_dwClockSkewMinutes = dw;
    }
    
    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegMaxIncomingMessageSize,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
        g_cbMaxIncomingMessageSize = dw;
    }

    // load CRL globals
    hr = CRLInit(g_wszSanitizedName);
    _JumpIfError(hr, error, "CRLInitializeGlobals");
    
    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegLogLevel,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
        g_dwLogLevel = dw;
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        g_wszRegHighSerial,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
        g_dwHighSerial = dw;
    }

    cbValue = sizeof(dw);
    hr = RegQueryValueEx(
        hkeyCN,
        wszLOCKICERTREQUEST,
        NULL,		// lpdwReserved
        &dwType,
        (BYTE *) &dw,
        &cbValue);
    if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue)
    {
        g_fLockICertRequest = dw;
    }


    hr = g_CASD.Initialize(g_wszSanitizedName);
    _JumpIfError(hr, error, "CProtectedSecurityDescriptor::Initialize");

    g_CASD.ImportResourceStrings(g_pwszAuditResources);

    // Functionality available only on advanced server:
    // - auditing
    // - restricted officers
    // - enforce role separation

    if (g_fAdvancedServer)
    {
        hr = g_OfficerRightsSD.Initialize(g_wszSanitizedName);
        _JumpIfError(hr, error, "CProtectedSecurityDescriptor::Initialize");

        g_OfficerRightsSD.ImportResourceStrings(g_pwszAuditResources);

        hr = AuditSettings.LoadFilter(g_wszSanitizedName);
        if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr)
        {
            _JumpIfError(hr, error, "CAuditEvent::LoadFilter");
        }

        hr = AuditSettings.RoleSeparationFlagLoad(g_wszSanitizedName);
        if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr)
        {
            _JumpIfError(hr, error, "CAuditEvent::RoleSeparationFlagLoad");
        }

    }
    g_dwAuditFilter = AuditSettings.GetFilter();

    
    hr = PKCSSetup(g_wszCommonName, g_wszSanitizedName);
    if (S_OK != hr)
    {
        fLogError = FALSE;		// PKCSSetup logs a specific error
        _JumpError(hr, error, "PKCSSetup");
    }
    
    hr = CertificateInterfaceInit(
        &ServerCallBacks,
        sizeof(ServerCallBacks));
    if (S_OK != hr)
    {
        LogMsg = MSG_CERTIF_MISMATCH;
        _JumpError(hr, error, "CertificateInterfaceInit");
    }

    hr = ComInit();
    _JumpIfError(hr, error, "ComInit");

    hr = RequestInitCAPropertyInfo();
    _JumpIfError(hr, error, "RequestInitCAPropertyInfo");

    // We must have a policy module to continue.
    hr = PolicyInit(g_wszCommonName, g_wszSanitizedName);
    if (S_OK != hr)
    {
        LogMsg = MSG_NO_POLICY;
        _JumpError(hr, error, "PolicyInit");
    }
    CSASSERT(g_fEnablePolicy);
    
    // On error, silently leave exit module(s) disabled.
    hr = ExitInit(g_wszCommonName, g_wszSanitizedName);
    _PrintIfError(hr, "ExitInit");
    
    if (NULL != g_pwszzSubjectTemplate)
    {
        hr = PKCSSetSubjectTemplate(g_pwszzSubjectTemplate);
        if (S_OK != hr)
        {
            LogMsg = MSG_E_REG_BAD_SUBJECT_TEMPLATE;
	    pwszLog = g_wszSanitizedName;
            _JumpError(hr, error, "PKCSSetSubjectTemplate");
        }
    }
    
    hr = myGetCertRegMultiStrValue(
			    g_wszSanitizedName,
			    NULL,
			    NULL,
			    wszSECUREDATTRIBUTES,
			    &g_wszzSecuredAttributes);
    if (S_OK != hr)
    {
        // Force defaults
        g_wszzSecuredAttributes = (LPWSTR)g_wszzSecuredAttributesDefault;
    }

    if (g_fServerUpgraded)
    {
	DBGPRINT((
	    DBG_SS_CERTSRV,
	    "CoreInit: clearing SETUP_SERVER_UPGRADED_FLAG\n"));

	hr = SetSetupStatus(NULL, SETUP_SERVER_UPGRADED_FLAG, FALSE);
	_PrintIfError(hr, "SetSetupStatus");
    }
    fLogError = FALSE;
    
error:
    if (fLogError)
    {
        LogEventString(EVENTLOG_ERROR_TYPE, LogMsg, pwszLog);
    }
    if (NULL != hkeyCN)
    {
        RegCloseKey(hkeyCN);
    }
    if (NULL != hkeyConfig)
    {
        RegCloseKey(hkeyConfig);
    }
    if (S_OK != hr)
    {
        CoreTerminate();
    }
    return(hr);
}


HRESULT
CoreSetRequestDispositionFields(
    IN ICertDBRow *prow,
    IN DWORD ErrCode,
    IN DWORD Disposition,
    IN WCHAR const *pwszDisposition)
{
    HRESULT hr;

    hr = CoreSetDisposition(prow, Disposition);
    _JumpIfError(hr, error, "CoreSetDisposition");

    hr = prow->SetProperty(
		    g_wszPropRequestStatusCode,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    sizeof(ErrCode),
		    (BYTE const *) &ErrCode);
    _JumpIfError(hr, error, "SetProperty(status code)");

    if (NULL != pwszDisposition)
    {
	hr = prow->SetProperty(
		    g_wszPropRequestDispositionMessage,
		    PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    MAXDWORD,
		    (BYTE const *) pwszDisposition);
	_JumpIfError(hr, error, "SetProperty(disposition message)");
    }

error:
    return(hr);
}


HRESULT
coreCreateRequest(
    IN DWORD dwFlags,
    IN WCHAR const *pwszUserName,
    IN DWORD cbRequest,
    IN BYTE const *pbRequest,
    IN WCHAR const *pwszAttributes,
    IN DWORD dwComContextIndex,
    OUT ICertDBRow **pprow,		// may return non-NULL on error
    IN OUT CERTSRV_RESULT_CONTEXT *pResult)
{
    HRESULT hr;
    DWORD dwRequestFlags;
    DWORD cb;
    
    ICertDBRow *prow = NULL;
    
    hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, pprow);
    _JumpIfError(hr, error, "OpenRow");
    
    prow = *pprow;
    
    hr = PropSetRequestTimeProperty(prow, g_wszPropRequestSubmittedWhen);
    _JumpIfError(hr, error, "PropSetRequestTimeProperty");
    
    hr = CoreSetDisposition(prow, DB_DISP_ACTIVE);
    _JumpIfError(hr, error, "CoreSetDisposition");
    
    hr = prow->SetProperty(
        g_wszPropRequestType,
        PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
        sizeof(dwFlags),
        (BYTE const *) &dwFlags);
    _JumpIfError(hr, error, "SetProperty(type)");
    
    if (L'\0' != *pwszUserName)
    {
        hr = prow->SetProperty(
            g_wszPropRequesterName,
            PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
            MAXDWORD,
            (BYTE const *) pwszUserName);
        _JumpIfError(hr, error, "SetProperty(requester)");

        hr = prow->SetProperty(
            g_wszPropCallerName,
            PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
            MAXDWORD,
            (BYTE const *) pwszUserName);
        _JumpIfError(hr, error, "SetProperty(caller)");
    }
    
    if (NULL != pwszAttributes && L'\0' != *pwszAttributes)
    {
        WCHAR awcAttributes[CCH_DBMAXTEXT_ATTRSTRING + 1];
        
        wcsncpy(awcAttributes, pwszAttributes, CCH_DBMAXTEXT_ATTRSTRING);
        awcAttributes[CCH_DBMAXTEXT_ATTRSTRING] = L'\0';
        if (wcslen(pwszAttributes) > CCH_DBMAXTEXT_ATTRSTRING)
        {
            DBGPRINT((
                DBG_SS_CERTSRV,
                "coreCreateRequest: truncating Attributes %u -> %u chars\n",
                wcslen(pwszAttributes),
                CCH_DBMAXTEXT_ATTRSTRING));
        }
        hr = prow->SetProperty(
            g_wszPropRequestAttributes,
            PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
            MAXDWORD,
            (BYTE const *) awcAttributes);
        _JumpIfError(hr, error, "SetProperty(attrib)");
    }
    
    hr = PropParseRequest(prow, dwFlags, cbRequest, pbRequest, pResult);
    _JumpIfError(hr, error, "PropParseRequest");

    hr = PKCSParseAttributes(
			prow,
			pwszAttributes,
			FALSE,
			PROPTABLE_REQUEST,
			NULL);
    _JumpIfError(hr, error, "PKCSParseAttributes");

    hr = prow->CopyRequestNames();	// after parsing request attributes!
    _JumpIfError(hr, error, "CopyRequestNames");
    
    hr = PKCSVerifyChallengeString(prow);
    _JumpIfError(hr, error, "PKCSVerifyChallengeString");

    cb = sizeof(dwRequestFlags);
    hr = prow->GetProperty(
		    g_wszPropRequestFlags,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    &cb,
		    (BYTE *) &dwRequestFlags);
    _JumpIfError(hr, error, "GetProperty");

    if (CR_FLG_ENROLLONBEHALFOF & dwRequestFlags)
    {
	hr = coreGetComContextUserDNFromSamName(
			    TRUE,		// fDeleteUserDNOnly
			    NULL,		// pwszSamName
			    0,			// Context
			    dwComContextIndex,
			    NULL);		// pwszDN
	_JumpIfError(hr, error, "coreGetComContextUserDNFromSamName");
    }
    hr = S_OK;
    
error:
    return(hr);
}


HRESULT
coreFetchCertificate(
    IN ICertDBRow *prow,
    OUT CERTTRANSBLOB *pctbCert)	// CoTaskMem*
{
    HRESULT hr;
    DWORD cbProp;
    
    pctbCert->pb = NULL;
    cbProp = 0;
    hr = prow->GetProperty(
        g_wszPropRawCertificate,
        PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
        &cbProp,
        NULL);
    _JumpIfError(hr, error, "GetProperty(raw cert size)");
    
    pctbCert->pb = (BYTE *) CoTaskMemAlloc(cbProp);
    if (NULL == pctbCert->pb)
    {
        hr = E_OUTOFMEMORY;
        _JumpError(hr, error, "CoTaskMemAlloc(raw cert)");
    }
    hr = prow->GetProperty(
        g_wszPropRawCertificate,
        PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
        &cbProp,
        pctbCert->pb);
    _JumpIfError(hr, error, "GetProperty(raw cert)");
    
error:
    if (S_OK != hr && NULL != pctbCert->pb)
    {
        CoTaskMemFree(pctbCert->pb);
        pctbCert->pb = NULL;
    }
    pctbCert->cb = cbProp;
    return(hr);
}


HRESULT
coreRetrievePending(
    IN ICertDBRow *prow,
    IN BOOL fIncludeCRLs,
    OUT WCHAR **ppwszDisposition,		// LocalAlloc
    OUT CACTX **ppCAContext,
    IN OUT CERTSRV_RESULT_CONTEXT *pResult)	// CoTaskMem*
{
    HRESULT hr;
    DWORD cbProp;
    WCHAR *pwszDisposition = NULL;
    DWORD Disposition;
    HRESULT hrRequest;
    BOOL fIssued;

    *ppwszDisposition = NULL;
    *ppCAContext = NULL;
    cbProp = sizeof(Disposition);
    hr = prow->GetProperty(
		g_wszPropRequestDisposition,
		PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		&cbProp,
		(BYTE *) &Disposition);
    _JumpIfError(hr, error, "GetProperty(disposition)");

    cbProp = sizeof(hrRequest);
    hr = prow->GetProperty(
		    g_wszPropRequestStatusCode,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    &cbProp,
		    (BYTE *) &hrRequest);
    _JumpIfError(hr, error, "GetProperty(status code)");

    hr = PKCSGetProperty(
		    prow,
		    g_wszPropRequestDispositionMessage,
		    PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    NULL,
		    (BYTE **) &pwszDisposition);
    _PrintIfError2(hr, "PKCSGetProperty", CERTSRV_E_PROPERTY_EMPTY);

    fIssued = FALSE;
    switch (Disposition)
    {
	FILETIME FileTime;

	case DB_DISP_ACTIVE:
	case DB_DISP_PENDING:
	    *pResult->pdwDisposition = CR_DISP_UNDER_SUBMISSION;
	    break;

	case DB_DISP_ISSUED:
	case DB_DISP_CA_CERT:
	case DB_DISP_CA_CERT_CHAIN:
	    hr = CERTSRV_E_PROPERTY_EMPTY;
	    if (DB_DISP_CA_CERT == Disposition && IsRootCA(g_CAType))
	    {
		cbProp = sizeof(FileTime);
		hr = prow->GetProperty(
			g_wszPropRequestRevokedEffectiveWhen,
			PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST,
			&cbProp,
			(BYTE *) &FileTime);
	    }
	    if (CERTSRV_E_PROPERTY_EMPTY == hr)
	    {
		*pResult->pdwDisposition = CR_DISP_ISSUED;
		fIssued = TRUE;
		break;
	    }
	    // FALLTHROUGH

	case DB_DISP_REVOKED:
	    *pResult->pdwDisposition = CR_DISP_REVOKED;
	    fIssued = TRUE;
	    break;

	case DB_DISP_ERROR:
	    *pResult->pdwDisposition = CR_DISP_ERROR;
	    break;

	case DB_DISP_DENIED:
	    *pResult->pdwDisposition = CR_DISP_DENIED;
	    if (FAILED(hrRequest))
	    {
		*pResult->pdwDisposition = hrRequest;
	    }
	    break;

	default:
	    *pResult->pdwDisposition = CR_DISP_INCOMPLETE;
	    break;
    }
    if (fIssued)
    {
	BOOL fErrorLogged = FALSE;

	hr = coreFetchCertificate(prow, pResult->pctbCert);
	_JumpIfError(hr, error, "coreFetchCertificate");

	CSASSERT(NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb);
	hr = PKCSCreateCertificate(
			    prow,
			    Disposition,
			    fIncludeCRLs,
			    &fErrorLogged,
			    ppCAContext,
			    pResult);

	CSASSERT(!fErrorLogged);

	if (S_OK != hr)
	{
	    if (CERTLOG_ERROR <= g_dwLogLevel)
	    {
		LogEventHResult(
			    EVENTLOG_ERROR_TYPE,
			    MSG_E_CANNOT_BUILD_CERT_OR_CHAIN,
			    hr);
	    }
	    _JumpError(hr, error, "PKCSCreateCertificate");
	}
    }
    *ppwszDisposition = pwszDisposition;
    pwszDisposition = NULL;
    hr = S_OK;

error:
    if (S_OK != hr && NULL != pResult->pctbCert->pb)
    {
        CoTaskMemFree(pResult->pctbCert->pb);
        pResult->pctbCert->pb = NULL;
    }
    if (NULL != pwszDisposition)
    {
	LocalFree(pwszDisposition);
    }
    return(hr);
}


VOID
CoreLogRequestStatus(
    IN ICertDBRow *prow,
    IN DWORD LogMsg,
    IN DWORD ErrCode,
    OPTIONAL IN WCHAR const *pwszDisposition)
{
    HRESULT hr;
    DWORD cbProp;
    WCHAR awcSubject[1024];
    WCHAR const *pwszSubject;
    WCHAR wszRequestId[11 + 1];
    WCHAR awchr[cwcHRESULTSTRING];
    WORD cString = 0;
    WCHAR const *apwsz[4];
    DWORD ReqId;
    DWORD infotype = EVENTLOG_INFORMATION_TYPE;
    WCHAR const *pwszMessageText = NULL;
    DWORD LogMsg2;
    
    prow->GetRowId(&ReqId);
    wsprintf(wszRequestId, L"%u", ReqId);
    apwsz[cString++] = wszRequestId;

    LogMsg2 = LogMsg;
    switch (LogMsg)
    {
	case MSG_DN_CERT_ISSUED:
	    LogMsg2 = MSG_DN_CERT_ISSUED_WITH_INFO;
	    break;

	case MSG_DN_CERT_PENDING:
	    LogMsg2 = MSG_DN_CERT_PENDING_WITH_INFO;
	    break;

        case MSG_DN_CERT_ADMIN_DENIED:
            LogMsg2 = MSG_DN_CERT_ADMIN_DENIED_WITH_INFO;
            break;

	case MSG_DN_CERT_DENIED:
	    LogMsg2 = MSG_DN_CERT_DENIED_WITH_INFO;
	    infotype = EVENTLOG_WARNING_TYPE;
	    break;

	case MSG_E_PROCESS_REQUEST_FAILED:
	    LogMsg2 = MSG_E_PROCESS_REQUEST_FAILED_WITH_INFO;
	    infotype = EVENTLOG_ERROR_TYPE;
	    break;
    }
    if (EVENTLOG_INFORMATION_TYPE != infotype)
    {
	if (S_OK == ErrCode)
	{
	    ErrCode = SEC_E_CERT_UNKNOWN;	// unknown error
	}
	pwszMessageText = myGetErrorMessageText(ErrCode, TRUE);
	if (NULL == pwszMessageText)
	{
	    pwszMessageText = myHResultToStringRaw(awchr, ErrCode);
	}
	apwsz[cString++] = pwszMessageText;
    }
    
    cbProp = sizeof(awcSubject);
    hr = prow->GetProperty(
		    g_wszPropSubjectDistinguishedName,
		    PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
		    &cbProp,
		    (BYTE *) awcSubject);
    if (CERTSRV_E_PROPERTY_EMPTY == hr)
    {
        cbProp = sizeof(awcSubject);
        hr = prow->GetProperty(
			g_wszPropSubjectDistinguishedName,
			PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
			&cbProp,
			(BYTE *) awcSubject);
    }
    if (CERTSRV_E_PROPERTY_EMPTY == hr)
    {
        cbProp = sizeof(awcSubject);
        hr = prow->GetProperty(
			g_wszPropRequesterName,
			PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
			&cbProp,
			(BYTE *) awcSubject);
    }
    pwszSubject = awcSubject;
    if (S_OK != hr)
    {
        _PrintError(hr, "GetProperty(DN/Requester)");
	pwszSubject = g_pwszUnknownSubject;
    }
    apwsz[cString++] = pwszSubject;
    
    if (NULL != pwszDisposition)
    {
	LogMsg = LogMsg2;
        apwsz[cString++] = pwszDisposition;
    }
    
    if (CERTLOG_VERBOSE <= g_dwLogLevel ||
        (EVENTLOG_WARNING_TYPE == infotype && CERTLOG_WARNING <= g_dwLogLevel) ||
        (EVENTLOG_ERROR_TYPE == infotype && CERTLOG_ERROR <= g_dwLogLevel))
    {
        LogEvent(infotype, LogMsg, cString, apwsz);
    }

#if 0 == i386
# define IOBUNALIGNED(pf) ((sizeof(WCHAR) - 1) & (DWORD) (ULONG_PTR) (pf)->_ptr)
# define ALIGNIOB(pf) \
    { \
	if (IOBUNALIGNED(pf)) \
	{ \
	    fflush(pf); /* fails when running as a service */ \
	} \
	if (IOBUNALIGNED(pf)) \
	{ \
	    fprintf(pf, " "); \
	    fflush(pf); \
	} \
    }
#else
# define IOBUNALIGNED(pf) FALSE
# define ALIGNIOB(pf)
#endif

    {
	BOOL fRetried = FALSE;
	
	while (TRUE)
	{
	    ALIGNIOB(stdout);
	    __try
	    {
		wprintf(
		    // L"\nCertSrv Request %u: rc=%x: %ws: %ws '%ws'\n"
		    g_pwszPrintfCertRequestDisposition,
		    ReqId,
		    ErrCode,
		    NULL != pwszMessageText? pwszMessageText : L"",
		    NULL != pwszDisposition? pwszDisposition : L"",
		    pwszSubject);
		hr = S_OK;
	    }
	    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
	    {
	    }
	    if (S_OK == hr || fRetried || !IOBUNALIGNED(stdout))
	    {
		break;
	    }
	    ALIGNIOB(stdout);
	    fRetried = TRUE;
	}
    }
    if (NULL != pwszMessageText && awchr != pwszMessageText)
    {
	LocalFree(const_cast<WCHAR *>(pwszMessageText));
    }
}


WCHAR *
CoreBuildDispositionString(
    OPTIONAL IN WCHAR const *pwszDispositionBase,
    OPTIONAL IN WCHAR const *pwszUserName,
    OPTIONAL IN WCHAR const *pwszDispositionDetail,
    OPTIONAL IN WCHAR const *pwszBy,
    IN HRESULT hrFail,
    IN BOOL fPublishError)
{
    DWORD cwc = 0;
    WCHAR *pwsz = NULL;
    WCHAR const *pwszMessageText = NULL;
    WCHAR awchr[cwcHRESULTSTRING];

    if (NULL == pwszUserName)
    {
	pwszUserName = L"";
    }
    if (NULL != pwszDispositionBase)
    {
	cwc += wcslen(pwszDispositionBase) + wcslen(pwszUserName);
    }
    if (NULL != pwszDispositionDetail)
    {
	if (0 != cwc)
	{
	    cwc += 2;  // spaces
	}
	cwc += wcslen(pwszDispositionDetail);
    }
    if (NULL != pwszBy)
    {
	if (0 != cwc)
	{
	    cwc += 2;  // spaces
	}
	cwc += wcslen(pwszBy) + wcslen(pwszUserName);
    }
    if (S_OK != hrFail)
    {
	pwszMessageText = myGetErrorMessageText(hrFail, TRUE);
	if (NULL == pwszMessageText)
	{
	    pwszMessageText = myHResultToStringRaw(awchr, hrFail);
	}
	if (0 != cwc)
	{
	    cwc += 2;  // spaces
	}
	if (fPublishError)
	{
	    cwc += wcslen(g_pwszPublishError);
	    cwc += 2;  // spaces
	}
	cwc += wcslen(pwszMessageText);
    }
    if (0 != cwc)
    {
	pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
	if (NULL != pwsz)
	{
	    pwsz[0] = L'\0';
	    if (NULL != pwszDispositionBase)
	    {
		wsprintf(pwsz, pwszDispositionBase, pwszUserName);
	    }
	    if (NULL != pwszDispositionDetail)
	    {
		if (L'\0' != pwsz[0])
		{
		    wcscat(pwsz, L"  ");
		}
		wcscat(pwsz, pwszDispositionDetail);
	    }
	    if (NULL != pwszBy)
	    {
		if (L'\0' != pwsz[0])
		{
		    wcscat(pwsz, L"  ");
		}
		wsprintf(&pwsz[wcslen(pwsz)], pwszBy, pwszUserName);
	    }
	    if (S_OK != hrFail)
	    {
		if (L'\0' != pwsz[0] && L'\n' != pwsz[wcslen(pwsz) - 1])
		{
		    wcscat(pwsz, L"  ");
		}
		if (fPublishError)
		{
		    wcscat(pwsz, g_pwszPublishError);
		    wcscat(pwsz, L"  ");
		}
		wcscat(pwsz, pwszMessageText);
	    }
	}
	CSASSERT(wcslen(pwsz) <= cwc);
    }

//error:
    if (NULL != pwszMessageText && awchr != pwszMessageText)
    {
	LocalFree(const_cast<WCHAR *>(pwszMessageText));
    }
    return(pwsz);
}


VOID
coreLogPublishError(
    IN DWORD RequestId,
    IN WCHAR const *pwszSamName,
    IN LDAP *pld,
    IN WCHAR const *pwszDN,
    OPTIONAL IN WCHAR const *pwszError,
    IN HRESULT hrPublish)
{
    HRESULT hr;
    WCHAR const *apwsz[6];
    WORD cpwsz;
    WCHAR wszRequestId[11 + 1];
    WCHAR awchr[cwcHRESULTSTRING];
    WCHAR const *pwszMessageText = NULL;
    WCHAR *pwszHostName = NULL;
    DWORD LogMsg;

    wsprintf(wszRequestId, L"%u", RequestId);
    if (NULL != pld)
    {
	myLdapGetDSHostName(pld, &pwszHostName);
    }
    pwszMessageText = myGetErrorMessageText(hrPublish, TRUE);
    if (NULL == pwszMessageText)
    {
	pwszMessageText = myHResultToStringRaw(awchr, hrPublish);
    }
    cpwsz = 0;
    apwsz[cpwsz++] = wszRequestId;
    apwsz[cpwsz++] = pwszDN;
    apwsz[cpwsz++] = pwszMessageText;

    LogMsg = MSG_E_CERT_PUBLICATION;
    if (NULL != pwszHostName)
    {
	LogMsg = MSG_E_CERT_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_WARNING <= g_dwLogLevel)
    {
	hr = LogEvent(EVENTLOG_WARNING_TYPE, LogMsg, cpwsz, apwsz);
	_PrintIfError(hr, "LogEvent");
    }

//error:
    if (NULL != pwszMessageText && awchr != pwszMessageText)
    {
	LocalFree(const_cast<WCHAR *>(pwszMessageText));
    }
}


HRESULT
corePublishKRACertificate(
    IN DWORD RequestId,
    IN WCHAR const *pwszSamName,
    IN CERT_CONTEXT const *pcc)
{
    HRESULT hr;
    LDAP *pld = NULL;
    HCERTSTORE hStore = NULL;
    DWORD dwDisposition;
    WCHAR *pwszError = NULL;

    hr = myRobustLdapBind(&pld, FALSE);
    _JumpIfError(hr, error, "myRobustLdapBind");

    hStore = CertOpenStore(
			CERT_STORE_PROV_SYSTEM_W,
			X509_ASN_ENCODING,
			NULL,		// hProv
			CERT_SYSTEM_STORE_LOCAL_MACHINE,
			wszKRA_CERTSTORE);
    if (NULL == hStore)
    {
	hr = myHLastError();
	_JumpErrorStr(hr, error, "CertOpenStore", wszKRA_CERTSTORE);
    }

    // It's a new cert.  CERT_STORE_ADD_ALWAYS is faster.

    if (!CertAddCertificateContextToStore(
				    hStore,
				    pcc,
				    CERT_STORE_ADD_ALWAYS,
				    NULL))
    {
	hr = myHLastError();
	_JumpError(hr, error, "CertAddCertificateContextToStore");
    }

    hr = myLdapPublishCertToDS(
			pld,
			pcc,
			g_pwszKRAPublishURL,
			wszDSKRACERTATTRIBUTE,
			LPC_KRAOBJECT,
			&dwDisposition,
			&pwszError);
    _JumpIfError(hr, error, "myLdapPublishCertToDS");

error:
    if (S_OK != hr)
    {
	coreLogPublishError(
			RequestId,
			pwszSamName,
			pld,
			g_pwszKRAPublishURL,
			pwszError,
			hr);
    }
    if (NULL != pwszError)
    {
	LocalFree(pwszError);
    }
    if (NULL != hStore)
    {
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
    }
    if (NULL != pld)
    {
	ldap_unbind(pld);
    }
    return(hr);
}


HRESULT
corePublishCrossCertificate(
    IN DWORD RequestId,
    IN WCHAR const *pwszSamName,
    IN CERT_CONTEXT const *pcc)
{
    HRESULT hr;
    HRESULT hr2;
    LDAP *pld = NULL;
    DWORD dwDisposition;
    WCHAR *pwszError = NULL;
    WCHAR const *pwszDN = g_pwszAIACrossCertPublishURL;
    CAutoLPWSTR pwszSubjectHash;
    CAutoLPWSTR pwszSubjectDN, pwszSubject;
    LPCWSTR pcwszFormatDN = L"LDAP:///CN=%s%s";
    DWORD dwFlags = LPC_CAOBJECT;
    bool fPublishToSubject = false;

    hr = myRobustLdapBind(&pld, FALSE);
    _JumpIfError(hr, error, "myRobustLdapBind");

    // Attempt to build the location based on subject name:
    //  
    //  LDAP:///CN=<subject DN hasn>,<AIA container DN>
    //
    // In case of errors, fall back to publishing to CA AIA object

    hr = CertNameToHashString(
        &pcc->pCertInfo->Subject,
        &pwszSubjectHash);

    if(S_OK==hr)
    {
        WCHAR *pchRelDN = wcschr(pwszDN, L',');
        if(pchRelDN)
        {
            pwszSubjectDN = (LPWSTR)LocalAlloc(LMEM_FIXED, 
                (wcslen(pwszSubjectHash)+
                 wcslen(pchRelDN)+
                 wcslen(pcwszFormatDN))*sizeof(WCHAR));
            if(pwszSubjectDN)
            {
                wsprintf(pwszSubjectDN, pcwszFormatDN, 
                    (LPCWSTR)pwszSubjectHash, pchRelDN);
                pwszDN = pwszSubjectDN;

                // we'll publish to subject's DS object which might not exist
                dwFlags |= LPC_CREATEOBJECT;

                fPublishToSubject = true;
            }
        }
    }

    hr = myLdapPublishCertToDS(
			pld,
			pcc,
			pwszDN,
			wszDSCROSSCERTPAIRATTRIBUTE,
			dwFlags,
			&dwDisposition,
			&pwszError);
    if (S_OK != hr)
    {
	    _PrintErrorStr(hr, "myLdapPublishCertToDS", pwszDN);
	    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr &&
	        IsRootCA(g_CAType))
	    {
	        hr = S_OK;
	    }
    }

    _JumpIfErrorStr(hr, error, "myLdapPublishCertToDS", pwszDN);

    if(fPublishToSubject)
    {
        hr = myCertNameToStr(
            X509_ASN_ENCODING,
            &pcc->pCertInfo->Subject,
            CERT_X500_NAME_STR, //| CERT_NAME_STR_REVERSE_FLAG,
            &pwszSubject);
        _JumpIfError(hr, error, "myCertNameToStr");

        hr = myLDAPSetStringAttribute(
            pld,
            pwszDN,
            CA_PROP_CERT_DN,
            pwszSubject,
            &dwDisposition, 
            &pwszError
            );
        _JumpIfErrorStr(hr, error, "myLDAPSetStringAttribute", pwszDN);
    }

error:
    if (S_OK != hr)
    {
	coreLogPublishError(RequestId, pwszSamName, pld, pwszDN, pwszError, hr);
    }
    if (NULL != pwszError)
    {
	LocalFree(pwszError);
    }
    if (NULL != pld)
    {
	ldap_unbind(pld);
    }
    return(hr);
}


VOID
coreFreeDSCacheEntry(
    IN OUT DSCACHE *pdsc)
{
    if (NULL != pdsc->pld)
    {
	ldap_unbind(pdsc->pld);
	pdsc->pld = NULL;
    }
    if (NULL != pdsc->pwszDomain)
    {
	LocalFree(pdsc->pwszDomain);
	pdsc->pwszDomain = NULL;
    }
    LocalFree(pdsc);
}


VOID
coreFreeDSCache()
{
    DSCACHE *pdsc;

    // only called during shutdown

    while (NULL != g_DSCache)
    {
       // remove from head of list

       pdsc = g_DSCache;
       g_DSCache = pdsc->pdscNext;

       coreFreeDSCacheEntry(pdsc);
    }
    coreDSUnBind(TRUE);
}


HRESULT
coreGetCachedDS(
    IN WCHAR const *pwszSamName,
    IN BOOL fRediscover,
    OUT LDAP **ppld)
{
    HRESULT hr;
    ULONG ldaperr;
    DS_NAME_RESULTW *pNameResults = NULL;
    WCHAR *pwszDomain = NULL;
    DSCACHE *pdsc = NULL;
    DSCACHE **ppdscPrev;
    DSCACHE *pdscFree = NULL;
    DWORD cwc;
    WCHAR *pwsz;

    CSASSERT(NULL != ppld);
    *ppld = NULL;

    // Copy domain out of the SamName

    pwsz = wcschr(pwszSamName, L'\\');
    if (NULL != pwsz)
    {
        cwc = SAFE_SUBTRACT_POINTERS(pwsz, pwszSamName);
    }
    else
    {
        cwc = wcslen(pwszSamName);
    }
    pwszDomain = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
    if (NULL == pwszDomain)
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "LocalAlloc");
    }
    CopyMemory(pwszDomain, pwszSamName, sizeof(WCHAR) * cwc);
    pwszDomain[cwc] = L'\0';

    ppdscPrev = &g_DSCache;
    for (pdsc = g_DSCache; NULL != pdsc; pdsc = pdsc->pdscNext)
    {
	if (0 == lstrcmpi(pdsc->pwszDomain, pwszDomain))
	{
	    // should we toss the cached entry?

	    if (fRediscover)
	    {
		// unhook from cache list & free the entry,

		*ppdscPrev = pdsc->pdscNext;
		coreFreeDSCacheEntry(pdsc);
		pdsc = NULL;
	    }
	    break;
	}
	ppdscPrev = &pdsc->pdscNext;
    }

    if (fRediscover)
    {
	coreDSUnBind(FALSE);
    }
    if (NULL == pdsc)
    {
	pdsc = (DSCACHE *) LocalAlloc(
				LMEM_FIXED | LMEM_ZEROINIT,
				sizeof(*pdsc));
	if (NULL == pdsc)
	{
	    hr = E_OUTOFMEMORY;
	    _JumpError(hr, error, "LocalAlloc");
	}
	pdscFree = pdsc;

	pdsc->pwszDomain = pwszDomain;
	pwszDomain = NULL;
    }
    CSASSERT(NULL != pdsc->pwszDomain);

    // if we don't have an ldap handle yet, get a good one

    if (NULL == pdsc->pld)
    {
	CSASSERT(pdsc == pdscFree);

	hr = myRobustLdapBindEx(
                        FALSE,			// fGC
                        FALSE,			// fRediscover
                        LDAP_VERSION2,		// uVersion
			pwszDomain,		// pwszDomainName
                        &pdsc->pld,		// ppld
                        NULL);			// ppwszForestDNSName
	_JumpIfError(hr, error, "Policy:myRobustLdapBindEx");

	// link into list: we have all data necessary

	pdsc->pdscNext = g_DSCache;
	g_DSCache = pdsc;
	pdscFree = NULL;
    }
    *ppld = pdsc->pld;

error:
    if (NULL != pwszDomain)
    {
        LocalFree(pwszDomain);
    }
    if (NULL != pNameResults)
    {
        DsFreeNameResult(pNameResults);
    }
    if (NULL != pdscFree)
    {
       coreFreeDSCacheEntry(pdscFree);
    }
    return(hr);
}


HRESULT
corePublishIssuedCertificate(
    IN DWORD RequestId,
    IN DWORD dwComContextIndex,
    IN WCHAR const *pwszSamName,
    IN CERT_CONTEXT const *pcc,
    IN DWORD dwObjectType)	// LPC_*
{
    HRESULT hr;
    WCHAR *pwszSamNamePatched = NULL;
    WCHAR const *pwszUserName;
    DWORD cbProp;
    LDAP *pld = NULL;
    WCHAR const *pwszDN;
    DWORD dwDisposition;
    BOOL fCritSecEntered = FALSE;
    WCHAR *pwszError = NULL;

    hr = myAddDomainName(pwszSamName, &pwszSamNamePatched, &pwszUserName);
    _JumpIfError(hr, error, "myAddDomainName");

    if (NULL != pwszSamNamePatched)
    {
	pwszSamName = pwszSamNamePatched;
    }

    EnterCriticalSection(&g_critsecDSCache);
    fCritSecEntered = TRUE;
    __try
    {
	BOOL fRediscover = FALSE;

	hr = coreGetComContextUserDNFromSamName(
			    FALSE,		// fDeleteUserDNOnly
			    pwszSamName,
			    0,			// Context
			    dwComContextIndex,
			    &pwszDN);
	_JumpIfError(hr, error, "coreGetComContextUserDNFromSamName");

	while (TRUE)
	{
	    if (NULL != pwszError)
	    {
		LocalFree(pwszError);
		pwszError = NULL;
	    }
	    pld = NULL;

	    hr = coreGetCachedDS(pwszSamName, fRediscover, &pld); 
	    if (S_OK != hr)
	    {
		_PrintErrorStr(
			    hr,
			    "coreGetCachedDS",
			    fRediscover? L"noncached" : L"cached");
		if (fRediscover)
		{
		    _leave;
		}
	    }
	    else
	    {
		hr = myLdapPublishCertToDS(
				    pld,
				    pcc,
				    pwszDN,
				    wszDSUSERCERTATTRIBUTE,
				    dwObjectType,	// LPC_*
				    &dwDisposition,
				    &pwszError);
		_PrintIfErrorStr(hr, "myLdapPublishCertToDS", pwszDN);
		if (fRediscover || S_OK == hr)
		{
		    break;
		}
		if (!myLdapRebindRequired(dwDisposition, pld))
		{
		    _LeaveError(hr, "myLdapPublishCertToDS");
		}
	    }
	    fRediscover = TRUE;
	}
    }
    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
    {
	_PrintError(hr, "Exception");
    }

error:
    if (S_OK != hr)
    {
	coreLogPublishError(RequestId, pwszSamName, pld, pwszDN, pwszError, hr);
    }
    if (NULL != pwszError)
    {
	LocalFree(pwszError);
    }
    if (NULL != pwszSamNamePatched)
    {
	LocalFree(pwszSamNamePatched);
    }
    if (fCritSecEntered)
    {
	LeaveCriticalSection(&g_critsecDSCache);
    }
    return(hr);
}


HRESULT
CorePublishCertificate(
    IN ICertDBRow *prow,
    IN DWORD dwComContextIndex)
{
    HRESULT hr;
    DWORD cbProp;
    DWORD RequestId;
    DWORD GeneralFlags;
    DWORD EnrollmentFlags;
    DWORD cbCert;
    BYTE *pbCert = NULL;
    CERT_CONTEXT const *pcc = NULL;
    WCHAR *pwszSamName = NULL;
    
    prow->GetRowId(&RequestId);

    cbProp = sizeof(EnrollmentFlags);
    hr = prow->GetProperty(
		    wszPROPCERTIFICATEENROLLMENTFLAGS,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
		    &cbProp,
		    (BYTE *) &EnrollmentFlags);
    _PrintIfError2(hr, "GetProperty", CERTSRV_E_PROPERTY_EMPTY);
    if (S_OK != hr)
    {
	EnrollmentFlags = 0;
    }

    if (0 == ((CT_FLAG_PUBLISH_TO_DS | CT_FLAG_PUBLISH_TO_KRA_CONTAINER) &
							    EnrollmentFlags))
    {
	hr = S_OK;
	goto error;
    }

    cbProp = sizeof(GeneralFlags);
    hr = prow->GetProperty(
		    wszPROPCERTIFICATEGENERALFLAGS,
		    PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
		    &cbProp,
		    (BYTE *) &GeneralFlags);
    _PrintIfError2(hr, "GetProperty", CERTSRV_E_PROPERTY_EMPTY);
    if (S_OK != hr)
    {
	GeneralFlags = 0;
    }

    // Get the name of the user or machine

    hr = PKCSGetProperty(
		    prow,
		    g_wszPropRequesterName,
		    PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
		    NULL,
		    (BYTE **) &pwszSamName);
    _JumpIfError(hr, error, "PKCSGetProperty");

    hr = PKCSGetProperty(
		prow, 
		g_wszPropRawCertificate,
		PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
		&cbCert,
		&pbCert);
    _JumpIfError(hr, error, "PKCSGetProperty(raw cert)");

    pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
    if (NULL == pcc)
    {
	hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
	_JumpError(hr, error, "CertCreateCertificateContext");
    }

    hr = S_OK;
    if (CT_FLAG_PUBLISH_TO_DS & EnrollmentFlags)
    {
	if (CT_FLAG_IS_CROSS_CA & GeneralFlags)
	{
	    hr = corePublishCrossCertificate(RequestId, pwszSamName, pcc);
	    _PrintIfError(hr, "corePublishCrossCertificate");
	}
	else
	{
	    hr = corePublishIssuedCertificate(
				    RequestId,
				    dwComContextIndex,
				    pwszSamName,
				    pcc,
				    (CT_FLAG_MACHINE_TYPE & GeneralFlags)?
					LPC_MACHINEOBJECT : LPC_USEROBJECT);
	    _PrintIfError(hr, "corePublishIssuedCertificate");
	}
    }

    if (CT_FLAG_PUBLISH_TO_KRA_CONTAINER & EnrollmentFlags)
    {
	HRESULT hr2;
	
	hr2 = corePublishKRACertificate(RequestId, pwszSamName, pcc);
	_PrintIfError(hr2, "corePublishKRACertificate");
	if (S_OK == hr)
	{
	    hr = hr2;
	}
    }
    _JumpIfError(hr, error, "CorePublishCertificate");

error:
    if (NULL != pwszSamName)
    {
	LocalFree(pwszSamName);
    }
    if (NULL != pcc)
    {
	CertFreeCertificateContext(pcc);
    }
    if (NULL != pbCert)
    {
	LocalFree(pbCert);
    }
    return(hr);
}


HRESULT
coreAcceptRequest(
    IN ICertDBRow *prow,
    IN BOOL fIncludeCRLs,
    IN DWORD dwComContextIndex,
    OUT BOOL *pfErrorLogged,
    OUT CACTX **ppCAContext,
    IN OUT CERTSRV_RESULT_CONTEXT *pResult,	// CoTaskMem*
    OUT HRESULT *phrPublish)
{
    HRESULT hr;

    *ppCAContext = NULL;
    *phrPublish = S_OK;

    // Force Cert creation:
    CSASSERT(NULL == pResult->pctbCert || NULL == pResult->pctbCert->pb);

    hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE);
    _JumpIfError(hr, error, "CoreValidateRequestId");

    hr = PKCSCreateCertificate(
			prow,
			DB_DISP_ACTIVE,
			fIncludeCRLs,
			pfErrorLogged,
			ppCAContext,
			pResult);
    _JumpIfError(hr, error, "PKCSCreateCertificate");

    *phrPublish = CorePublishCertificate(prow, dwComContextIndex);
    _PrintIfError(*phrPublish, "CorePublishCertificate");
    if (S_OK != *phrPublish)
    {
	hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_PUBLISHERROR);
	_JumpIfError(hr, error, "PKCSSetRequestFlags");
    }
    CSASSERT(S_OK == hr);

error:
    return(hr);
}


HRESULT
coreVerifyRequest(
    IN OUT ICertDBRow **pprow,
    IN DWORD OpRequest,
    IN BOOL fIncludeCRLs,
    OPTIONAL IN WCHAR const *pwszUserName,
    IN DWORD dwComContextIndex,
    OUT DWORD *pReqId,
    OUT LONG *pExitEvent,
    OUT WCHAR **ppwszDisposition,		// LocalAlloc
    OUT CACTX **ppCAContext,
    IN OUT CERTSRV_RESULT_CONTEXT *pResult)	// CoTaskMem*
{
    HRESULT hr;
    HRESULT hr2;
    HRESULT hrRequest = S_OK;
    HRESULT hrPublish = S_OK;
    DWORD VerifyStatus;
    DWORD DBDisposition;
    BOOL fResolved;
    LONG ExitEvent;
    BOOL fPending;
    BOOL fSubmit;
    BOOL fRetrieve;
    BOOL fDenied;
    BOOL fUpdateDisposition = FALSE;

    WCHAR *pwszDispositionRetrieved = NULL;
    WCHAR const *pwszDispositionBase = NULL;
    WCHAR *pwszDispositionDetail = NULL;
    WCHAR *pwszDisposition = NULL;
    WCHAR const *pwszBy = NULL;

    DWORD LogMsg = MSG_E_PROCESS_REQUEST_FAILED;
    BOOL fErrorLogged = FALSE;
    DWORD ReqId;
    ICertDBRow *prow = *pprow;
    
    prow->GetRowId(&ReqId);
    
    *ppCAContext = NULL;
    *pResult->pdwDisposition = CR_DISP_ERROR;
    DBDisposition = DB_DISP_ERROR;
    *ppwszDisposition = NULL;
    
    ExitEvent = EXITEVENT_INVALID;
    
    fSubmit = CR_IN_NEW == OpRequest || CR_IN_RESUBMIT == OpRequest;
    fPending = CR_IN_DENY == OpRequest || CR_IN_RESUBMIT == OpRequest;
    fRetrieve = CR_IN_RETRIEVE == OpRequest;
    
#if DBG_COMTEST
    if (fSubmit && fComTest && !ComTest((LONG) ReqId))
    {
        _PrintError(0, "ComTest");
    }
#endif

    if (fRetrieve)
    {
        hr = coreRetrievePending(
			    prow,
			    fIncludeCRLs,
			    &pwszDispositionRetrieved,
			    ppCAContext,
			    pResult);	// CoTaskMem*
	_JumpIfError(hr, error, "coreRetrievePending");

	pwszDispositionBase = pwszDispositionRetrieved;
        ExitEvent = EXITEVENT_CERTRETRIEVEPENDING;
    }
    else
    {
        // If the current status is expected to be pending, verify that now,
        // and make the request active.
	//
	// If it was already marked active, then something went wrong last time
	// we processed the request (out of disk space?), and we can try to
	// pick up where we left off, by resubmitting or denying the request.

        if (fPending)
        {
            hr = CoreValidateRequestId(prow, DB_DISP_PENDING);
	    if (CERTSRV_E_BAD_REQUESTSTATUS == hr)
	    {
		hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE);
	    }
	    if (CERTSRV_E_BAD_REQUESTSTATUS == hr && fSubmit)
	    {
		hr = CoreValidateRequestId(prow, DB_DISP_DENIED);
		if (S_OK == hr)
		{
		    DBGPRINT((
			DBG_SS_CERTSRV,
			"Resubmit failed request %u\n",
			ReqId));
		}
	    }
            _JumpIfError(hr, error, "CoreValidateRequestId");

            hr = CoreSetDisposition(prow, DB_DISP_ACTIVE);
            _JumpIfError(hr, error, "CoreSetDisposition");
        }
        fUpdateDisposition = TRUE;
        if (fSubmit)
        {
	    if (fPending)
	    {
		pwszBy = g_pwszResubmittedBy;

		hr = PKCSSetServerProperties(
				prow,
				g_lValidityPeriodCount,
				g_enumValidityPeriod);
		_JumpIfError(hr, error, "PKCSSetServerProperties");
	    }

            hr = prow->CommitTransaction(TRUE);
            _JumpIfError(hr, error, "CommitTransaction");

            prow->Release();
            prow = NULL;
            *pprow = NULL;

            hr = PolicyVerifyRequest(
		            g_wszCommonName,
		            ReqId,
		            g_PolicyFlags,
		            CR_IN_NEW == OpRequest,
			    dwComContextIndex,
			    &pwszDispositionDetail, 
		            &VerifyStatus);
	    if (S_OK != hr)
	    {
		_PrintError(hr, "PolicyVerifyRequest");
		if (SUCCEEDED(hr))
		{
		    if (S_FALSE == hr)
		    {
			hr = E_UNEXPECTED;
		    }
		    else
		    {
			hr = myHError(hr);
		    }
		}
		VerifyStatus = hr;
	    }

            hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, ReqId, NULL, &prow);
	    _JumpIfError(hr, error, "OpenRow");

	    CSASSERT(NULL != prow);
            *pprow = prow;
        }
        else	// else we're denying the request!
        {
            VerifyStatus = VR_INSTANT_BAD;
        }

        fResolved = FALSE;
        fDenied = FALSE;
        switch (VerifyStatus)
        {
            case VR_PENDING:
		hr = S_OK;
		DBDisposition = DB_DISP_PENDING;
		ExitEvent = EXITEVENT_CERTPENDING;
		LogMsg = MSG_DN_CERT_PENDING;
		*pResult->pdwDisposition = CR_DISP_UNDER_SUBMISSION;
		pwszDispositionBase = g_pwszUnderSubmission;
		break;

            case VR_INSTANT_OK:
		hr = coreAcceptRequest(
				prow,
				fIncludeCRLs,
				dwComContextIndex,
				&fErrorLogged,
				ppCAContext,
				pResult,
				&hrPublish);
		if (S_OK != hr)
		{
	            CSASSERT(FAILED(hr));
	            _PrintError(hr, "coreAcceptRequest");
	            pwszDispositionBase = g_pwszRequestProcessingError;
	            VerifyStatus = hr;
	            hr = S_OK;
	            fDenied = TRUE;
		}
		else
		{
	            fResolved = TRUE;
	            DBDisposition = DB_DISP_ISSUED;
	            ExitEvent = EXITEVENT_CERTISSUED;
	            LogMsg = MSG_DN_CERT_ISSUED;
	            *pResult->pdwDisposition = CR_DISP_ISSUED;
	            pwszDispositionBase = g_pwszIssued;
		}
		break;

            default:
		if (SUCCEEDED(VerifyStatus))
		{
		    CSASSERT(
			VerifyStatus == VR_PENDING ||
			VerifyStatus == VR_INSTANT_OK ||
			VerifyStatus == VR_INSTANT_BAD);
		    hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
		    _JumpError(hr, error, "VerifyStatus");
		}
		// FALLTHROUGH

            case VR_INSTANT_BAD:
		hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE);
		_JumpIfError(hr, error, "CoreValidateRequestId");

		fDenied = TRUE;
		break;
        }
        if (fDenied)
        {
            fResolved = TRUE;
            DBDisposition = DB_DISP_DENIED;
            ExitEvent = EXITEVENT_CERTDENIED;

            *pResult->pdwDisposition = CR_DISP_DENIED;
            if (FAILED(VerifyStatus))
            {
                *pResult->pdwDisposition = VerifyStatus;
                hrRequest = VerifyStatus;
            }

            if (fSubmit)
            {
	        if (NULL == pwszDispositionBase)
		{
		    pwszDispositionBase = g_pwszPolicyDeniedRequest;
		}
                LogMsg = MSG_DN_CERT_DENIED;
            }
            else
            {
	        pwszDispositionBase = g_pwszDeniedBy;
                LogMsg = MSG_DN_CERT_ADMIN_DENIED;
            }
        }
        if (fResolved)
        {
            hr = PropSetRequestTimeProperty(prow, g_wszPropRequestResolvedWhen);
            _JumpIfError(hr, error, "PropSetRequestTimeProperty");
        }
    }
    
error:
    *pReqId = ReqId;
    *pExitEvent = ExitEvent;
    
    // If we verified or denied the request, set the status & disposition

    // Build the full disposition string

    pwszDisposition = CoreBuildDispositionString(
				    pwszDispositionBase,
				    pwszUserName,
				    pwszDispositionDetail,
				    pwszBy,
				    hrPublish,
				    TRUE);

    if (NULL != pwszDispositionDetail)
    {
        LocalFree(pwszDispositionDetail);
    }

    if (S_OK == hrRequest && S_OK != hr)
    {
	hrRequest = hr;
    }
    if (fUpdateDisposition && NULL != prow)
    {
        hr2 = CoreSetRequestDispositionFields(
				    prow,
				    hrRequest,
				    DBDisposition,
				    pwszDisposition);
        if (S_OK == hr)
        {
            hr = hr2;
        }
    }

    if (!fErrorLogged &&
         NULL != prow &&
        (fUpdateDisposition || S_OK != hr))
    {
        CoreLogRequestStatus(prow, LogMsg, hrRequest, pwszDisposition);
    }

    if (NULL != ppwszDisposition)
    {
        *ppwszDisposition = pwszDisposition;
    }
    else if (NULL != pwszDisposition)
    {
        LocalFree(pwszDisposition);
    }
    if (NULL != pwszDispositionRetrieved)
    {
        LocalFree(pwszDispositionRetrieved);
    }
    return(hr);
}


HRESULT
coreAuditAddStringProperty(
    IN ICertDBRow *prow,
    IN WCHAR const *pwszPropName,
    IN CertSrv::CAuditEvent *pevent)
{
    HRESULT hr;
    WCHAR const *pwszLogValue = L"";
    WCHAR *pwszPropValue = NULL;

    hr = PKCSGetProperty(
		prow, 
		pwszPropName,
		PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
		NULL,
		(BYTE **) &pwszPropValue);
    _PrintIfErrorStr(hr, "PKCSGetProperty", pwszPropName);
    if (S_OK == hr)
    {
	pwszLogValue = pwszPropValue;
    }
    hr = pevent->AddData(pwszLogValue);
    _JumpIfError(hr, error, "CAuditEvent::AddData");

error:
    if (NULL != pwszPropValue)
    {
	LocalFree(pwszPropValue);
    }
    return(hr);
}

    
HRESULT
coreAuditRequestDisposition(
    OPTIONAL IN ICertDBRow *prow,
    IN DWORD ReqId,
    IN WCHAR const *pwszUserName,
    IN WCHAR const *pwszAttributes,
    IN DWORD dwDisposition)
{
    HRESULT hr;

    CertSrv::CAuditEvent 
    audit(0, g_dwAuditFilter);

    hr = audit.AddData(ReqId); // %1 request ID
    _JumpIfError(hr, error, "CAuditEvent::AddData");

    hr = audit.AddData(pwszUserName); // %2 requester
    _JumpIfError(hr, error, "CAuditEvent::AddData");

    hr = audit.AddData(pwszAttributes); // %3 attributes
    _JumpIfError(hr, error, "CAuditEvent::AddData");

    hr = audit.AddData(dwDisposition); // %4 disposition
    _JumpIfError(hr, error, "CAuditEvent::AddData");

    if (NULL != prow)
    {
	hr = coreAuditAddStringProperty(
			prow,
			g_wszPropCertificateSubjectKeyIdentifier,
			&audit); // %5 SKI
	_JumpIfError(hr, error, "coreAuditAddStringProperty");

	hr = coreAuditAddStringProperty(
			prow,
			g_wszPropSubjectDistinguishedName,
			&audit); // %6 Subject
	_JumpIfError(hr, error, "coreAuditAddStringProperty");
    }
    else // we need to guarantee the same number of audit params
    {
        hr = audit.AddData(L""); // %5 SKI
        _JumpIfError(hr, error, "");

        hr = audit.AddData(L""); // %6 Subject
        _JumpIfError(hr, error, "");
    }

    switch (dwDisposition)
    {
	case CR_DISP_ISSUED: 
	    audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTAPPROVED);
	    hr = audit.Report();
	    _JumpIfError(hr, error, "CAuditEvent::Report");

	    break;

	case CR_DISP_UNDER_SUBMISSION:
	    audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTPENDING);
	    hr = audit.Report();
	    _JumpIfError(hr, error, "CAuditEvent::Report");

	    break;

	case CR_DISP_DENIED: // fail over
	default:
	    audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTDENIED);
	    hr = audit.Report(false);
	    _JumpIfError(hr, error, "CAuditEvent::Report");
        break;

    }
    CSASSERT(S_OK == hr);

error:
    return(hr);
}

#define LOGMSG_ATTACK_DELAY_SECONDS (CVT_MINUTES * 20)
static FILETIME g_ftLogNextAttackMsg = {0};

HRESULT ValidateMessageSize(OPTIONAL LPCWSTR pwszUser, DWORD cbRequest)
{
    HRESULT hr = S_OK;

    if (cbRequest > g_cbMaxIncomingMessageSize)
    {
        hr = HRESULT_FROM_WIN32(ERROR_MESSAGE_EXCEEDS_MAX_SIZE);

        SYSTEMTIME stNow;
        FILETIME ftNow;
        GetSystemTime(&stNow);

        // if VERBOSE -or- it's time to log the next msg (Next < Now)
        if ( (CERTLOG_VERBOSE <= g_dwLogLevel) ||
             ( (SystemTimeToFileTime(&stNow, &ftNow)) && (0 > CompareFileTime(&g_ftLogNextAttackMsg, &ftNow)) )
           )
        {
            // g_ftLogNextAttackMsg = ftNow + DELAY;

            ULARGE_INTEGER ui;
            ui.LowPart = ftNow.dwLowDateTime;
            ui.HighPart = ftNow.dwHighDateTime;
            ui.QuadPart += ((LONGLONG)LOGMSG_ATTACK_DELAY_SECONDS) * CVT_BASE;
            g_ftLogNextAttackMsg.dwLowDateTime = ui.LowPart;
            g_ftLogNextAttackMsg.dwHighDateTime = ui.HighPart;

            // don't pass NULL
            if (NULL == pwszUser)
               pwszUser = L"?";

            LogEventStringHResult(EVENTLOG_ERROR_TYPE,
			    MSG_E_POSSIBLE_DENIAL_OF_SERVICE_ATTACK,
                            pwszUser,
			    hr);
        }
    }

    return hr;
}


HRESULT
coreInitRequest(
    IN DWORD dwFlags,
    OPTIONAL IN WCHAR const *pwszUserName,
    IN DWORD cbRequest,
    OPTIONAL IN BYTE const *pbRequest,
    OPTIONAL IN WCHAR const *pwszAttributes,
    OPTIONAL IN WCHAR const *pwszSerialNumber,
    IN DWORD dwComContextIndex,
    OUT DWORD *pOpRequest,
    OUT ICertDBRow **pprow,		// may return non-NULL on error
    OUT WCHAR **ppwszDisposition,
    IN OUT CERTSRV_RESULT_CONTEXT *pResult)
{
    HRESULT hr;
    
    *pprow = NULL;
    *ppwszDisposition = NULL;

    // for Denial-of-Service reasons, don't do anything with a too-long message

    hr = ValidateMessageSize(pwszUserName, cbRequest);
    _JumpIfError(hr, error, "ValidateMessageSize");

    // Called in several cases:
    //
    // - CR_IN_NEW: Create a new request and return error/pending/etc &
    //	 possibly the cert:
    //       NULL != pbRequest && NULL != pResult->pctbCert, etc.
    //
    // - CR_IN_DENY: Deny a pending request:
    //       NULL == pbRequest && NULL == pResult->pctbCert, etc.
    //
    // - CR_IN_RESUBMIT: Resubmit a pending request and return hr/pending/etc.
    //       NULL == pbRequest && NULL == pResult->pctbCert, etc.
    //
    // - CR_IN_RETRIEVE: Retrieve a cert for a processed request and return
    //   error/pending/etc & possibly the cert:
    //       NULL == pbRequest && NULL != pResult->pctbCert, etc.
    
    *pOpRequest = (CR_IN_COREMASK & dwFlags);
    switch (*pOpRequest)
    {
        // Process a new request:

        case CR_IN_NEW:
            CSASSERT(NULL != pwszUserName);
            CSASSERT(0 != cbRequest);
            CSASSERT(NULL != pbRequest);

            CSASSERT(0 == *pResult->pdwRequestId);
            *pResult->pdwRequestId = 0;

            hr = coreCreateRequest(
			    ~CR_IN_COREMASK & dwFlags,
		            pwszUserName,
		            cbRequest,
		            pbRequest,
		            pwszAttributes,
			    dwComContextIndex,
		            pprow,
			    pResult);
            _JumpIfError(hr, error, "coreCreateRequest");

            (*pprow)->GetRowId(pResult->pdwRequestId);
            {         
                CertSrv::CAuditEvent 
                    audit(SE_AUDITID_CERTSRV_NEWREQUEST, g_dwAuditFilter);

                hr = audit.AddData(*pResult->pdwRequestId); // %1 request ID
                _JumpIfError(hr, error, "CAuditEvent::AddData");

                hr = audit.AddData(pwszUserName); // %2 requester
                _JumpIfError(hr, error, "CAuditEvent::AddData");

                hr = audit.AddData(pwszAttributes); // %3 attributes
                _JumpIfError(hr, error, "CAuditEvent::AddData");

                hr = audit.Report();
                _JumpIfError(hr, error, "CAuditEvent::Report");
            }
            break;

        // Deny a request:
        // Resubmit a request:

        case CR_IN_DENY:
        case CR_IN_RESUBMIT:
            break;

        // Retrieve a cert:

        case CR_IN_RETRIEVE:
            break;

        default:
	    CSASSERT(*pOpRequest != *pOpRequest);
	    break;
    }
    if (CR_IN_NEW != *pOpRequest)
    {
	hr = E_INVALIDARG;
	if (0 != cbRequest || NULL != pbRequest)
	{
	    _JumpError(hr, error, "unexpected request");
	}
        if ((0 != *pResult->pdwRequestId) ^ (NULL == pwszSerialNumber))
	{
	    _JumpError(hr, error, "expected RequestId or SerialNumber");
	}

	// RetrievePending by RequestId OR SerialNumber in pwszSerialNumber

        hr = g_pCertDB->OpenRow(
			    PROPTABLE_REQCERT,
			    *pResult->pdwRequestId,
			    pwszSerialNumber,
			    pprow);
        _JumpIfError(hr, error, "OpenRow");
    }
    hr = S_OK;

error:
    if (S_OK != hr)
    {
	HRESULT hr2;
	WCHAR const *pwszDisp = g_pwszRequestParsingError;
	
	hr2 = myDupString(pwszDisp, ppwszDisposition);
	_PrintIfError(hr2, "myDupString");

	if (NULL != *pprow)
	{
	    hr2 = CoreSetRequestDispositionFields(
					    *pprow,
					    hr,
					    DB_DISP_ERROR,
					    pwszDisp);
	    _PrintIfError(hr2, "CoreSetRequestDispositionFields");
	    
	    CoreLogRequestStatus(
			    *pprow,
			    MSG_E_PROCESS_REQUEST_FAILED,
			    hr,
			    pwszDisp);
	}
    }
    return(hr);
}


HRESULT
CoreProcessRequest(
    IN DWORD dwFlags,
    OPTIONAL IN WCHAR const *pwszUserName,
    IN DWORD cbRequest,
    OPTIONAL IN BYTE const *pbRequest,
    OPTIONAL IN WCHAR const *pwszAttributes,
    OPTIONAL IN WCHAR const *pwszSerialNumber,
    IN DWORD dwComContextIndex,
    IN DWORD dwRequestId,
    OUT CERTSRV_RESULT_CONTEXT *pResult)
{
    HRESULT hr;
    HRESULT hr2;
    WCHAR *pwszDisposition = NULL;
    DWORD OpRequest;
    ICertDBRow *prow = NULL;
    DWORD ReqId;
    LONG ExitEvent = EXITEVENT_INVALID;
    BOOL fCoInitialized = FALSE;
    CACTX *pCAContext;
    BOOL fCommitted = FALSE;
    
    CSASSERT(NULL != pResult->pdwRequestId);
    CSASSERT(NULL != pResult->pdwDisposition);

    if (MAXDWORD == dwRequestId)
    {
	dwRequestId = 0;
    }
    *pResult->pdwRequestId = dwRequestId;
    *pResult->pdwDisposition = CR_DISP_ERROR;
    
    hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
    if (S_OK != hr && S_FALSE != hr)
    {
        _JumpError(hr, error, "CoInitializeEx");
    }
    fCoInitialized = TRUE;

    hr = coreInitRequest(
		    dwFlags,
		    pwszUserName,
		    cbRequest,
		    pbRequest,
		    pwszAttributes,
		    pwszSerialNumber,
		    dwComContextIndex,
		    &OpRequest,
		    &prow,
		    &pwszDisposition,
		    pResult);
    _PrintIfError(hr, "coreInitRequest");

    pCAContext = NULL;
    if (S_OK == hr)
    {
	CSASSERT(NULL == pwszDisposition);	// error string only
	if (CR_IN_NEW != OpRequest)
	{
	    DWORD cb;
	    DWORD dw;
	    
	    cb = sizeof(dw);
	    hr = prow->GetProperty(
			g_wszPropRequestType,
			PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
			&cb,
			(BYTE *) &dw);
	    if (S_OK == hr)
	    {
		dwFlags |= (CR_IN_CRLS & dw);
	    }
	}
	hr = coreVerifyRequest(
			&prow,
			OpRequest,
			0 != (CR_IN_CRLS & dwFlags),
			pwszUserName,
			dwComContextIndex,
			&ReqId,
			&ExitEvent,
			&pwszDisposition,
			&pCAContext,
			pResult);		// CoTaskMem
	_PrintIfError(hr, "coreVerifyRequest");
    }
    else
    {
	WCHAR *pwszDisposition2 = CoreBuildDispositionString(
				    pwszDisposition,
				    NULL,	// pwszUserName
				    NULL,	// pwszDispositionDetail
				    NULL,	// pwszBy
				    hr,
				    FALSE);
	if (NULL != pwszDisposition2)
	{
	    if (NULL != pwszDisposition)
	    {
		LocalFree(pwszDisposition);
	    }
	    pwszDisposition = pwszDisposition2;
	}
    }

    if (NULL != pResult->pctbFullResponse)
    {
	BYTE const *pbCert = NULL;
	DWORD cbCert = 0;
	
	if (NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb)
	{
	    pbCert = pResult->pctbCert->pb;
	    cbCert = pResult->pctbCert->cb;
	}
	CSASSERT(NULL == pResult->pctbFullResponse->pb);
	hr2 = PKCSEncodeFullResponse(
			    prow,
			    pResult,
			    hr,
			    pwszDisposition,
			    pCAContext,
			    pbCert,		// pbCertLeaf
			    cbCert,		// cbCertLeaf
			    0 != (CR_IN_CRLS & dwFlags),
			    &pResult->pctbFullResponse->pb,	// CoTaskMem*
			    &pResult->pctbFullResponse->cb);
	_PrintIfError(hr2, "PKCSEncodeFullResponse");
	if (S_OK == hr &&
	    (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr2 || IsWhistler()))
	{
	    hr = hr2;
	}
    }

    hr2 = coreAuditRequestDisposition(
				prow,
				ReqId,
				pwszUserName,
				pwszAttributes,
				*pResult->pdwDisposition);
    _PrintIfError(hr2, "coreAuditRequestDisposition");
    if (S_OK == hr)
    {
	hr = hr2;
    }
    
    if (NULL != pwszDisposition && NULL != pResult->pctbDispositionMessage)
    {
	DWORD cbAlloc = (wcslen(pwszDisposition) + 1) * sizeof(WCHAR);
	BYTE *pbAlloc;

        pbAlloc = (BYTE *) CoTaskMemAlloc(cbAlloc);
        if (NULL != pbAlloc)
        {
	    CopyMemory(pbAlloc, pwszDisposition, cbAlloc);
	    pResult->pctbDispositionMessage->pb = pbAlloc;
	    pResult->pctbDispositionMessage->cb = cbAlloc;
        }
    }
    if (NULL != prow)
    {
	if (pResult->fKeyArchived && (KRAF_SAVEBADREQUESTKEY & g_KRAFlags))
	{
	    BOOL fSave = FALSE;

	    switch (*pResult->pdwDisposition)
	    {
		case CR_DISP_INCOMPLETE:
		case CR_DISP_ERROR:
		case CR_DISP_DENIED:
		    fSave = TRUE;
		    break;

		default:
		    if (S_OK != hr)
		    {
			fSave = TRUE;
		    }
		    break;
	    }
	    if (fSave)
	    {
		hr2 = prow->SetProperty(
			g_wszPropRequestRawRequest,
			PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
			cbRequest,
			pbRequest);
		_PrintIfError(hr2, "SetProperty(request)");
		if (S_OK == hr)
		{
		    hr = hr2;
		}
	    }
	}

        hr2 = prow->CommitTransaction(TRUE);
        _PrintIfError(hr2, "CommitTransaction");
	fCommitted = S_OK == hr2;
        if (S_OK == hr)
        {
            hr = hr2;
        }
    }
    
error:
    // If the request exists, clean up the DB
    
    if (NULL != prow)
    {
	if (S_OK != hr && !fCommitted)
        {
            hr2 = prow->CommitTransaction(FALSE);
            _PrintIfError(hr2, "CommitTransaction");
        }
        prow->Release();
    }
    if (EXITEVENT_INVALID != ExitEvent)
    {
        CSASSERT(fCoInitialized);
        ExitNotify(ExitEvent, ReqId, dwComContextIndex);
    }
    if (fCoInitialized)
    {
        CoUninitialize();
    }
    if (S_OK != hr)
    {
        WCHAR const *pwszMsg;

        pwszMsg = myGetErrorMessageText(hr, TRUE);
        if (NULL != pwszMsg)
        {
            CONSOLEPRINT1((DBG_SS_CERTSRV, "%ws\n", pwszMsg));
            LocalFree(const_cast<WCHAR *>(pwszMsg));
        }
    }
    if (NULL != pwszDisposition)
    {
        LocalFree(pwszDisposition);
    }

    // Hide the failed HRESULT in the returned Disposition.
    // This allows the encoded Full response and disposition message to be
    // returned via DCOM or RPC.  Returning S_OK != hr defeats this mechanism.

    if (FAILED(hr) &&
	(CR_DISP_ERROR == *pResult->pdwDisposition ||
	 CR_DISP_DENIED == *pResult->pdwDisposition))
    {
	*pResult->pdwDisposition = hr;
	hr = S_OK;
    }
    return(hr);
}


HRESULT
CoreValidateRequestId(
    IN ICertDBRow *prow,
    IN DWORD ExpectedDisposition)
{
    HRESULT hr;
    DWORD cbProp;
    DWORD Disposition;
    
    cbProp = sizeof(Disposition);
    hr = prow->GetProperty(
        g_wszPropRequestDisposition,
        PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
        &cbProp,
        (BYTE *) &Disposition);
    _PrintIfError(hr, "GetProperty");
    if (S_OK != hr || sizeof(Disposition) != cbProp)
    {
        hr = CERTSRV_E_NO_REQUEST;
    }
    else if (Disposition != ExpectedDisposition)
    {
        hr = CERTSRV_E_BAD_REQUESTSTATUS;
    }
    return(hr);
}


HRESULT
SetCAObjectFlags(
    IN DWORD dwFlags)
{
    HRESULT hr = S_OK;
    HCAINFO hCAInfo = NULL;
    DWORD dwCAFlags;

    hr = CAFindByName(
		g_wszSanitizedName,
		NULL,
		CA_FIND_LOCAL_SYSTEM | CA_FIND_INCLUDE_UNTRUSTED,
		&hCAInfo);
    _JumpIfError(hr, error, "CAFindByName");

    hr = CAGetCAFlags(hCAInfo, &dwCAFlags);
    _JumpIfError(hr, error, "CAGetCAFlags");

    dwCAFlags |= dwFlags;

    hr = CASetCAFlags(hCAInfo, dwCAFlags);
    _JumpIfError(hr, error, "CASetCAFlags");

    hr = CAUpdateCA(hCAInfo);
    _JumpIfError(hr, error, "CAUpdateCA");

error:
    if (NULL != hCAInfo)
    {
        CACloseCA(hCAInfo);
    }
    return(hr);
}