You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
15538 lines
368 KiB
15538 lines
368 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: pkcs.cpp
|
|
//
|
|
// Contents: Cert Server Extension interfaces -- PKCS implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <pch.cpp>
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
#include <esent.h>
|
|
|
|
#include "dbtable.h"
|
|
#include "resource.h"
|
|
#include "cscom.h"
|
|
#include "csprop.h"
|
|
#include "elog.h"
|
|
#include "certlog.h"
|
|
#include "csdisp.h"
|
|
#include "cscsp.h"
|
|
#include <certca.h>
|
|
#include <esent.h>
|
|
|
|
#include <winldap.h>
|
|
#include "csldap.h"
|
|
#include "cainfop.h"
|
|
|
|
#define __dwFILE__ __dwFILE_CERTSRV_PKCS_CPP__
|
|
|
|
|
|
CSURLTEMPLATE *g_paRevURL = NULL;
|
|
DWORD g_caRevURL = 0;
|
|
|
|
CSURLTEMPLATE *g_paCACertURL = NULL;
|
|
DWORD g_caCACertURL = 0;
|
|
|
|
BSTR g_strDomainDN = NULL;
|
|
BSTR g_strConfigDN = NULL;
|
|
|
|
WCHAR *g_pwszKRAPublishURL = NULL;
|
|
WCHAR *g_pwszAIACrossCertPublishURL = NULL;
|
|
WCHAR *g_pwszRootTrustCrossCertPublishURL = NULL;
|
|
|
|
|
|
// Note on Renewal and Key reuse:
|
|
//
|
|
// Cert Indexes, Key Indexes and CRL Name Indexes are all zero based.
|
|
//
|
|
// One CRL is issued by this CA for each unique key. Each CRL covers all of
|
|
// the certs issued by this CA for one key, even though the key may have been
|
|
// used by multiple renewal certs.
|
|
//
|
|
// The database IssuerNameID PROPTYPE_LONG column holds the Key Index in the
|
|
// top 16 bits and the Cert Index in the bottom 16 bits. This allows a pair of
|
|
// query restrictions to reduce the result row set to those revoked certs
|
|
// that will be placed into a single CRL.
|
|
//
|
|
// When Cert File, Key Container, CRL File or DS Object name templates include
|
|
// an Index Suffix, an empty string suffix, "", is used when the index is zero.
|
|
// Otherwise the Suffix is "(%u)", where the index itself is passed to wsprintf
|
|
// to construct the %u field.
|
|
//
|
|
// Cert Indexes increment each time the CA cert is renewed. Because 16 bit
|
|
// Cert Indexes are stored in the database, a CA may have up to 64k certs,
|
|
// and be renewed a maximum of 64k-1 times.
|
|
//
|
|
// The Cert File Name Suffix is built from the Cert Index.
|
|
//
|
|
// Key Indexes: The original installed CA cert uses a Key Index of 0.
|
|
// If a renewal cert uses the same key as any previous cert used by this CA,
|
|
// the Key Index for the new CA cert is taken from the previous CA cert.
|
|
// If a renewal cert uses a new Key, the Cert Index is used for the Key Index.
|
|
// The primary reason sequential Key Indexes are not used for new keys is that
|
|
// too much context information is required to determine the next Key Index --
|
|
// which is particularly difficult to obtain when performing PFX restore.
|
|
//
|
|
// The Key Container Name Suffix is built from the Key Index.
|
|
//
|
|
//
|
|
// CRL Indexes: same as Key Index.
|
|
// CRL File Name Suffix: same as Key Container Name Suffix.
|
|
//
|
|
// Example: Cert CertName Key KeyName CRL CRLName
|
|
// Index Suffix Index Suffix Index Suffix
|
|
// Original Install 0 "" 0 "" 0 ""
|
|
//
|
|
// Renew, new Key 1 "(1)" 1 "(1)" 1 "(1)"
|
|
// *Renew, reuse Key 2 "(2)" 1 "(1)" 1 "(1)"
|
|
// *Renew, reuse Key 3 "(3)" 1 "(1)" 1 "(1)"
|
|
//
|
|
// Renew, new Key 4 "(4)" 4 "(4)" 4 "(4)"
|
|
// *Renew, reuse Key 5 "(5)" 4 "(4)" 4 "(4)"
|
|
//
|
|
// Renew, new Key 6 "(6)" 6 "(6)" 6 "(6)"
|
|
// *Renew, reuse Key 7 "(7)" 6 "(6)" 6 "(6)"
|
|
//
|
|
//
|
|
// CCertRequest::GetCACertificate can be used to fetch Certs and CRLs by Index.
|
|
// This API always accepts a Cert Index.
|
|
//
|
|
// When fetching a certificate: If the Cert Index is valid, the appropriate
|
|
// certificate or chain is returned, even if it is expired or revoked.
|
|
//
|
|
// When fetching a CRL: If the Cert Index is valid AND if the Cert Index
|
|
// MATCHES the Key Index for the indexed Cert, the appropriate CRL is returned.
|
|
// This means that an error will be returned when requesting CRLs associated
|
|
// with entries in the above table that reused keys (marked with an asterisk
|
|
// in the first column). The nearest previous unmarked entry's CRL covers
|
|
// revocations for the marked entries.
|
|
//
|
|
//
|
|
// CCertServer{Policy,Exit}::GetCertificateProperty can be used to fetch
|
|
// information about Certs and CRLs. This API allows an optional numeric
|
|
// suffix on the property name, as in "RawCRL.3". The suffix is always
|
|
// interpreted as a Cert Index.
|
|
//
|
|
// wszPROPCERTCOUNT: Returns total CA Cert count, including expired and
|
|
// revoked certs. No numeric Cert Index suffix is allowed.
|
|
//
|
|
// wszPROPRAWCACERTIFICATE: Returns the Cert for the passed Cert Index.
|
|
// Returns the Cert for the most recent Cert Index if no Cert Index is
|
|
// specified. Expired and revoked certs are still retrievable.
|
|
//
|
|
// wszPROPCERTSTATE: Returns the Cert State for the passed Cert Index.
|
|
// Returns the Cert State for the most recent Cert Index if no Cert Index is
|
|
// specified.
|
|
// Values for wszPROPCERTSTATE (see certadm.h):
|
|
// CA_DISP_REVOKED // This Cert has been revoked.
|
|
// CA_DISP_VALID // This Cert is still valid
|
|
// CA_DISP_INVALID // This Cert has expired.
|
|
// CA_DISP_ERROR // Cert unavailable (placehholder in registry?)
|
|
//
|
|
// wszPROPCERTSUFFIX: Returns the Cert FileName Suffix for the passed Cert
|
|
// Index. Returns the Cert FileName Suffix for the most recent Cert Index if
|
|
// no Cert Index is specified.
|
|
//
|
|
// wszPROPRAWCRL: Returns the CRL for the passed Cert Index. As with
|
|
// CCertRequest::GetCACertificate, it is an error to fetch a CRL for a Cert
|
|
// that reused keys. In the above table, only "RawCRL.0", "RawCRL.1",
|
|
// "RawCRL.4", "RawCRL.6" & "RawCRL" are allowed. "RawCRL" will fetch the most
|
|
// recent CRL. Use the wszPROPCRLSTATE with a numeric Cert Index suffix to
|
|
// determine which CRLs are valid to fetch. CA_DISP_ERROR indicates the
|
|
// CRL cannot be fetched. CA_DISP_REVOKED and CA_DISP_INVALID CRLs are still
|
|
// retrievable via this method call.
|
|
//
|
|
// All of the other CRL-related property fetches are supported for all valid
|
|
// Cert Index values:
|
|
//
|
|
// wszPROPCRLINDEX: Returns the CRL Index value for the passed Cert Index.
|
|
// Returns the CRL Index value for the most recent Cert Index if no Cert Index
|
|
// is specified.
|
|
//
|
|
// wszPROPCRLSTATE: Returns the CRL State for the passed Cert Index.
|
|
// Returns the CRL State for the most recent Cert Index if no Cert Index is
|
|
// specified.
|
|
// Values for wszPROPCRLSTATE (see certadm.h):
|
|
// CA_DISP_REVOKED // All unexpired certs using this Cert's CRL have been
|
|
// // revoked.
|
|
// CA_DISP_VALID // This Cert is still publishing CRLs as needed.
|
|
// CA_DISP_INVALID // All certs using this Cert's CRL are expired.
|
|
// CA_DISP_ERROR // This Cert's CRL is managed by another Cert.
|
|
//
|
|
// wszPROPCRLSUFFIX: Returns the CRL FileName Suffix for the passed Cert Index.
|
|
// Returns the CRL FileName Suffix for the most recent Cert Index if no Cert
|
|
// Index is specified.
|
|
|
|
|
|
CACTX *g_aCAContext; // allocated array of CACTXs
|
|
CACTX *g_pCAContextCurrent; // current CACTX is last g_aCAContext element
|
|
CACROSSCTX *g_aCACrossForward; // allocated array of CACROSSCTXs; root CA only
|
|
CACROSSCTX *g_aCACrossBackward; // allocated array of CACROSSCTXs; root CA only
|
|
|
|
typedef struct _KRACTX
|
|
{
|
|
DWORD Flags;
|
|
HRESULT hrVerifyStatus;
|
|
CERT_CONTEXT const *pccKRA;
|
|
BSTR strKRAHash;
|
|
} KRACTX;
|
|
|
|
KRACTX *g_aKRAContext;
|
|
|
|
DWORD g_cKRACertsRoundRobin = 0;
|
|
|
|
DWORD g_cCAKeys; // Total number of unique CA keys managed by this CA
|
|
DWORD g_cCACerts; // Total number of CA certs managed by this CA
|
|
DWORD g_cKRACerts; // Total number of KRA certs loaded by this CA
|
|
|
|
BOOL g_fcritsecCAXchg = FALSE;
|
|
CRITICAL_SECTION g_critsecCAXchg;
|
|
|
|
CAXCHGCTX *g_aCAXchgContext; // allocated array of CAXCHGCTXs
|
|
CAXCHGCTX *g_pCAXchgContextCurrent; // current CAXCHGCTX is last element
|
|
DWORD g_cCAXchgCerts; // number of CA Xchg certs managed by this CA
|
|
HCERTSTORE g_hStoreCAXchg = NULL;
|
|
DWORD g_dwXchgProvType;
|
|
WCHAR *g_pwszXchgProvName = NULL;
|
|
ALG_ID g_XchgidAlg;
|
|
BOOL g_fXchgMachineKeyset;
|
|
DWORD g_dwXchgKeySize;
|
|
DWORD g_dwVerifyCertFlags;
|
|
|
|
LONG g_lValidityPeriodCount = dwVALIDITYPERIODCOUNTDEFAULT_STANDALONE;
|
|
enum ENUM_PERIOD g_enumValidityPeriod = dwVALIDITYPERIODENUMDEFAULT;
|
|
|
|
enum ENUM_PERIOD g_enumCAXchgValidityPeriod = dwCAXCHGVALIDITYPERIODENUMDEFAULT;
|
|
LONG g_lCAXchgValidityPeriodCount = dwCAXCHGVALIDITYPERIODCOUNTDEFAULT;
|
|
|
|
enum ENUM_PERIOD g_enumCAXchgOverlapPeriod = dwCAXCHGOVERLAPPERIODENUMDEFAULT;
|
|
LONG g_lCAXchgOverlapPeriodCount = dwCAXCHGOVERLAPPERIODCOUNTDEFAULT;
|
|
|
|
typedef enum {
|
|
ST_COUNTRY = 0,
|
|
ST_ORGANIZATION,
|
|
ST_ORGANIZATIONALUNIT,
|
|
ST_COMMONNAME,
|
|
ST_LOCALITY,
|
|
ST_STATEORPROVINCE,
|
|
ST_TITLE,
|
|
ST_GIVENNAME,
|
|
ST_INITIALS,
|
|
ST_SURNAME,
|
|
ST_DOMAINCOMPONENT,
|
|
ST_EMAIL,
|
|
ST_STREETADDRESS,
|
|
ST_UNSTRUCTUREDNAME,
|
|
ST_UNSTRUCTUREDADDRESS,
|
|
ST_DEVICESERIALNUMBER,
|
|
ST_NULL
|
|
};
|
|
|
|
typedef struct _SUBJECTTABLE
|
|
{
|
|
WCHAR const *pwszPropName;
|
|
CHAR const *pszObjId;
|
|
WCHAR const * const *apwszAttributeName;
|
|
DWORD cchMax;
|
|
DWORD dwValueType;
|
|
DWORD dwSubjectTableIndex;
|
|
DWORD dwSubjectTemplateIndex;
|
|
} SUBJECTTABLE;
|
|
|
|
|
|
WCHAR const *apwszAttrCountry[] = {
|
|
wszATTRCOUNTRY1,
|
|
wszATTRCOUNTRY2,
|
|
TEXT(szOID_COUNTRY_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrOrg[] = {
|
|
wszATTRORG1,
|
|
wszATTRORG2,
|
|
wszATTRORG3,
|
|
TEXT(szOID_ORGANIZATION_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrOrgUnit[] = {
|
|
wszATTRORGUNIT1,
|
|
wszATTRORGUNIT2,
|
|
wszATTRORGUNIT3,
|
|
wszATTRORGUNIT4,
|
|
TEXT(szOID_ORGANIZATIONAL_UNIT_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrCommonName[] = {
|
|
wszATTRCOMMONNAME1,
|
|
wszATTRCOMMONNAME2,
|
|
TEXT(szOID_COMMON_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrLocality[] = {
|
|
wszATTRLOCALITY1,
|
|
wszATTRLOCALITY2,
|
|
TEXT(szOID_LOCALITY_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrState[] = {
|
|
wszATTRSTATE1,
|
|
wszATTRSTATE2,
|
|
wszATTRSTATE3,
|
|
TEXT(szOID_STATE_OR_PROVINCE_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrTitle[] = {
|
|
wszATTRTITLE1,
|
|
wszATTRTITLE2,
|
|
TEXT(szOID_TITLE),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrGivenName[] = {
|
|
wszATTRGIVENNAME1,
|
|
wszATTRGIVENNAME2,
|
|
TEXT(szOID_GIVEN_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrInitials[] = {
|
|
wszATTRINITIALS1,
|
|
wszATTRINITIALS2,
|
|
TEXT(szOID_INITIALS),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrSurName[] = {
|
|
wszATTRSURNAME1,
|
|
wszATTRSURNAME2,
|
|
TEXT(szOID_SUR_NAME),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrDomComp[] = {
|
|
wszATTRDOMAINCOMPONENT1,
|
|
wszATTRDOMAINCOMPONENT2,
|
|
TEXT(szOID_DOMAIN_COMPONENT),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrEMail[] = {
|
|
wszATTREMAIL1,
|
|
wszATTREMAIL2,
|
|
TEXT(szOID_RSA_emailAddr),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrStreetAddr[] = {
|
|
wszATTRSTREETADDRESS1,
|
|
wszATTRSTREETADDRESS2,
|
|
TEXT(szOID_STREET_ADDRESS),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrUnstructName[] = {
|
|
wszATTRUNSTRUCTUREDNAME1,
|
|
TEXT(szOID_RSA_unstructName),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrUnstructAddr[] = {
|
|
wszATTRUNSTRUCTUREDADDRESS1,
|
|
TEXT(szOID_RSA_unstructAddr),
|
|
NULL
|
|
};
|
|
|
|
WCHAR const *apwszAttrDeviceSerialNumber[] = {
|
|
wszATTRDEVICESERIALNUMBER1,
|
|
TEXT(szOID_DEVICE_SERIAL_NUMBER),
|
|
NULL
|
|
};
|
|
|
|
|
|
SUBJECTTABLE pkcs_subject[] =
|
|
{
|
|
{
|
|
// "Country",
|
|
g_wszPropSubjectCountry,
|
|
szOID_COUNTRY_NAME,
|
|
apwszAttrCountry,
|
|
cchCOUNTRYNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_COUNTRY,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "Organization",
|
|
g_wszPropSubjectOrganization,
|
|
szOID_ORGANIZATION_NAME,
|
|
apwszAttrOrg,
|
|
cchORGANIZATIONNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_ORGANIZATION,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "OrganizationalUnit",
|
|
g_wszPropSubjectOrgUnit,
|
|
szOID_ORGANIZATIONAL_UNIT_NAME,
|
|
apwszAttrOrgUnit,
|
|
cchORGANIZATIONALUNITNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_ORGANIZATIONALUNIT,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "CommonName",
|
|
g_wszPropSubjectCommonName,
|
|
szOID_COMMON_NAME,
|
|
apwszAttrCommonName,
|
|
cchCOMMONNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_COMMONNAME,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "Locality",
|
|
g_wszPropSubjectLocality,
|
|
szOID_LOCALITY_NAME,
|
|
apwszAttrLocality,
|
|
cchLOCALITYMANAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_LOCALITY,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "StateOrProvince",
|
|
g_wszPropSubjectState,
|
|
szOID_STATE_OR_PROVINCE_NAME,
|
|
apwszAttrState,
|
|
cchSTATEORPROVINCENAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_STATEORPROVINCE,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "Title",
|
|
g_wszPropSubjectTitle,
|
|
szOID_TITLE,
|
|
apwszAttrTitle,
|
|
cchTITLEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_TITLE,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "GivenName",
|
|
g_wszPropSubjectGivenName,
|
|
szOID_GIVEN_NAME,
|
|
apwszAttrGivenName,
|
|
cchGIVENNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_GIVENNAME,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "Initials",
|
|
g_wszPropSubjectInitials,
|
|
szOID_INITIALS,
|
|
apwszAttrInitials,
|
|
cchINITIALSMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_INITIALS,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "SurName",
|
|
g_wszPropSubjectSurName,
|
|
szOID_SUR_NAME,
|
|
apwszAttrSurName,
|
|
cchSURNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_SURNAME,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "DomainComponent",
|
|
g_wszPropSubjectDomainComponent,
|
|
szOID_DOMAIN_COMPONENT,
|
|
apwszAttrDomComp,
|
|
cchDOMAINCOMPONENTMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_DOMAINCOMPONENT,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "EMail",
|
|
g_wszPropSubjectEMail,
|
|
szOID_RSA_emailAddr,
|
|
apwszAttrEMail,
|
|
cchEMAILMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_EMAIL,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "StreetAddress",
|
|
g_wszPropSubjectStreetAddress,
|
|
szOID_STREET_ADDRESS,
|
|
apwszAttrStreetAddr,
|
|
cchSTREETADDRESSMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_STREETADDRESS,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "UnstructuredName",
|
|
g_wszPropSubjectUnstructuredName,
|
|
szOID_RSA_unstructName,
|
|
apwszAttrUnstructName,
|
|
cchUNSTRUCTUREDNAMEMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_UNSTRUCTUREDNAME,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "UnstructuredAddress",
|
|
g_wszPropSubjectUnstructuredAddress,
|
|
szOID_RSA_unstructAddr,
|
|
apwszAttrUnstructAddr,
|
|
cchUNSTRUCTUREDADDRESSMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_UNSTRUCTUREDADDRESS,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
// "DeviceSerialNumber",
|
|
g_wszPropSubjectDeviceSerialNumber,
|
|
szOID_DEVICE_SERIAL_NUMBER,
|
|
apwszAttrDeviceSerialNumber,
|
|
cchDEVICESERIALNUMBERMAX,
|
|
CERT_RDN_PRINTABLE_STRING,
|
|
ST_DEVICESERIALNUMBER,
|
|
MAXDWORD,
|
|
},
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
ST_NULL,
|
|
MAXDWORD,
|
|
},
|
|
};
|
|
|
|
#define CSUBJECTTABLE (sizeof(pkcs_subject) / sizeof(pkcs_subject[0]))
|
|
|
|
SUBJECTTABLE const *pkcs_apSubject[CSUBJECTTABLE];
|
|
SUBJECTTABLE const **pkcs_ppSubjectLast;
|
|
BOOL pkcsfSubjectTemplate = FALSE;
|
|
|
|
WCHAR const g_wszCNXchgSuffix[] = wszCNXCHGSUFFIX;
|
|
|
|
|
|
VOID
|
|
pkcsSetDistinguishedName(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD dwTable,
|
|
IN CERT_NAME_BLOB const *pSubject)
|
|
{
|
|
DWORD cwc;
|
|
WCHAR awcName[CCH_DBMAXTEXT_DN + 1];
|
|
HRESULT hr;
|
|
|
|
CSASSERT(PROPTABLE_REQUEST == dwTable || PROPTABLE_CERTIFICATE == dwTable);
|
|
|
|
cwc = CertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
const_cast<CERT_NAME_BLOB *>(pSubject),
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
awcName,
|
|
ARRAYSIZE(awcName));
|
|
if (0 != cwc && L'\0' != awcName[0])
|
|
{
|
|
#if DBG_CERTSRV
|
|
DWORD ReqId;
|
|
|
|
prow->GetRowId(&ReqId);
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"%ws DN(%u): '%ws'\n",
|
|
PROPTABLE_REQUEST == dwTable? L"Request" : L"Certificate",
|
|
ReqId,
|
|
awcName));
|
|
#endif
|
|
if (wcslen(awcName) == ARRAYSIZE(awcName) - 1)
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"pkcsSetDistinguishedName: possible DN truncation: %u chars: '%ws'\n",
|
|
ARRAYSIZE(awcName) - 1,
|
|
awcName));
|
|
}
|
|
hr = prow->SetProperty(
|
|
g_wszPropSubjectDistinguishedName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | dwTable,
|
|
MAXDWORD,
|
|
(BYTE const *) awcName);
|
|
_PrintIfError(hr, "SetProperty(DN)");
|
|
}
|
|
}
|
|
|
|
|
|
WCHAR const *
|
|
PKCSMapAttributeName(
|
|
OPTIONAL IN WCHAR const *pwszAttributeName,
|
|
OPTIONAL IN CHAR const *pszObjId,
|
|
OUT DWORD *pdwIndex,
|
|
OUT DWORD *pcchMax)
|
|
{
|
|
SUBJECTTABLE const *pSubjectTable;
|
|
WCHAR const *pwszPropName = NULL;
|
|
|
|
for (pSubjectTable = pkcs_subject; ; pSubjectTable++)
|
|
{
|
|
WCHAR const * const *ppwsz;
|
|
|
|
if (NULL == pSubjectTable->pwszPropName)
|
|
{
|
|
goto error;
|
|
}
|
|
if (NULL != pwszAttributeName)
|
|
{
|
|
for (ppwsz = pSubjectTable->apwszAttributeName;
|
|
NULL != *ppwsz;
|
|
ppwsz++)
|
|
{
|
|
if (0 == mylstrcmpiS(pwszAttributeName, *ppwsz))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL != *ppwsz ||
|
|
0 == mylstrcmpiS(pwszAttributeName, pSubjectTable->pwszPropName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL != pszObjId &&
|
|
0 == strcmp(pszObjId, pSubjectTable->pszObjId))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
CSASSERT(NULL != pSubjectTable->pwszPropName);
|
|
pwszPropName = pSubjectTable->pwszPropName;
|
|
*pdwIndex = pSubjectTable->dwSubjectTableIndex;
|
|
*pcchMax = pSubjectTable->cchMax;
|
|
|
|
error:
|
|
return(pwszPropName);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsFindCAContext(
|
|
IN DWORD iCert, // MAXDWORD -> use current
|
|
IN DWORD iKey, // MAXDWORD -> use current
|
|
OUT CACTX **ppCAContext)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
DWORD i;
|
|
CACTX *pCAContext;
|
|
|
|
*ppCAContext = NULL;
|
|
|
|
// Lookup is either by cert index OR by key index, but not both or neither
|
|
|
|
CSASSERT((MAXDWORD == iCert) ^ (MAXDWORD == iKey));
|
|
|
|
if (MAXDWORD != iCert)
|
|
{
|
|
if ((~_16BITMASK & iCert) || iCert >= g_cCACerts)
|
|
{
|
|
_JumpError(hr, error, "bad cert index");
|
|
}
|
|
*ppCAContext = &g_aCAContext[iCert];
|
|
CSASSERT(iCert == (*ppCAContext)->iCert);
|
|
}
|
|
else
|
|
{
|
|
CSASSERT(MAXDWORD != iKey);
|
|
|
|
if ((~_16BITMASK & iKey) || iKey >= g_cCAKeys)
|
|
{
|
|
_JumpError(hr, error, "bad key index");
|
|
}
|
|
for (i = g_cCACerts; ; i--)
|
|
{
|
|
if (0 == i)
|
|
{
|
|
_JumpError(hr, error, "key index not found");
|
|
}
|
|
pCAContext = &g_aCAContext[i - 1];
|
|
if (iKey == pCAContext->iKey)
|
|
{
|
|
*ppCAContext = pCAContext;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
hr = S_OK; // found it!
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Returns Cert Index in *piCert on success.
|
|
//
|
|
// returned in *piCert:
|
|
// If iCert input value is not MAXDWORD, validate & return iCert.
|
|
// If iCert input value is MAXDWORD, return the most current Cert Index.
|
|
|
|
HRESULT
|
|
PKCSMapCertIndex(
|
|
IN DWORD iCert,
|
|
OUT DWORD *piCert,
|
|
OUT DWORD *pState)
|
|
{
|
|
HRESULT hr;
|
|
CACTX *pCAContext;
|
|
DBGCODE(DWORD iCertSave = iCert);
|
|
|
|
*pState = CA_DISP_ERROR;
|
|
if (MAXDWORD == iCert)
|
|
{
|
|
iCert = g_cCACerts - 1;
|
|
}
|
|
if (iCert >= g_cCACerts)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad CertIndex");
|
|
}
|
|
pCAContext = &g_aCAContext[iCert];
|
|
hr = PKCSVerifyCAState(pCAContext);
|
|
_PrintIfError(hr, "PKCSVerifyCAState");
|
|
|
|
*pState = CA_DISP_VALID;
|
|
if (CTXF_CERTMISSING & pCAContext->Flags)
|
|
{
|
|
*pState = CA_DISP_ERROR;
|
|
}
|
|
else
|
|
if (CTXF_REVOKED & pCAContext->Flags)
|
|
{
|
|
*pState = CA_DISP_REVOKED;
|
|
}
|
|
else
|
|
if (CTXF_EXPIRED & pCAContext->Flags)
|
|
{
|
|
*pState = CA_DISP_INVALID;
|
|
}
|
|
*piCert = iCert;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"PKCSMapCertIndex(%u) --> %u, s=%u, hr=%x\n",
|
|
iCertSave,
|
|
*piCert,
|
|
*pState,
|
|
hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsVerifyCACrossState(
|
|
IN OUT CACROSSCTX *pCACross)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (0 == pCACross->Flags && NULL != pCACross->pccCACross)
|
|
{
|
|
hr = myVerifyCertContext(
|
|
pCACross->pccCACross, // pCert
|
|
0, // dwFlags
|
|
0, // cUsageOids
|
|
NULL, // apszUsageOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
NULL, // hAdditionalStore
|
|
NULL); // ppwszMissingIssuer
|
|
pCACross->hrVerifyStatus = hr;
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError2(hr, "myVerifyCertContext", CRYPT_E_REVOCATION_OFFLINE);
|
|
if (CERT_E_UNTRUSTEDROOT == hr || TRUST_E_CERT_SIGNATURE == hr)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
if (CERT_E_EXPIRED == hr)
|
|
{
|
|
pCACross->Flags |= CTXF_EXPIRED;
|
|
}
|
|
else if (CRYPT_E_REVOKED == hr || CERT_E_REVOKED == hr)
|
|
{
|
|
pCACross->Flags |= CTXF_REVOKED;
|
|
}
|
|
else if (CRYPT_E_REVOCATION_OFFLINE == hr)
|
|
{
|
|
HRESULT hr2;
|
|
DWORD dwState;
|
|
|
|
hr2 = GetSetupStatus(NULL, &dwState);
|
|
if ((S_OK == hr2 && (SETUP_CREATEDB_FLAG & dwState)) ||
|
|
CERTLOG_WARNING > g_dwLogLevel)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else if (CRYPT_E_NO_REVOCATION_CHECK == hr)
|
|
{
|
|
if (CERTLOG_VERBOSE > g_dwLogLevel)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "myVerifyCertContext");
|
|
}
|
|
}
|
|
error:
|
|
;
|
|
}
|
|
|
|
|
|
// Returns Cert Index in *piCert on success.
|
|
//
|
|
// returned in *piCert:
|
|
// If iCert input value is not MAXDWORD, validate & return iCert.
|
|
// If iCert input value is MAXDWORD, return the most current Cert Index.
|
|
|
|
HRESULT
|
|
pkcsMapCrossCertIndex(
|
|
IN BOOL fForward,
|
|
IN DWORD iCert,
|
|
OUT DWORD *piCert,
|
|
OUT DWORD *pState)
|
|
{
|
|
HRESULT hr;
|
|
CACROSSCTX *pCACross;
|
|
DBGCODE(DWORD iCertSave = iCert);
|
|
|
|
*pState = CA_DISP_ERROR;
|
|
if (MAXDWORD == iCert)
|
|
{
|
|
iCert = g_cCACerts - 1;
|
|
}
|
|
if (iCert >= g_cCACerts)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad CertIndex");
|
|
}
|
|
pCACross = fForward? g_aCACrossForward : g_aCACrossBackward;
|
|
if (NULL != pCACross)
|
|
{
|
|
pCACross += iCert;
|
|
|
|
if (NULL != pCACross->pccCACross)
|
|
{
|
|
pkcsVerifyCACrossState(pCACross);
|
|
|
|
*pState = CA_DISP_VALID;
|
|
if (CTXF_CERTMISSING & pCACross->Flags)
|
|
{
|
|
*pState = CA_DISP_ERROR;
|
|
}
|
|
else
|
|
if (CTXF_REVOKED & pCACross->Flags)
|
|
{
|
|
*pState = CA_DISP_REVOKED;
|
|
}
|
|
else
|
|
if (CTXF_EXPIRED & pCACross->Flags)
|
|
{
|
|
*pState = CA_DISP_INVALID;
|
|
}
|
|
}
|
|
}
|
|
*piCert = iCert;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"PKCSMapCrossCertIndex(%u) --> %u, s=%u, hr=%x\n",
|
|
iCertSave,
|
|
*piCert,
|
|
*pState,
|
|
hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Returns Cert Index in *piCert and CRL Index in *piCRL on success.
|
|
//
|
|
// returned in *piCert:
|
|
// If iCert input value is not MAXDWORD, validate iCert. Look up the newest
|
|
// Cert Index that uses the same key as the passed iCert.
|
|
// If iCert input value is MAXDWORD, return the most current Cert Index.
|
|
//
|
|
// returned in *piCRL:
|
|
// CRL index (same as Key Index)
|
|
//
|
|
|
|
HRESULT
|
|
PKCSMapCRLIndex(
|
|
IN DWORD iCert,
|
|
OUT DWORD *piCert, // returns newest iCert w/matching iKey for passed iCert
|
|
OUT DWORD *piCRL,
|
|
OUT DWORD *pState)
|
|
{
|
|
HRESULT hr;
|
|
CACTX *pCAContext;
|
|
CACTX *pCAContextNewest;
|
|
DWORD i;
|
|
DBGCODE(DWORD iCertSave = iCert);
|
|
|
|
hr = PKCSMapCertIndex(iCert, piCert, pState);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
// Now we know *piCert is a valid Cert Index:
|
|
|
|
pCAContext = &g_aCAContext[*piCert];
|
|
*piCRL = pCAContext->iKey;
|
|
|
|
// find the newest iCert with matching iKey
|
|
|
|
for (i = *piCert + 1; i < g_cCACerts; i++)
|
|
{
|
|
if (*piCRL != g_aCAContext[i].iKey)
|
|
{
|
|
break;
|
|
}
|
|
*piCert = i;
|
|
}
|
|
pCAContextNewest = &g_aCAContext[*piCert];
|
|
|
|
if (CTXF_CRLZOMBIE & pCAContext->Flags)
|
|
{
|
|
*pState = CA_DISP_VALID;
|
|
}
|
|
else
|
|
if (pCAContext->iCert != pCAContext->iKey)
|
|
{
|
|
*pState = CA_DISP_ERROR;
|
|
}
|
|
else
|
|
if ((CTXF_EXPIRED & pCAContext->Flags) &&
|
|
0 == (CTXF_SKIPCRL & pCAContextNewest->Flags))
|
|
{
|
|
*pState = CA_DISP_VALID;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"PKCSMapCRLIndex(%u) --> %u, iCRL=%u, s=%u, hr=%x\n",
|
|
iCertSave,
|
|
*piCert,
|
|
*piCRL,
|
|
*pState,
|
|
hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCACertStatusCode(
|
|
IN DWORD iCert,
|
|
OUT HRESULT *phrCAStatusCode)
|
|
{
|
|
HRESULT hr;
|
|
DWORD State;
|
|
|
|
*phrCAStatusCode = E_FAIL;
|
|
|
|
hr = PKCSMapCertIndex(iCert, &iCert, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
*phrCAStatusCode = g_aCAContext[iCert].hrVerifyStatus;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCAState(
|
|
IN LONG PropId, // CR_PROP_*
|
|
OUT BYTE *pb)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
DWORD State;
|
|
|
|
switch (PropId)
|
|
{
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
|
|
case CR_PROP_CAFORWARDCROSSCERTSTATE:
|
|
case CR_PROP_CABACKWARDCROSSCERTSTATE:
|
|
hr = pkcsMapCrossCertIndex(
|
|
CR_PROP_CAFORWARDCROSSCERTSTATE == PropId,
|
|
i,
|
|
&iCert,
|
|
&State);
|
|
_JumpIfError(hr, error, "pkcsMapCrossCertIndex");
|
|
|
|
break;
|
|
|
|
case CR_PROP_CACERTSTATE:
|
|
case CR_PROP_CACERTVERSION:
|
|
hr = PKCSMapCertIndex(i, &iCert, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
break;
|
|
|
|
case CR_PROP_CRLSTATE:
|
|
hr = PKCSMapCRLIndex(i, &iCert, &iCRL, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCRLIndex");
|
|
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "PropId");
|
|
|
|
break;
|
|
}
|
|
CSASSERT(0 == (~0xff & State));
|
|
*pb++ = (BYTE) State;
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCAVersion(
|
|
OUT DWORD *pdw)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
DWORD State;
|
|
DWORD iCert;
|
|
|
|
hr = PKCSMapCertIndex(i, &iCert, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
// Now we know iCert is a valid Cert Index:
|
|
|
|
*pdw++ = g_aCAContext[iCert].NameId;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
inline DWORD
|
|
MapHRESULTToKRADisposition(
|
|
IN HRESULT hr)
|
|
{
|
|
switch(hr)
|
|
{
|
|
case CERT_E_EXPIRED: return KRA_DISP_EXPIRED;
|
|
|
|
case CRYPT_E_NOT_FOUND: return KRA_DISP_NOTFOUND;
|
|
|
|
case CERT_E_REVOKED:
|
|
case CRYPT_E_REVOKED: return KRA_DISP_REVOKED;
|
|
|
|
case S_OK: return KRA_DISP_VALID;
|
|
|
|
case CERT_E_UNTRUSTEDROOT:
|
|
case CERT_E_CHAINING: return KRA_DISP_UNTRUSTED;
|
|
|
|
case ERROR_NOT_FOUND: return KRA_DISP_NOTLOADED;
|
|
|
|
default: return KRA_DISP_INVALID;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetKRAState(
|
|
IN DWORD cKRA,
|
|
OUT BYTE *pb)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwCount;
|
|
HCERTSTORE hKRAStore = NULL;
|
|
CERT_CONTEXT const *pCertContext = NULL;
|
|
|
|
hKRAStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
|
|
wszKRA_CERTSTORE);
|
|
if (NULL == hKRAStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore KRA");
|
|
}
|
|
|
|
for (dwCount = 0; dwCount < cKRA; dwCount++)
|
|
{
|
|
hr = myFindCACertByHashIndex(
|
|
hKRAStore,
|
|
g_wszSanitizedName,
|
|
CSRH_CAKRACERT,
|
|
dwCount,
|
|
NULL,
|
|
&pCertContext);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = myVerifyKRACertContext(pCertContext, g_dwVerifyCertFlags);
|
|
|
|
// check if the CA is using this cert (was able to load it last
|
|
// time it started)
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = ERROR_NOT_FOUND;
|
|
if (NULL != g_aKRAContext)
|
|
{
|
|
DWORD iKRALoaded;
|
|
|
|
for (iKRALoaded = 0; iKRALoaded < g_cKRACerts; iKRALoaded++)
|
|
{
|
|
if (myAreCertContextBlobsSame(
|
|
pCertContext,
|
|
g_aKRAContext[iKRALoaded].pccKRA))
|
|
{
|
|
// the CA loaded this KRA cert
|
|
|
|
hr = g_aKRAContext[iKRALoaded].hrVerifyStatus;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CSASSERT(0 == (~0xff & MapHRESULTToKRADisposition(hr)));
|
|
pb[dwCount] = (BYTE)MapHRESULTToKRADisposition(hr);
|
|
hr = S_OK;
|
|
CertFreeCertificateContext(pCertContext);
|
|
pCertContext = NULL;
|
|
}
|
|
|
|
error:
|
|
|
|
if (NULL != pCertContext)
|
|
{
|
|
CertFreeCertificateContext(pCertContext);
|
|
}
|
|
|
|
if (NULL != hKRAStore)
|
|
{
|
|
CertCloseStore(hKRAStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetRequestNameInfo(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_NAME_BLOB const *pSubject,
|
|
OPTIONAL IN WCHAR const *pwszCNSuffix,
|
|
IN BOOL fReorderLikeRDNs,
|
|
IN OUT DWORD *pdwRequestFlags,
|
|
OUT BOOL *pfSubjectNameSet)
|
|
{
|
|
HRESULT hr;
|
|
CERT_RDN *prdn;
|
|
CERT_RDN *prdnEnd;
|
|
CERT_NAME_INFO *pNameInfo = NULL;
|
|
WCHAR const *pwszPropName;
|
|
DWORD cbNameInfo;
|
|
DWORD dwIndex;
|
|
DWORD cchMax;
|
|
BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note
|
|
|
|
*pfSubjectNameSet = FALSE;
|
|
ZeroMemory(afSubjectTable, sizeof(afSubjectTable));
|
|
CSASSERT(CSExpr(0 == FALSE));
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropSubjectRawName,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
pSubject->cbData,
|
|
pSubject->pbData);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
pkcsSetDistinguishedName(prow, PROPTABLE_REQUEST, pSubject);
|
|
|
|
if (!myDecodeName(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_NAME,
|
|
pSubject->pbData,
|
|
pSubject->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pNameInfo,
|
|
&cbNameInfo))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeName");
|
|
}
|
|
|
|
if (ENUM_TELETEX_ON == (ENUM_TELETEX_MASK & g_fForceTeletex))
|
|
{
|
|
*pdwRequestFlags |= CR_FLG_FORCETELETEX;
|
|
}
|
|
if (ENUM_TELETEX_UTF8 & g_fForceTeletex)
|
|
{
|
|
*pdwRequestFlags |= CR_FLG_FORCEUTF8;
|
|
}
|
|
if (fReorderLikeRDNs)
|
|
{
|
|
DWORD dwSubjectTemplateIndex = 0;
|
|
|
|
for (
|
|
prdn = pNameInfo->rgRDN, prdnEnd = &prdn[pNameInfo->cRDN];
|
|
fReorderLikeRDNs && prdn < prdnEnd;
|
|
prdn++)
|
|
{
|
|
if (1 != prdn->cRDNAttr)
|
|
{
|
|
fReorderLikeRDNs = FALSE;
|
|
break;
|
|
}
|
|
pwszPropName = PKCSMapAttributeName(
|
|
NULL,
|
|
prdn->rgRDNAttr[0].pszObjId,
|
|
&dwIndex,
|
|
&cchMax);
|
|
if (NULL != pwszPropName)
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"dwSubjectTemplateIndex: %x -> %x\n",
|
|
dwSubjectTemplateIndex,
|
|
pkcs_subject[dwIndex].dwSubjectTemplateIndex));
|
|
if (MAXDWORD == pkcs_subject[dwIndex].dwSubjectTemplateIndex ||
|
|
dwSubjectTemplateIndex >
|
|
pkcs_subject[dwIndex].dwSubjectTemplateIndex)
|
|
{
|
|
fReorderLikeRDNs = FALSE;
|
|
break;
|
|
}
|
|
dwSubjectTemplateIndex =
|
|
pkcs_subject[dwIndex].dwSubjectTemplateIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (
|
|
prdn = pNameInfo->rgRDN, prdnEnd = &prdn[pNameInfo->cRDN];
|
|
prdn < prdnEnd;
|
|
prdn++)
|
|
{
|
|
CERT_RDN_ATTR *prdna;
|
|
CERT_RDN_ATTR *prdnaEnd;
|
|
|
|
for (
|
|
prdna = prdn->rgRDNAttr, prdnaEnd = &prdna[prdn->cRDNAttr];
|
|
prdna < prdnaEnd;
|
|
prdna++)
|
|
{
|
|
switch (prdna->dwValueType)
|
|
{
|
|
case CERT_RDN_PRINTABLE_STRING:
|
|
case CERT_RDN_UNICODE_STRING:
|
|
case CERT_RDN_TELETEX_STRING:
|
|
case CERT_RDN_IA5_STRING:
|
|
case CERT_RDN_UTF8_STRING:
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (NULL == prdna->Value.pbData ||
|
|
sizeof(WCHAR) > prdna->Value.cbData ||
|
|
((sizeof(WCHAR) - 1) & prdna->Value.cbData) ||
|
|
L'\0' == *(WCHAR *) prdna->Value.pbData ||
|
|
L'\0' != *(WCHAR *) &prdna->Value.pbData[prdna->Value.cbData])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (CERT_RDN_TELETEX_STRING == prdna->dwValueType &&
|
|
ENUM_TELETEX_AUTO == (ENUM_TELETEX_MASK & g_fForceTeletex))
|
|
{
|
|
*pdwRequestFlags |= CR_FLG_FORCETELETEX;
|
|
}
|
|
|
|
pwszPropName = PKCSMapAttributeName(
|
|
NULL,
|
|
prdna->pszObjId,
|
|
&dwIndex,
|
|
&cchMax);
|
|
if (NULL != pwszPropName)
|
|
{
|
|
BOOL fCN;
|
|
|
|
// CAPI null-terminates strings
|
|
|
|
CSASSERT(
|
|
sizeof(WCHAR) * wcslen((WCHAR const *) prdna->Value.pbData) ==
|
|
prdna->Value.cbData);
|
|
|
|
fCN = 0 == strcmp(szOID_COMMON_NAME, prdna->pszObjId);
|
|
|
|
hr = PropSetAttributeProperty(
|
|
prow,
|
|
afSubjectTable[dwIndex], // fConcatenateRDNs
|
|
fReorderLikeRDNs,
|
|
PROPTABLE_REQUEST,
|
|
cchMax,
|
|
fCN? pwszCNSuffix : NULL,
|
|
pwszPropName,
|
|
(WCHAR const *) prdna->Value.pbData);
|
|
if (HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) == hr)
|
|
{
|
|
hr = CERTSRV_E_BAD_REQUESTSUBJECT;
|
|
}
|
|
_JumpIfError(hr, error, "PropSetAttributeProperty");
|
|
|
|
afSubjectTable[dwIndex] = TRUE;
|
|
*pfSubjectNameSet = TRUE;
|
|
|
|
if (fCN)
|
|
{
|
|
pwszCNSuffix = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pNameInfo)
|
|
{
|
|
LocalFree(pNameInfo);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSSetRequestFlags(
|
|
IN ICertDBRow *prow,
|
|
IN BOOL fSet,
|
|
IN DWORD dwChange)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwOld;
|
|
DWORD dwNew;
|
|
DWORD cb;
|
|
|
|
cb = sizeof(dwOld);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &dwOld);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
if (fSet)
|
|
{
|
|
dwNew = dwOld | dwChange;
|
|
}
|
|
else
|
|
{
|
|
dwNew = dwOld & ~dwChange;
|
|
}
|
|
|
|
if (dwOld != dwNew)
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwNew),
|
|
(BYTE const *) &dwNew);
|
|
_JumpIfError(hr, error, "SetProperty(RequestFlags)");
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetAttributeProperty(
|
|
IN ICertDBRow *prow,
|
|
IN WCHAR const *pwszName,
|
|
IN WCHAR const *pwszValue,
|
|
IN DWORD dwTable,
|
|
IN BYTE afSubjectTable[],
|
|
OUT BOOL *pfSubjectModified,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszPropName;
|
|
DWORD dwIndex;
|
|
DWORD cwcMax;
|
|
BOOL fConcatenateRDNs;
|
|
|
|
*pfSubjectModified = FALSE;
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
|
|
pwszPropName = NULL;
|
|
cwcMax = MAXDWORD;
|
|
dwIndex = 0;
|
|
if (CRLF_ALLOW_REQUEST_ATTRIBUTE_SUBJECT & g_dwCRLFlags)
|
|
{
|
|
// See if the attribute name can be mapped to a standard property.
|
|
|
|
pwszPropName = PKCSMapAttributeName(pwszName, NULL, &dwIndex, &cwcMax);
|
|
}
|
|
if (NULL != pwszPropName)
|
|
{
|
|
fConcatenateRDNs = afSubjectTable[dwIndex];
|
|
afSubjectTable[dwIndex] = TRUE;
|
|
*pfSubjectModified = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pwszPropName = pwszName;
|
|
fConcatenateRDNs = FALSE;
|
|
dwTable = PROPTABLE_ATTRIBUTE;
|
|
|
|
if (0 == mylstrcmpiS(pwszPropName, g_wszPropRequesterName))
|
|
{
|
|
if (NULL == pfEnrollOnBehalfOf)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "NULL pfEnrollOnBehalfOf");
|
|
}
|
|
*pfEnrollOnBehalfOf = TRUE;
|
|
dwTable = PROPTABLE_REQUEST;
|
|
}
|
|
}
|
|
hr = PropSetAttributeProperty(
|
|
prow,
|
|
fConcatenateRDNs,
|
|
FALSE, // fPrependNewValue
|
|
dwTable,
|
|
cwcMax,
|
|
NULL, // pwszSuffix
|
|
pwszPropName,
|
|
pwszValue);
|
|
_JumpIfError(hr, error, "PropSetAttributeProperty");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Note on Request Attribute and Subject RDN processing:
|
|
//
|
|
// Subject RDN strings and Request Attributes may be set several ways, in
|
|
// the following order. Subsequent changes overwrite earlier changes, so the
|
|
// order implies precedence:
|
|
//
|
|
// - Subject in the inner PKCS10 (if no PKCS10 subject, then use the Subject
|
|
// in the PKCS7 renewal cert)
|
|
// - the next outer PKCS7 or CMC Attributes
|
|
// - ...
|
|
// - the most outer PKCS7 or CMC Attributes
|
|
// - Request Attribute string passed with the request when first submitted
|
|
// - Policy Module may set subject RDNs in the certificate table
|
|
// - ICertAdmin::SetAttributes' Request Attribute string (if request pending)
|
|
//
|
|
// "PKCS7 or CMC Attributes" means either of the following:
|
|
// 1) Authenticated Attributes associated with a (non-CMC) PKCS7 signer info.
|
|
// 2) Tagged Attributes and/or RegInfo Control Attributes in a CMC request.
|
|
//
|
|
// None of the secured attributes listed in the registry (which is set to
|
|
// wszzDEFAULTSIGNEDATTRIBUTES by default) may be set unless the source is
|
|
// PKCS7 or CMC Attributes.
|
|
//
|
|
// The original request attribute string is stored in the RequestAttributes
|
|
// column in the request table. It is never modified after that. Individual
|
|
// request attribute values are parsed out of this string (when the request is
|
|
// submitted and when ICertAdmin::SetAttributes is called) and stored in a
|
|
// Subject RDN column of the request or certificate table if the attribute
|
|
// name matches an alias for a Subject RDN, or in a unique row in the
|
|
// attribute table otherwise.
|
|
//
|
|
// Individual Subject RDNs may be specified multiple times (multiple "OU",
|
|
// "CN", strings). If all of the RDNs were set from the same source,
|
|
// they must be concatenated, but if some RDNs were specified from one source,
|
|
// then modified by another source, the previous set of RDNs should be
|
|
// overwritten by the new ones. If the original Request Attribute string
|
|
// specified "CN:foo\nOU:ou2\nCN:bar", the two CN strings should be
|
|
// concatenated. If one or more CN values are also specified later by a
|
|
// single call to ICertAdmin::SetAttributes, the original CN values should be
|
|
// overwritten by the new value(s).
|
|
//
|
|
// It is possible to have the CN strings specified by one source and the OU
|
|
// strings specified by another.
|
|
//
|
|
// Before the policy module gets control, all Subject RDN changes are written
|
|
// to the Request table. Just before dispatching to the policy module, the
|
|
// Request Subject RDNs are copied to the Certificate Table RDNs. The policy
|
|
// module may modify the Certificate Table RDNs only.
|
|
//
|
|
// If the request is made pending, ICertAdmin::SetAttributes may be used to
|
|
// modify request attributes and Certificate Table RDNs.
|
|
//
|
|
// The certificate Subject is constructed from the Certificate Table RDNs.
|
|
|
|
HRESULT
|
|
PKCSParseAttributes(
|
|
IN ICertDBRow *prow,
|
|
IN WCHAR const *pwszAttributes,
|
|
IN BOOL fRegInfo,
|
|
IN BOOL fPending,
|
|
IN DWORD dwRDNTable,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszDup = NULL;
|
|
WCHAR *pwszBuf;
|
|
WCHAR const *pwszName;
|
|
WCHAR const *pwszValue;
|
|
DWORD iSecuredAttribute;
|
|
BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note
|
|
WCHAR *pwszNameAlloc = NULL;
|
|
WCHAR *pwszValueAlloc = NULL;
|
|
BOOL fSubjectModified = FALSE;
|
|
static WCHAR const s_wszzPendingAttributes[] =
|
|
wszPROPCERTTEMPLATE
|
|
L"\0";
|
|
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
if (NULL == pwszAttributes)
|
|
{
|
|
hr = S_OK;
|
|
goto error; // silently ignore empty string
|
|
}
|
|
|
|
hr = myDupString(pwszAttributes, &pwszDup);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
pwszBuf = pwszDup;
|
|
|
|
ZeroMemory(afSubjectTable, sizeof(afSubjectTable));
|
|
CSASSERT(CSExpr(0 == FALSE));
|
|
|
|
for (;;)
|
|
{
|
|
hr = myParseNextAttribute(&pwszBuf, fRegInfo, &pwszName, &pwszValue);
|
|
if (S_FALSE == hr)
|
|
{
|
|
break;
|
|
}
|
|
_JumpIfError(hr, error, "myParseNextAttribute");
|
|
|
|
if (fRegInfo)
|
|
{
|
|
if (NULL != pwszNameAlloc)
|
|
{
|
|
LocalFree(pwszNameAlloc);
|
|
pwszNameAlloc = NULL;
|
|
}
|
|
if (NULL != pwszValueAlloc)
|
|
{
|
|
LocalFree(pwszValueAlloc);
|
|
pwszValueAlloc = NULL;
|
|
}
|
|
hr = myUncanonicalizeURLParm(pwszName, &pwszNameAlloc);
|
|
_JumpIfError(hr, error, "myUncanonicalizeURLParm");
|
|
|
|
hr = myUncanonicalizeURLParm(pwszValue, &pwszValueAlloc);
|
|
_JumpIfError(hr, error, "myUncanonicalizeURLParm");
|
|
|
|
pwszName = pwszNameAlloc;
|
|
pwszValue = pwszValueAlloc;
|
|
}
|
|
|
|
iSecuredAttribute = MAXDWORD;
|
|
if (!fRegInfo)
|
|
{
|
|
// Only set the attribute if it's not one of the attributes
|
|
// that is required to be secure.
|
|
|
|
iSecuredAttribute = CRLIsStringInList(
|
|
pwszName,
|
|
g_wszzSecuredAttributes);
|
|
}
|
|
if (fPending && MAXDWORD == iSecuredAttribute)
|
|
{
|
|
// Only set the attribute if it's not one of the attributes
|
|
// that is disallowed for pending requests.
|
|
|
|
iSecuredAttribute = CRLIsStringInList(
|
|
pwszName,
|
|
s_wszzPendingAttributes);
|
|
}
|
|
if (MAXDWORD == iSecuredAttribute)
|
|
{
|
|
BOOL fEnrollOnBehalfOf = FALSE;
|
|
BOOL fSubjectModifiedT = FALSE;
|
|
|
|
hr = pkcsSetAttributeProperty(
|
|
prow,
|
|
pwszName,
|
|
pwszValue,
|
|
dwRDNTable,
|
|
afSubjectTable,
|
|
&fSubjectModified,
|
|
NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
|
|
if (fSubjectModifiedT)
|
|
{
|
|
fSubjectModified = TRUE;
|
|
}
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (fSubjectModified)
|
|
{
|
|
hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszNameAlloc)
|
|
{
|
|
LocalFree(pwszNameAlloc);
|
|
}
|
|
if (NULL != pwszValueAlloc)
|
|
{
|
|
LocalFree(pwszValueAlloc);
|
|
}
|
|
if (NULL != pwszDup)
|
|
{
|
|
LocalFree(pwszDup);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetAltSubjectNameExtension(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD ExtFlags,
|
|
IN CERT_EXTENSION const *rgExtension,
|
|
IN DWORD cExtension,
|
|
IN DWORD cAltSubjectExtension)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CERT_ALT_NAME_INFO **apInfo = NULL;
|
|
DWORD i;
|
|
DWORD j;
|
|
DWORD cInfo = 0;
|
|
DWORD cb;
|
|
CERT_ALT_NAME_INFO ResultInfo;
|
|
DWORD cbResult;
|
|
BYTE *pbResult = NULL;
|
|
|
|
ResultInfo.cAltEntry = 0;
|
|
ResultInfo.rgAltEntry = NULL;
|
|
|
|
apInfo = (CERT_ALT_NAME_INFO **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
sizeof(CERT_ALT_NAME_INFO *) * cAltSubjectExtension);
|
|
if (NULL == apInfo)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// Decode all AltNames
|
|
|
|
for (i = 0; i < cExtension; i++)
|
|
{
|
|
// This is an OID, generated by capi2, so we don't need to
|
|
// do a case-insensitive comparison
|
|
|
|
if (0 == strcmp(rgExtension[i].pszObjId, szOID_SUBJECT_ALT_NAME2))
|
|
{
|
|
CSASSERT(cInfo < cAltSubjectExtension);
|
|
|
|
// Decode to plain text
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_ALTERNATE_NAME,
|
|
rgExtension[i].Value.pbData,
|
|
rgExtension[i].Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &apInfo[cInfo],
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
if (rgExtension[i].fCritical)
|
|
{
|
|
ExtFlags |= EXTENSION_CRITICAL_FLAG;
|
|
}
|
|
ResultInfo.cAltEntry += apInfo[cInfo]->cAltEntry;
|
|
cInfo++;
|
|
}
|
|
}
|
|
CSASSERT(cInfo == cAltSubjectExtension);
|
|
|
|
ResultInfo.rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
ResultInfo.cAltEntry * sizeof(CERT_ALT_NAME_ENTRY));
|
|
if (NULL == ResultInfo.rgAltEntry)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpIfError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
j = 0;
|
|
for (i = 0; i < cInfo; i++)
|
|
{
|
|
CopyMemory(
|
|
&ResultInfo.rgAltEntry[j],
|
|
apInfo[i]->rgAltEntry,
|
|
apInfo[i]->cAltEntry * sizeof(CERT_ALT_NAME_ENTRY));
|
|
j += apInfo[i]->cAltEntry;
|
|
}
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_ALTERNATE_NAME,
|
|
&ResultInfo,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbResult,
|
|
&cbResult))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_REQUEST,
|
|
TEXT(szOID_SUBJECT_ALT_NAME2),
|
|
ExtFlags,
|
|
cbResult,
|
|
pbResult);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
error:
|
|
if (NULL != apInfo)
|
|
{
|
|
for (i = 0; i < cInfo; i++)
|
|
{
|
|
if (NULL != apInfo[i])
|
|
{
|
|
LocalFree(apInfo[i]);
|
|
}
|
|
}
|
|
LocalFree(apInfo);
|
|
}
|
|
if (NULL != ResultInfo.rgAltEntry)
|
|
{
|
|
LocalFree(ResultInfo.rgAltEntry);
|
|
}
|
|
if (NULL != pbResult)
|
|
{
|
|
LocalFree(pbResult);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Scan extension array, and merge all the AltSubjectName Extensions into one.
|
|
|
|
HRESULT
|
|
pkcsSetExtensions(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD ExtFlags,
|
|
IN CERT_EXTENSION const *rgExtension,
|
|
IN DWORD cExtension)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszObjId = NULL;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_EXTENSION const *pExtEnd;
|
|
DWORD cAltSubjectExtension = 0;
|
|
|
|
pExtEnd = &rgExtension[cExtension];
|
|
for (pExt = rgExtension; pExt < pExtEnd; pExt++)
|
|
{
|
|
DWORD ExtFlagsT;
|
|
|
|
if (EXTENSION_ORIGIN_RENEWALCERT == (EXTENSION_ORIGIN_MASK & ExtFlags))
|
|
{
|
|
char const * const *ppszObjId;
|
|
static char const * const apszObjIdFilter[] = {
|
|
szOID_CERTSRV_CA_VERSION,
|
|
szOID_AUTHORITY_INFO_ACCESS,
|
|
szOID_CRL_DIST_POINTS,
|
|
szOID_AUTHORITY_KEY_IDENTIFIER2,
|
|
szOID_SUBJECT_KEY_IDENTIFIER,
|
|
NULL
|
|
};
|
|
for (ppszObjId = apszObjIdFilter; NULL != *ppszObjId; ppszObjId++)
|
|
{
|
|
if (0 == strcmp(*ppszObjId, pExt->pszObjId))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL != *ppszObjId) // if in list
|
|
{
|
|
continue; // skip this extension
|
|
}
|
|
}
|
|
else
|
|
if (EXTENSION_ORIGIN_CACERT == (EXTENSION_ORIGIN_MASK & ExtFlags))
|
|
{
|
|
char const * const *ppszObjId;
|
|
static char const * const apszObjIdAllowed[] = {
|
|
szOID_SUBJECT_KEY_IDENTIFIER,
|
|
NULL
|
|
};
|
|
for (ppszObjId = apszObjIdAllowed; NULL != *ppszObjId; ppszObjId++)
|
|
{
|
|
if (0 == strcmp(*ppszObjId, pExt->pszObjId))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL == *ppszObjId) // if not in list
|
|
{
|
|
continue; // skip this extension
|
|
}
|
|
}
|
|
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
pwszObjId = NULL;
|
|
}
|
|
if (!myConvertSzToWsz(&pwszObjId, pExt->pszObjId, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(ObjId)");
|
|
}
|
|
ExtFlagsT = ExtFlags;
|
|
if (pExt->fCritical)
|
|
{
|
|
ExtFlagsT |= EXTENSION_CRITICAL_FLAG;
|
|
}
|
|
|
|
// AltSubjectName needs to be merged, so we do that later.
|
|
// This is an OID, generated by capi2, so we don't need to
|
|
// do a case-insensitive comparison.
|
|
|
|
if (0 == lstrcmp(pwszObjId, TEXT(szOID_SUBJECT_ALT_NAME2)))
|
|
{
|
|
cAltSubjectExtension++;
|
|
continue;
|
|
}
|
|
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_REQUEST,
|
|
pwszObjId,
|
|
ExtFlagsT,
|
|
pExt->Value.cbData,
|
|
pExt->Value.pbData);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"PropSetExtension(%ws, f=%x, cb=%x, pb=%x)\n",
|
|
pwszObjId,
|
|
ExtFlagsT,
|
|
pExt->Value.cbData,
|
|
pExt->Value.pbData));
|
|
}
|
|
|
|
if (0 != cAltSubjectExtension)
|
|
{
|
|
hr = pkcsSetAltSubjectNameExtension(
|
|
prow,
|
|
ExtFlags,
|
|
rgExtension,
|
|
cExtension,
|
|
cAltSubjectExtension);
|
|
_JumpIfError(hr, error, "pkcsSetAltSubjectNameExtension");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetOSVersion(
|
|
IN ICertDBRow *prow,
|
|
IN CRYPT_ATTR_BLOB *pAttrBlob)
|
|
{
|
|
HRESULT hr;
|
|
CERT_NAME_VALUE *pOSVersionString = NULL;
|
|
BSTR strVersion = NULL;
|
|
DWORD cb;
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_ANY_STRING,
|
|
pAttrBlob->pbData,
|
|
pAttrBlob->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pOSVersionString,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
if (NULL != pOSVersionString)
|
|
{
|
|
if (!IS_CERT_RDN_CHAR_STRING(pOSVersionString->dwValueType))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "version string is numeric data");
|
|
}
|
|
|
|
// If it's an 8 bit string, convert it to UNICODE
|
|
|
|
if (CERT_RDN_UNIVERSAL_STRING > pOSVersionString->dwValueType)
|
|
{
|
|
// Pass byte count in to allocate enough characters for
|
|
// the converted Unicode string
|
|
|
|
strVersion = SysAllocStringLen(
|
|
NULL,
|
|
pOSVersionString->Value.cbData);
|
|
|
|
// This is expected to be only numbers and '.'s,
|
|
|
|
if (NULL == strVersion)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocStringLen");
|
|
}
|
|
mbstowcs(
|
|
strVersion,
|
|
(char const *) pOSVersionString->Value.pbData,
|
|
pOSVersionString->Value.cbData);
|
|
}
|
|
else if (CERT_RDN_BMP_STRING == pOSVersionString->dwValueType ||
|
|
CERT_RDN_UNICODE_STRING == pOSVersionString->dwValueType)
|
|
{
|
|
strVersion = SysAllocStringLen(
|
|
(WCHAR *) pOSVersionString->Value.pbData,
|
|
pOSVersionString->Value.cbData/sizeof(WCHAR));
|
|
if (NULL == strVersion)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocStringLen");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "unknown string type");
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestOSVersion,
|
|
PROPTYPE_STRING |
|
|
PROPCALLER_SERVER |
|
|
PROPTABLE_ATTRIBUTE,
|
|
SysStringByteLen(strVersion),
|
|
(BYTE *) strVersion);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != strVersion)
|
|
{
|
|
SysFreeString(strVersion);
|
|
}
|
|
if (NULL != pOSVersionString)
|
|
{
|
|
LocalFree(pOSVersionString);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetCSPProvider(
|
|
IN ICertDBRow *prow,
|
|
IN CRYPT_ATTR_BLOB *pAttrBlob)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_CSP_PROVIDER *pccp = NULL;
|
|
|
|
hr = myDecodeCSPProviderAttribute(
|
|
pAttrBlob->pbData,
|
|
pAttrBlob->cbData,
|
|
&pccp);
|
|
_JumpIfError(hr, error, "myDecodeCSPProviderAttribute");
|
|
|
|
if (NULL != pccp->pwszProviderName && L'\0' != *pccp->pwszProviderName)
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestCSPProvider,
|
|
PROPTYPE_STRING |
|
|
PROPCALLER_SERVER |
|
|
PROPTABLE_ATTRIBUTE,
|
|
MAXDWORD,
|
|
(BYTE const *) pccp->pwszProviderName);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pccp)
|
|
{
|
|
LocalFree(pccp);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetExtensionsFromAttributeBlob(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD ExtFlags,
|
|
IN CRYPT_ATTRIBUTE const *pAttrib)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_ATTR_BLOB *pAttrBlob;
|
|
CERT_NAME_VALUE *pNameInfo = NULL;
|
|
CERT_EXTENSIONS *pCertExtensions = NULL;
|
|
DWORD cb;
|
|
|
|
pAttrBlob = pAttrib->rgValue;
|
|
for (;;)
|
|
{
|
|
if (NULL != pCertExtensions)
|
|
{
|
|
LocalFree(pCertExtensions);
|
|
pCertExtensions = NULL;
|
|
}
|
|
if (myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_EXTENSIONS,
|
|
pAttrBlob->pbData,
|
|
pAttrBlob->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pCertExtensions,
|
|
&cb))
|
|
{
|
|
break; // success
|
|
}
|
|
hr = myHLastError();
|
|
|
|
// if we already decoded the attribute as a T61 string, or if it is
|
|
// not a PKCS 9.14 attribute, fail -- we don't know what it contains.
|
|
|
|
if (NULL != pNameInfo ||
|
|
0 != strcmp(pAttrib->pszObjId, szOID_RSA_certExtensions))
|
|
{
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
// Decode the attribute as a T61 string. Some implementations wrap the
|
|
// PKCS 9.14 extension array in an extra level of encoding as a Teletex
|
|
// string.
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_ANY_STRING,
|
|
pAttrBlob->pbData,
|
|
pAttrBlob->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pNameInfo,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
// Loop again and try to decode the raw name blob as X509_EXTENSIONS.
|
|
|
|
pAttrBlob = &pNameInfo->Value;
|
|
}
|
|
hr = pkcsSetExtensions(
|
|
prow,
|
|
EXTENSION_DISABLE_FLAG | ExtFlags,
|
|
pCertExtensions->rgExtension,
|
|
pCertExtensions->cExtension);
|
|
_JumpIfError(hr, error, "pkcsSetExtensions(attributes)");
|
|
|
|
error:
|
|
if (NULL != pNameInfo)
|
|
{
|
|
LocalFree(pNameInfo);
|
|
}
|
|
if (NULL != pCertExtensions)
|
|
{
|
|
LocalFree(pCertExtensions);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetProperty(
|
|
IN ICertDBRow *prow,
|
|
IN WCHAR const *pwszPropName,
|
|
IN DWORD Flags,
|
|
OPTIONAL OUT DWORD *pcbData,
|
|
OUT BYTE **ppbData)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbData = NULL;
|
|
DWORD cbData;
|
|
|
|
if (NULL != pcbData)
|
|
{
|
|
*pcbData = 0;
|
|
}
|
|
*ppbData = NULL;
|
|
|
|
cbData = 0;
|
|
hr = prow->GetProperty(pwszPropName, Flags, NULL, &cbData, pbData);
|
|
_JumpIfError2(hr, error, "GetProperty", CERTSRV_E_PROPERTY_EMPTY);
|
|
|
|
pbData = (BYTE *) LocalAlloc(LMEM_FIXED, cbData);
|
|
if (NULL == pbData)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
hr = prow->GetProperty(pwszPropName, Flags, NULL, &cbData, pbData);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
if (NULL != pcbData)
|
|
{
|
|
*pcbData = cbData;
|
|
}
|
|
*ppbData = pbData;
|
|
pbData = NULL;
|
|
|
|
error:
|
|
if (NULL != pbData)
|
|
{
|
|
LocalFree(pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsFreePublicKeyInfo(
|
|
IN OUT CERT_PUBLIC_KEY_INFO *pPublicKeyInfo)
|
|
{
|
|
if (NULL != pPublicKeyInfo->Algorithm.pszObjId)
|
|
{
|
|
LocalFree(pPublicKeyInfo->Algorithm.pszObjId);
|
|
}
|
|
if (NULL != pPublicKeyInfo->Algorithm.Parameters.pbData)
|
|
{
|
|
LocalFree(pPublicKeyInfo->Algorithm.Parameters.pbData);
|
|
}
|
|
if (NULL != pPublicKeyInfo->PublicKey.pbData)
|
|
{
|
|
LocalFree(pPublicKeyInfo->PublicKey.pbData);
|
|
}
|
|
ZeroMemory(pPublicKeyInfo, sizeof(*pPublicKeyInfo));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetPublicKeyInfo(
|
|
IN ICertDBRow *prow,
|
|
OUT CERT_PUBLIC_KEY_INFO *pPublicKeyInfo)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszObjId = NULL;
|
|
|
|
ZeroMemory(pPublicKeyInfo, sizeof(*pPublicKeyInfo));
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropCertificatePublicKeyAlgorithm,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
NULL,
|
|
(BYTE **) &pwszObjId);
|
|
_JumpIfError(hr, error, "PKCSGetProperty");
|
|
|
|
if (!myConvertWszToSz(&pPublicKeyInfo->Algorithm.pszObjId, pwszObjId, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertWszToSz(AlgObjId)");
|
|
}
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropCertificateRawPublicKeyAlgorithmParameters,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&pPublicKeyInfo->Algorithm.Parameters.cbData,
|
|
&pPublicKeyInfo->Algorithm.Parameters.pbData);
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
_JumpIfError(hr, error, "PKCSGetProperty");
|
|
}
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropCertificateRawPublicKey,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&pPublicKeyInfo->PublicKey.cbData,
|
|
&pPublicKeyInfo->PublicKey.pbData);
|
|
_JumpIfError(hr, error, "PKCSGetProperty");
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
pkcsFreePublicKeyInfo(pPublicKeyInfo);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsLogKRACertError(
|
|
IN DWORD LogMsg,
|
|
IN DWORD iHash,
|
|
OPTIONAL IN CERT_CONTEXT const *pcc,
|
|
IN HRESULT hrLog)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
WCHAR *pwszName = NULL;
|
|
WCHAR const *pwszError = NULL;
|
|
WCHAR const *apwsz[3];
|
|
|
|
wsprintf(awc, L"%u", iHash);
|
|
apwsz[0] = awc;
|
|
|
|
if (NULL != pcc)
|
|
{
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&pcc->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
&pwszName);
|
|
_PrintIfError(hr, "myCertNameToStr");
|
|
}
|
|
apwsz[1] = NULL != pwszName? pwszName : L"";
|
|
|
|
pwszError = myGetErrorMessageText(hrLog, TRUE);
|
|
apwsz[2] = NULL != pwszError? pwszError : L"";
|
|
|
|
LogEvent(EVENTLOG_ERROR_TYPE, LogMsg, ARRAYSIZE(apwsz), apwsz);
|
|
|
|
//error:
|
|
if (NULL != pwszName)
|
|
{
|
|
LocalFree(pwszName);
|
|
}
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(pwszError));
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCryptGetDefaultProvider(
|
|
DWORD dwProvType,
|
|
DWORD dwFlags,
|
|
WCHAR **ppwszProvName)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cb;
|
|
|
|
*ppwszProvName = NULL;
|
|
cb = 0;
|
|
for (;;)
|
|
{
|
|
if (!CryptGetDefaultProvider(
|
|
dwProvType,
|
|
NULL, // pdwReserved
|
|
dwFlags,
|
|
*ppwszProvName,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptGetDefaultProvider");
|
|
}
|
|
if (NULL != *ppwszProvName)
|
|
{
|
|
break;
|
|
}
|
|
*ppwszProvName = (WCHAR *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == *ppwszProvName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsEncryptPrivateKey(
|
|
IN BYTE *pbDecrypted,
|
|
IN DWORD cbDecrypted,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult) // CoTaskMem*
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD iKRACert;
|
|
DWORD cwc;
|
|
CERT_CONTEXT const **rgKRACerts = NULL;
|
|
WCHAR *pwszKRAHashes = NULL;
|
|
DWORD *rgKRAIndexes = NULL;
|
|
DWORD cKRAUsed;
|
|
KRACTX *pKRAContext;
|
|
BOOL fInvalidated;
|
|
DWORD iKRAStart;
|
|
WCHAR *pwszProviderName = NULL;
|
|
static bool fUseCAProv = true;
|
|
|
|
if (NULL != pResult->pbArchivedKey || NULL != pResult->pwszKRAHashes)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
|
|
_JumpError(hr, error, "multiple archived keys");
|
|
}
|
|
|
|
CSASSERT(
|
|
NULL != g_aKRAContext &&
|
|
0 != g_cKRACerts &&
|
|
0 != g_cKRACertsRoundRobin);
|
|
|
|
rgKRAIndexes = (DWORD *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
g_cKRACertsRoundRobin * sizeof(rgKRAIndexes[0]));
|
|
if (NULL == rgKRAIndexes)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if (!CryptGenRandom(
|
|
g_pCAContextCurrent->hProvCA,
|
|
sizeof(iKRAStart),
|
|
(BYTE *) &iKRAStart))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptGenRandom");
|
|
}
|
|
cwc = 0;
|
|
fInvalidated = FALSE;
|
|
for (
|
|
cKRAUsed = 0, i = 0;
|
|
cKRAUsed < g_cKRACertsRoundRobin && i < g_cKRACerts;
|
|
i++)
|
|
{
|
|
iKRACert = (iKRAStart + i) % g_cKRACerts;
|
|
pKRAContext = &g_aKRAContext[iKRACert];
|
|
|
|
if (S_OK != pKRAContext->hrVerifyStatus)
|
|
{
|
|
continue;
|
|
}
|
|
hr = myVerifyKRACertContext(pKRAContext->pccKRA, g_dwVerifyCertFlags);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myVerifyKRACertContext");
|
|
pKRAContext->hrVerifyStatus = hr;
|
|
if (CERT_E_EXPIRED == hr)
|
|
{
|
|
pKRAContext->Flags |= CTXF_EXPIRED;
|
|
}
|
|
else
|
|
// Assume revoked for other errors
|
|
// if (CRYPT_E_REVOKED == hr || CERT_E_REVOKED == hr)
|
|
{
|
|
pKRAContext->Flags |= CTXF_REVOKED;
|
|
}
|
|
pkcsLogKRACertError(
|
|
MSG_E_INVALID_KRA_CERT,
|
|
iKRACert,
|
|
pKRAContext->pccKRA,
|
|
hr);
|
|
fInvalidated = TRUE;
|
|
continue;
|
|
}
|
|
cwc += wcslen(pKRAContext->strKRAHash) + 1;
|
|
rgKRAIndexes[cKRAUsed] = iKRACert;
|
|
cKRAUsed++;
|
|
}
|
|
|
|
if (0 == cKRAUsed || g_cKRACertsRoundRobin > cKRAUsed)
|
|
{
|
|
if (fInvalidated)
|
|
{
|
|
LogEvent(EVENTLOG_ERROR_TYPE, MSG_E_TOO_MANY_KRA_INVALID, 0, NULL);
|
|
}
|
|
hr = CERTSRV_E_NO_VALID_KRA;
|
|
_JumpError(hr, error, "too many invalid KRA certs");
|
|
}
|
|
|
|
rgKRACerts = (CERT_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
cKRAUsed * sizeof(rgKRACerts[0]));
|
|
if (NULL == rgKRACerts)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
pwszKRAHashes = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == pwszKRAHashes)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
pwszKRAHashes[0] = L'\0';
|
|
for (i = 0; i < cKRAUsed; i++)
|
|
{
|
|
pKRAContext = &g_aKRAContext[rgKRAIndexes[i]];
|
|
rgKRACerts[i] = pKRAContext->pccKRA;
|
|
if (0 != i)
|
|
{
|
|
wcscat(pwszKRAHashes, L"\n");
|
|
}
|
|
wcscat(pwszKRAHashes, pKRAContext->strKRAHash);
|
|
}
|
|
CSASSERT(wcslen(pwszKRAHashes) + 1 == cwc);
|
|
|
|
if (CRLF_REVCHECK_IGNORE_OFFLINE & g_KRAFlags)
|
|
{
|
|
fUseCAProv = false;
|
|
}
|
|
hr = myCryptEncryptMessage(
|
|
g_XchgidAlg,
|
|
cKRAUsed, // cCertRecipient
|
|
rgKRACerts, // rgCertRecipient
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
fUseCAProv? g_pCAContextCurrent->hProvCA : NULL,
|
|
&pResult->pbArchivedKey,
|
|
&pResult->cbArchivedKey);
|
|
if (FAILED(hr) && fUseCAProv)
|
|
{
|
|
// Failed to use the CA HCRYPTPROV, fall back to default
|
|
|
|
hr = pkcsCryptGetDefaultProvider(
|
|
PROV_RSA_FULL,
|
|
CRYPT_MACHINE_DEFAULT,
|
|
&pwszProviderName);
|
|
_JumpIfError(hr, error, "pkcsCryptGetDefaultProvider");
|
|
|
|
hr = LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_E_USE_DEFAULT_CA_XCHG_CSP,
|
|
1, // cpwsz
|
|
&pwszProviderName); // apwsz
|
|
_PrintIfError(hr, "LogEvent");
|
|
|
|
fUseCAProv = false;
|
|
hr = myCryptEncryptMessage(
|
|
g_XchgidAlg,
|
|
cKRAUsed, // cCertRecipient
|
|
rgKRACerts, // rgCertRecipient
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
NULL,
|
|
&pResult->pbArchivedKey,
|
|
&pResult->cbArchivedKey);
|
|
}
|
|
_JumpIfError(hr, error, "myCryptEncryptMessage");
|
|
|
|
pResult->pwszKRAHashes = pwszKRAHashes;
|
|
pwszKRAHashes = NULL;
|
|
|
|
error:
|
|
if (NULL != pwszProviderName)
|
|
{
|
|
LocalFree(pwszProviderName);
|
|
}
|
|
if (NULL != rgKRAIndexes)
|
|
{
|
|
LocalFree(rgKRAIndexes);
|
|
}
|
|
if (NULL != pwszKRAHashes)
|
|
{
|
|
LocalFree(pwszKRAHashes);
|
|
}
|
|
if (NULL != rgKRACerts)
|
|
{
|
|
LocalFree(rgKRACerts);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
// PKCSArchivePrivateKey -- archive a private key
|
|
//
|
|
// Description of Algorithm:
|
|
// -------------------------
|
|
//
|
|
// Client constructs request:
|
|
// CryptExportKey(NULL, NULL, PRIVATEKEYBLOB, ...)
|
|
// CryptEncryptMessage(CALG_3DES, &CAExchangeCert, ...)
|
|
//
|
|
// Server decrypts and verifies key in request:
|
|
// CryptDecryptMessage(..., &PrivateKeyBlob)
|
|
//
|
|
// Server imports decrypted key into temporary key container:
|
|
// CryptAcquireContext(
|
|
// &hProv,
|
|
// pwszTempKeyContainerName,
|
|
// NULL,
|
|
// PROV_RSA_FULL,
|
|
// CRYPT_NEWKEYSET)
|
|
// CryptImportKey(PrivateKeyBlob, &PrivateKey)
|
|
// CryptDestroyKey(PrivateKey)
|
|
// CryptReleaseContext()
|
|
//
|
|
// Server validates key for encryption:
|
|
// CryptAcquireContext(
|
|
// &hProv,
|
|
// pwszTempKeyContainerName,
|
|
// NULL,
|
|
// PROV_RSA_FULL,
|
|
// 0)
|
|
// CryptImportPublicKeyInfo(RequestPublicKey, &PublicKey)
|
|
// CryptGenKey(hProv, CALC_RC4, CRYPT_EXPORTABLE, &SymmetricKey)
|
|
// CryptGenRandom(hProv, ...)
|
|
// CryptEncrypt(SymmetricKey, ..)
|
|
// CryptExportKey(SymmetricKey, PublicKey, SIMPLEBLOB, &SymmetricKeyBlob)
|
|
// CryptDestroyKey(SymmetricKey)
|
|
//
|
|
// CryptGetUserKey(hProv, AT_KEYEXCHANGE, &PrivateKey)
|
|
// CryptImportKey(hProv, SymmetricKeyBlob, PrivateKey, ..., &SymmetricKey)
|
|
// CryptDecrypt(SymmetricKey, ...)
|
|
//
|
|
// CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, &ExportedPublicKey)
|
|
// CertComparePublicKeyInfo(ExportedPublicKey, RequestPublicKey)
|
|
// memcmp(RandomCleartext, DecryptedClearText)
|
|
// CryptDestroyKey(all keys)
|
|
// CryptReleaseContext()
|
|
//
|
|
// Server verifies request public key matches decrypted public key:
|
|
// CryptExportPublicKeyInfo(
|
|
// hProv,
|
|
// AT_KEYEXCHANGE,
|
|
// &PublicKeyBlobFromDecryptedPrivateKey)
|
|
// CertComparePublicKeyInfo(
|
|
// PublicKeyBlobFromDecryptedPrivateKe,
|
|
// PublicKeyBlobFromRequest),
|
|
//
|
|
// Server deletes temporary key container:
|
|
// CryptAcquireContext(
|
|
// &hProv,
|
|
// pwszTempKeyContainerName,
|
|
// NULL,
|
|
// PROV_RSA_FULL,
|
|
// CRYPT_DELETEKEYSET)
|
|
//
|
|
// Server encrypts key to one or more KRA certs:
|
|
// CryptEncryptMessage(CALG_3DES, KRACertCount, &KRACertArray, ...)
|
|
|
|
HRESULT
|
|
PKCSArchivePrivateKey(
|
|
IN ICertDBRow *prow,
|
|
IN BOOL fV1Cert,
|
|
IN BOOL fOverwrite,
|
|
IN CRYPT_ATTR_BLOB const *pBlobEncrypted,
|
|
OPTIONAL IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbDecrypted = NULL;
|
|
DWORD cbDecrypted;
|
|
DWORD iCertSig;
|
|
BYTE *pbCert; // do not free!
|
|
DWORD cbCert;
|
|
DWORD cb;
|
|
CERT_PUBLIC_KEY_INFO PublicKeyInfo;
|
|
WCHAR *pwszUserName = NULL;
|
|
BYTE *pbKeyHash = NULL;
|
|
DWORD cbKeyHash;
|
|
CERTSRV_RESULT_CONTEXT tempResultContext;
|
|
BOOL fSigningKey = FALSE;
|
|
|
|
ZeroMemory(&tempResultContext, sizeof(tempResultContext));
|
|
ZeroMemory(&PublicKeyInfo, sizeof(PublicKeyInfo));
|
|
cbDecrypted = 0;
|
|
cbKeyHash = 0;
|
|
|
|
if (0 == g_cKRACerts || g_cKRACertsRoundRobin > g_cKRACerts)
|
|
{
|
|
hr = CERTSRV_E_KEY_ARCHIVAL_NOT_CONFIGURED;
|
|
_JumpError(hr, error, "not enough KRA certs loaded");
|
|
}
|
|
|
|
if (NULL != pResult)
|
|
{
|
|
if (NULL == pResult->pbKeyHashIn)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "missing encrypted key hash");
|
|
}
|
|
hr = myCalculateKeyArchivalHash(
|
|
pBlobEncrypted->pbData,
|
|
pBlobEncrypted->cbData,
|
|
&pbKeyHash,
|
|
&cbKeyHash);
|
|
_JumpIfError(hr, error, "myCalculateKeyArchivalHash");
|
|
|
|
if (pResult->cbKeyHashIn != cbKeyHash ||
|
|
0 != memcmp(pResult->pbKeyHashIn, pbKeyHash, cbKeyHash))
|
|
{
|
|
hr = NTE_BAD_KEY;
|
|
_JumpError(hr, error, "key hash mismatch");
|
|
}
|
|
hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_VALIDENCRYPTEDKEYHASH);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
}
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRawArchivedKey,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
NULL);
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
if (S_OK == hr && !fOverwrite)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
|
|
}
|
|
_JumpIfError2(
|
|
hr,
|
|
error,
|
|
"GetProperty",
|
|
HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS));
|
|
}
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRequesterName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
(BYTE **) &pwszUserName);
|
|
_JumpIfError(hr, error, "PKCSGetProperty");
|
|
|
|
if (NULL == g_hStoreCAXchg)
|
|
{
|
|
hr = PKCSGetCAXchgCert(0, pwszUserName, &iCertSig, &pbCert, &cbCert);
|
|
_JumpIfError(hr, error, "PKCSGetCAXchgCert");
|
|
}
|
|
CSASSERT(NULL != g_hStoreCAXchg);
|
|
hr = myCryptDecryptMessage(
|
|
g_hStoreCAXchg,
|
|
pBlobEncrypted->pbData,
|
|
pBlobEncrypted->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbDecrypted,
|
|
&cbDecrypted);
|
|
_JumpIfError(hr, error, "myCryptDecryptMessage");
|
|
|
|
DBGDUMPHEX((DBG_SS_CERTSRVI, DH_PRIVATEDATA, pbDecrypted, cbDecrypted));
|
|
|
|
hr = pkcsGetPublicKeyInfo(prow, &PublicKeyInfo);
|
|
_JumpIfError(hr, error, "pkcsGetPublicKeyInfo");
|
|
|
|
hr = myValidateKeyBlob(
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
&PublicKeyInfo,
|
|
fV1Cert,
|
|
&fSigningKey,
|
|
NULL);
|
|
_JumpIfError(hr, error, "myValidateKeyBlob");
|
|
|
|
if (fSigningKey && 0 == (KRAF_ENABLEARCHIVEALL & g_KRAFlags))
|
|
{
|
|
if (NULL != pResult)
|
|
{
|
|
pResult->dwResultFlags |= CRCF_ARCHIVESIGNINGKEYERROR;
|
|
}
|
|
hr = NTE_BAD_KEY_STATE;
|
|
_JumpError(hr, error, "fSigningKey");
|
|
}
|
|
|
|
hr = pkcsEncryptPrivateKey(
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
NULL != pResult? pResult : &tempResultContext);
|
|
_JumpIfError(hr, error, "pkcsEncryptPrivateKey");
|
|
|
|
// if a key import, save to database
|
|
|
|
if (NULL == pResult)
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestRawArchivedKey,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
tempResultContext.cbArchivedKey,
|
|
tempResultContext.pbArchivedKey);
|
|
_JumpIfError(hr, error, "SetProperty(ArchivedKey)");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestKeyRecoveryHashes,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) tempResultContext.pwszKRAHashes);
|
|
_JumpIfError(hr, error, "SetProperty(KRAHashes)");
|
|
}
|
|
|
|
if (NULL != pResult && NULL == pResult->pbKeyHashOut)
|
|
{
|
|
pResult->pbKeyHashOut = pbKeyHash;
|
|
pResult->cbKeyHashOut = cbKeyHash;
|
|
pbKeyHash = NULL;
|
|
}
|
|
|
|
{
|
|
CertSrv::CAuditEvent audit(SE_AUDITID_CERTSRV_KEYARCHIVED, g_dwAuditFilter);
|
|
DWORD dwRequestID = 0;
|
|
cb = sizeof(DWORD);
|
|
|
|
if (audit.IsEventEnabled())
|
|
{
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRequestID,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *)&dwRequestID);
|
|
_JumpIfError(hr, error, "Report");
|
|
|
|
hr = audit.AddData(dwRequestID); // %1 request ID
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
hr = audit.AddData(pwszUserName); // %2 requester
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
// %3 KRA hashes
|
|
hr = audit.AddData(
|
|
(NULL != pResult? pResult : &tempResultContext)->pwszKRAHashes);
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
hr = audit.Report();
|
|
_JumpIfError(hr, error, "Report");
|
|
}
|
|
}
|
|
|
|
error:
|
|
if (S_OK != hr && NULL != pResult)
|
|
{
|
|
pResult->dwResultFlags |= CRCF_KEYARCHIVALERROR;
|
|
}
|
|
pkcsFreePublicKeyInfo(&PublicKeyInfo);
|
|
if (NULL != pbDecrypted)
|
|
{
|
|
SecureZeroMemory(pbDecrypted, cbDecrypted); // Private Key Material!
|
|
LocalFree(pbDecrypted);
|
|
}
|
|
if (NULL != pwszUserName)
|
|
{
|
|
LocalFree(pwszUserName);
|
|
}
|
|
if (NULL != pbKeyHash)
|
|
{
|
|
LocalFree(pbKeyHash);
|
|
}
|
|
ReleaseResult(&tempResultContext);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSaveRequestWithoutArchivedKey(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbIn,
|
|
IN BYTE const *pbIn)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTMSG hMsg = NULL;
|
|
DWORD cSigner;
|
|
DWORD iSigner;
|
|
DWORD i;
|
|
DWORD cb;
|
|
BYTE *pbWithoutKey = NULL;
|
|
DWORD cbWithoutKey;
|
|
CRYPT_ATTRIBUTES *pAttrib = NULL;
|
|
BOOL fKeyDeleted = FALSE;
|
|
|
|
hMsg = CryptMsgOpenToDecode(
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
0, // dwFlags
|
|
0, // dwMsgType
|
|
NULL, // hCryptProv
|
|
NULL, // pRecipientInfo
|
|
NULL); // pStreamInfo
|
|
if (NULL == hMsg)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgOpenToDecode");
|
|
}
|
|
|
|
if (!CryptMsgUpdate(hMsg, pbIn, cbIn, TRUE))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgUpdate");
|
|
}
|
|
|
|
cb = sizeof(cSigner);
|
|
if (!CryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_SIGNER_COUNT_PARAM,
|
|
0,
|
|
&cSigner,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgGetParam(signer count)");
|
|
}
|
|
|
|
DBGPRINT((DBG_SS_CERTSRV, "cSigner=%u\n", cSigner));
|
|
|
|
for (iSigner = 0; iSigner < cSigner; iSigner++)
|
|
{
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_SIGNER_UNAUTH_ATTR_PARAM,
|
|
iSigner, // dwIndex
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pAttrib,
|
|
&cb);
|
|
_PrintIfError2(hr, "myCryptMsgGetParam(content)", hr);
|
|
if (S_FALSE == hr)
|
|
{
|
|
continue;
|
|
}
|
|
_JumpIfError(hr, error, "myCryptMsgGetParam(content)");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"iSigner=%u, cAttr=%u\n",
|
|
iSigner,
|
|
pAttrib->cAttr));
|
|
|
|
// Loop through deleting attributes from the end to avoid invalidated
|
|
// indexes, which may result from deleting earlier attributes.
|
|
|
|
for (i = 0; i < pAttrib->cAttr; i++)
|
|
{
|
|
CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA DelPara;
|
|
DWORD iAttr = pAttrib->cAttr - i - 1;
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"iSigner=%u, iAttr=%u %hs\n",
|
|
iSigner,
|
|
iAttr,
|
|
pAttrib->rgAttr[iAttr].pszObjId));
|
|
if (0 == strcmp(
|
|
pAttrib->rgAttr[iAttr].pszObjId,
|
|
szOID_ARCHIVED_KEY_ATTR))
|
|
{
|
|
ZeroMemory(&DelPara, sizeof(DelPara));
|
|
DelPara.cbSize = sizeof(DelPara);
|
|
DelPara.dwSignerIndex = iSigner;
|
|
DelPara.dwUnauthAttrIndex = iAttr;
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Delete Key(signer=%u, attrib=%u)\n",
|
|
DelPara.dwSignerIndex,
|
|
DelPara.dwUnauthAttrIndex));
|
|
|
|
if (!CryptMsgControl(
|
|
hMsg,
|
|
0,
|
|
CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR,
|
|
&DelPara))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptMsgControl");
|
|
}
|
|
fKeyDeleted = TRUE;
|
|
}
|
|
}
|
|
LocalFree(pAttrib);
|
|
pAttrib = NULL;
|
|
}
|
|
if (!fKeyDeleted)
|
|
{
|
|
hr = S_FALSE;
|
|
_JumpError(hr, error, "no Encrypted Key attribute");
|
|
}
|
|
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_ENCODED_MESSAGE,
|
|
0, // dwIndex
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pbWithoutKey,
|
|
&cbWithoutKey);
|
|
_JumpIfError(hr, error, "myCryptMsgGetParam(content)");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestRawRequest,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
cbWithoutKey,
|
|
pbWithoutKey);
|
|
_JumpIfError(hr, error, "SetProperty(request)");
|
|
|
|
error:
|
|
if (NULL != pAttrib)
|
|
{
|
|
LocalFree(pAttrib);
|
|
}
|
|
if (NULL != pbWithoutKey)
|
|
{
|
|
LocalFree(pbWithoutKey);
|
|
}
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define PSA_DISALLOW_EXTENSIONS 0x00000001
|
|
#define PSA_DISALLOW_NAMEVALUEPAIRS 0x00000002
|
|
#define PSA_DISALLOW_ARCHIVEDKEY 0x00000004
|
|
|
|
HRESULT
|
|
pkcsSetAttributes(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD ExtFlags,
|
|
IN DWORD dwDisallowFlags,
|
|
IN CRYPT_ATTRIBUTE const *rgAttrib,
|
|
IN DWORD cAttrib,
|
|
IN DWORD cbRequest,
|
|
OPTIONAL IN BYTE const *pbRequest,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_ATTRIBUTE const *pAttrib;
|
|
CRYPT_ATTRIBUTE const *pAttribEnd;
|
|
DWORD i;
|
|
BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note
|
|
CRYPT_DATA_BLOB *pBlob = NULL;
|
|
BOOL fSubjectModified = FALSE;
|
|
|
|
ZeroMemory(afSubjectTable, sizeof(afSubjectTable));
|
|
CSASSERT(CSExpr(0 == FALSE));
|
|
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
|
|
pAttribEnd = &rgAttrib[cAttrib];
|
|
for (pAttrib = rgAttrib; pAttrib < pAttribEnd; pAttrib++)
|
|
{
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_OS_VERSION))
|
|
{
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
hr = pkcsSetOSVersion(prow, pAttrib->rgValue);
|
|
_JumpIfError(hr, error, "pkcsSetOSVersion");
|
|
}
|
|
else
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_ENROLLMENT_CSP_PROVIDER))
|
|
{
|
|
// Check to see if we have a CSPPROVIDER attribute. We use this in
|
|
// the policy module to determine if xenroll generated the request,
|
|
// so we can behave differently for old xenroll requests (put the
|
|
// UPN in the subject to avoid enrollment loops)
|
|
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
hr = pkcsSetCSPProvider(prow, pAttrib->rgValue);
|
|
_JumpIfError(hr, error, "pkcsSetCSPProvider");
|
|
}
|
|
else
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_ENCRYPTED_KEY_HASH))
|
|
{
|
|
DWORD cb;
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (NULL != pResult->pbKeyHashIn || NULL != pBlob)
|
|
{
|
|
_JumpError(hr, error, "Multiple key hashes");
|
|
}
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
pAttrib->rgValue[0].pbData,
|
|
pAttrib->rgValue[0].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pBlob,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
pResult->pbKeyHashIn = (BYTE *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
pBlob->cbData);
|
|
if (NULL == pResult->pbKeyHashIn)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pResult->pbKeyHashIn, pBlob->pbData, pBlob->cbData);
|
|
pResult->cbKeyHashIn = pBlob->cbData;
|
|
}
|
|
else
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_ARCHIVED_KEY_ATTR))
|
|
{
|
|
// Pull encrypted private key out of the attribute for archival.
|
|
//
|
|
// Save request in database without private key now, to keep the
|
|
// error path from saving the request later *with* the key.
|
|
|
|
if (NULL != pbRequest)
|
|
{
|
|
hr = pkcsSaveRequestWithoutArchivedKey(
|
|
prow,
|
|
cbRequest,
|
|
pbRequest);
|
|
_PrintIfError(hr, "pkcsSaveRequestWithoutArchivedKey");
|
|
if (S_OK == hr)
|
|
{
|
|
pResult->fRequestSavedWithoutKey = TRUE;
|
|
}
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (PSA_DISALLOW_ARCHIVEDKEY & dwDisallowFlags)
|
|
{
|
|
hr = CERTSRV_E_BAD_REQUEST_KEY_ARCHIVAL;
|
|
_JumpError(hr, error, "archived key disallowed");
|
|
}
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
hr = PKCSArchivePrivateKey(
|
|
prow,
|
|
FALSE,
|
|
FALSE,
|
|
&pAttrib->rgValue[0],
|
|
pResult);
|
|
_JumpIfError(hr, error, "PKCSArchivePrivateKey");
|
|
}
|
|
else
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_CERT_EXTENSIONS) ||
|
|
0 == strcmp(pAttrib->pszObjId, szOID_RSA_certExtensions))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (PSA_DISALLOW_EXTENSIONS & dwDisallowFlags)
|
|
{
|
|
_JumpError(hr, error, "extensions disallowed");
|
|
}
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
hr = pkcsSetExtensionsFromAttributeBlob(
|
|
prow,
|
|
ExtFlags,
|
|
pAttrib);
|
|
_JumpIfError(hr, error, "pkcsSetExtensionsFromAttributeBlob");
|
|
}
|
|
else
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_ENROLLMENT_NAME_VALUE_PAIR))
|
|
{
|
|
// Can't apply name value pair attributes to a renewal or CMC
|
|
|
|
if (PSA_DISALLOW_NAMEVALUEPAIRS & dwDisallowFlags)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "name/value pairs disallowed");
|
|
}
|
|
|
|
for (i = 0; i < pAttrib->cValue; i++)
|
|
{
|
|
CRYPT_ENROLLMENT_NAME_VALUE_PAIR *pInfo = NULL;
|
|
DWORD cbInfo = 0;
|
|
CRYPT_ATTR_BLOB const *pvalue = &pAttrib->rgValue[i];
|
|
|
|
if (!myDecodeNameValuePair(
|
|
X509_ASN_ENCODING,
|
|
pvalue->pbData,
|
|
pvalue->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pInfo,
|
|
&cbInfo))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "myDecodeNameValuePair");
|
|
|
|
// if the attribute name & value are both non-empty ...
|
|
}
|
|
|
|
BOOL fEnrollOnBehalfOf = FALSE;
|
|
BOOL fSubjectModifiedT = FALSE;
|
|
|
|
hr = pkcsSetAttributeProperty(
|
|
prow,
|
|
pInfo->pwszName,
|
|
pInfo->pwszValue,
|
|
PROPTABLE_REQUEST,
|
|
afSubjectTable,
|
|
&fSubjectModified,
|
|
NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL);
|
|
if (fSubjectModifiedT)
|
|
{
|
|
fSubjectModified = TRUE;
|
|
}
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = TRUE;
|
|
}
|
|
if (NULL != pInfo)
|
|
{
|
|
LocalFree(pInfo);
|
|
}
|
|
_JumpIfError(hr, error, "pkcsSetAttributeProperty");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"Skipping authenticated attribute %hs\n",
|
|
pAttrib->pszObjId));
|
|
}
|
|
}
|
|
if (fSubjectModified)
|
|
{
|
|
hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pBlob)
|
|
{
|
|
LocalFree(pBlob);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifyCertContext(
|
|
OPTIONAL IN FILETIME const *pft,
|
|
IN BOOL fTimeOnly,
|
|
IN CERT_CONTEXT const *pcc)
|
|
{
|
|
HRESULT hr;
|
|
FILETIME ft;
|
|
|
|
if (NULL == pft)
|
|
{
|
|
GetSystemTimeAsFileTime(&ft);
|
|
pft = &ft;
|
|
}
|
|
if (0 > CompareFileTime(pft, &pcc->pCertInfo->NotBefore))
|
|
{
|
|
hr = CERT_E_EXPIRED;
|
|
_JumpError(hr, error, "cert not yet valid");
|
|
}
|
|
if (0 < CompareFileTime(pft, &pcc->pCertInfo->NotAfter))
|
|
{
|
|
hr = CERT_E_EXPIRED;
|
|
_JumpError(hr, error, "cert is expired");
|
|
}
|
|
if (!fTimeOnly)
|
|
{
|
|
hr = myVerifyCertContext(
|
|
pcc, // pCert
|
|
g_dwVerifyCertFlags, // dwFlags
|
|
0, // cUsageOids
|
|
NULL, // apszUsageOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
NULL, // hAdditionalStore
|
|
NULL); // ppwszMissingIssuer
|
|
_JumpIfError(hr, error, "myVerifyCertContext");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetValidityPeriod(
|
|
IN ICertDBRow *prow,
|
|
IN CACTX *pCAContext,
|
|
OPTIONAL IN FILETIME const *pftNotBefore,
|
|
OPTIONAL IN FILETIME const *pftNotAfter,
|
|
IN LONG lValidityPeriodCount,
|
|
IN enum ENUM_PERIOD enumValidityPeriod)
|
|
{
|
|
HRESULT hr;
|
|
FILETIME ftNotBefore;
|
|
FILETIME ftNotAfter;
|
|
LONGLONG delta;
|
|
|
|
GetSystemTimeAsFileTime(&ftNotBefore);
|
|
|
|
hr = pkcsVerifyCertContext(&ftNotBefore, TRUE, pCAContext->pccCA);
|
|
_JumpIfErrorStr(hr, error, "pkcsVerifyCertContext", L"CA cert invalid");
|
|
|
|
if (NULL != pftNotBefore && NULL != pftNotAfter)
|
|
{
|
|
ftNotBefore = *pftNotBefore; // Caller already computed tome stamps
|
|
ftNotAfter = *pftNotAfter;
|
|
}
|
|
else
|
|
{
|
|
ftNotAfter = ftNotBefore;
|
|
|
|
// Set start date to the current time minus clock skew. But ensure the
|
|
// new cert's start date is not before the CA certificate's start date.
|
|
|
|
delta = g_dwClockSkewMinutes * CVT_MINUTES;
|
|
myAddToFileTime(&ftNotBefore, -delta * CVT_BASE);
|
|
|
|
if (0 > CompareFileTime(
|
|
&ftNotBefore,
|
|
&pCAContext->pccCA->pCertInfo->NotBefore))
|
|
{
|
|
ftNotBefore = pCAContext->pccCA->pCertInfo->NotBefore;
|
|
}
|
|
|
|
// Set the end date to the start date plus the registry-configured
|
|
// validity period. Then clamp the new cert's end date to the CA
|
|
// certificate's end date.
|
|
|
|
myMakeExprDateTime(
|
|
&ftNotAfter,
|
|
lValidityPeriodCount,
|
|
enumValidityPeriod);
|
|
|
|
if (0 < CompareFileTime(
|
|
&ftNotAfter,
|
|
&pCAContext->pccCA->pCertInfo->NotAfter))
|
|
{
|
|
ftNotAfter = pCAContext->pccCA->pCertInfo->NotAfter;
|
|
if (CERTLOG_VERBOSE <= g_dwLogLevel)
|
|
{
|
|
DWORD dwRequestId;
|
|
WCHAR const *apwsz[2];
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
|
|
prow->GetRowId(&dwRequestId);
|
|
wsprintf(awc, L"%u", dwRequestId);
|
|
apwsz[0] = g_wszCommonName;
|
|
apwsz[1] = awc;
|
|
|
|
LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_CLAMPED_BY_CA_CERT,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateNotBeforeDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(ftNotBefore),
|
|
(BYTE *) &ftNotBefore);
|
|
_JumpIfError(hr, error, "pkcsSetValidityPeriod:SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateNotAfterDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(ftNotAfter),
|
|
(BYTE *) &ftNotAfter);
|
|
_JumpIfError(hr, error, "pkcsSetValidityPeriod:SetProperty");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetServerExtension(
|
|
IN ICertDBRow *prow,
|
|
IN WCHAR const *pwszObjId,
|
|
IN DWORD cbExt,
|
|
OPTIONAL IN BYTE const *pbExt)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbOld = NULL;
|
|
DWORD cbOld;
|
|
DWORD ExtFlags;
|
|
|
|
ExtFlags = 0;
|
|
if (NULL == pbExt)
|
|
{
|
|
CSASSERT(0 == cbExt);
|
|
|
|
hr = PropGetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
pwszObjId,
|
|
&ExtFlags,
|
|
&cbOld,
|
|
&pbOld);
|
|
if (S_OK != hr)
|
|
{
|
|
ExtFlags = 0;
|
|
}
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
ExtFlags |= EXTENSION_DISABLE_FLAG;
|
|
}
|
|
}
|
|
if (NULL != pbExt || (EXTENSION_DISABLE_FLAG & ExtFlags))
|
|
{
|
|
ExtFlags &= ~EXTENSION_ORIGIN_MASK;
|
|
ExtFlags |= EXTENSION_ORIGIN_SERVER;
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
pwszObjId,
|
|
ExtFlags,
|
|
cbExt,
|
|
pbExt);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pbOld)
|
|
{
|
|
LocalFree(pbOld);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetPublicKeyProperties(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_PUBLIC_KEY_INFO const *pSubjectPublicKeyInfo)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszObjId = NULL;
|
|
CERT_EXTENSION ext;
|
|
DWORD ExtFlags;
|
|
DWORD dwCaller;
|
|
DWORD cbitKey;
|
|
|
|
ext.Value.pbData = NULL;
|
|
|
|
// Public Key size must be a multiple of 8 bits.
|
|
if (0 != pSubjectPublicKeyInfo->PublicKey.cUnusedBits)
|
|
{
|
|
hr = NTE_BAD_KEY;
|
|
_JumpError(hr, error, "PublicKey.cUnusedBits");
|
|
}
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateRawPublicKey,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
pSubjectPublicKeyInfo->PublicKey.cbData,
|
|
pSubjectPublicKeyInfo->PublicKey.pbData);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
cbitKey = CertGetPublicKeyLength(
|
|
X509_ASN_ENCODING,
|
|
const_cast<CERT_PUBLIC_KEY_INFO *>(pSubjectPublicKeyInfo));
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificatePublicKeyLength,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(cbitKey),
|
|
(BYTE const *) &cbitKey);
|
|
_JumpIfError(hr, error, "SetProperty(KeyLength)");
|
|
|
|
if (!myConvertSzToWsz(
|
|
&pwszObjId,
|
|
pSubjectPublicKeyInfo->Algorithm.pszObjId,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(AlgObjId)");
|
|
}
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificatePublicKeyAlgorithm,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszObjId);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
if (NULL != pSubjectPublicKeyInfo->Algorithm.Parameters.pbData)
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateRawPublicKeyAlgorithmParameters,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
pSubjectPublicKeyInfo->Algorithm.Parameters.cbData,
|
|
pSubjectPublicKeyInfo->Algorithm.Parameters.pbData);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
|
|
// Subject Key Identifier extension:
|
|
|
|
hr = PropGetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
TEXT(szOID_SUBJECT_KEY_IDENTIFIER),
|
|
&ExtFlags,
|
|
&ext.Value.cbData,
|
|
&ext.Value.pbData);
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
_JumpIfError(hr, error, "PropGetExtension");
|
|
|
|
dwCaller = PROPCALLER_REQUEST;
|
|
ExtFlags &= ~EXTENSION_DISABLE_FLAG;
|
|
}
|
|
else
|
|
{
|
|
dwCaller = PROPCALLER_SERVER;
|
|
ExtFlags = EXTENSION_ORIGIN_SERVER;
|
|
|
|
hr = myCreateSubjectKeyIdentifierExtension(
|
|
pSubjectPublicKeyInfo,
|
|
&ext.Value.pbData,
|
|
&ext.Value.cbData);
|
|
_JumpIfError(hr, error, "myCreateSubjectKeyIdentifierExtension");
|
|
}
|
|
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | dwCaller,
|
|
TEXT(szOID_SUBJECT_KEY_IDENTIFIER),
|
|
ExtFlags,
|
|
ext.Value.cbData,
|
|
ext.Value.pbData);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
error:
|
|
if (NULL != ext.Value.pbData)
|
|
{
|
|
LocalFree(ext.Value.pbData);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsParsePKCS10Request(
|
|
IN DWORD DBGCODE(dwFlags),
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbRequest,
|
|
IN BYTE const *pbRequest,
|
|
IN CERT_CONTEXT const *pSigningAuthority,
|
|
OUT BOOL *pfRenewal,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbCertInfo;
|
|
CERT_REQUEST_INFO *pRequestInfo = NULL;
|
|
CRYPT_ATTRIBUTE const *pAttrib;
|
|
CRYPT_ATTRIBUTE const *pAttribEnd;
|
|
CRYPT_ATTR_BLOB *pAttrBlob;
|
|
CERT_CONTEXT const *pOldCert = NULL;
|
|
DWORD dwRequestFlags = 0;
|
|
BOOL fRenewal = FALSE;
|
|
BOOL fSubjectNameSet;
|
|
BOOL fReorderLikeRDNs;
|
|
|
|
CSASSERT(CR_IN_PKCS10 == (CR_IN_FORMATMASK & dwFlags));
|
|
CSASSERT(
|
|
CR_IN_PKCS10 == (CR_IN_FORMATMASK & pResult->dwFlagsTop) ||
|
|
CR_IN_PKCS7 == (CR_IN_FORMATMASK & pResult->dwFlagsTop) ||
|
|
CR_IN_CMC == (CR_IN_FORMATMASK & pResult->dwFlagsTop));
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_REQUEST_TO_BE_SIGNED,
|
|
pbRequest,
|
|
cbRequest,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pRequestInfo,
|
|
&cbCertInfo))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
// verify with the public key passed in the PKCS10
|
|
|
|
if (!CryptVerifyCertificateSignature(
|
|
NULL,
|
|
X509_ASN_ENCODING,
|
|
const_cast<BYTE *>(pbRequest),
|
|
cbRequest,
|
|
&pRequestInfo->SubjectPublicKeyInfo))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError3(
|
|
hr,
|
|
"CryptVerifyCertificateSignature",
|
|
E_INVALIDARG,
|
|
CRYPT_E_ASN1_BADTAG);
|
|
if (CR_IN_CMC == (CR_IN_FORMATMASK & pResult->dwFlagsTop))
|
|
{
|
|
if (E_INVALIDARG == hr) // NULL signature?
|
|
{
|
|
CRYPT_DATA_BLOB Blob;
|
|
|
|
Blob.cbData = cbRequest;
|
|
Blob.pbData = const_cast<BYTE *>(pbRequest);
|
|
if (!CryptVerifyCertificateSignatureEx(
|
|
NULL, // hCryptProv
|
|
X509_ASN_ENCODING,
|
|
CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB,
|
|
&Blob,
|
|
CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL,
|
|
NULL, // pvIssuer
|
|
0, // dwFlags
|
|
NULL)) // pvReserved
|
|
{
|
|
HRESULT hr2 = myHLastError();
|
|
|
|
_PrintError(hr2, "CryptVerifyCertificateSignatureEx");
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
if (E_INVALIDARG == hr || CRYPT_E_ASN1_BADTAG == hr)
|
|
{
|
|
hr = NTE_BAD_SIGNATURE;
|
|
}
|
|
_JumpIfError(hr, error, "CryptVerifyCertificateSignature");
|
|
}
|
|
|
|
// handle renewal certificate extensions BEFORE processing the rest of
|
|
// the request attributes (which may also contain extensions)
|
|
|
|
pAttribEnd = &pRequestInfo->rgAttribute[pRequestInfo->cAttribute];
|
|
for (pAttrib = pRequestInfo->rgAttribute; pAttrib < pAttribEnd; pAttrib++)
|
|
{
|
|
if (0 == strcmp(pAttrib->pszObjId, szOID_RENEWAL_CERTIFICATE))
|
|
{
|
|
hr = CERTSRV_E_BAD_RENEWAL_CERT_ATTRIBUTE;
|
|
if (fRenewal)
|
|
{
|
|
_JumpError(hr, error, "Multiple renewal certs!");
|
|
}
|
|
if (CR_IN_PKCS7 != (CR_IN_FORMATMASK & pResult->dwFlagsTop) &&
|
|
CR_IN_CMC != (CR_IN_FORMATMASK & pResult->dwFlagsTop))
|
|
{
|
|
_JumpError(hr, error, "renewal cert must be in PKCS7 or CMC");
|
|
}
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
pAttrBlob = pAttrib->rgValue;
|
|
|
|
pOldCert = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pAttrBlob->pbData,
|
|
pAttrBlob->cbData);
|
|
if (NULL == pOldCert)
|
|
{
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
// The old raw certificate, and the signer of the PKCS7 must match!
|
|
|
|
if (NULL == pSigningAuthority ||
|
|
!myAreCertContextBlobsSame(pSigningAuthority, pOldCert))
|
|
{
|
|
_JumpError(hr, error, "myAreCertContextBlobsSame");
|
|
}
|
|
|
|
// This is a renewal, mark it as such.
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestRawOldCertificate,
|
|
PROPTYPE_BINARY |
|
|
PROPCALLER_SERVER |
|
|
PROPTABLE_REQUEST,
|
|
pAttrBlob->cbData,
|
|
pAttrBlob->pbData);
|
|
_JumpIfError(hr, error, "SetProperty(old cert)");
|
|
|
|
hr = pkcsSetExtensions(
|
|
prow,
|
|
EXTENSION_ORIGIN_RENEWALCERT | EXTENSION_DISABLE_FLAG,
|
|
pOldCert->pCertInfo->rgExtension,
|
|
pOldCert->pCertInfo->cExtension);
|
|
_JumpIfError(hr, error, "pkcsSetExtensions(old cert)");
|
|
|
|
fRenewal = TRUE;
|
|
|
|
if (CertComparePublicKeyInfo(
|
|
X509_ASN_ENCODING,
|
|
&pRequestInfo->SubjectPublicKeyInfo,
|
|
&pOldCert->pCertInfo->SubjectPublicKeyInfo))
|
|
{
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pOldCert,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
hr = MultiByteIntegerToBstr(
|
|
TRUE,
|
|
cbHash,
|
|
abHash,
|
|
&pResult->strRenewalCertHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle certificate extensions/known atributes
|
|
|
|
hr = pkcsSetAttributes(
|
|
prow,
|
|
EXTENSION_ORIGIN_REQUEST,
|
|
PSA_DISALLOW_ARCHIVEDKEY,
|
|
pRequestInfo->rgAttribute,
|
|
pRequestInfo->cAttribute,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetAttributes(PKCS10)");
|
|
|
|
// If an XEnroll request, reverse the order of similar adjacent RDNs
|
|
|
|
fReorderLikeRDNs = FALSE;
|
|
if (0 == (CRLF_DISABLE_RDN_REORDER & g_dwCRLFlags))
|
|
{
|
|
DWORD cb;
|
|
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestOSVersion,
|
|
PROPTYPE_STRING |
|
|
PROPCALLER_SERVER |
|
|
PROPTABLE_ATTRIBUTE,
|
|
NULL,
|
|
&cb,
|
|
NULL);
|
|
_PrintIfError(hr, "GetProperty");
|
|
if (S_OK == hr && 0 != cb)
|
|
{
|
|
fReorderLikeRDNs = TRUE;
|
|
}
|
|
}
|
|
hr = pkcsSetRequestNameInfo(
|
|
prow,
|
|
&pRequestInfo->Subject,
|
|
NULL, // pwszCNSuffix
|
|
fReorderLikeRDNs,
|
|
&dwRequestFlags,
|
|
&fSubjectNameSet);
|
|
_JumpIfError(hr, error, "pkcsSetRequestNameInfo");
|
|
|
|
if (fSubjectNameSet)
|
|
{
|
|
dwRequestFlags |= CR_FLG_SUBJECTUNMODIFIED;
|
|
}
|
|
|
|
if (fRenewal)
|
|
{
|
|
if (!fSubjectNameSet)
|
|
{
|
|
CSASSERT(NULL != pOldCert);
|
|
CSASSERT(NULL != pOldCert->pCertInfo);
|
|
hr = pkcsSetRequestNameInfo(
|
|
prow,
|
|
&pOldCert->pCertInfo->Subject,
|
|
NULL, // pwszCNSuffix
|
|
FALSE, // fReorderLikeRDNs
|
|
&dwRequestFlags,
|
|
&fSubjectNameSet);
|
|
_JumpIfError(hr, error, "pkcsSetRequestNameInfo");
|
|
}
|
|
dwRequestFlags |= CR_FLG_RENEWAL;
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwRequestFlags),
|
|
(BYTE const *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "SetProperty(RequestFlags)");
|
|
|
|
hr = pkcsSetPublicKeyProperties(prow, &pRequestInfo->SubjectPublicKeyInfo);
|
|
_JumpIfError(hr, error, "pkcsSetPublicKeyProperties");
|
|
|
|
hr = PKCSSetServerProperties(
|
|
prow,
|
|
NULL, // use default signing context
|
|
NULL, // pftNotBefore
|
|
NULL, // pftNotAfter
|
|
g_lValidityPeriodCount,
|
|
g_enumValidityPeriod);
|
|
_JumpIfError(hr, error, "PKCSSetServerProperties");
|
|
|
|
if (NULL != pfRenewal)
|
|
{
|
|
*pfRenewal = fRenewal;
|
|
}
|
|
|
|
error:
|
|
if (NULL != pOldCert)
|
|
{
|
|
CertFreeCertificateContext(pOldCert);
|
|
}
|
|
if (NULL != pRequestInfo)
|
|
{
|
|
LocalFree(pRequestInfo);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSVerifyChallengeString(
|
|
IN ICertDBRow *prow)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cb;
|
|
WCHAR wszPassed[MAX_PATH];
|
|
WCHAR wszExpected[MAX_PATH];
|
|
|
|
cb = sizeof(wszExpected);
|
|
hr = prow->GetProperty(
|
|
g_wszPropExpectedChallenge,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) wszExpected);
|
|
if (S_OK != hr || L'\0' == wszExpected[0])
|
|
{
|
|
hr = S_OK; // no challenge expected
|
|
goto error;
|
|
}
|
|
|
|
cb = sizeof(wszPassed);
|
|
hr = prow->GetProperty(
|
|
g_wszPropChallenge,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) wszPassed);
|
|
if (S_OK != hr)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD);
|
|
_JumpError(hr, error, "Missing Challenge String");
|
|
}
|
|
if (0 != wcscmp(wszExpected, wszPassed))
|
|
{
|
|
CONSOLEPRINT2((
|
|
DBG_SS_CERTSRV,
|
|
"Challenge: passed(%ws) expected(%ws)\n",
|
|
wszPassed,
|
|
wszExpected));
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD);
|
|
_JumpError(hr, error, "Invalid Challenge String");
|
|
}
|
|
hr = prow->SetProperty(
|
|
g_wszPropExpectedChallenge,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE,
|
|
0,
|
|
NULL);
|
|
_PrintIfError(hr, "SetProperty");
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsParseKeyGenRequest(
|
|
IN DWORD DBGCODE(dwFlags),
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbRequest,
|
|
IN BYTE const *pbRequest,
|
|
IN OUT CERTSRV_RESULT_CONTEXT * /* pResult */ )
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbKeyGenRequest;
|
|
CERT_KEYGEN_REQUEST_INFO *pKeyGenRequest = NULL;
|
|
DWORD dwRequestFlags;
|
|
|
|
CSASSERT(CR_IN_KEYGEN == (CR_IN_FORMATMASK & dwFlags));
|
|
|
|
// Decode KeyGenRequest structure
|
|
if (!myDecodeKeyGenRequest(
|
|
pbRequest,
|
|
cbRequest,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pKeyGenRequest,
|
|
&cbKeyGenRequest))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeKeyGen");
|
|
}
|
|
|
|
// verify with the public key passed in the PKCS10
|
|
if (!CryptVerifyCertificateSignature(
|
|
NULL,
|
|
X509_ASN_ENCODING,
|
|
(BYTE *) pbRequest,
|
|
cbRequest,
|
|
&pKeyGenRequest->SubjectPublicKeyInfo))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptVerifyCertificateSignature");
|
|
}
|
|
|
|
hr = pkcsSetPublicKeyProperties(
|
|
prow,
|
|
&pKeyGenRequest->SubjectPublicKeyInfo);
|
|
_JumpIfError(hr, error, "pkcsSetPublicKeyProperties");
|
|
|
|
hr = PKCSSetServerProperties(
|
|
prow,
|
|
NULL, // use default signing context
|
|
NULL, // pftNotBefore
|
|
NULL, // pftNotAfter
|
|
g_lValidityPeriodCount,
|
|
g_enumValidityPeriod);
|
|
_JumpIfError(hr, error, "PKCSSetServerProperties");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropExpectedChallenge,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE,
|
|
MAXDWORD,
|
|
(BYTE *) pKeyGenRequest->pwszChallengeString);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
dwRequestFlags = 0;
|
|
switch (ENUM_TELETEX_MASK & g_fForceTeletex)
|
|
{
|
|
case ENUM_TELETEX_ON:
|
|
case ENUM_TELETEX_AUTO:
|
|
dwRequestFlags |= CR_FLG_FORCETELETEX;
|
|
break;
|
|
}
|
|
if (ENUM_TELETEX_UTF8 & g_fForceTeletex)
|
|
{
|
|
dwRequestFlags |= CR_FLG_FORCEUTF8;
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwRequestFlags),
|
|
(BYTE const *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "SetProperty(RequestFlags)");
|
|
|
|
error:
|
|
if (NULL != pKeyGenRequest)
|
|
{
|
|
LocalFree(pKeyGenRequest);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Validate the certificate:
|
|
// Signed by CA Certificate
|
|
// issuer name == CA Certificate subject
|
|
// NotBefore >= CA Certificate NotBefore
|
|
// NotAfter <= CA Certificate NotAfter
|
|
// if KEYID2 issuer KeyId set: == CA Certificate KeyId
|
|
// if KEYID2 issuer Name set: == CA Certificate Issuer
|
|
// if KEYID2 issuer Serial Number set: == CA Certificate serial number
|
|
|
|
HRESULT
|
|
pkcsVerifyCertIssuer(
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN CACTX const *pCAContext)
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
CERT_INFO const *pCertInfo = pCert->pCertInfo;
|
|
CERT_INFO const *pCACertInfo;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_EXTENSION const *pExtEnd;
|
|
CERT_AUTHORITY_KEY_ID2_INFO *pkeyAuth = NULL;
|
|
DWORD cbkeyAuth;
|
|
CERT_NAME_BLOB const *pName;
|
|
|
|
CSASSERT(NULL != pCAContext);
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
hr = NTE_BAD_SIGNATURE;
|
|
_JumpError2(hr, error, "pCAContext->pccCA", hr);
|
|
}
|
|
pCACertInfo = pCAContext->pccCA->pCertInfo;
|
|
|
|
// verify with the CA cert's public key
|
|
|
|
if (!CryptVerifyCertificateSignature(
|
|
NULL,
|
|
X509_ASN_ENCODING,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
const_cast<CERT_PUBLIC_KEY_INFO *>(
|
|
&pCACertInfo->SubjectPublicKeyInfo)))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError2(hr, error, "CryptVerifyCertificateSignature", hr);
|
|
}
|
|
|
|
// Check Issuer name:
|
|
|
|
if (!myAreBlobsSame(
|
|
pCACertInfo->Subject.pbData,
|
|
pCACertInfo->Subject.cbData,
|
|
pCertInfo->Issuer.pbData,
|
|
pCertInfo->Issuer.cbData))
|
|
{
|
|
_JumpError(hr, error, "Bad Issuer Name");
|
|
}
|
|
|
|
// Check that NotBefore >= CA Certificate NotBefore
|
|
|
|
if (0 > CompareFileTime(&pCertInfo->NotBefore, &pCACertInfo->NotBefore))
|
|
{
|
|
_JumpError(hr, error, "NotBefore too early");
|
|
}
|
|
|
|
// Check that NotAfter <= CA Certificate NotAfter
|
|
|
|
if (0 < CompareFileTime(&pCertInfo->NotAfter, &pCACertInfo->NotAfter))
|
|
{
|
|
_JumpError(hr, error, "NotAfter too late");
|
|
}
|
|
|
|
pExtEnd = &pCert->pCertInfo->rgExtension[pCert->pCertInfo->cExtension];
|
|
for (pExt = pCert->pCertInfo->rgExtension; pExt < pExtEnd; pExt++)
|
|
{
|
|
if (0 == strcmp(pExt->pszObjId, szOID_AUTHORITY_KEY_IDENTIFIER2))
|
|
{
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_AUTHORITY_KEY_ID2,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pkeyAuth,
|
|
&cbkeyAuth))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
// Check Issuer KeyId:
|
|
|
|
if (NULL != pCAContext->IssuerKeyId.pbData &&
|
|
NULL != pkeyAuth->KeyId.pbData &&
|
|
!myAreBlobsSame(
|
|
pCAContext->IssuerKeyId.pbData,
|
|
pCAContext->IssuerKeyId.cbData,
|
|
pkeyAuth->KeyId.pbData,
|
|
pkeyAuth->KeyId.cbData))
|
|
{
|
|
_JumpError(hr, error, "Bad AuthorityKeyId KeyId");
|
|
}
|
|
|
|
// Check Issuer name:
|
|
|
|
if (1 == pkeyAuth->AuthorityCertIssuer.cAltEntry &&
|
|
CERT_ALT_NAME_DIRECTORY_NAME ==
|
|
pkeyAuth->AuthorityCertIssuer.rgAltEntry[0].dwAltNameChoice)
|
|
{
|
|
pName = &pkeyAuth->AuthorityCertIssuer.rgAltEntry[0].DirectoryName;
|
|
|
|
if (NULL != pName->pbData &&
|
|
!myAreBlobsSame(
|
|
pCACertInfo->Issuer.pbData,
|
|
pCACertInfo->Issuer.cbData,
|
|
pName->pbData,
|
|
pName->cbData))
|
|
{
|
|
_JumpError(hr, error, "Bad AuthorityKeyId Issuer Name");
|
|
}
|
|
}
|
|
|
|
// Check Issuer SerialNumber:
|
|
|
|
if (NULL != pkeyAuth->AuthorityCertSerialNumber.pbData &&
|
|
!myAreSerialNumberBlobsSame(
|
|
&pCACertInfo->SerialNumber,
|
|
&pkeyAuth->AuthorityCertSerialNumber))
|
|
{
|
|
_JumpError(hr, error, "Bad AuthorityKeyId Issuer Serial Number");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pkeyAuth)
|
|
{
|
|
LocalFree(pkeyAuth);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSVerifyIssuedCertificate(
|
|
IN CERT_CONTEXT const *pCert,
|
|
OUT CACTX **ppCAContext)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
CACTX *pCAContext;
|
|
|
|
*ppCAContext = NULL;
|
|
|
|
CSASSERT(0 != g_cCACerts);
|
|
hr = S_OK;
|
|
for (i = g_cCACerts; i > 0; i--)
|
|
{
|
|
pCAContext = &g_aCAContext[i - 1];
|
|
|
|
hr = pkcsVerifyCertIssuer(pCert, pCAContext);
|
|
if (S_OK == hr)
|
|
{
|
|
*ppCAContext = pCAContext;
|
|
break;
|
|
}
|
|
if (HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY) != hr)
|
|
{
|
|
_PrintError2(hr, "pkcsVerifyCertIssuer", NTE_BAD_SIGNATURE);
|
|
}
|
|
}
|
|
|
|
//error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetCertHash(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_CONTEXT const *pcc)
|
|
{
|
|
HRESULT hr;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
BSTR strHash = NULL;
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateHash,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE const *) strHash);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
error:
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetCertAndKeyHashes(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_CONTEXT const *pcc)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbHash = NULL;
|
|
DWORD cbHash;
|
|
BSTR strHash = NULL;
|
|
|
|
hr = pkcsSetCertHash(prow, pcc);
|
|
_JumpIfError(hr, error, "pkcsSetCertHash");
|
|
|
|
hr = myGetPublicKeyHash(
|
|
pcc->pCertInfo,
|
|
&pcc->pCertInfo->SubjectPublicKeyInfo,
|
|
&pbHash,
|
|
&cbHash);
|
|
_JumpIfError(hr, error, "myGetPublicKeyHash");
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateSubjectKeyIdentifier,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE const *) strHash);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
error:
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
if (NULL != pbHash)
|
|
{
|
|
LocalFree(pbHash);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetTemplateProperty(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
HRESULT hr;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_NAME_VALUE *pName = NULL;
|
|
CERT_TEMPLATE_EXT *pTemplate = NULL;
|
|
DWORD cb;
|
|
WCHAR const *pwszTemplate = NULL;
|
|
WCHAR *pwszObjId = NULL;
|
|
|
|
// look for v2 template extension
|
|
|
|
pExt = CertFindExtension(
|
|
szOID_CERTIFICATE_TEMPLATE,
|
|
pCert->pCertInfo->cExtension,
|
|
pCert->pCertInfo->rgExtension);
|
|
if (NULL != pExt)
|
|
{
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERTIFICATE_TEMPLATE,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pTemplate,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "Policy:myDecodeObject");
|
|
}
|
|
if (!myConvertSzToWsz(&pwszObjId, pTemplate->pszObjId, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "Policy:myConvertSzToBstr");
|
|
}
|
|
pwszTemplate = pwszObjId;
|
|
}
|
|
else
|
|
{
|
|
// look for v1 template extension
|
|
|
|
pExt = CertFindExtension(
|
|
szOID_ENROLL_CERTTYPE_EXTENSION,
|
|
pCert->pCertInfo->cExtension,
|
|
pCert->pCertInfo->rgExtension);
|
|
if (NULL != pExt)
|
|
{
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pName,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "Policy:myDecodeObject");
|
|
}
|
|
pwszTemplate = (WCHAR const *) pName->Value.pbData;
|
|
}
|
|
}
|
|
if (NULL != pwszTemplate)
|
|
{
|
|
hr = prow->SetProperty(
|
|
wszPROPCERTIFICATETEMPLATE,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszTemplate);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pName)
|
|
{
|
|
LocalFree(pName);
|
|
}
|
|
if (NULL != pTemplate)
|
|
{
|
|
LocalFree(pTemplate);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetRevocationFields(
|
|
IN ICertDBRow *prow)
|
|
{
|
|
HRESULT hr;
|
|
DWORD DBDisposition;
|
|
DWORD Reason;
|
|
WCHAR *pwszMachineRequesterName = NULL;
|
|
WCHAR const *pwszDisposition = NULL;
|
|
|
|
DBDisposition = DB_DISP_REVOKED;
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestDisposition,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(DBDisposition),
|
|
(BYTE *) &DBDisposition);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = PropSetRequestTimeProperty(prow, g_wszPropRequestRevokedWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
hr = PropSetRequestTimeProperty(
|
|
prow,
|
|
g_wszPropRequestRevokedEffectiveWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
Reason = CRL_REASON_UNSPECIFIED;
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestRevokedReason,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(Reason),
|
|
(BYTE const *) &Reason);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = myGetComputerObjectName(NameSamCompatible, &pwszMachineRequesterName);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myGetComputerObjectName");
|
|
|
|
hr = myGetUserNameEx(NameSamCompatible, &pwszMachineRequesterName);
|
|
_JumpIfError(hr, error, "myGetUserNameEx");
|
|
}
|
|
|
|
pwszDisposition = CoreBuildDispositionString(
|
|
g_pwszRevokedBy,
|
|
pwszMachineRequesterName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
S_OK,
|
|
FALSE);
|
|
if (NULL == pwszDisposition)
|
|
{
|
|
pwszDisposition = g_pwszRevokedBy;
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestDispositionMessage,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszDisposition);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
error:
|
|
if (NULL != pwszMachineRequesterName)
|
|
{
|
|
LocalFree(pwszMachineRequesterName);
|
|
}
|
|
if (NULL != pwszDisposition && pwszDisposition != g_pwszRevokedBy)
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(pwszDisposition));
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSParseImportedCertificate(
|
|
IN ICertDBRow *prow,
|
|
IN BOOL fCrossCert, // else random imported cert
|
|
IN DWORD Disposition,
|
|
OPTIONAL IN CACTX const *pCAContext,
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
HRESULT hr;
|
|
CERT_INFO const *pCertInfo = pCert->pCertInfo;
|
|
DWORD dwRequestFlags = 0;
|
|
BOOL fSubjectNameSet;
|
|
HRESULT ErrCode = S_OK;
|
|
BSTR strSerialNumber = NULL;
|
|
|
|
// set raw cert property in the db
|
|
hr = prow->SetProperty(
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
pCert->cbCertEncoded,
|
|
pCert->pbCertEncoded);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
// set extensions
|
|
hr = pkcsSetExtensions(
|
|
prow,
|
|
EXTENSION_ORIGIN_IMPORTEDCERT,
|
|
pCertInfo->rgExtension,
|
|
pCertInfo->cExtension);
|
|
_JumpIfError(hr, error, "pkcsSetExtensions");
|
|
|
|
// set request name info
|
|
hr = pkcsSetRequestNameInfo(
|
|
prow,
|
|
&pCertInfo->Subject,
|
|
NULL, // pwszCNSuffix
|
|
FALSE, // fReorderLikeRDNs
|
|
&dwRequestFlags,
|
|
&fSubjectNameSet);
|
|
_JumpIfError(hr, error, "pkcsSetRequestNameInfo");
|
|
|
|
hr = pkcsSetPublicKeyProperties(prow, &pCertInfo->SubjectPublicKeyInfo);
|
|
_JumpIfError(hr, error, "pkcsSetPublicKeyProperties");
|
|
|
|
hr = prow->CopyRequestNames();
|
|
_JumpIfError(hr, error, "CopyRequestNames");
|
|
|
|
hr = pkcsSetCertAndKeyHashes(prow, pCert);
|
|
_JumpIfError(hr, error, "pkcsSetCertAndKeyHashes");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateNotBeforeDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(pCertInfo->NotBefore),
|
|
(BYTE *) &pCertInfo->NotBefore);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateNotAfterDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(pCertInfo->NotAfter),
|
|
(BYTE *) &pCertInfo->NotAfter);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropSubjectRawName,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
pCertInfo->Subject.cbData,
|
|
pCertInfo->Subject.pbData);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
// set distinguished name
|
|
pkcsSetDistinguishedName(prow, PROPTABLE_CERTIFICATE, &pCertInfo->Subject);
|
|
|
|
if (fCrossCert)
|
|
{
|
|
dwRequestFlags |= CR_FLG_CACROSSCERT;
|
|
}
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwRequestFlags),
|
|
(BYTE const *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "SetProperty(RequestFlags)");
|
|
|
|
// set disposition issued
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestDisposition,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(Disposition),
|
|
(BYTE const *) &Disposition);
|
|
_JumpIfError(hr, error, "SetProperty(disposition)");
|
|
|
|
if (DB_DISP_REVOKED == Disposition)
|
|
{
|
|
hr = pkcsSetRevocationFields(prow);
|
|
_JumpIfError(hr, error, "pkcsSetRevocationFields");
|
|
}
|
|
|
|
// set disposition status code
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestStatusCode,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(ErrCode),
|
|
(BYTE const *) &ErrCode);
|
|
_JumpIfError(hr, error, "SetProperty(status code)");
|
|
|
|
if (NULL != pCAContext)
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateIssuerNameID,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(pCAContext->NameId),
|
|
(BYTE *) &pCAContext->NameId);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
|
|
hr = PropSetRequestTimeProperty(prow, g_wszPropRequestSubmittedWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
hr = PropSetRequestTimeProperty(prow, g_wszPropRequestResolvedWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
hr = pkcsSetTemplateProperty(prow, pCert);
|
|
_JumpIfError(hr, error, "pkcsSetTemplateProperty");
|
|
|
|
// Convert serial number to string and set in DB
|
|
|
|
hr = MultiByteIntegerToBstr(
|
|
FALSE,
|
|
pCertInfo->SerialNumber.cbData,
|
|
pCertInfo->SerialNumber.pbData,
|
|
&strSerialNumber);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateSerialNumber,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE *) strSerialNumber);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
error:
|
|
if (NULL != strSerialNumber)
|
|
{
|
|
SysFreeString(strSerialNumber);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Return TRUE if Data and Cert references apply to the specified CMC message
|
|
|
|
BOOL
|
|
pkcsCMCReferenceMatch(
|
|
IN DWORD DataReference, // nested CMC message Body Part Id
|
|
IN DWORD CertReference, // PKCS10 Cert Request Body Part Id
|
|
IN DWORD dwCmcDataReference,
|
|
IN DWORD cCertReference,
|
|
IN DWORD const *rgdwCertReference)
|
|
{
|
|
BOOL fMatch = FALSE;
|
|
DWORD i;
|
|
|
|
if (MAXDWORD != DataReference && dwCmcDataReference == DataReference)
|
|
{
|
|
fMatch = TRUE;
|
|
}
|
|
else if (MAXDWORD != CertReference && 0 == dwCmcDataReference)
|
|
{
|
|
for (i = 0; i < cCertReference; i++)
|
|
{
|
|
if (rgdwCertReference[i] == CertReference)
|
|
{
|
|
fMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(fMatch);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetCMCExtensions(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD DataReference, // nested CMC message Body Part Id
|
|
IN DWORD CertReference, // PKCS10 Cert Request Body Part Id
|
|
IN BYTE const *pbData,
|
|
IN DWORD cbData)
|
|
{
|
|
HRESULT hr;
|
|
CMC_ADD_EXTENSIONS_INFO *pcmcExt = NULL;
|
|
DWORD cb;
|
|
|
|
// Decode CMC_ADD_EXTENSIONS_INFO from Attribute Blob
|
|
|
|
CSASSERT(NULL == pcmcExt);
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_ADD_EXTENSIONS,
|
|
pbData,
|
|
cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcmcExt,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
if (pkcsCMCReferenceMatch(
|
|
DataReference,
|
|
CertReference,
|
|
pcmcExt->dwCmcDataReference,
|
|
pcmcExt->cCertReference,
|
|
pcmcExt->rgdwCertReference))
|
|
{
|
|
hr = pkcsSetExtensions(
|
|
prow,
|
|
EXTENSION_ORIGIN_CMC | EXTENSION_DISABLE_FLAG,
|
|
pcmcExt->rgExtension,
|
|
pcmcExt->cExtension);
|
|
_JumpIfError(hr, error, "pkcsSetExtensions(request)");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pcmcExt)
|
|
{
|
|
LocalFree(pcmcExt);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetCMCAttributes(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD DataReference, // nested CMC message Body Part Id
|
|
IN DWORD CertReference, // PKCS10 Cert Request Body Part Id
|
|
IN BYTE const *pbData,
|
|
IN DWORD cbData,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
CMC_ADD_ATTRIBUTES_INFO *pcmcAttrib = NULL;
|
|
DWORD cb;
|
|
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
|
|
// Decode CMC_ADD_ATTRIBUTES_INFO from Attribute Blob
|
|
|
|
CSASSERT(NULL == pcmcAttrib);
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_ADD_ATTRIBUTES,
|
|
pbData,
|
|
cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcmcAttrib,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
if (pkcsCMCReferenceMatch(
|
|
DataReference,
|
|
CertReference,
|
|
pcmcAttrib->dwCmcDataReference,
|
|
pcmcAttrib->cCertReference,
|
|
pcmcAttrib->rgdwCertReference))
|
|
{
|
|
hr = pkcsSetAttributes(
|
|
prow,
|
|
EXTENSION_ORIGIN_CMC,
|
|
PSA_DISALLOW_EXTENSIONS | PSA_DISALLOW_ARCHIVEDKEY,
|
|
pcmcAttrib->rgAttribute,
|
|
pcmcAttrib->cAttribute,
|
|
0,
|
|
NULL,
|
|
pfEnrollOnBehalfOf,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetAttributes(CMC)");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pcmcAttrib)
|
|
{
|
|
LocalFree(pcmcAttrib);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// map "email_mail" to "email"
|
|
// map "email_*" to "*"?
|
|
// mail_firstName=Terry&mail_lastName=Cheung+CMC+Zero+2&mail_email=
|
|
// tcheung%40verisign%2Ecom&challenge=test&
|
|
|
|
HRESULT
|
|
pkcsSetCMCRegInfo(
|
|
IN ICertDBRow *prow,
|
|
IN BYTE const *pbOctet,
|
|
IN DWORD cbOctet,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszRA = NULL;
|
|
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
hr = myDecodeCMCRegInfo(pbOctet, cbOctet, &pwszRA);
|
|
_JumpIfError(hr, error, "myDecodeCMCRegInfo");
|
|
|
|
hr = PKCSParseAttributes(
|
|
prow,
|
|
pwszRA,
|
|
TRUE,
|
|
FALSE,
|
|
PROPTABLE_REQUEST,
|
|
pfEnrollOnBehalfOf);
|
|
_JumpIfError(hr, error, "PKCSParseAttributes");
|
|
|
|
error:
|
|
if (NULL != pwszRA)
|
|
{
|
|
LocalFree(pwszRA);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSetTaggedAttributes(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD DataReference, // nested CMC message Body Part Id
|
|
IN DWORD CertReference, // PKCS10 Cert Request Body Part Id
|
|
IN CMC_TAGGED_ATTRIBUTE const *pTaggedAttribute,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
CRYPT_ATTRIBUTE const *pAttribute = &pTaggedAttribute->Attribute;
|
|
DWORD cb;
|
|
BOOL fEnrollOnBehalfOf;
|
|
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
for (i = 0; i < pAttribute->cValue; i++)
|
|
{
|
|
if (0 == strcmp(szOID_CMC_ADD_EXTENSIONS, pAttribute->pszObjId))
|
|
{
|
|
hr = pkcsSetCMCExtensions(
|
|
prow,
|
|
DataReference,
|
|
CertReference,
|
|
pAttribute->rgValue[i].pbData,
|
|
pAttribute->rgValue[i].cbData);
|
|
_JumpIfError(hr, error, "pkcsSetCMCExtensions");
|
|
}
|
|
else
|
|
if (0 == strcmp(szOID_CMC_ADD_ATTRIBUTES, pAttribute->pszObjId))
|
|
{
|
|
fEnrollOnBehalfOf = FALSE;
|
|
|
|
hr = pkcsSetCMCAttributes(
|
|
prow,
|
|
DataReference,
|
|
CertReference,
|
|
pAttribute->rgValue[i].pbData,
|
|
pAttribute->rgValue[i].cbData,
|
|
NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetCMCAttributes");
|
|
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
CSASSERT(NULL != pfEnrollOnBehalfOf);
|
|
*pfEnrollOnBehalfOf = TRUE;
|
|
}
|
|
}
|
|
else
|
|
if (0 == strcmp(szOID_CMC_REG_INFO, pAttribute->pszObjId))
|
|
{
|
|
fEnrollOnBehalfOf = FALSE;
|
|
|
|
hr = pkcsSetCMCRegInfo(
|
|
prow,
|
|
pAttribute->rgValue[i].pbData,
|
|
pAttribute->rgValue[i].cbData,
|
|
NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL);
|
|
_JumpIfError(hr, error, "pkcsSetCMCRegInfo");
|
|
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
CSASSERT(NULL != pfEnrollOnBehalfOf);
|
|
*pfEnrollOnBehalfOf = TRUE;
|
|
}
|
|
}
|
|
else
|
|
if (0 == strcmp(szOID_CMC_TRANSACTION_ID, pAttribute->pszObjId))
|
|
{
|
|
DWORD dwTransactionId;
|
|
|
|
cb = sizeof(dwTransactionId);
|
|
dwTransactionId = 0;
|
|
if (!CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
pAttribute->rgValue[i].pbData,
|
|
pAttribute->rgValue[i].cbData,
|
|
0,
|
|
&dwTransactionId,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptDecodeObject");
|
|
}
|
|
pResult->fTransactionId = TRUE;
|
|
pResult->dwTransactionId = dwTransactionId;
|
|
}
|
|
else
|
|
if (0 == strcmp(szOID_CMC_SENDER_NONCE, pAttribute->pszObjId))
|
|
{
|
|
CRYPT_DATA_BLOB *pBlob;
|
|
BYTE *pb;
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
pAttribute->rgValue[i].pbData,
|
|
pAttribute->rgValue[i].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pBlob,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
pb = (BYTE *) LocalAlloc(LMEM_FIXED, pBlob->cbData);
|
|
if (NULL == pb)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
CopyMemory(pb, pBlob->pbData, pBlob->cbData);
|
|
if (NULL != pResult->pbSenderNonce)
|
|
{
|
|
LocalFree(pResult->pbSenderNonce);
|
|
}
|
|
pResult->pbSenderNonce = pb;
|
|
pResult->cbSenderNonce = pBlob->cbData;
|
|
hr = S_OK;
|
|
}
|
|
LocalFree(pBlob);
|
|
_JumpIfError(hr, error, "LocalAlloc");
|
|
}
|
|
else if (0 == (CRLF_IGNORE_UNKNOWN_CMC_ATTRIBUTES & g_dwCRLFlags))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "unknown tagged attribute");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
// pkcsParseCMCRequest
|
|
//
|
|
// Crack a CMC request and dig the goodies out of it.
|
|
// Crack the contents of the CMC request recursively.
|
|
//-------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
pkcsParseCMCRequest(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbIn,
|
|
IN BYTE const *pbIn,
|
|
OPTIONAL IN CERT_CONTEXT const *pCertSigner,
|
|
OPTIONAL OUT BOOL *pfRenewal,
|
|
OPTIONAL OUT BOOL *pfEnrollOnBehalfOf,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cb;
|
|
CMC_DATA_INFO *pcmcData = NULL;
|
|
DWORD i;
|
|
DWORD DataReference = MAXDWORD; // nested CMC message Body Part Id
|
|
DWORD CertReference = MAXDWORD; // PKCS10 Cert Request Body Part Id
|
|
|
|
if (NULL != pfRenewal)
|
|
{
|
|
*pfRenewal = FALSE;
|
|
}
|
|
if (NULL != pfEnrollOnBehalfOf)
|
|
{
|
|
*pfEnrollOnBehalfOf = FALSE;
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_DATA,
|
|
pbIn,
|
|
cbIn,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcmcData,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
if (0 != pcmcData->cTaggedOtherMsg)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "unknown other message");
|
|
}
|
|
|
|
// Process nested CMC messages
|
|
|
|
if (0 != pcmcData->cTaggedContentInfo)
|
|
{
|
|
CMC_TAGGED_CONTENT_INFO const *pTaggedContentInfo;
|
|
|
|
// Only handle one CMC message at a time for now.
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (1 < pcmcData->cTaggedContentInfo)
|
|
{
|
|
_JumpError(hr, error, "multiple nested CMC messages");
|
|
}
|
|
|
|
// Disallow CMC message recursion below a PKCS10 request.
|
|
|
|
if (0 != pcmcData->cTaggedRequest)
|
|
{
|
|
_JumpError(hr, error, "recursion below PKCS10 request");
|
|
}
|
|
|
|
// Recurse on the nested CMC message
|
|
|
|
pTaggedContentInfo = &pcmcData->rgTaggedContentInfo[0];
|
|
|
|
hr = PKCSParseRequest(
|
|
CR_IN_CMC | (~CR_IN_FORMATMASK & pResult->dwFlagsTop),
|
|
prow,
|
|
pTaggedContentInfo->EncodedContentInfo.cbData,
|
|
pTaggedContentInfo->EncodedContentInfo.pbData,
|
|
pCertSigner,
|
|
pfRenewal,
|
|
pResult);
|
|
_JumpIfError(hr, error, "PKCSParseRequest");
|
|
|
|
DataReference = pTaggedContentInfo->dwBodyPartID;
|
|
}
|
|
|
|
// Process nested PKCS10 requests
|
|
|
|
if (0 != pcmcData->cTaggedRequest)
|
|
{
|
|
CMC_TAGGED_REQUEST const *pTaggedRequest;
|
|
CMC_TAGGED_CERT_REQUEST const *pTaggedCertRequest;
|
|
|
|
// Only handle one request at a time for now.
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (1 < pcmcData->cTaggedRequest)
|
|
{
|
|
_JumpError(hr, error, "multiple PKCS10 requests");
|
|
}
|
|
|
|
pTaggedRequest = &pcmcData->rgTaggedRequest[0];
|
|
|
|
// The request must be a PKCS10 request
|
|
|
|
if (CMC_TAGGED_CERT_REQUEST_CHOICE !=
|
|
pTaggedRequest->dwTaggedRequestChoice)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "recursion below PKCS10 request");
|
|
}
|
|
|
|
pTaggedCertRequest = pTaggedRequest->pTaggedCertRequest;
|
|
|
|
hr = PKCSParseRequest(
|
|
CR_IN_PKCS10 | (~CR_IN_FORMATMASK & pResult->dwFlagsTop),
|
|
prow,
|
|
pTaggedCertRequest->SignedCertRequest.cbData,
|
|
pTaggedCertRequest->SignedCertRequest.pbData,
|
|
pCertSigner,
|
|
pfRenewal,
|
|
pResult);
|
|
_JumpIfError(hr, error, "PKCSParseRequest");
|
|
|
|
CertReference = pTaggedCertRequest->dwBodyPartID;
|
|
}
|
|
|
|
// Process extensions and attributes
|
|
|
|
for (i = 0; i < pcmcData->cTaggedAttribute; i++)
|
|
{
|
|
hr = pkcsSetTaggedAttributes(
|
|
prow,
|
|
DataReference,
|
|
CertReference,
|
|
&pcmcData->rgTaggedAttribute[i],
|
|
pfEnrollOnBehalfOf,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetTaggedAttributes");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pcmcData)
|
|
{
|
|
LocalFree(pcmcData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAppendPolicies(
|
|
IN ICertDBRow *prow,
|
|
IN WCHAR const *pwszPropName,
|
|
OPTIONAL IN WCHAR const *pwszzPolicies)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszOld = NULL;
|
|
WCHAR *pwszNew = NULL;
|
|
WCHAR const *pwszIn;
|
|
WCHAR *pwsz;
|
|
DWORD cwc = 0;
|
|
DWORD cb;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
pwszPropName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
&cb,
|
|
(BYTE **) &pwszOld);
|
|
_PrintIfError2(hr, "PKCSGetProperty", hr);
|
|
if (S_OK != hr)
|
|
{
|
|
pwszOld = NULL;
|
|
}
|
|
if (NULL != pwszOld)
|
|
{
|
|
cwc = wcslen(pwszOld) + 1; // allow for \n separator
|
|
}
|
|
|
|
// pwszzPolicies == NULL means the cert is good for *all* policies.
|
|
// Store "*"
|
|
//
|
|
// *pwszzPolicies == L'\0' means the cert is good for *no* policies.
|
|
// Store "-"
|
|
|
|
if (NULL == pwszzPolicies)
|
|
{
|
|
pwszzPolicies = L"*\0";
|
|
}
|
|
else if (L'\0' == *pwszzPolicies)
|
|
{
|
|
pwszzPolicies = L"-\0";
|
|
}
|
|
for (pwszIn = pwszzPolicies; L'\0' != *pwszIn; pwszIn += wcslen(pwszIn) + 1)
|
|
;
|
|
|
|
cwc += SAFE_SUBTRACT_POINTERS(pwszIn, pwszzPolicies);
|
|
pwszNew = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == pwszNew)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
pwsz = pwszNew;
|
|
|
|
if (NULL != pwszOld)
|
|
{
|
|
wcscpy(pwsz, pwszOld);
|
|
pwsz += wcslen(pwsz);
|
|
|
|
wcscpy(pwsz, L"\n");
|
|
pwsz++;
|
|
}
|
|
|
|
for (pwszIn = pwszzPolicies; L'\0' != *pwszIn; pwszIn += wcslen(pwszIn) + 1)
|
|
{
|
|
if (pwszIn != pwszzPolicies)
|
|
{
|
|
wcscpy(pwsz, L",");
|
|
pwsz++;
|
|
}
|
|
wcscpy(pwsz, pwszIn);
|
|
pwsz += wcslen(pwsz);
|
|
}
|
|
CSASSERT(&pwsz[1] == &pwszNew[cwc]);
|
|
|
|
hr = prow->SetProperty(
|
|
pwszPropName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszNew);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
error:
|
|
if (NULL != pwszOld)
|
|
{
|
|
LocalFree(pwszOld);
|
|
}
|
|
if (NULL != pwszNew)
|
|
{
|
|
LocalFree(pwszNew);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
// pkcsParsePKCS7Request
|
|
//
|
|
// Crack a PKCS7 and dig the goodies out of it.
|
|
// Verify the signature of the 7 against the cert given in the 7.
|
|
// Crack the contents of the 7 recursively.
|
|
//-------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
pkcsParsePKCS7Request(
|
|
IN BOOL fTopLevel,
|
|
IN DWORD dwFlags,
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbIn,
|
|
IN BYTE const *pbIn,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbContents = NULL;
|
|
DWORD cbContents;
|
|
CERT_CONTEXT const *pCertSigner = NULL;
|
|
HCERTSTORE hStore = NULL;
|
|
HCRYPTMSG hMsg = NULL;
|
|
char *pszInnerContentObjId = NULL;
|
|
DWORD i;
|
|
BOOL fCMC;
|
|
BOOL fRenewal = FALSE;
|
|
char *apszEnrollOids[] = {szOID_ENROLLMENT_AGENT};
|
|
DWORD dwVerifyContextFlags;
|
|
DWORD dwMsgType;
|
|
DWORD cSigner;
|
|
DWORD cFirstSigner;
|
|
BOOL fFirstSigner;
|
|
DWORD cRecipient;
|
|
DWORD cb;
|
|
CMSG_CMS_SIGNER_INFO *pcsi = NULL;
|
|
DWORD dwDisallowFlags;
|
|
DWORD iElement;
|
|
WCHAR *pwszzIssuancePolicies = NULL;
|
|
WCHAR *pwszzApplicationPolicies = NULL;
|
|
WCHAR *pwszExtendedErrorInfo = NULL;
|
|
CERT_REQUEST_INFO *pRequest = NULL;
|
|
BOOL fEnrollOnBehalfOf;
|
|
|
|
CSASSERT(
|
|
CR_IN_PKCS7 == (CR_IN_FORMATMASK & dwFlags) ||
|
|
CR_IN_CMC == (CR_IN_FORMATMASK & dwFlags));
|
|
|
|
// Crack the 7 and verify the signature.
|
|
|
|
hr = myDecodePKCS7(
|
|
pbIn,
|
|
cbIn,
|
|
&pbContents,
|
|
&cbContents,
|
|
&dwMsgType,
|
|
&pszInnerContentObjId,
|
|
&cSigner,
|
|
&cRecipient,
|
|
&hStore,
|
|
&hMsg);
|
|
_JumpIfError(hr, error, "myDecodePKCS7");
|
|
|
|
if (CMSG_SIGNED != dwMsgType || 0 == cSigner)
|
|
{
|
|
hr = CRYPT_E_NO_SIGNER;
|
|
_JumpIfError(hr, error, "myDecodePKCS7(no signing cert)");
|
|
}
|
|
|
|
fCMC = NULL != pszInnerContentObjId &&
|
|
0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA);
|
|
|
|
// Decode the contents.
|
|
|
|
if (fCMC)
|
|
{
|
|
if (CR_IN_CMC != (CR_IN_FORMATMASK & dwFlags))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "dwFlags");
|
|
}
|
|
// CMC renewal requests may have one 'first' signer (non-NULL KeyId or
|
|
// Dummy signer) and one additional Issuer+Serial signer. If the
|
|
// request has the appropriate signatures, pass the cert to the lowest
|
|
// level to see if it really is a renewal request.
|
|
|
|
if (1 <= cSigner && 2 >= cSigner)
|
|
{
|
|
DWORD iCertSigner = MAXDWORD;
|
|
|
|
cFirstSigner = 0;
|
|
for (i = 0; i < cSigner; i++)
|
|
{
|
|
if (NULL != pcsi)
|
|
{
|
|
LocalFree(pcsi);
|
|
pcsi = NULL;
|
|
}
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_CMS_SIGNER_INFO_PARAM,
|
|
i,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcsi,
|
|
&cb);
|
|
_JumpIfError(hr, error, "myCryptMsgGetParam");
|
|
|
|
fFirstSigner = FALSE;
|
|
if (CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice ||
|
|
(NULL != pcsi->HashEncryptionAlgorithm.pszObjId &&
|
|
0 == strcmp(
|
|
szOID_PKIX_NO_SIGNATURE,
|
|
pcsi->HashEncryptionAlgorithm.pszObjId)))
|
|
{
|
|
fFirstSigner = TRUE;
|
|
cFirstSigner++;
|
|
}
|
|
else
|
|
{
|
|
if (MAXDWORD != iCertSigner)
|
|
{
|
|
iCertSigner = MAXDWORD; // must not be a renewal
|
|
break;
|
|
}
|
|
iCertSigner = i;
|
|
}
|
|
}
|
|
if (MAXDWORD != iCertSigner && 1 >= cFirstSigner)
|
|
{
|
|
iElement = iCertSigner;
|
|
if (!CryptMsgGetAndVerifySigner(
|
|
hMsg,
|
|
0, // cSignerStore
|
|
NULL, // rghSignerStore
|
|
CMSG_USE_SIGNER_INDEX_FLAG,
|
|
&pCertSigner,
|
|
&iElement))
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgGetAndVerifySigner");
|
|
}
|
|
}
|
|
}
|
|
fEnrollOnBehalfOf = FALSE;
|
|
hr = pkcsParseCMCRequest(
|
|
prow,
|
|
cbContents,
|
|
pbContents,
|
|
pCertSigner,
|
|
&fRenewal,
|
|
&fEnrollOnBehalfOf,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsParseCMCRequest");
|
|
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
pResult->fEnrollOnBehalfOf = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CR_IN_PKCS7 != (CR_IN_FORMATMASK & dwFlags))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "dwFlags");
|
|
}
|
|
|
|
// Expect only one signer for PKCS7 renewal requests. Pass the cert
|
|
// to the lowest level to see if it really is a renewal request.
|
|
|
|
iElement = 0;
|
|
if (!CryptMsgGetAndVerifySigner(
|
|
hMsg,
|
|
0, // cSignerStore
|
|
NULL, // rghSignerStore
|
|
CMSG_USE_SIGNER_INDEX_FLAG,
|
|
&pCertSigner,
|
|
&iElement))
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgGetAndVerifySigner");
|
|
}
|
|
hr = PKCSParseRequest(
|
|
CR_IN_FORMATANY | (~CR_IN_FORMATMASK & pResult->dwFlagsTop),
|
|
prow,
|
|
cbContents,
|
|
pbContents,
|
|
pCertSigner,
|
|
&fRenewal,
|
|
pResult);
|
|
_JumpIfError(hr, error, "PKCSParseRequest");
|
|
}
|
|
|
|
// Loop through the signers, verifying signatures and saving attributes.
|
|
|
|
cFirstSigner = 0;
|
|
for (i = 0; i < cSigner; i++)
|
|
{
|
|
if (NULL != pcsi)
|
|
{
|
|
LocalFree(pcsi);
|
|
pcsi = NULL;
|
|
}
|
|
if (NULL != pwszzIssuancePolicies)
|
|
{
|
|
LocalFree(pwszzIssuancePolicies);
|
|
pwszzIssuancePolicies = NULL;
|
|
}
|
|
if (NULL != pwszzApplicationPolicies)
|
|
{
|
|
LocalFree(pwszzApplicationPolicies);
|
|
pwszzApplicationPolicies = NULL;
|
|
}
|
|
if (NULL != pwszExtendedErrorInfo)
|
|
{
|
|
LocalFree(pwszExtendedErrorInfo);
|
|
pwszExtendedErrorInfo = NULL;
|
|
}
|
|
if (NULL != pCertSigner)
|
|
{
|
|
CertFreeCertificateContext(pCertSigner);
|
|
pCertSigner = NULL;
|
|
}
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_CMS_SIGNER_INFO_PARAM,
|
|
i,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcsi,
|
|
&cb);
|
|
if (S_OK != hr)
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
_JumpError(hr, error, "myCryptMsgGetParam");
|
|
}
|
|
|
|
fFirstSigner = FALSE;
|
|
if (fCMC &&
|
|
(CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice ||
|
|
(NULL != pcsi->HashEncryptionAlgorithm.pszObjId &&
|
|
0 == strcmp(
|
|
szOID_PKIX_NO_SIGNATURE,
|
|
pcsi->HashEncryptionAlgorithm.pszObjId))))
|
|
{
|
|
CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA cvse;
|
|
|
|
fFirstSigner = TRUE;
|
|
ZeroMemory(&cvse, sizeof(cvse));
|
|
cvse.cbSize = sizeof(cvse);
|
|
cvse.dwSignerIndex = i;
|
|
|
|
if (CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice)
|
|
{
|
|
if (NULL == pRequest)
|
|
{
|
|
hr = myGetInnerPKCS10(
|
|
hMsg,
|
|
pszInnerContentObjId,
|
|
&pRequest);
|
|
_JumpIfError(hr, error, "myGetInnerPKCS10");
|
|
}
|
|
cvse.dwSignerType = CMSG_VERIFY_SIGNER_PUBKEY;
|
|
cvse.pvSigner = &pRequest->SubjectPublicKeyInfo;
|
|
}
|
|
else
|
|
{
|
|
cvse.dwSignerType = CMSG_VERIFY_SIGNER_NULL;
|
|
}
|
|
|
|
if (!CryptMsgControl(
|
|
hMsg,
|
|
0, // dwFlags
|
|
CMSG_CTRL_VERIFY_SIGNATURE_EX,
|
|
&cvse))
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgControl(VerifySig)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iElement = i;
|
|
if (!CryptMsgGetAndVerifySigner(
|
|
hMsg,
|
|
0, // cSignerStore
|
|
NULL, // rghSignerStore
|
|
CMSG_USE_SIGNER_INDEX_FLAG,
|
|
&pCertSigner,
|
|
&iElement))
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgGetAndVerifySigner");
|
|
}
|
|
}
|
|
|
|
// Only enroll-on-behalf-of requests may contain Name, Value pairs.
|
|
// Only enroll-on-behalf-of requests and renewal requests may contain
|
|
// certificate extensions.
|
|
|
|
dwDisallowFlags = PSA_DISALLOW_ARCHIVEDKEY;
|
|
if (fRenewal)
|
|
{
|
|
dwDisallowFlags |= PSA_DISALLOW_NAMEVALUEPAIRS;
|
|
pResult->dwResultFlags |= CRCF_RENEWAL;
|
|
}
|
|
if (fCMC)
|
|
{
|
|
dwDisallowFlags |= PSA_DISALLOW_EXTENSIONS |
|
|
PSA_DISALLOW_NAMEVALUEPAIRS;
|
|
}
|
|
fEnrollOnBehalfOf = FALSE;
|
|
hr = pkcsSetAttributes(
|
|
prow,
|
|
EXTENSION_ORIGIN_PKCS7,
|
|
dwDisallowFlags,
|
|
pcsi->AuthAttrs.rgAttr,
|
|
pcsi->AuthAttrs.cAttr,
|
|
0,
|
|
NULL,
|
|
&fEnrollOnBehalfOf,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetAttributes(Authenticated)");
|
|
|
|
if (fEnrollOnBehalfOf)
|
|
{
|
|
pResult->fEnrollOnBehalfOf = TRUE;
|
|
}
|
|
|
|
// Pull encrypted private key out of unauthenticated attributes
|
|
|
|
hr = pkcsSetAttributes(
|
|
prow,
|
|
EXTENSION_ORIGIN_PKCS7,
|
|
((fTopLevel && fFirstSigner)? 0 : PSA_DISALLOW_ARCHIVEDKEY) |
|
|
PSA_DISALLOW_EXTENSIONS |
|
|
PSA_DISALLOW_NAMEVALUEPAIRS,
|
|
pcsi->UnauthAttrs.rgAttr,
|
|
pcsi->UnauthAttrs.cAttr,
|
|
cbIn,
|
|
fTopLevel? pbIn : NULL,
|
|
NULL,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsSetAttributes(UNauthenticated)");
|
|
|
|
if (fFirstSigner)
|
|
{
|
|
cFirstSigner++;
|
|
}
|
|
else
|
|
{
|
|
BOOL fEnrollmentAgent;
|
|
|
|
// This is a renewal request, an enroll-on-behalf-of request, a CMC
|
|
// request or just a request inside a PKCS 7 -- verify the cert
|
|
// chain for all signers. If enroll-on-behalf-of on an Enterprise
|
|
// CA (if requester name is set in the authenticated attributes),
|
|
// check the signing cert via NTAuth policy and check for
|
|
// szOID_ENROLLMENT_AGENT usage. NtAuth verification was added to
|
|
// control the ability of enroll-on-behalf agents to add usernames
|
|
// to the PKCS7 wrapper.
|
|
|
|
fEnrollmentAgent = pResult->fEnrollOnBehalfOf &&
|
|
#ifdef CERTSRV_EOBO_DCR_APPROVED
|
|
(CRLF_ENFORCE_ENROLLMENT_AGENT & g_dwCRLFlags);
|
|
#else
|
|
((CRLF_ENFORCE_ENROLLMENT_AGENT & g_dwCRLFlags) ||
|
|
IsEnterpriseCA(g_CAType));
|
|
#endif
|
|
dwVerifyContextFlags = g_dwVerifyCertFlags;
|
|
if (pResult->fEnrollOnBehalfOf && IsEnterpriseCA(g_CAType))
|
|
{
|
|
dwVerifyContextFlags |= CA_VERIFY_FLAGS_NT_AUTH;
|
|
}
|
|
|
|
hr = myVerifyCertContextEx(
|
|
pCertSigner,
|
|
dwVerifyContextFlags,
|
|
0, // dwmsTimeout
|
|
fEnrollmentAgent? ARRAYSIZE(apszEnrollOids) : 0,
|
|
fEnrollmentAgent? apszEnrollOids : NULL,
|
|
0, // cIssuanceOids
|
|
NULL, // apszIssuanceOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
NULL, // pft
|
|
hStore, // hAdditionalStore
|
|
NULL, // pfnCallback
|
|
NULL, // ppwszMissingIssuer
|
|
&pwszzIssuancePolicies,
|
|
&pwszzApplicationPolicies,
|
|
&pwszExtendedErrorInfo,
|
|
NULL); // pTrustStatus
|
|
if (S_OK != hr)
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
_JumpError(hr, error, "myVerifyCertContextEx");
|
|
}
|
|
if (NULL != pwszExtendedErrorInfo)
|
|
{
|
|
hr = myAppendString(
|
|
pwszExtendedErrorInfo,
|
|
L", ",
|
|
&pResult->pwszExtendedErrorInfo);
|
|
_JumpIfError(hr, error, "myAppendString");
|
|
}
|
|
if (fTopLevel)
|
|
{
|
|
// save Issuance Policies
|
|
|
|
hr = pkcsAppendPolicies(
|
|
prow,
|
|
wszPROPSIGNERPOLICIES,
|
|
pwszzIssuancePolicies);
|
|
_JumpIfError(hr, error, "pkcsAppendPolicies");
|
|
|
|
// save Application Policies
|
|
|
|
hr = pkcsAppendPolicies(
|
|
prow,
|
|
wszPROPSIGNERAPPLICATIONPOLICIES,
|
|
pwszzApplicationPolicies);
|
|
_JumpIfError(hr, error, "pkcsAppendPolicies");
|
|
}
|
|
}
|
|
}
|
|
if (pResult->fEnrollOnBehalfOf)
|
|
{
|
|
hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_ENROLLONBEHALFOF);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
|
|
if (fCMC && cSigner == cFirstSigner)
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = CRYPT_E_NO_TRUSTED_SIGNER;
|
|
_JumpError(hr, error, "No NTAuth signer");
|
|
}
|
|
}
|
|
if ((fCMC && 1 < cFirstSigner) || (!fCMC && 0 < cFirstSigner))
|
|
{
|
|
pResult->dwResultFlags |= CRCF_SIGNATUREERROR;
|
|
hr = NTE_BAD_SIGNATURE;
|
|
_JumpError(hr, error, "cFirstSigner");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pRequest)
|
|
{
|
|
LocalFree(pRequest);
|
|
}
|
|
if (NULL != pcsi)
|
|
{
|
|
LocalFree(pcsi);
|
|
}
|
|
if (NULL != pwszzIssuancePolicies)
|
|
{
|
|
LocalFree(pwszzIssuancePolicies);
|
|
}
|
|
if (NULL != pwszzApplicationPolicies)
|
|
{
|
|
LocalFree(pwszzApplicationPolicies);
|
|
}
|
|
if (NULL != pwszExtendedErrorInfo)
|
|
{
|
|
LocalFree(pwszExtendedErrorInfo);
|
|
}
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
if (NULL != pCertSigner)
|
|
{
|
|
CertFreeCertificateContext(pCertSigner);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pbContents)
|
|
{
|
|
LocalFree(pbContents);
|
|
}
|
|
if (NULL != pszInnerContentObjId)
|
|
{
|
|
LocalFree(pszInnerContentObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
typedef struct _REQUESTFORMATS
|
|
{
|
|
char const *pszFormat;
|
|
DWORD dwFlags;
|
|
} REQUESTFORMATS;
|
|
|
|
|
|
REQUESTFORMATS g_arf[] = {
|
|
{ X509_CERT_REQUEST_TO_BE_SIGNED, CR_IN_PKCS10 },
|
|
{ X509_KEYGEN_REQUEST_TO_BE_SIGNED, CR_IN_KEYGEN },
|
|
};
|
|
#define CREQUESTFORMATS ARRAYSIZE(g_arf)
|
|
|
|
|
|
HRESULT
|
|
pkcsCrackRequestType(
|
|
IN DWORD cbRequest,
|
|
IN BYTE const *pbRequest,
|
|
OUT DWORD *pdwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD cb;
|
|
BYTE *pbDecoded = NULL;
|
|
REQUESTFORMATS const *prf;
|
|
REQUESTFORMATS const *prfEnd;
|
|
HCRYPTMSG hMsg = NULL;
|
|
char *pszInnerContentObjId = NULL;
|
|
|
|
prfEnd = &g_arf[CREQUESTFORMATS];
|
|
for (prf = g_arf; prf < prfEnd; prf++)
|
|
{
|
|
CSASSERT(NULL == pbDecoded);
|
|
if (myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
prf->pszFormat,
|
|
pbRequest,
|
|
cbRequest,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pbDecoded,
|
|
&cb))
|
|
{
|
|
*pdwFlags = prf->dwFlags;
|
|
break;
|
|
}
|
|
hr = myHLastError();
|
|
CSASSERT(S_OK != hr);
|
|
}
|
|
if (prf >= prfEnd)
|
|
{
|
|
CSASSERT(S_OK != hr);
|
|
|
|
hr = myDecodePKCS7(
|
|
pbRequest,
|
|
cbRequest,
|
|
NULL, // ppbContents
|
|
NULL, // pcbContents
|
|
NULL, // pdwMsgType
|
|
&pszInnerContentObjId,
|
|
NULL, // pcSigner
|
|
NULL, // pcRecipient
|
|
NULL, // phStore
|
|
&hMsg);
|
|
_JumpIfError(hr, error, "myDecodePKCS7");
|
|
|
|
*pdwFlags = CR_IN_PKCS7; // default to renewal
|
|
|
|
if (NULL != pszInnerContentObjId &&
|
|
0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA))
|
|
{
|
|
*pdwFlags = CR_IN_CMC;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
if (NULL != pszInnerContentObjId)
|
|
{
|
|
LocalFree(pszInnerContentObjId);
|
|
}
|
|
if (NULL != pbDecoded)
|
|
{
|
|
LocalFree(pbDecoded);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSParseRequest(
|
|
IN DWORD dwFlags,
|
|
IN ICertDBRow *prow,
|
|
IN DWORD cbRequest,
|
|
IN BYTE const *pbRequest,
|
|
IN CERT_CONTEXT const *pSigningAuthority,
|
|
OPTIONAL OUT BOOL *pfRenewal,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL != pfRenewal)
|
|
{
|
|
*pfRenewal = FALSE;
|
|
}
|
|
|
|
if (CR_IN_FORMATANY == (CR_IN_FORMATMASK & dwFlags))
|
|
{
|
|
hr = pkcsCrackRequestType(cbRequest, pbRequest, &dwFlags);
|
|
_JumpIfError(hr, error, "pkcsCrackRequestType");
|
|
|
|
dwFlags |= ~CR_IN_FORMATMASK & pResult->dwFlagsTop;
|
|
|
|
// If this is the top level caller, store a more specific request type:
|
|
|
|
if (NULL == pfRenewal)
|
|
{
|
|
pResult->dwFlagsTop = dwFlags;
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestType,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwFlags),
|
|
(BYTE const *) &dwFlags);
|
|
_JumpIfError(hr, error, "SetProperty(reqtype)");
|
|
}
|
|
}
|
|
|
|
switch (CR_IN_FORMATMASK & dwFlags)
|
|
{
|
|
case CR_IN_PKCS10:
|
|
hr = pkcsParsePKCS10Request(
|
|
dwFlags,
|
|
prow,
|
|
cbRequest,
|
|
pbRequest,
|
|
pSigningAuthority,
|
|
pfRenewal,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsParsePKCS10Request");
|
|
break;
|
|
|
|
case CR_IN_KEYGEN:
|
|
hr = pkcsParseKeyGenRequest(
|
|
dwFlags,
|
|
prow,
|
|
cbRequest,
|
|
pbRequest,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsParseKeyGenRequest");
|
|
break;
|
|
|
|
case CR_IN_CMC:
|
|
case CR_IN_PKCS7:
|
|
// PKCS7 requests can either be an 'enroll on behalf of', renewal
|
|
// request or a CMC request. We need to recursively unwrap it to
|
|
// process it.
|
|
|
|
hr = pkcsParsePKCS7Request(
|
|
NULL == pfRenewal, // fTopLevel
|
|
dwFlags,
|
|
prow,
|
|
cbRequest,
|
|
pbRequest,
|
|
pResult);
|
|
_JumpIfError(hr, error, "pkcsParsePKCS7Request");
|
|
|
|
break;
|
|
|
|
default:
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "dwFlags");
|
|
}
|
|
|
|
error:
|
|
if (NULL == pfRenewal)
|
|
{
|
|
HRESULT hr2;
|
|
DWORD cbData;
|
|
|
|
hr2 = prow->GetProperty(
|
|
g_wszPropRequestRawRequest,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbData,
|
|
NULL);
|
|
if (S_OK != hr2)
|
|
{
|
|
hr2 = prow->SetProperty(
|
|
g_wszPropRequestRawRequest,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
cbRequest,
|
|
pbRequest);
|
|
_PrintIfError(hr2, "SetProperty(request)");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCRLList(
|
|
IN BOOL fDelta,
|
|
IN DWORD iCert,
|
|
OUT WCHAR const * const **ppapwszCRLList)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
*ppapwszCRLList = NULL;
|
|
if (iCert < g_cCACerts)
|
|
{
|
|
CACTX *pCAContext = &g_aCAContext[iCert];
|
|
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
hr = S_FALSE;
|
|
goto error;
|
|
}
|
|
*ppapwszCRLList = fDelta?
|
|
pCAContext->papwszDeltaCRLFiles :
|
|
pCAContext->papwszCRLFiles;
|
|
if (NULL != *ppapwszCRLList)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildCRLList(
|
|
IN BOOL fDelta,
|
|
IN OUT CACTX *pCAContext,
|
|
OUT WCHAR ***ppapwszOut)
|
|
{
|
|
HRESULT hr;
|
|
DWORD PublishFlag = fDelta? CSURL_SERVERPUBLISHDELTA : CSURL_SERVERPUBLISH;
|
|
DWORD cFiles;
|
|
CSURLTEMPLATE const *pTemplate;
|
|
CSURLTEMPLATE const *pTemplateEnd;
|
|
|
|
cFiles = 0;
|
|
pTemplateEnd = &g_paRevURL[g_caRevURL];
|
|
for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (PublishFlag & pTemplate->Flags)
|
|
{
|
|
cFiles++;
|
|
}
|
|
}
|
|
*ppapwszOut = (WCHAR **) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
(cFiles + 1) * sizeof((*ppapwszOut)[0]));
|
|
if (NULL == *ppapwszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"CRLList alloc[%u] = %x @%x\n",
|
|
cFiles,
|
|
*ppapwszOut,
|
|
ppapwszOut));
|
|
|
|
CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN);
|
|
cFiles = 0;
|
|
for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (PublishFlag & pTemplate->Flags)
|
|
{
|
|
hr = myFormatCertsrvStringArray(
|
|
FALSE, // fURL
|
|
g_pwszServerName, // pwszServerName_p1_2
|
|
g_wszSanitizedName, // pwszSanitizedName_p3_7
|
|
pCAContext->iKey, // iCert_p4 -- use iKey!!
|
|
MAXDWORD, // iCertTarget_p4
|
|
g_strDomainDN, // pwszDomainDN_p5
|
|
g_strConfigDN, // pwszConfigDN_p6
|
|
pCAContext->iKey, // iCRL_p8
|
|
fDelta, // fDeltaCRL_p9
|
|
FALSE, // fDSAttrib_p10_11
|
|
1, // cStrings
|
|
(LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn
|
|
&(*ppapwszOut)[cFiles]); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"CRLList format[%u] = %x @%x (%ws)\n",
|
|
cFiles,
|
|
(*ppapwszOut)[cFiles],
|
|
&(*ppapwszOut)[cFiles],
|
|
(*ppapwszOut)[cFiles]));
|
|
|
|
cFiles++;
|
|
}
|
|
}
|
|
(*ppapwszOut)[cFiles] = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
// Freeing the CACTX structure during shutdown will free orphaned CRL paths
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildKeyAuthority2(
|
|
IN DWORD EditFlags,
|
|
IN CACTX const *pCAContext,
|
|
OUT CRYPT_OBJID_BLOB *pKeyAuthority2)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CERT_AUTHORITY_KEY_ID2_INFO keyAuth;
|
|
CERT_ALT_NAME_ENTRY AltNameEntry;
|
|
|
|
if (0 ==
|
|
((EDITF_ENABLEAKIKEYID |
|
|
EDITF_ENABLEAKIISSUERNAME |
|
|
EDITF_ENABLEAKIISSUERSERIAL) & EditFlags))
|
|
{
|
|
goto error;
|
|
}
|
|
ZeroMemory(&keyAuth, sizeof(keyAuth));
|
|
|
|
// Issuer's KeyId:
|
|
|
|
if ((EDITF_ENABLEAKIKEYID & EditFlags) &&
|
|
NULL != pCAContext->IssuerKeyId.pbData)
|
|
{
|
|
keyAuth.KeyId = pCAContext->IssuerKeyId;
|
|
}
|
|
|
|
// The Issuer's Issuer name and the Issuer's SerialNumber combined
|
|
// should uniquely identify the Issuer cert.
|
|
|
|
// Issuer's Issuer name:
|
|
// -------- ------ ----
|
|
|
|
if (EDITF_ENABLEAKIISSUERNAME & EditFlags)
|
|
{
|
|
AltNameEntry.dwAltNameChoice = CERT_ALT_NAME_DIRECTORY_NAME;
|
|
AltNameEntry.DirectoryName = pCAContext->pccCA->pCertInfo->Issuer;
|
|
keyAuth.AuthorityCertIssuer.cAltEntry = 1;
|
|
keyAuth.AuthorityCertIssuer.rgAltEntry = &AltNameEntry;
|
|
}
|
|
|
|
// Issuer's SerialNumber:
|
|
|
|
if (EDITF_ENABLEAKIISSUERSERIAL & EditFlags)
|
|
{
|
|
keyAuth.AuthorityCertSerialNumber =
|
|
pCAContext->pccCA->pCertInfo->SerialNumber;
|
|
}
|
|
|
|
// put in Key Authority Info
|
|
|
|
if (!myEncodeKeyAuthority2(
|
|
X509_ASN_ENCODING,
|
|
&keyAuth,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pKeyAuthority2->pbData,
|
|
&pKeyAuthority2->cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeKeyAuthority2");
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildCDP(
|
|
IN DWORD Flags,
|
|
IN BOOL fDelta,
|
|
IN CACTX const *pCAContext,
|
|
OUT CRYPT_OBJID_BLOB *pCDP)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
CSURLTEMPLATE const *pTemplate;
|
|
CSURLTEMPLATE const *pTemplateEnd;
|
|
CRL_DIST_POINTS_INFO CRLDistInfo;
|
|
CRL_DIST_POINT CRLDistPoint;
|
|
CERT_ALT_NAME_INFO *pAltInfo;
|
|
|
|
ZeroMemory(&CRLDistPoint, sizeof(CRLDistPoint));
|
|
pAltInfo = &CRLDistPoint.DistPointName.FullName;
|
|
|
|
pCDP->pbData = NULL;
|
|
pCDP->cbData = 0;
|
|
|
|
pTemplateEnd = g_paCACertURL;
|
|
if (0 != g_caRevURL)
|
|
{
|
|
pTemplateEnd = &g_paRevURL[g_caRevURL];
|
|
for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (Flags & pTemplate->Flags)
|
|
{
|
|
pAltInfo->cAltEntry++;
|
|
}
|
|
}
|
|
}
|
|
if (0 == pAltInfo->cAltEntry)
|
|
{
|
|
hr = S_FALSE;
|
|
goto error;
|
|
}
|
|
|
|
CRLDistInfo.cDistPoint = 1;
|
|
CRLDistInfo.rgDistPoint = &CRLDistPoint;
|
|
|
|
CRLDistPoint.DistPointName.dwDistPointNameChoice = CRL_DIST_POINT_FULL_NAME;
|
|
|
|
pAltInfo->rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
pAltInfo->cAltEntry * sizeof(pAltInfo->rgAltEntry[0]));
|
|
if (NULL == pAltInfo->rgAltEntry)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN);
|
|
i = 0;
|
|
for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (Flags & pTemplate->Flags)
|
|
{
|
|
hr = myFormatCertsrvStringArray(
|
|
TRUE, // fURL
|
|
g_pwszServerName, // pwszServerName_p1_2
|
|
g_wszSanitizedName, // pwszSanitizedName_p3_7
|
|
pCAContext->iCert, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
g_strDomainDN, // pwszDomainDN_p5
|
|
g_strConfigDN, // pwszConfigDN_p6
|
|
pCAContext->iKey, // iCRL_p8
|
|
fDelta, // fDeltaCRL_p9
|
|
TRUE, // fDSAttrib_p10_11
|
|
1, // cStrings
|
|
(LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn
|
|
&pAltInfo->rgAltEntry[i].pwszURL); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
pAltInfo->rgAltEntry[i].dwAltNameChoice = CERT_ALT_NAME_URL;
|
|
i++;
|
|
}
|
|
}
|
|
CSASSERT(pAltInfo->cAltEntry == i);
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CRL_DIST_POINTS,
|
|
&CRLDistInfo,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pCDP->pbData,
|
|
&pCDP->cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "myEncodeObject");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pAltInfo->rgAltEntry)
|
|
{
|
|
for (i = 0; i < pAltInfo->cAltEntry; i++)
|
|
{
|
|
if (NULL != pAltInfo->rgAltEntry[i].pwszURL)
|
|
{
|
|
LocalFree(pAltInfo->rgAltEntry[i].pwszURL);
|
|
}
|
|
}
|
|
LocalFree(pAltInfo->rgAltEntry);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildAIA(
|
|
IN DWORD Flags,
|
|
IN CACTX const *pCAContext,
|
|
OUT CRYPT_OBJID_BLOB *pAIA)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cAIA;
|
|
DWORD i;
|
|
CSURLTEMPLATE const *pTemplate;
|
|
CSURLTEMPLATE const *pTemplateEnd;
|
|
CERT_AUTHORITY_INFO_ACCESS caio;
|
|
CERT_ACCESS_DESCRIPTION *pcad;
|
|
|
|
caio.cAccDescr = 0;
|
|
caio.rgAccDescr = NULL;
|
|
|
|
pAIA->pbData = NULL;
|
|
pAIA->cbData = 0;
|
|
|
|
cAIA = 0;
|
|
pTemplateEnd = g_paCACertURL;
|
|
if (0 != g_caRevURL)
|
|
{
|
|
pTemplateEnd = &g_paCACertURL[g_caCACertURL];
|
|
for (pTemplate = g_paCACertURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (Flags & pTemplate->Flags)
|
|
{
|
|
cAIA++;
|
|
}
|
|
}
|
|
}
|
|
if (0 == cAIA)
|
|
{
|
|
hr = S_FALSE;
|
|
goto error;
|
|
}
|
|
|
|
caio.rgAccDescr = (CERT_ACCESS_DESCRIPTION *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cAIA * sizeof(caio.rgAccDescr[0]));
|
|
if (NULL == caio.rgAccDescr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN);
|
|
for (pTemplate = g_paCACertURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (Flags & pTemplate->Flags)
|
|
{
|
|
pcad = &caio.rgAccDescr[caio.cAccDescr];
|
|
|
|
pcad->pszAccessMethod = (CSURL_ADDTOCERTOCSP & pTemplate->Flags)?
|
|
szOID_PKIX_OCSP : szOID_PKIX_CA_ISSUERS;
|
|
pcad->AccessLocation.dwAltNameChoice = CERT_ALT_NAME_URL;
|
|
|
|
hr = myFormatCertsrvStringArray(
|
|
TRUE, // fURL
|
|
g_pwszServerName, // pwszServerName_p1_2
|
|
g_wszSanitizedName, // pwszSanitizedName_p3_7
|
|
pCAContext->iCert, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
g_strDomainDN, // pwszDomainDN_p5
|
|
g_strConfigDN, // pwszConfigDN_p6
|
|
pCAContext->iKey, // iCRL_p8
|
|
FALSE, // fDeltaCRL_p9
|
|
TRUE, // fDSAttrib_p10_11
|
|
1, // cStrings
|
|
(LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn
|
|
&pcad->AccessLocation.pwszURL); // apwszStringsOut
|
|
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
caio.cAccDescr++;
|
|
}
|
|
}
|
|
CSASSERT(caio.cAccDescr == cAIA);
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_AUTHORITY_INFO_ACCESS,
|
|
&caio,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pAIA->pbData,
|
|
&pAIA->cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "myEncodeObject");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != caio.rgAccDescr)
|
|
{
|
|
for (i = 0; i < caio.cAccDescr; i++)
|
|
{
|
|
pcad = &caio.rgAccDescr[i];
|
|
if (NULL != pcad->AccessLocation.pwszURL)
|
|
{
|
|
LocalFree(pcad->AccessLocation.pwszURL);
|
|
}
|
|
}
|
|
LocalFree(caio.rgAccDescr);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSSetServerProperties(
|
|
IN ICertDBRow *prow,
|
|
OPTIONAL IN CACTX *pCAContext, // signing CACTX
|
|
OPTIONAL IN FILETIME const *pftNotBefore,
|
|
OPTIONAL IN FILETIME const *pftNotAfter,
|
|
IN LONG lValidityPeriodCount,
|
|
IN enum ENUM_PERIOD enumValidityPeriod)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_OBJID_BLOB aBlob[3];
|
|
CRYPT_OBJID_BLOB *pBlob;
|
|
DWORD i;
|
|
|
|
ZeroMemory(aBlob, sizeof(aBlob));
|
|
if (NULL == pCAContext)
|
|
{
|
|
pCAContext = g_pCAContextCurrent;
|
|
}
|
|
hr = pkcsBuildKeyAuthority2(g_CRLEditFlags, pCAContext, &aBlob[0]);
|
|
_JumpIfError(hr, error, "pkcsBuildKeyAuthority2");
|
|
|
|
hr = pkcsSetServerExtension(
|
|
prow,
|
|
TEXT(szOID_AUTHORITY_KEY_IDENTIFIER2),
|
|
aBlob[0].cbData,
|
|
aBlob[0].pbData);
|
|
_JumpIfError(hr, error, "pkcsSetServerExtension");
|
|
|
|
pBlob = &pCAContext->CDPCert;
|
|
if (pCAContext != g_pCAContextCurrent)
|
|
{
|
|
hr = pkcsBuildCDP(
|
|
CSURL_ADDTOCERTCDP,
|
|
FALSE, // fDelta
|
|
pCAContext,
|
|
&aBlob[1]);
|
|
_JumpIfError(hr, error, "pkcsBuildCDP");
|
|
|
|
pBlob = &aBlob[1];
|
|
}
|
|
hr = pkcsSetServerExtension(
|
|
prow,
|
|
TEXT(szOID_CRL_DIST_POINTS),
|
|
pBlob->cbData,
|
|
pBlob->pbData);
|
|
_JumpIfError(hr, error, "pkcsSetServerExtension");
|
|
|
|
pBlob = &pCAContext->AIACert;
|
|
if (pCAContext != g_pCAContextCurrent)
|
|
{
|
|
hr = pkcsBuildAIA(
|
|
CSURL_ADDTOCERTCDP | CSURL_ADDTOCERTOCSP,
|
|
pCAContext,
|
|
&aBlob[2]);
|
|
_JumpIfError(hr, error, "pkcsBuildAIA");
|
|
|
|
pBlob = &aBlob[2];
|
|
}
|
|
hr = pkcsSetServerExtension(
|
|
prow,
|
|
TEXT(szOID_AUTHORITY_INFO_ACCESS),
|
|
pBlob->cbData,
|
|
pBlob->pbData);
|
|
_JumpIfError(hr, error, "pkcsSetServerExtension");
|
|
|
|
hr = pkcsSetValidityPeriod(
|
|
prow,
|
|
pCAContext,
|
|
pftNotBefore,
|
|
pftNotAfter,
|
|
lValidityPeriodCount,
|
|
enumValidityPeriod);
|
|
_JumpIfError(hr, error, "pkcsSetValidityPeriod");
|
|
|
|
error:
|
|
for (i = 0; i < ARRAYSIZE(aBlob); i++)
|
|
{
|
|
if (NULL != aBlob[i].pbData)
|
|
{
|
|
LocalFree(aBlob[i].pbData);
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Find the newest cert with the matching key container name:
|
|
|
|
HRESULT
|
|
pkcsFindMatchingKeyContext(
|
|
OPTIONAL IN CERT_PUBLIC_KEY_INFO *pPublicKeyInfo,
|
|
IN DWORD iKey,
|
|
OUT CACTX **ppCAContext)
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
DWORD i;
|
|
CACTX *pCAContext;
|
|
|
|
*ppCAContext = NULL;
|
|
for (i = g_cCACerts; i > 0; i--)
|
|
{
|
|
pCAContext = &g_aCAContext[i - 1];
|
|
|
|
if ((MAXDWORD != iKey && iKey == pCAContext->iKey) ||
|
|
(NULL != pCAContext->pccCA &&
|
|
NULL != pPublicKeyInfo &&
|
|
CertComparePublicKeyInfo(
|
|
X509_ASN_ENCODING,
|
|
pPublicKeyInfo,
|
|
&pCAContext->pccCA->pCertInfo->SubjectPublicKeyInfo)))
|
|
{
|
|
// by design, CertComparePublicKeyInfo doesn't set last error!
|
|
|
|
*ppCAContext = pCAContext;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadURLTemplates(
|
|
IN WCHAR const *pwszRegName,
|
|
OUT CSURLTEMPLATE **ppaURL,
|
|
OUT DWORD *pcaURL)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszzTemplates = NULL;
|
|
WCHAR *pwsz;
|
|
DWORD cTemplate = 0;
|
|
CSURLTEMPLATE *pTemplate;
|
|
DWORD Flags;
|
|
WCHAR *pwsz2;
|
|
|
|
*ppaURL = NULL;
|
|
*pcaURL = 0;
|
|
|
|
// get (multiple) path templates
|
|
|
|
hr = myGetCertRegMultiStrValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
pwszRegName,
|
|
&pwszzTemplates);
|
|
_JumpIfError(hr, error, "myGetCertRegStrValue");
|
|
|
|
for (pwsz = pwszzTemplates; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
Flags = _wtoi(pwsz);
|
|
pwsz2 = pwsz;
|
|
while (iswdigit(*pwsz2))
|
|
{
|
|
pwsz2++;
|
|
}
|
|
if (0 != Flags && pwsz2 > pwsz && L':' == *pwsz2)
|
|
{
|
|
cTemplate++;
|
|
}
|
|
}
|
|
if (0 != cTemplate)
|
|
{
|
|
*ppaURL = (CSURLTEMPLATE *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cTemplate * sizeof((*ppaURL)[0]));
|
|
if (NULL == *ppaURL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
pTemplate = *ppaURL;
|
|
*pcaURL = cTemplate;
|
|
|
|
for (pwsz = pwszzTemplates; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
Flags = _wtoi(pwsz);
|
|
pwsz2 = pwsz;
|
|
while (iswdigit(*pwsz2))
|
|
{
|
|
pwsz2++;
|
|
}
|
|
if (0 != Flags && pwsz2 > pwsz && L':' == *pwsz2)
|
|
{
|
|
pTemplate->Flags = Flags;
|
|
|
|
hr = myDupString(&pwsz2[1], &pTemplate->pwszURL);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
pTemplate++;
|
|
}
|
|
}
|
|
CSASSERT(pTemplate == &(*ppaURL)[*pcaURL]);
|
|
}
|
|
|
|
error:
|
|
if (NULL != pwszzTemplates)
|
|
{
|
|
LocalFree(pwszzTemplates);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsFreeTemplates(
|
|
IN OUT CSURLTEMPLATE **ppaURL,
|
|
IN OUT DWORD *pcaURL)
|
|
{
|
|
CSURLTEMPLATE *pTemplate;
|
|
CSURLTEMPLATE *pTemplateEnd;
|
|
|
|
if (0 != *pcaURL && NULL != *ppaURL)
|
|
{
|
|
pTemplateEnd = &(*ppaURL)[*pcaURL];
|
|
for (pTemplate = *ppaURL; pTemplate < pTemplateEnd; pTemplate++)
|
|
{
|
|
if (NULL != pTemplate->pwszURL)
|
|
{
|
|
LocalFree(pTemplate->pwszURL);
|
|
}
|
|
}
|
|
LocalFree(*ppaURL);
|
|
*ppaURL = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetCertFilename(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD iCert,
|
|
IN DWORD iCertTarget,
|
|
OUT WCHAR **ppwszCertFile)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR wszBuf[MAX_PATH];
|
|
WCHAR *pwszIndexedName = NULL;
|
|
DWORD cwc;
|
|
|
|
*ppwszCertFile = NULL;
|
|
|
|
hr = myAllocIndexedName(
|
|
pwszSanitizedName,
|
|
iCert,
|
|
iCertTarget,
|
|
&pwszIndexedName);
|
|
_JumpIfError(hr, error, "myAllocIndexedName");
|
|
|
|
cwc = GetEnvironmentVariable(L"SystemRoot", wszBuf, ARRAYSIZE(wszBuf));
|
|
if (0 == cwc)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "GetEnvironmentVariable");
|
|
}
|
|
if (ARRAYSIZE(wszBuf) < cwc)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
_JumpError(hr, error, "%SystemRoot%");
|
|
}
|
|
cwc = wcslen(wszBuf) +
|
|
WSZARRAYSIZE(L"\\System32\\" wszCERTENROLLSHAREPATH L"\\") +
|
|
wcslen(g_pwszServerName) +
|
|
WSZARRAYSIZE(L"_") +
|
|
wcslen(pwszIndexedName) +
|
|
WSZARRAYSIZE(L".crt") +
|
|
1;
|
|
|
|
*ppwszCertFile = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == *ppwszCertFile)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
wcscpy(*ppwszCertFile, wszBuf);
|
|
wcscat(*ppwszCertFile, L"\\System32\\" wszCERTENROLLSHAREPATH L"\\");
|
|
wcscat(*ppwszCertFile, g_pwszServerName);
|
|
wcscat(*ppwszCertFile, L"_");
|
|
wcscat(*ppwszCertFile, pwszIndexedName);
|
|
wcscat(*ppwszCertFile, L".crt");
|
|
CSASSERT(1 + wcslen(*ppwszCertFile) == cwc);
|
|
|
|
error:
|
|
if (NULL != pwszIndexedName)
|
|
{
|
|
LocalFree(pwszIndexedName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsReloadMissingCertByHash(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD dwRegHashChoice,
|
|
IN BYTE const *pbHashReg,
|
|
IN DWORD cbHashReg,
|
|
IN DWORD iHash,
|
|
IN WCHAR const *pwszStoreName)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbHash;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
BSTR strHash = NULL;
|
|
ICertDBRow *prow = NULL;
|
|
DWORD cbCert;
|
|
BYTE *pbCert = NULL;
|
|
WCHAR *pwszCertFile = NULL;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
HCERTSTORE hStore = NULL;
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHashReg, pbHashReg, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Reloading %wsContext[%u]\n %ws\n",
|
|
CSRH_CASIGCERT == dwRegHashChoice? L"CA" : L"KRA",
|
|
iHash,
|
|
strHash));
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY |
|
|
PROPOPEN_CERTHASH |
|
|
PROPTABLE_REQCERT,
|
|
0,
|
|
strHash,
|
|
&prow);
|
|
_PrintIfErrorStr(hr, "OpenRow", strHash);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cbCert,
|
|
(BYTE **) &pbCert);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(cert)");
|
|
}
|
|
else if (CSRH_CASIGCERT != dwRegHashChoice)
|
|
{
|
|
_JumpError(hr, error, "OpenRow");
|
|
}
|
|
else
|
|
{
|
|
hr = pkcsGetCertFilename(
|
|
pwszSanitizedName,
|
|
iHash,
|
|
MAXDWORD, // iCertTarget
|
|
&pwszCertFile);
|
|
_JumpIfError(hr, error, "myGetCertFilename");
|
|
|
|
hr = DecodeFileW(pwszCertFile, &pbCert, &cbCert, CRYPT_STRING_ANY);
|
|
_JumpIfError(hr, error, "DecodeFileW");
|
|
}
|
|
|
|
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
if (cbHash != cbHashReg || 0 != memcmp(abHash, pbHashReg, cbHash))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "wrong Cert");
|
|
}
|
|
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
pwszStoreName);
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(hr, error, "CertOpenStore", pwszStoreName);
|
|
}
|
|
if (!CertAddCertificateContextToStore(
|
|
hStore,
|
|
pcc,
|
|
CERT_STORE_ADD_USE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
|
|
// Add as encoded blob to avoid all properties, key prov info, etc.
|
|
|
|
if (!CertAddEncodedCertificateToStore(
|
|
hStore,
|
|
X509_ASN_ENCODING,
|
|
pbCert,
|
|
cbCert,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL)) // ppCertContext
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddEncodedCertificateToStore");
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Reloaded %wsContext[%u]\n",
|
|
CSRH_CASIGCERT == dwRegHashChoice? L"CA" : L"KRA",
|
|
iHash));
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pwszCertFile)
|
|
{
|
|
LocalFree(pwszCertFile);
|
|
}
|
|
if (NULL != prow)
|
|
{
|
|
prow->Release();
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsReloadMissingCAOrKRACert(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD dwRegHashChoice,
|
|
IN DWORD iHash,
|
|
IN WCHAR const *pwszStoreName)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbHashReg = NULL;
|
|
DWORD cbHashReg;
|
|
|
|
hr = myGetCARegHash(
|
|
pwszSanitizedName,
|
|
dwRegHashChoice,
|
|
iHash,
|
|
&pbHashReg,
|
|
&cbHashReg);
|
|
_JumpIfError(hr, error, "myGetCARegHash");
|
|
|
|
hr = pkcsReloadMissingCertByHash(
|
|
pwszSanitizedName,
|
|
dwRegHashChoice,
|
|
pbHashReg,
|
|
cbHashReg,
|
|
iHash,
|
|
pwszStoreName);
|
|
_JumpIfError(hr, error, "pkcsReloadMissingCertByHash");
|
|
|
|
error:
|
|
if (NULL != pbHashReg)
|
|
{
|
|
LocalFree(pbHashReg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsFreeBlobArray(
|
|
IN DWORD cBlob,
|
|
CERT_BLOB *rgBlob)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < cBlob; i++)
|
|
{
|
|
if (NULL != rgBlob[i].pbData)
|
|
{
|
|
LocalFree(rgBlob[i].pbData);
|
|
}
|
|
}
|
|
LocalFree(rgBlob);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetKRACertBlobs(
|
|
IN ICertDBRow *prow,
|
|
OUT DWORD *pcCertBlob,
|
|
OUT CERT_BLOB **prgCertBlob)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszHashes = NULL;
|
|
WCHAR *pwsz;
|
|
DWORD cb;
|
|
DWORD cHash;
|
|
DWORD i;
|
|
CERT_BLOB *rgBlob = NULL;
|
|
HCERTSTORE hStore = NULL;
|
|
CRYPT_DATA_BLOB HashBlob;
|
|
DWORD cBlobLoaded;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
|
|
HashBlob.pbData = NULL;
|
|
cBlobLoaded = 0;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRequestKeyRecoveryHashes,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
&cb,
|
|
(BYTE **) &pwszHashes);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(KRA hashes)");
|
|
|
|
cHash = 1;
|
|
pwsz = pwszHashes;
|
|
for (;;)
|
|
{
|
|
pwsz = wcschr(pwsz, L'\n');
|
|
if (NULL == pwsz)
|
|
{
|
|
break;
|
|
}
|
|
*pwsz++ = L'\0';
|
|
cHash++;
|
|
}
|
|
rgBlob = (CERT_BLOB *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cHash * sizeof(rgBlob[0]));
|
|
if (NULL == rgBlob)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// open KRA store
|
|
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
|
|
wszKRA_CERTSTORE);
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
pwsz = pwszHashes;
|
|
for (i = 0; i < cHash; i++)
|
|
{
|
|
BOOL fReloaded;
|
|
|
|
hr = WszToMultiByteInteger(
|
|
TRUE,
|
|
pwsz,
|
|
&HashBlob.cbData,
|
|
&HashBlob.pbData);
|
|
_JumpIfError(hr, error, "WszToMultiByteInteger");
|
|
|
|
fReloaded = FALSE;
|
|
for (;;)
|
|
{
|
|
pcc = CertFindCertificateInStore(
|
|
hStore,
|
|
X509_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_HASH,
|
|
&HashBlob,
|
|
NULL);
|
|
if (fReloaded || NULL != pcc)
|
|
{
|
|
break;
|
|
}
|
|
hr = pkcsReloadMissingCertByHash(
|
|
g_wszSanitizedName,
|
|
CSRH_CAKRACERT,
|
|
HashBlob.pbData,
|
|
HashBlob.cbData,
|
|
i,
|
|
wszKRA_CERTSTORE);
|
|
_PrintIfError(hr, "pkcsReloadMissingCertByHash");
|
|
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
|
CERT_STORE_READONLY_FLAG,
|
|
wszKRA_CERTSTORE);
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
fReloaded = TRUE;
|
|
}
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CertFindCertificateInStore");
|
|
}
|
|
else
|
|
{
|
|
rgBlob[cBlobLoaded].pbData = (BYTE *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
pcc->cbCertEncoded);
|
|
if (NULL == rgBlob[cBlobLoaded].pbData)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
rgBlob[cBlobLoaded].cbData = pcc->cbCertEncoded;
|
|
CopyMemory(
|
|
rgBlob[cBlobLoaded].pbData,
|
|
pcc->pbCertEncoded,
|
|
pcc->cbCertEncoded);
|
|
cBlobLoaded++;
|
|
|
|
CertFreeCertificateContext(pcc);
|
|
pcc = NULL;
|
|
}
|
|
pwsz += wcslen(pwsz) + 1;
|
|
LocalFree(HashBlob.pbData);
|
|
HashBlob.pbData = NULL;
|
|
}
|
|
*pcCertBlob = cBlobLoaded;
|
|
*prgCertBlob = rgBlob;
|
|
rgBlob = NULL;
|
|
|
|
error:
|
|
if (NULL != rgBlob)
|
|
{
|
|
pkcsFreeBlobArray(cBlobLoaded, rgBlob);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
if (NULL != HashBlob.pbData)
|
|
{
|
|
LocalFree(HashBlob.pbData);
|
|
}
|
|
if (NULL != pwszHashes)
|
|
{
|
|
LocalFree(pwszHashes);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetHashAsOctet(
|
|
IN ICertDBRow *prow,
|
|
OUT BYTE **ppbData,
|
|
OUT DWORD *pcbData)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszHash = NULL;
|
|
DWORD cb;
|
|
CRYPT_DATA_BLOB Blob;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
BYTE *pbCert = NULL;
|
|
DWORD cbCert;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
|
|
*ppbData = NULL;
|
|
Blob.pbData = NULL;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropCertificateHash,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cb,
|
|
(BYTE **) &pwszHash);
|
|
if (S_OK != hr)
|
|
{
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
_JumpError(hr, error, "PKCSGetProperty(hash)");
|
|
}
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cbCert,
|
|
&pbCert);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(cert)");
|
|
|
|
pcc = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbCert,
|
|
cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
Blob.cbData = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&Blob.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
Blob.pbData = abHash;
|
|
}
|
|
else
|
|
{
|
|
hr = WszToMultiByteInteger(TRUE, pwszHash, &Blob.cbData, &Blob.pbData);
|
|
_JumpIfError(hr, error, "WszToMultiByteInteger");
|
|
}
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&Blob,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbData,
|
|
pcbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszHash)
|
|
{
|
|
LocalFree(pwszHash);
|
|
}
|
|
if (NULL != Blob.pbData && abHash != Blob.pbData)
|
|
{
|
|
LocalFree(Blob.pbData);
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
AddCertBlobToArray(
|
|
IN CERT_CONTEXT const *pcc,
|
|
IN CERT_BLOB *rgCertBlobAll,
|
|
IN CERT_BLOB **ppCertBlob)
|
|
{
|
|
CERT_BLOB *pCertBlob = *ppCertBlob;
|
|
DWORD i;
|
|
DWORD cBlob;
|
|
|
|
cBlob = SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll);
|
|
for (i = 0; i < cBlob; i++)
|
|
{
|
|
if (rgCertBlobAll[i].cbData == pcc->cbCertEncoded &&
|
|
0 == memcmp(
|
|
rgCertBlobAll[i].pbData,
|
|
pcc->pbCertEncoded,
|
|
pcc->cbCertEncoded))
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Duplicate Recovery Blob Cert[%u]\n",
|
|
i));
|
|
goto error;
|
|
}
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Adding Recovery Blob Cert[%u]\n",
|
|
cBlob));
|
|
pCertBlob->cbData = pcc->cbCertEncoded;
|
|
pCertBlob->pbData = pcc->pbCertEncoded;
|
|
pCertBlob++;
|
|
|
|
*ppCertBlob = pCertBlob;
|
|
|
|
error:
|
|
;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetArchivedKey(
|
|
IN DWORD dwRequestId,
|
|
OUT BYTE **ppbArchivedKey, // CoTaskMem*
|
|
OUT DWORD *pcbArchivedKey)
|
|
{
|
|
HRESULT hr;
|
|
ICertDBRow *prow = NULL;
|
|
BYTE *pbKey = NULL;
|
|
DWORD cbKey;
|
|
BYTE *pbCertUser = NULL;
|
|
DWORD cbCertUser;
|
|
HCRYPTMSG hMsg = NULL;
|
|
CACTX *pCAContext;
|
|
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
|
|
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
|
|
CERT_CONTEXT const *pccUser = NULL;
|
|
CERT_CHAIN_CONTEXT const *pCertChainContextUser = NULL;
|
|
CERT_CHAIN_PARA CertChainPara;
|
|
CERT_BLOB *rgCertBlobKRA = NULL;
|
|
DWORD cCertBlobKRA;
|
|
CERT_BLOB *rgCertBlobAll = NULL;
|
|
DWORD cCertBlobAll;
|
|
CERT_BLOB *pCertBlob;
|
|
CRYPT_ATTRIBUTE HashAttrib;
|
|
CRYPT_ATTR_BLOB HashAttribBlob;
|
|
DWORD i;
|
|
|
|
*ppbArchivedKey = NULL;
|
|
HashAttribBlob.pbData = NULL;
|
|
cCertBlobKRA = 0;
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY | PROPTABLE_REQCERT,
|
|
dwRequestId,
|
|
NULL,
|
|
&prow);
|
|
_JumpIfError(hr, error, "OpenRow");
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRequestRawArchivedKey,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
&cbKey,
|
|
&pbKey);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(key)");
|
|
|
|
hr = pkcsGetKRACertBlobs(prow, &cCertBlobKRA, &rgCertBlobKRA);
|
|
_JumpIfError(hr, error, "pkcsGetKRACertBlobs");
|
|
|
|
hr = pkcsGetHashAsOctet(
|
|
prow,
|
|
&HashAttribBlob.pbData,
|
|
&HashAttribBlob.cbData);
|
|
_JumpIfError(hr, error, "pkcsGetHashAsOctet");
|
|
|
|
HashAttrib.pszObjId = szOID_ARCHIVED_KEY_CERT_HASH;
|
|
HashAttrib.cValue = 1;
|
|
HashAttrib.rgValue = &HashAttribBlob;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cbCertUser,
|
|
&pbCertUser);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(cert)");
|
|
|
|
pccUser = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbCertUser,
|
|
cbCertUser);
|
|
if (NULL == pccUser)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
// build the user cert chain
|
|
|
|
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
|
|
CertChainPara.cbSize = sizeof(CertChainPara);
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE,
|
|
pccUser,
|
|
NULL,
|
|
NULL,
|
|
&CertChainPara,
|
|
0,
|
|
NULL,
|
|
&pCertChainContextUser))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
|
|
// make sure there is at least 1 simple chain
|
|
|
|
if (0 == pCertChainContextUser->cChain)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "No user chain");
|
|
}
|
|
|
|
// Encode the encrypted key into a PKCS 7, signed by the current CA cert.
|
|
// Initialize the CMSG_SIGNER_ENCODE_INFO structure for one signer.
|
|
|
|
pCAContext = g_pCAContextCurrent;
|
|
cCertBlobAll = cCertBlobKRA +
|
|
pCAContext->cCACertChain +
|
|
pCertChainContextUser->rgpChain[0]->cElement;
|
|
|
|
rgCertBlobAll = (CERT_BLOB *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
cCertBlobAll * sizeof(rgCertBlobAll[0]));
|
|
if (NULL == rgCertBlobAll)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
pCertBlob = rgCertBlobAll;
|
|
|
|
CopyMemory(pCertBlob, rgCertBlobKRA, cCertBlobKRA * sizeof(pCertBlob[0]));
|
|
pCertBlob += cCertBlobKRA;
|
|
|
|
// Add the current CA cert chain
|
|
|
|
for (i = 0; i < pCAContext->cCACertChain; i++)
|
|
{
|
|
AddCertBlobToArray(
|
|
pCAContext->apCACertChain[i],
|
|
rgCertBlobAll,
|
|
&pCertBlob);
|
|
}
|
|
|
|
// Add the user cert chain
|
|
|
|
{
|
|
CERT_SIMPLE_CHAIN *pSimpleChain;
|
|
|
|
pSimpleChain = pCertChainContextUser->rgpChain[0];
|
|
for (i = 0; i < pSimpleChain->cElement; i++)
|
|
{
|
|
AddCertBlobToArray(
|
|
pSimpleChain->rgpElement[i]->pCertContext,
|
|
rgCertBlobAll,
|
|
&pCertBlob);
|
|
}
|
|
}
|
|
CSASSERT(pCertBlob <= &rgCertBlobAll[cCertBlobAll]);
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Recovery Certs: %u --> %u\n",
|
|
cCertBlobAll,
|
|
SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll)));
|
|
cCertBlobAll = SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll);
|
|
|
|
ZeroMemory(&SignerEncodeInfo, sizeof(SignerEncodeInfo));
|
|
SignerEncodeInfo.cbSize = sizeof(SignerEncodeInfo);
|
|
SignerEncodeInfo.pCertInfo = pCAContext->pccCA->pCertInfo;
|
|
SignerEncodeInfo.hCryptProv = pCAContext->hProvCA;
|
|
SignerEncodeInfo.dwKeySpec = AT_SIGNATURE;
|
|
SignerEncodeInfo.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1;
|
|
SignerEncodeInfo.cAuthAttr = 1;
|
|
SignerEncodeInfo.rgAuthAttr = &HashAttrib;
|
|
//SignerEncodeInfo.cUnauthAttr = 0;
|
|
//SignerEncodeInfo.rgUnauthAttr = NULL;
|
|
//SignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = ???;
|
|
|
|
// CERT_ID_SHA1_HASH is not yet implemented in CryptMsgOpenToEncode
|
|
//SignerEncodeInfo.SignerId.dwIdChoice = CERT_ID_SHA1_HASH;
|
|
//SignerEncodeInfo.SignerId.HashId.cbData = cb;
|
|
//SignerEncodeInfo.SignerId.HashId.pbData = abHash;
|
|
|
|
ZeroMemory(&SignedMsgEncodeInfo, sizeof(SignedMsgEncodeInfo));
|
|
SignedMsgEncodeInfo.cbSize = sizeof(SignedMsgEncodeInfo);
|
|
SignedMsgEncodeInfo.cSigners = 1;
|
|
SignedMsgEncodeInfo.rgSigners = &SignerEncodeInfo;
|
|
SignedMsgEncodeInfo.cCertEncoded = cCertBlobAll;
|
|
SignedMsgEncodeInfo.rgCertEncoded = rgCertBlobAll;
|
|
//SignedMsgEncodeInfo.cCrlEncoded = 0;
|
|
//SignedMsgEncodeInfo.rgCrlEncoded = NULL;
|
|
|
|
hMsg = CryptMsgOpenToEncode(
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
0, // dwFlags
|
|
CMSG_SIGNED, // dwMsgType
|
|
&SignedMsgEncodeInfo, // pvMsgEncodeInfo
|
|
NULL, // pszInnerContentObjID
|
|
NULL); // pStreamInfo
|
|
if (NULL == hMsg)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgOpenToDecode");
|
|
}
|
|
|
|
if (!CryptMsgUpdate(hMsg, pbKey, cbKey, TRUE))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgUpdate");
|
|
}
|
|
|
|
// Return the encoded and signed content.
|
|
// Use CMSG_CONTENT_PARAM to get the signed message.
|
|
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_CONTENT_PARAM,
|
|
0,
|
|
CERTLIB_USE_COTASKMEMALLOC,
|
|
(VOID **) ppbArchivedKey,
|
|
pcbArchivedKey);
|
|
_JumpIfError(hr, error, "myCryptMsgGetParam");
|
|
|
|
error:
|
|
if (pCertChainContextUser != NULL)
|
|
{
|
|
CertFreeCertificateChain(pCertChainContextUser);
|
|
}
|
|
if (NULL != pccUser)
|
|
{
|
|
CertFreeCertificateContext(pccUser);
|
|
}
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
if (NULL != rgCertBlobKRA)
|
|
{
|
|
pkcsFreeBlobArray(cCertBlobKRA, rgCertBlobKRA);
|
|
}
|
|
if (NULL != rgCertBlobAll)
|
|
{
|
|
LocalFree(rgCertBlobAll);
|
|
}
|
|
if (NULL != HashAttribBlob.pbData)
|
|
{
|
|
LocalFree(HashAttribBlob.pbData);
|
|
}
|
|
if (NULL != pbKey)
|
|
{
|
|
LocalFree(pbKey);
|
|
}
|
|
if (NULL != pbCertUser)
|
|
{
|
|
LocalFree(pbCertUser);
|
|
}
|
|
if (NULL != prow)
|
|
{
|
|
prow->Release();
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetKeyContainerName(
|
|
IN CERT_CONTEXT const *pccCA,
|
|
OUT WCHAR **ppwszKeyContainerName)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_HASH_BLOB KeyIdentifier;
|
|
CRYPT_KEY_PROV_INFO *pkpi = NULL;
|
|
DWORD cb;
|
|
|
|
KeyIdentifier.pbData = NULL;
|
|
*ppwszKeyContainerName = NULL;
|
|
|
|
hr = myGetPublicKeyHash(
|
|
pccCA->pCertInfo,
|
|
&pccCA->pCertInfo->SubjectPublicKeyInfo,
|
|
&KeyIdentifier.pbData,
|
|
&KeyIdentifier.cbData);
|
|
_JumpIfError(hr, error, "myGetPublicKeyHash");
|
|
|
|
cb = 0;
|
|
for (;;)
|
|
{
|
|
if (!CryptGetKeyIdentifierProperty(
|
|
&KeyIdentifier,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
CRYPT_KEYID_MACHINE_FLAG,
|
|
NULL, // pwszComputerName
|
|
NULL, // pvReserved
|
|
pkpi,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "Cert index");
|
|
}
|
|
if (NULL != pkpi)
|
|
{
|
|
break;
|
|
}
|
|
pkpi = (CRYPT_KEY_PROV_INFO *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pkpi)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
hr = myDupString(pkpi->pwszContainerName, ppwszKeyContainerName);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
error:
|
|
if (NULL != pkpi)
|
|
{
|
|
LocalFree(pkpi);
|
|
}
|
|
if (NULL != KeyIdentifier.pbData)
|
|
{
|
|
LocalFree(KeyIdentifier.pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCAContext(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN WCHAR *pwszProvName,
|
|
IN DWORD dwProvType,
|
|
IN ALG_ID idAlg,
|
|
IN BOOL fMachineKeyset,
|
|
IN DWORD iHash,
|
|
IN OUT HCERTSTORE *phMyStore)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTPROV hProvCA = NULL;
|
|
char *pszObjIdSignatureAlgorithm = NULL;
|
|
WCHAR *pwszKeyContainerName = NULL;
|
|
CERT_CONTEXT const *pccCA = NULL;
|
|
DWORD cCACertChain;
|
|
CERT_CONTEXT const **apCACertChain = NULL;
|
|
CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
|
|
CERT_CHAIN_PARA CertChainPara;
|
|
CRYPT_KEY_PROV_INFO *pKey = NULL;
|
|
CACTX *pCAContext;
|
|
int i;
|
|
DWORD cbKey;
|
|
DWORD iCert;
|
|
DWORD iKey;
|
|
DWORD NameId;
|
|
BOOL fReloaded;
|
|
|
|
hr = myGetSigningOID(
|
|
NULL, // hProv
|
|
pwszProvName,
|
|
dwProvType,
|
|
idAlg,
|
|
&pszObjIdSignatureAlgorithm);
|
|
_JumpIfError(hr, error, "myGetSigningOID");
|
|
|
|
if (~_16BITMASK & iHash)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Cert index");
|
|
}
|
|
|
|
fReloaded = FALSE;
|
|
cCACertChain = 0;
|
|
for (;;)
|
|
{
|
|
hr = myFindCACertByHashIndex(
|
|
*phMyStore,
|
|
pwszSanitizedName,
|
|
CSRH_CASIGCERT,
|
|
iHash,
|
|
&NameId,
|
|
&pccCA);
|
|
iCert = iHash;
|
|
iKey = iCert;
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// if no hash entry exists for this index, fake up a CA Context
|
|
// as a place holder.
|
|
|
|
if (S_FALSE == hr)
|
|
{
|
|
CSASSERT(MAXDWORD == NameId);
|
|
CSASSERT(NULL == pccCA);
|
|
break;
|
|
}
|
|
if (fReloaded || CRYPT_E_NOT_FOUND != hr)
|
|
{
|
|
_JumpError(hr, error, "myFindCACertByHashIndex");
|
|
}
|
|
_PrintError(hr, "myFindCACertByHashIndex");
|
|
|
|
// The CA cert is missing from the HKLM "my" store -- look it up in
|
|
// the DB or the CertEnroll directory, and put it back in the store.
|
|
|
|
hr = pkcsReloadMissingCAOrKRACert(
|
|
pwszSanitizedName,
|
|
CSRH_CASIGCERT,
|
|
iHash,
|
|
wszMY_CERTSTORE);
|
|
_JumpIfError(hr, error, "pkcsReloadMissingCAOrKRACert");
|
|
|
|
CertCloseStore(*phMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
*phMyStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
wszMY_CERTSTORE);
|
|
if (NULL == *phMyStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
fReloaded = TRUE;
|
|
}
|
|
|
|
CSASSERT(S_FALSE == hr || S_OK == hr);
|
|
if (S_OK == hr)
|
|
{
|
|
if (MAXDWORD != NameId && iCert != CANAMEIDTOICERT(NameId))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"NameId=%u.%u iCert=%u\n",
|
|
CANAMEIDTOICERT(NameId),
|
|
CANAMEIDTOIKEY(NameId),
|
|
iCert));
|
|
_JumpError(hr, error, "bad iCert");
|
|
}
|
|
|
|
fReloaded = FALSE;
|
|
for (;;)
|
|
{
|
|
if (NULL != pwszKeyContainerName)
|
|
{
|
|
LocalFree(pwszKeyContainerName);
|
|
pwszKeyContainerName = NULL;
|
|
}
|
|
if (!fReloaded)
|
|
{
|
|
// get the private key provider info
|
|
|
|
if (!myCertGetCertificateContextProperty(
|
|
pccCA,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pKey,
|
|
&cbKey))
|
|
{
|
|
hr = myHLastError();
|
|
if (CRYPT_E_NOT_FOUND != hr)
|
|
{
|
|
_JumpError(hr, error, "myCertGetCertificateContextProperty");
|
|
}
|
|
_PrintError(hr, "CertGetCertificateContextProperty");
|
|
|
|
// The Key Provider Info is missing -- use the sanitized
|
|
// name and key index to construct the key container name.
|
|
// If that key matches, we'll write out the new Key
|
|
// Provider Info below.
|
|
|
|
hr = myAllocIndexedName(
|
|
pwszSanitizedName,
|
|
MAXDWORD != NameId? CANAMEIDTOIKEY(NameId) : iCert,
|
|
MAXDWORD, // iCertTarget
|
|
&pwszKeyContainerName);
|
|
_JumpIfError(hr, error, "myAllocIndexedName");
|
|
}
|
|
else
|
|
{
|
|
hr = myDupString(pKey->pwszContainerName, &pwszKeyContainerName);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = pkcsGetKeyContainerName(pccCA, &pwszKeyContainerName);
|
|
_JumpIfError(hr, error, "pkcsGetKeyContainerName");
|
|
}
|
|
|
|
// test signing
|
|
|
|
hr = myValidateSigningKey(
|
|
pwszKeyContainerName,
|
|
pwszProvName,
|
|
dwProvType,
|
|
0 != g_CryptSilent,
|
|
fMachineKeyset,
|
|
FALSE, // fForceSignatureTest
|
|
pccCA,
|
|
NULL, // pPublicKeyInfo
|
|
idAlg,
|
|
NULL, // pfSigningTestAttempted
|
|
NULL); // phProv
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
if (fReloaded)
|
|
{
|
|
_JumpError(hr, error, "myValidateSigningKey");
|
|
}
|
|
_PrintError(hr, "myValidateSigningKey");
|
|
|
|
fReloaded = TRUE;
|
|
}
|
|
|
|
// If the Key Provider Info is missing, write out new Key Provider Info
|
|
|
|
if (NULL == pKey)
|
|
{
|
|
CRYPT_KEY_PROV_INFO kpi;
|
|
|
|
ZeroMemory(&kpi, sizeof(kpi));
|
|
kpi.pwszContainerName = pwszKeyContainerName;
|
|
kpi.pwszProvName = pwszProvName;
|
|
kpi.dwProvType = dwProvType;
|
|
kpi.dwFlags = fMachineKeyset? CRYPT_MACHINE_KEYSET : 0;
|
|
kpi.dwKeySpec = AT_SIGNATURE;
|
|
|
|
if (!CertSetCertificateContextProperty(
|
|
pccCA,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
0,
|
|
&kpi))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertSetCertificateContextProperty");
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Reloaded CAContext[%u] KeyProvInfo[%u]\n",
|
|
iCert,
|
|
iKey));
|
|
}
|
|
|
|
hr = pkcsFindMatchingKeyContext(
|
|
&pccCA->pCertInfo->SubjectPublicKeyInfo,
|
|
MAXDWORD,
|
|
&pCAContext);
|
|
if (S_OK != hr && MAXDWORD != NameId)
|
|
{
|
|
iKey = CANAMEIDTOIKEY(NameId);
|
|
if (iKey < iCert)
|
|
{
|
|
hr = pkcsFindMatchingKeyContext(NULL, iKey, &pCAContext);
|
|
_JumpIfError(hr, error, "pkcsFindMatchingKeyContext");
|
|
}
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
iKey = pCAContext->iKey;
|
|
if (MAXDWORD != NameId && iKey != CANAMEIDTOIKEY(NameId))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "bad iKey");
|
|
}
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
CSASSERT(pCAContext->Flags & CTXF_CERTMISSING);
|
|
pCAContext->Flags |= CTXF_CRLZOMBIE;
|
|
}
|
|
else
|
|
{
|
|
// CRLs will be handled by the newest CA cert for this key.
|
|
// Turn off CRLs for older CA certs with this key.
|
|
|
|
CSASSERT(0 == (pCAContext->Flags & CTXF_CERTMISSING));
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"pkcsLoadCAContext(%u) DUPKEY, %u.%u: f=%x->%x\n",
|
|
iHash,
|
|
pCAContext->iCert,
|
|
pCAContext->iKey,
|
|
pCAContext->Flags,
|
|
pCAContext->Flags | CTXF_SKIPCRL));
|
|
pCAContext->Flags |= CTXF_SKIPCRL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_cCAKeys++; // this key has not previously been loaded
|
|
}
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"CAContext[%u]: Key %u: %ws\n",
|
|
iCert,
|
|
iKey,
|
|
pwszKeyContainerName));
|
|
|
|
// get private key handler for later use if current CA
|
|
|
|
if (!myCertSrvCryptAcquireContext(
|
|
&hProvCA,
|
|
pwszKeyContainerName,
|
|
pwszProvName,
|
|
dwProvType,
|
|
g_CryptSilent,
|
|
fMachineKeyset))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(
|
|
hr,
|
|
error,
|
|
"myCertSrvCryptAcquireContext",
|
|
pwszKeyContainerName);
|
|
}
|
|
|
|
// now try to figure out the chain
|
|
|
|
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
|
|
CertChainPara.cbSize = sizeof(CertChainPara);
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE,
|
|
pccCA,
|
|
NULL,
|
|
NULL,
|
|
&CertChainPara,
|
|
0,
|
|
NULL,
|
|
&pCertChainContext))
|
|
{
|
|
hr = myHLastError();
|
|
goto error;
|
|
}
|
|
|
|
// make sure there is at least 1 simple chain
|
|
|
|
if (pCertChainContext->cChain == 0)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "No valid trust chain could be formed");
|
|
}
|
|
|
|
// tell global how many elements we have in our chain
|
|
|
|
cCACertChain = pCertChainContext->rgpChain[0]->cElement;
|
|
|
|
// Allocate memory for global. Allocate one extra pointer to allow loop
|
|
// to assign NULL pointer in place in array. Leave the count set to the
|
|
// actual number of CA cert contexts, excluding the NULL pointer.
|
|
|
|
apCACertChain = (CERT_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cCACertChain + 1) * sizeof(apCACertChain[0]));
|
|
if (NULL == apCACertChain)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// copy chain in reverse order: from parent to child
|
|
|
|
for (i = cCACertChain - 1; i >= 0; i--)
|
|
{
|
|
apCACertChain[i] = CertDuplicateCertificateContext(
|
|
pCertChainContext->rgpChain[0]->rgpElement[i]->pCertContext);
|
|
|
|
if (NULL == apCACertChain[i])
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDuplicateCertificateContext");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < (int) g_cCACerts; i++)
|
|
{
|
|
if (iCert < g_aCAContext[i].iCert)
|
|
{
|
|
MoveMemory(
|
|
&g_aCAContext[i + 1],
|
|
&g_aCAContext[i],
|
|
(g_cCACerts - i) * sizeof(g_aCAContext[0]));
|
|
break;
|
|
}
|
|
}
|
|
g_cCACerts++;
|
|
|
|
pCAContext = &g_aCAContext[i];
|
|
ZeroMemory(pCAContext, sizeof(*pCAContext));
|
|
|
|
if (NULL == pccCA)
|
|
{
|
|
pCAContext->Flags |= CTXF_CERTMISSING | CTXF_SKIPCRL;
|
|
}
|
|
pCAContext->iCert = iCert;
|
|
pCAContext->iKey = iKey;
|
|
pCAContext->NameId = MAKECANAMEID(iCert, iKey);
|
|
|
|
pCAContext->hProvCA = hProvCA;
|
|
hProvCA = NULL;
|
|
|
|
pCAContext->pccCA = pccCA;
|
|
pccCA = NULL;
|
|
|
|
if (NULL != apCACertChain)
|
|
{
|
|
pCAContext->cCACertChain = cCACertChain;
|
|
pCAContext->apCACertChain = apCACertChain;
|
|
apCACertChain = NULL;
|
|
}
|
|
|
|
pCAContext->pszObjIdSignatureAlgorithm = pszObjIdSignatureAlgorithm;
|
|
pszObjIdSignatureAlgorithm = NULL;
|
|
|
|
pCAContext->pwszKeyContainerName = pwszKeyContainerName;
|
|
pwszKeyContainerName = NULL;
|
|
|
|
|
|
// Ignore failure from here on -- collected data is optional
|
|
|
|
if (NULL != pCAContext->pccCA)
|
|
{
|
|
hr = myGetPublicKeyHash(
|
|
pCAContext->pccCA->pCertInfo,
|
|
&pCAContext->pccCA->pCertInfo->SubjectPublicKeyInfo,
|
|
&pCAContext->IssuerKeyId.pbData,
|
|
&pCAContext->IssuerKeyId.cbData);
|
|
_PrintIfError(hr, "myGetPublicKeyHash");
|
|
|
|
if (0 == (CTXF_SKIPCRL & pCAContext->Flags))
|
|
{
|
|
hr = pkcsBuildKeyAuthority2(
|
|
g_CRLEditFlags,
|
|
pCAContext,
|
|
&pCAContext->KeyAuthority2CRL);
|
|
_PrintIfError(hr, "pkcsBuildKeyAuthority2");
|
|
|
|
hr = pkcsBuildCDP(
|
|
CSURL_ADDTOFRESHESTCRL,
|
|
TRUE, // fDelta
|
|
pCAContext,
|
|
&pCAContext->CDPCRLFreshest);
|
|
_PrintIfError(hr, "pkcsBuildCDP");
|
|
|
|
hr = pkcsBuildCDP(
|
|
CSURL_ADDTOCRLCDP,
|
|
FALSE, // fDelta
|
|
pCAContext,
|
|
&pCAContext->CDPCRLBase);
|
|
_PrintIfError(hr, "pkcsBuildCDP");
|
|
|
|
hr = pkcsBuildCDP(
|
|
CSURL_ADDTOCRLCDP,
|
|
TRUE, // fDelta
|
|
pCAContext,
|
|
&pCAContext->CDPCRLDelta);
|
|
_PrintIfError(hr, "pkcsBuildCDP");
|
|
|
|
hr = pkcsBuildCRLList(
|
|
FALSE,
|
|
pCAContext,
|
|
&pCAContext->papwszCRLFiles);
|
|
_JumpIfError(hr, error, "pkcsBuildCRLList");
|
|
|
|
hr = pkcsBuildCRLList(
|
|
TRUE,
|
|
pCAContext,
|
|
&pCAContext->papwszDeltaCRLFiles);
|
|
_JumpIfError(hr, error, "pkcsBuildCRLList");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hProvCA)
|
|
{
|
|
CryptReleaseContext(hProvCA, 0);
|
|
}
|
|
if (NULL != pszObjIdSignatureAlgorithm)
|
|
{
|
|
LocalFree(pszObjIdSignatureAlgorithm);
|
|
}
|
|
if (NULL != pwszKeyContainerName)
|
|
{
|
|
LocalFree(pwszKeyContainerName);
|
|
}
|
|
if (NULL != pKey)
|
|
{
|
|
LocalFree(pKey);
|
|
}
|
|
if (pCertChainContext != NULL)
|
|
{
|
|
CertFreeCertificateChain(pCertChainContext);
|
|
}
|
|
if (NULL != pccCA)
|
|
{
|
|
CertFreeCertificateContext(pccCA);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCAContextArray(
|
|
IN WCHAR const *pwszSanitizedName)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cCACerts;
|
|
HCERTSTORE hMyStore = NULL;
|
|
WCHAR *pwszProvName = NULL;
|
|
DWORD dwProvType;
|
|
ALG_ID idAlg;
|
|
BOOL fMachineKeyset;
|
|
DWORD iHash;
|
|
|
|
// get provider name
|
|
|
|
hr = myGetCertSrvCSP(
|
|
FALSE, // fEncryptionCSP
|
|
pwszSanitizedName,
|
|
&dwProvType,
|
|
&pwszProvName,
|
|
&idAlg,
|
|
&fMachineKeyset,
|
|
NULL); // pdwKeySize
|
|
_JumpIfError(hr, error, "myGetCertSrvCSP");
|
|
|
|
// open MY store
|
|
|
|
hMyStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
wszMY_CERTSTORE);
|
|
if (NULL == hMyStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
// find & load CA certs, etc.
|
|
|
|
hr = myGetCARegHashCount(pwszSanitizedName, CSRH_CASIGCERT, &cCACerts);
|
|
if (S_OK == hr && 0 == cCACerts)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
_JumpIfError(hr, error, "myGetCARegHashCount");
|
|
|
|
g_aCAContext = (CACTX *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cCACerts * sizeof(g_aCAContext[0]));
|
|
if (NULL == g_aCAContext)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
for (iHash = 0; iHash < cCACerts; iHash++)
|
|
{
|
|
hr = pkcsLoadCAContext(
|
|
pwszSanitizedName,
|
|
pwszProvName,
|
|
dwProvType,
|
|
idAlg,
|
|
fMachineKeyset,
|
|
iHash,
|
|
&hMyStore);
|
|
if (S_FALSE == hr)
|
|
{
|
|
continue;
|
|
}
|
|
_JumpIfError(hr, error, "pkcsLoadCAContext");
|
|
}
|
|
|
|
g_pCAContextCurrent = &g_aCAContext[g_cCACerts - 1];
|
|
|
|
// Only build a Key Authority extension for the current CACTX -- it's the
|
|
// only one used to issue certs.
|
|
|
|
hr = pkcsBuildKeyAuthority2(
|
|
EDITF_ENABLEAKIKEYID |
|
|
EDITF_ENABLEAKIISSUERNAME |
|
|
EDITF_ENABLEAKIISSUERSERIAL,
|
|
g_pCAContextCurrent,
|
|
&g_pCAContextCurrent->KeyAuthority2Cert);
|
|
_PrintIfError(hr, "pkcsBuildKeyAuthority2");
|
|
|
|
// Only build a CDP extension for the current CACTX -- it's the
|
|
// only one used to issue certs.
|
|
|
|
hr = pkcsBuildCDP(
|
|
CSURL_ADDTOCERTCDP,
|
|
FALSE, // fDelta
|
|
g_pCAContextCurrent,
|
|
&g_pCAContextCurrent->CDPCert);
|
|
_PrintIfError(hr, "pkcsBuildCDP");
|
|
|
|
// Only build an AIA extension for the current CACTX -- it's the
|
|
// only one used to issue certs.
|
|
|
|
hr = pkcsBuildAIA(
|
|
CSURL_ADDTOCERTCDP | CSURL_ADDTOCERTOCSP,
|
|
g_pCAContextCurrent,
|
|
&g_pCAContextCurrent->AIACert);
|
|
_PrintIfError(hr, "pkcsBuildAIA");
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszProvName)
|
|
{
|
|
LocalFree(pwszProvName);
|
|
}
|
|
if (NULL != hMyStore)
|
|
{
|
|
CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pkcsCompareBlob(
|
|
IN CERT_BLOB const *pBlob1,
|
|
IN CERT_BLOB const *pBlob2)
|
|
{
|
|
return(
|
|
pBlob1->cbData == pBlob2->cbData &&
|
|
0 == memcmp(pBlob1->pbData, pBlob2->pbData, pBlob1->cbData));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAddInheritedExtension(
|
|
IN ICertDBRow *prow,
|
|
IN char const *pszObjId,
|
|
OPTIONAL IN CACTX const *pCAContext,
|
|
OPTIONAL IN CACTX const *pCAContextTarget,
|
|
OUT BOOL *pfAdded)
|
|
{
|
|
HRESULT hr;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_EXTENSION const *pExtTarget;
|
|
WCHAR *pwszObjId = NULL;
|
|
|
|
*pfAdded = FALSE;
|
|
if (NULL == pCAContext)
|
|
{
|
|
pCAContext = g_pCAContextCurrent;
|
|
}
|
|
pExt = CertFindExtension(
|
|
pszObjId,
|
|
pCAContext->pccCA->pCertInfo->cExtension,
|
|
pCAContext->pccCA->pCertInfo->rgExtension);
|
|
if (NULL != pCAContextTarget)
|
|
{
|
|
pExtTarget = CertFindExtension(
|
|
pszObjId,
|
|
pCAContextTarget->pccCA->pCertInfo->cExtension,
|
|
pCAContextTarget->pccCA->pCertInfo->rgExtension);
|
|
if ((NULL != pExt) ^ (NULL != pExtTarget))
|
|
{
|
|
hr = CERTSRV_E_INVALID_CA_CERTIFICATE;
|
|
_JumpError(hr, error, "target/source ext inconsistent");
|
|
}
|
|
if (NULL != pExt && !pkcsCompareBlob(&pExt->Value, &pExtTarget->Value))
|
|
{
|
|
hr = CERTSRV_E_INVALID_CA_CERTIFICATE;
|
|
_JumpError(hr, error, "target/source ext inconsistent");
|
|
}
|
|
}
|
|
if (NULL != pExt)
|
|
{
|
|
if (!myConvertSzToWsz(&pwszObjId, pszObjId, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(ObjId)");
|
|
}
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
pwszObjId,
|
|
EXTENSION_ORIGIN_CACERT, // ExtFlags
|
|
pExt->Value.cbData,
|
|
pExt->Value.pbData);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
*pfAdded = TRUE;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAddCannedCertExtensions(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD Flags,
|
|
OPTIONAL IN CACTX const *pCAContext,
|
|
OPTIONAL IN CACTX const *pCAContextTarget,
|
|
OUT char const **ppszObjIdExtError)
|
|
{
|
|
HRESULT hr;
|
|
CERT_EXTENSION aExt[5];
|
|
WCHAR const *apwszObjId[ARRAYSIZE(aExt)];
|
|
DWORD cExt = 0;
|
|
DWORD i;
|
|
BOOL fExtAdded;
|
|
char const *pszObjId = NULL;
|
|
static char *s_apszObjIdXchg[] =
|
|
{
|
|
szOID_KP_CA_EXCHANGE,
|
|
};
|
|
static char *s_apszObjIdAll[] =
|
|
{
|
|
szOID_ANY_CERT_POLICY,
|
|
};
|
|
|
|
ZeroMemory(aExt, sizeof(aExt));
|
|
if (NULL == pCAContext)
|
|
{
|
|
pCAContext = g_pCAContextCurrent;
|
|
}
|
|
|
|
// szOID_KEY_USAGE
|
|
{
|
|
CRYPT_BIT_BLOB KeyUsage;
|
|
BYTE abKeyUsage[1];
|
|
|
|
abKeyUsage[0] = (BYTE) (CRLF_USE_XCHG_CERT_TEMPLATE == Flags?
|
|
CERT_KEY_ENCIPHERMENT_KEY_USAGE : myCASIGN_KEY_USAGE);
|
|
|
|
KeyUsage.pbData = abKeyUsage;
|
|
KeyUsage.cbData = sizeof(abKeyUsage);
|
|
KeyUsage.cUnusedBits = 0;
|
|
|
|
if (!myEncodeKeyUsage(
|
|
X509_ASN_ENCODING,
|
|
&KeyUsage,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aExt[cExt].Value.pbData,
|
|
&aExt[cExt].Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeKeyUsage");
|
|
}
|
|
apwszObjId[cExt] = TEXT(szOID_KEY_USAGE);
|
|
cExt++;
|
|
}
|
|
|
|
if (CRLF_USE_XCHG_CERT_TEMPLATE == Flags)
|
|
{
|
|
// szOID_ENHANCED_KEY_USAGE
|
|
{
|
|
CERT_ENHKEY_USAGE eku;
|
|
|
|
eku.cUsageIdentifier = ARRAYSIZE(s_apszObjIdXchg);
|
|
eku.rgpszUsageIdentifier = s_apszObjIdXchg;
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_ENHANCED_KEY_USAGE,
|
|
&eku,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aExt[cExt].Value.pbData,
|
|
&aExt[cExt].Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
apwszObjId[cExt] = TEXT(szOID_ENHANCED_KEY_USAGE);
|
|
cExt++;
|
|
}
|
|
|
|
// szOID_APPLICATION_CERT_POLICIES
|
|
{
|
|
CERT_POLICY_INFO acpi[ARRAYSIZE(s_apszObjIdXchg)];
|
|
CERT_POLICIES_INFO cps;
|
|
|
|
ZeroMemory(acpi, sizeof(acpi));
|
|
cps.cPolicyInfo = ARRAYSIZE(s_apszObjIdXchg);
|
|
cps.rgPolicyInfo = acpi;
|
|
for (i = 0; i < ARRAYSIZE(s_apszObjIdXchg); i++)
|
|
{
|
|
acpi[i].pszPolicyIdentifier = s_apszObjIdXchg[i];
|
|
}
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_POLICIES,
|
|
&cps,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aExt[cExt].Value.pbData,
|
|
&aExt[cExt].Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
apwszObjId[cExt] = TEXT(szOID_APPLICATION_CERT_POLICIES);
|
|
cExt++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// szOID_ENHANCED_KEY_USAGE
|
|
|
|
pszObjId = szOID_ENHANCED_KEY_USAGE;
|
|
hr = pkcsAddInheritedExtension(
|
|
prow,
|
|
pszObjId,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
&fExtAdded);
|
|
_JumpIfError(hr, error, "pkcsAddInheritedExtension");
|
|
|
|
// szOID_APPLICATION_CERT_POLICIES
|
|
|
|
pszObjId = szOID_APPLICATION_CERT_POLICIES;
|
|
hr = pkcsAddInheritedExtension(
|
|
prow,
|
|
pszObjId,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
&fExtAdded);
|
|
_JumpIfError(hr, error, "pkcsAddInheritedExtension");
|
|
}
|
|
|
|
// szOID_CERT_POLICIES
|
|
|
|
pszObjId = szOID_CERT_POLICIES;
|
|
hr = pkcsAddInheritedExtension(
|
|
prow,
|
|
pszObjId,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
&fExtAdded);
|
|
_JumpIfError(hr, error, "pkcsAddInheritedExtension");
|
|
|
|
pszObjId = NULL;
|
|
|
|
if (CRLF_USE_XCHG_CERT_TEMPLATE != Flags && !fExtAdded)
|
|
{
|
|
CERT_POLICY_INFO acpi[ARRAYSIZE(s_apszObjIdAll)];
|
|
CERT_POLICIES_INFO cps;
|
|
|
|
ZeroMemory(acpi, sizeof(acpi));
|
|
cps.cPolicyInfo = ARRAYSIZE(s_apszObjIdAll);
|
|
cps.rgPolicyInfo = acpi;
|
|
for (i = 0; i < ARRAYSIZE(s_apszObjIdAll); i++)
|
|
{
|
|
acpi[i].pszPolicyIdentifier = s_apszObjIdAll[i];
|
|
}
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_POLICIES,
|
|
&cps,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aExt[cExt].Value.pbData,
|
|
&aExt[cExt].Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
apwszObjId[cExt] = TEXT(szOID_CERT_POLICIES);
|
|
cExt++;
|
|
}
|
|
|
|
if (CRLF_USE_XCHG_CERT_TEMPLATE != Flags)
|
|
{
|
|
// szOID_BASIC_CONSTRAINTS2
|
|
|
|
CERT_BASIC_CONSTRAINTS2_INFO Constraints;
|
|
|
|
Constraints.fCA = TRUE;
|
|
Constraints.fPathLenConstraint = FALSE;
|
|
Constraints.dwPathLenConstraint = 0;
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_BASIC_CONSTRAINTS2,
|
|
&Constraints,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aExt[cExt].Value.pbData,
|
|
&aExt[cExt].Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
aExt[cExt].fCritical = TRUE;
|
|
apwszObjId[cExt] = TEXT(szOID_BASIC_CONSTRAINTS2);
|
|
cExt++;
|
|
}
|
|
CSASSERT(cExt <= ARRAYSIZE(aExt));
|
|
hr = S_OK;
|
|
|
|
error:
|
|
*ppszObjIdExtError = pszObjId;
|
|
for (i = 0; i < ARRAYSIZE(aExt); i++)
|
|
{
|
|
if (NULL != aExt[i].Value.pbData)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
apwszObjId[i],
|
|
EXTENSION_ORIGIN_SERVER |
|
|
(aExt[i].fCritical? EXTENSION_CRITICAL_FLAG : 0),
|
|
aExt[i].Value.cbData,
|
|
aExt[i].Value.pbData);
|
|
_PrintIfErrorStr(hr2, "PropSetExtension", apwszObjId[i]);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
LocalFree(aExt[i].Value.pbData);
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAddInternalCertExtensions(
|
|
IN ICertDBRow *prow,
|
|
IN BOOL fCrossCert, // else CA Xchg cert
|
|
IN WCHAR const *pwszTemplate,
|
|
OPTIONAL IN CACTX *pCAContext, // signing CACTX
|
|
OPTIONAL IN CACTX *pCAContextTarget, // target CACTX
|
|
OUT char const **ppszObjIdExtError)
|
|
{
|
|
HRESULT hr;
|
|
CERT_EXTENSION Ext;
|
|
HCERTTYPE hCertType = NULL;
|
|
CERT_EXTENSIONS *pExtensions = NULL;
|
|
WCHAR *pwszObjId = NULL;
|
|
DWORD Flags = fCrossCert?
|
|
CRLF_USE_CROSS_CERT_TEMPLATE : CRLF_USE_XCHG_CERT_TEMPLATE;
|
|
|
|
ZeroMemory(&Ext, sizeof(Ext));
|
|
*ppszObjIdExtError = NULL;
|
|
|
|
// szOID_ENROLL_CERTTYPE_EXTENSION
|
|
|
|
hr = myBuildCertTypeExtension(pwszTemplate, &Ext);
|
|
_JumpIfError(hr, error, "myBuildCertTypeExtension");
|
|
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
TEXT(szOID_ENROLL_CERTTYPE_EXTENSION),
|
|
EXTENSION_ORIGIN_SERVER, // ExtFlags
|
|
Ext.Value.cbData,
|
|
Ext.Value.pbData);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
hr = S_FALSE;
|
|
if (!fCrossCert || (Flags & g_dwCRLFlags))
|
|
{
|
|
hr = CAFindCertTypeByName(
|
|
pwszTemplate,
|
|
NULL,
|
|
CT_FIND_LOCAL_SYSTEM |
|
|
CT_ENUM_MACHINE_TYPES |
|
|
CT_ENUM_USER_TYPES |
|
|
CT_FLAG_NO_CACHE_LOOKUP,
|
|
&hCertType);
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintErrorStr2(hr, "CAFindCertTypeByName", pwszTemplate, S_FALSE);
|
|
if (Flags & g_dwCRLFlags)
|
|
{
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
}
|
|
hr = pkcsAddCannedCertExtensions(
|
|
prow,
|
|
Flags,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
ppszObjIdExtError);
|
|
_JumpIfError(hr, error, "pkcsAddCannedCertExtensions");
|
|
}
|
|
else
|
|
{
|
|
BOOL fExtAdded;
|
|
|
|
if (Flags & g_dwCRLFlags)
|
|
{
|
|
hr = CACertTypeAccessCheckEx(
|
|
hCertType,
|
|
NULL,
|
|
CERTTYPE_ACCESS_CHECK_ENROLL);
|
|
if (E_ACCESSDENIED == hr)
|
|
{
|
|
// map E_ACCESSDENIED to a more meaningful error
|
|
|
|
hr = CERTSRV_E_TEMPLATE_DENIED;
|
|
}
|
|
_JumpIfError(hr, error, "CACertTypeAccessCheckEx");
|
|
}
|
|
|
|
hr = CAGetCertTypeExtensions(hCertType, &pExtensions);
|
|
_JumpIfError(hr, error, "CAGetCertTypeExtensions");
|
|
|
|
// szOID_CERT_POLICIES
|
|
|
|
hr = pkcsAddInheritedExtension(
|
|
prow,
|
|
szOID_CERT_POLICIES,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
&fExtAdded);
|
|
if (S_OK != hr)
|
|
{
|
|
*ppszObjIdExtError = szOID_CERT_POLICIES;
|
|
_JumpError(hr, error, "pkcsAddInheritedExtension");
|
|
}
|
|
|
|
if (NULL != pExtensions && 0 != pExtensions->cExtension)
|
|
{
|
|
CERT_EXTENSION *pExt;
|
|
CERT_EXTENSION *pExtEnd;
|
|
|
|
pExt = pExtensions->rgExtension;
|
|
pExtEnd = &pExt[pExtensions->cExtension];
|
|
for ( ; pExt < pExtEnd; pExt++)
|
|
{
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
pwszObjId = NULL;
|
|
}
|
|
if (!myConvertSzToWsz(&pwszObjId, pExt->pszObjId, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(ObjId)");
|
|
}
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
pwszObjId,
|
|
EXTENSION_ORIGIN_SERVER |
|
|
(pExt->fCritical? EXTENSION_CRITICAL_FLAG : 0),
|
|
pExt->Value.cbData,
|
|
pExt->Value.pbData);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
}
|
|
}
|
|
|
|
if (CRLF_USE_XCHG_CERT_TEMPLATE == Flags)
|
|
{
|
|
hr = PKCSUpdateXchgValidityPeriods(hCertType);
|
|
_PrintIfError(hr, "PKCSUpdateXchgValidityPeriods");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != Ext.Value.pbData)
|
|
{
|
|
LocalFree(Ext.Value.pbData);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
if (NULL != hCertType)
|
|
{
|
|
if (NULL != pExtensions)
|
|
{
|
|
CAFreeCertTypeExtensions(hCertType, pExtensions);
|
|
}
|
|
CACloseCertType(hCertType);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAddCrossCertVersionExtension(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD CrossCAVersion)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbExt = NULL;
|
|
DWORD cbExt;
|
|
|
|
// Build the Cross CA Version extension
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
&CrossCAVersion,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbExt,
|
|
&cbExt))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
TEXT(szOID_CERTSRV_CROSSCA_VERSION),
|
|
EXTENSION_ORIGIN_SERVER, // ExtFlags
|
|
cbExt,
|
|
pbExt);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
|
|
error:
|
|
if (NULL != pbExt)
|
|
{
|
|
LocalFree(pbExt);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCreateNewInternalCert(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_NAME_BLOB const *pSubject,
|
|
IN HCRYPTPROV hProv,
|
|
OPTIONAL IN WCHAR const *pwszUserName,
|
|
IN BOOL fCrossCert, // else CA Xchg cert
|
|
OPTIONAL IN CACTX *pCAContext, // signing CACTX
|
|
OPTIONAL IN CACTX *pCAContextTarget,// target CACTX
|
|
OPTIONAL IN FILETIME const *pftNotBefore,
|
|
OPTIONAL IN FILETIME const *pftNotAfter,
|
|
OUT CERT_CONTEXT const **ppcc)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
BOOL fErrorLogged = FALSE;
|
|
DWORD dwRequestFlags;
|
|
WCHAR const *pwszTemplate;
|
|
BOOL fSubjectNameSet;
|
|
CERT_PUBLIC_KEY_INFO *pPubKey = NULL;
|
|
DWORD cb;
|
|
CERTTRANSBLOB ctbCert; // CoTaskMem*
|
|
CERTSRV_RESULT_CONTEXT Result;
|
|
WCHAR *pwszDisposition = NULL;
|
|
WCHAR *pwszDispositionCreateCert = NULL;
|
|
WCHAR *pwszMachineRequesterName = NULL;
|
|
char const *pszObjIdExtError = NULL;
|
|
|
|
ZeroMemory(&ctbCert, sizeof(ctbCert));
|
|
*ppcc = NULL;
|
|
|
|
dwRequestFlags = fCrossCert? CR_FLG_CACROSSCERT : CR_FLG_CAXCHGCERT;
|
|
pwszTemplate = fCrossCert? wszCERTTYPE_CROSS_CA : wszCERTTYPE_CA_EXCHANGE;
|
|
|
|
hr = myGetComputerObjectName(NameSamCompatible, &pwszMachineRequesterName);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myGetComputerObjectName");
|
|
|
|
hr = myGetUserNameEx(NameSamCompatible, &pwszMachineRequesterName);
|
|
_JumpIfError(hr, error, "myGetUserNameEx");
|
|
}
|
|
if (NULL == pwszUserName)
|
|
{
|
|
pwszUserName = pwszMachineRequesterName;
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequesterName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszMachineRequesterName);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCallerName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszUserName);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
wszPROPCERTIFICATETEMPLATE,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszTemplate);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
if (!myCryptExportPublicKeyInfo(
|
|
hProv,
|
|
fCrossCert? AT_SIGNATURE : AT_KEYEXCHANGE,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pPubKey,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCryptExportPublicKeyInfo");
|
|
}
|
|
|
|
hr = PropSetRequestTimeProperty(prow, g_wszPropRequestSubmittedWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
hr = PropSetRequestTimeProperty(prow, g_wszPropRequestResolvedWhen);
|
|
_JumpIfError(hr, error, "PropSetRequestTimeProperty");
|
|
|
|
hr = CoreSetDisposition(prow, DB_DISP_ACTIVE);
|
|
_JumpIfError(hr, error, "CoreSetDisposition");
|
|
|
|
hr = pkcsSetRequestNameInfo(
|
|
prow,
|
|
pSubject,
|
|
fCrossCert? NULL : g_wszCNXchgSuffix,
|
|
FALSE, // fReorderLikeRDNs
|
|
&dwRequestFlags,
|
|
&fSubjectNameSet);
|
|
_JumpIfError(hr, error, "pkcsSetRequestNameInfo");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
sizeof(dwRequestFlags),
|
|
(BYTE const *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "SetProperty(RequestFlags)");
|
|
|
|
CSASSERT(fSubjectNameSet);
|
|
|
|
hr = pkcsSetPublicKeyProperties(prow, pPubKey);
|
|
_JumpIfError(hr, error, "pkcsSetPublicKeyProperties");
|
|
|
|
hr = prow->CopyRequestNames();
|
|
_JumpIfError(hr, error, "CopyRequestNames");
|
|
|
|
hr = PKCSSetServerProperties(
|
|
prow,
|
|
pCAContext, // optional signing context
|
|
pftNotBefore,
|
|
pftNotAfter,
|
|
g_lCAXchgValidityPeriodCount,
|
|
g_enumCAXchgValidityPeriod);
|
|
_JumpIfError(hr, error, "PKCSSetServerProperties");
|
|
|
|
hr = pkcsAddInternalCertExtensions(
|
|
prow,
|
|
fCrossCert,
|
|
pwszTemplate,
|
|
pCAContext,
|
|
pCAContextTarget,
|
|
&pszObjIdExtError);
|
|
_JumpIfError(hr, error, "pkcsAddInternalCertExtensions");
|
|
|
|
ZeroMemory(&Result, sizeof(Result));
|
|
Result.pctbCert = &ctbCert;
|
|
hr = PKCSCreateCertificate(
|
|
prow,
|
|
DB_DISP_ISSUED,
|
|
FALSE, // fIncludeCRLs
|
|
fCrossCert,
|
|
pCAContext, // optional signing context
|
|
&fErrorLogged,
|
|
NULL, // ppCAContext
|
|
&pwszDispositionCreateCert,
|
|
&Result);
|
|
_JumpIfError(hr, error, "PKCSCreateCertificate");
|
|
|
|
*ppcc = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
ctbCert.pb,
|
|
ctbCert.cb);
|
|
if (NULL == *ppcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
pwszDisposition = CoreBuildDispositionString(
|
|
g_pwszRequestedBy,
|
|
pwszUserName,
|
|
pwszDispositionCreateCert,
|
|
NULL,
|
|
NULL,
|
|
S_OK,
|
|
FALSE);
|
|
|
|
hr2 = CoreSetRequestDispositionFields(
|
|
prow,
|
|
hr,
|
|
S_OK == hr? DB_DISP_ISSUED : DB_DISP_DENIED,
|
|
pwszDisposition);
|
|
_PrintIfError(hr2, "CoreSetRequestDispositionFields");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
if (S_OK != hr && !fErrorLogged)
|
|
{
|
|
DWORD LogMsg;
|
|
WCHAR const *apwsz[3];
|
|
DWORD cpwsz;
|
|
WCHAR wszVersion[2 * cwcDWORDSPRINTF + 1];
|
|
|
|
cpwsz = 0;
|
|
LogMsg = MSG_E_CANNOT_CREATE_XCHG_CERT;
|
|
if (fCrossCert)
|
|
{
|
|
wszVersion[0] = L'\0';
|
|
if (NULL != pCAContext && NULL != pCAContextTarget)
|
|
{
|
|
wsprintf(
|
|
wszVersion, L"(%u-%u)",
|
|
pCAContext->iCert,
|
|
pCAContextTarget->iCert);
|
|
}
|
|
apwsz[cpwsz++] = wszVersion;
|
|
LogMsg = MSG_E_CANNOT_CREATE_CROSS_CERT;
|
|
if (NULL != pszObjIdExtError)
|
|
{
|
|
LogMsg = MSG_E_CROSS_CERT_EXTENSION_CONFLICT;
|
|
apwsz[cpwsz++] = myGetOIDNameA(pszObjIdExtError);
|
|
}
|
|
}
|
|
apwsz[cpwsz++] = NULL != pwszDisposition?
|
|
pwszDisposition : pwszDispositionCreateCert;
|
|
|
|
LogEventStringArrayHResult(
|
|
EVENTLOG_ERROR_TYPE,
|
|
LogMsg,
|
|
cpwsz,
|
|
apwsz,
|
|
hr);
|
|
}
|
|
if (NULL != pwszMachineRequesterName)
|
|
{
|
|
LocalFree(pwszMachineRequesterName);
|
|
}
|
|
if (NULL != pwszDisposition)
|
|
{
|
|
LocalFree(pwszDisposition);
|
|
}
|
|
if (NULL != pwszDispositionCreateCert)
|
|
{
|
|
LocalFree(pwszDispositionCreateCert);
|
|
}
|
|
if (NULL != ctbCert.pb)
|
|
{
|
|
CoTaskMemFree(ctbCert.pb);
|
|
}
|
|
if (NULL != pPubKey)
|
|
{
|
|
LocalFree(pPubKey);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsFormXchgKeyContainerName(
|
|
IN DWORD dwRequestId,
|
|
OUT WCHAR **ppwszKeyContainer)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cwcSuffix;
|
|
DWORD cwcName;
|
|
WCHAR wszSuffix[ARRAYSIZE(g_wszCNXchgSuffix) + 1 + cwcDWORDSPRINTF + 1];
|
|
WCHAR wszKeyContainer[MAX_PATH];
|
|
|
|
*ppwszKeyContainer = NULL;
|
|
|
|
cwcSuffix = wsprintf(wszSuffix, L"%ws(%u)", g_wszCNXchgSuffix, dwRequestId);
|
|
CSASSERT(ARRAYSIZE(wszSuffix) > cwcSuffix);
|
|
|
|
cwcName = wcslen(g_wszSanitizedName);
|
|
if (cwcName > MAX_PATH - cwcSuffix)
|
|
{
|
|
cwcName = MAX_PATH - cwcSuffix;
|
|
}
|
|
CSASSERT(ARRAYSIZE(wszKeyContainer) > cwcName);
|
|
wcscpy(wszKeyContainer, g_wszSanitizedName);
|
|
wcscpy(&wszKeyContainer[cwcName], wszSuffix);
|
|
|
|
hr = myDupString(wszKeyContainer, ppwszKeyContainer);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"pkcsFormXchgKeyContainerName: %ws\n",
|
|
*ppwszKeyContainer));
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsDeleteKey(
|
|
OPTIONAL IN WCHAR const *pwszKeyContainer)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTPROV hProv;
|
|
|
|
if (NULL != pwszKeyContainer)
|
|
{
|
|
if (!CryptAcquireContext(
|
|
&hProv,
|
|
pwszKeyContainer,
|
|
g_pwszXchgProvName,
|
|
g_dwXchgProvType,
|
|
CRYPT_DELETEKEYSET |
|
|
g_CryptSilent |
|
|
(g_fXchgMachineKeyset? CRYPT_MACHINE_KEYSET : 0)))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
}
|
|
error:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsLoadCAXchgCSPInfo(
|
|
IN BOOL fSetDefaults)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (NULL != g_pwszXchgProvName)
|
|
{
|
|
LocalFree(g_pwszXchgProvName);
|
|
g_pwszXchgProvName = NULL;
|
|
}
|
|
if (!fSetDefaults)
|
|
{
|
|
hr = myGetCertSrvCSP(
|
|
TRUE, // fEncryptionCSP
|
|
g_wszSanitizedName,
|
|
&g_dwXchgProvType,
|
|
&g_pwszXchgProvName,
|
|
&g_XchgidAlg,
|
|
&g_fXchgMachineKeyset,
|
|
&g_dwXchgKeySize);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myGetCertSrvCSP(CAXchg)");
|
|
}
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
g_dwXchgProvType = PROV_RSA_FULL;
|
|
g_pwszXchgProvName = NULL;
|
|
g_XchgidAlg = CALG_3DES;
|
|
g_fXchgMachineKeyset = TRUE;
|
|
g_dwXchgKeySize = 0;
|
|
}
|
|
if (0 == g_dwXchgKeySize)
|
|
{
|
|
g_dwXchgKeySize = 1024;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCreateNewCAXchgCert(
|
|
IN WCHAR const *pwszUserName)
|
|
{
|
|
HRESULT hr;
|
|
ICertDBRow *prow = NULL;
|
|
BOOL fErrorLogged = FALSE;
|
|
DWORD i;
|
|
CAXCHGCTX CAXchgContext;
|
|
CAXCHGCTX *rgCAXchgContext;
|
|
|
|
ZeroMemory(&CAXchgContext, sizeof(CAXchgContext));
|
|
|
|
hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, &prow);
|
|
_JumpIfError(hr, error, "OpenRow");
|
|
|
|
prow->GetRowId(&CAXchgContext.ReqId);
|
|
|
|
hr = pkcsFormXchgKeyContainerName(
|
|
CAXchgContext.ReqId,
|
|
&CAXchgContext.pwszKeyContainerName);
|
|
_JumpIfError(hr, error, "pkcsFormXchgKeyContainerName");
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
hr = myGenerateKeys(
|
|
CAXchgContext.pwszKeyContainerName,
|
|
g_pwszXchgProvName,
|
|
g_CryptSilent,
|
|
g_fXchgMachineKeyset,
|
|
AT_KEYEXCHANGE,
|
|
g_dwXchgProvType,
|
|
g_dwXchgKeySize,
|
|
&CAXchgContext.hProvCA);
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
_PrintErrorStr(hr, "myGenerateKeys", g_pwszXchgProvName);
|
|
LogEventHResult(
|
|
NULL == g_pwszXchgProvName?
|
|
EVENTLOG_ERROR_TYPE :
|
|
EVENTLOG_WARNING_TYPE,
|
|
NULL == g_pwszXchgProvName?
|
|
MSG_E_BAD_DEFAULT_CA_XCHG_CSP :
|
|
MSG_E_BAD_REGISTRY_CA_XCHG_CSP,
|
|
hr);
|
|
if (0 != i || NULL == g_pwszXchgProvName)
|
|
{
|
|
fErrorLogged = TRUE;
|
|
_JumpError(hr, error, "myGenerateKeys");
|
|
}
|
|
pkcsLoadCAXchgCSPInfo(TRUE); // switch to default CSP
|
|
}
|
|
if (0 != i)
|
|
{
|
|
hr = LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_E_USE_DEFAULT_CA_XCHG_CSP,
|
|
0, // cpwsz
|
|
NULL); // apwsz
|
|
_PrintIfError(hr, "LogEvent");
|
|
}
|
|
hr = mySetKeyContainerSecurity(CAXchgContext.hProvCA);
|
|
_JumpIfError(hr, error, "mySetKeyContainerSecurity");
|
|
|
|
hr = pkcsCreateNewInternalCert(
|
|
prow,
|
|
&g_pCAContextCurrent->pccCA->pCertInfo->Subject,
|
|
CAXchgContext.hProvCA,
|
|
pwszUserName,
|
|
FALSE, // fCrossCert
|
|
NULL, // use default signing CACTX
|
|
NULL, // pCAContextTarget
|
|
NULL, // pftNotBefore
|
|
NULL, // pftNotAfter
|
|
&CAXchgContext.pccCAXchg);
|
|
_JumpIfError(hr, error, "pkcsCreateNewInternalCert");
|
|
|
|
if (NULL == g_aCAXchgContext)
|
|
{
|
|
CSASSERT(0 == g_cCAXchgCerts);
|
|
rgCAXchgContext = (CAXCHGCTX *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
sizeof(rgCAXchgContext[0]));
|
|
}
|
|
else
|
|
{
|
|
rgCAXchgContext = (CAXCHGCTX *) LocalReAlloc(
|
|
g_aCAXchgContext,
|
|
(g_cCAXchgCerts + 1) * sizeof(rgCAXchgContext[0]),
|
|
LMEM_MOVEABLE);
|
|
}
|
|
if (NULL == rgCAXchgContext)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc/ReAlloc");
|
|
}
|
|
g_aCAXchgContext = rgCAXchgContext;
|
|
g_aCAXchgContext[g_cCAXchgCerts] = CAXchgContext;
|
|
|
|
g_pCAXchgContextCurrent = &g_aCAXchgContext[g_cCAXchgCerts];
|
|
|
|
g_cCAXchgCerts++;
|
|
ZeroMemory(&CAXchgContext, sizeof(CAXchgContext));
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != prow)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = prow->CommitTransaction(TRUE);
|
|
_PrintIfError(hr2, "CommitTransaction");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
if (S_OK != hr2)
|
|
{
|
|
hr2 = prow->CommitTransaction(FALSE);
|
|
_PrintIfError(hr2, "CommitTransaction");
|
|
}
|
|
prow->Release();
|
|
}
|
|
if (NULL != CAXchgContext.pccCAXchg)
|
|
{
|
|
CertFreeCertificateContext(CAXchgContext.pccCAXchg);
|
|
}
|
|
if (NULL != CAXchgContext.hProvCA)
|
|
{
|
|
CryptReleaseContext(CAXchgContext.hProvCA, 0);
|
|
pkcsDeleteKey(CAXchgContext.pwszKeyContainerName);
|
|
}
|
|
if (NULL != CAXchgContext.pwszKeyContainerName)
|
|
{
|
|
LocalFree(CAXchgContext.pwszKeyContainerName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCreateNewCrossCert(
|
|
IN OUT CACROSSCTX *pCACross,
|
|
IN FILETIME const *pftNotBefore,
|
|
IN FILETIME const *pftNotAfter)
|
|
{
|
|
HRESULT hr;
|
|
ICertDBRow *prow = NULL;
|
|
WCHAR wszIndex[2 * cwcDWORDSPRINTF + 1];
|
|
CERT_INFO const *pCertInfoCATarget;
|
|
|
|
wsprintf(
|
|
wszIndex,
|
|
L"%u-%u",
|
|
pCACross->pCAContext->iCert,
|
|
pCACross->pCAContextTarget->iCert);
|
|
|
|
hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, &prow);
|
|
_JumpIfErrorStr(hr, error, "OpenRow", wszIndex);
|
|
|
|
prow->GetRowId(&pCACross->ReqId);
|
|
|
|
pCertInfoCATarget = pCACross->pCAContextTarget->pccCA->pCertInfo;
|
|
hr = pkcsSetExtensions(
|
|
prow,
|
|
EXTENSION_ORIGIN_CACERT,
|
|
pCertInfoCATarget->rgExtension,
|
|
pCertInfoCATarget->cExtension);
|
|
_JumpIfError(hr, error, "pkcsSetExtensions(old cert)");
|
|
|
|
hr = pkcsAddCrossCertVersionExtension(
|
|
prow,
|
|
MAKECROSSCAVERSION(
|
|
pCACross->pCAContext->iCert,
|
|
pCACross->pCAContextTarget->iCert));
|
|
_JumpIfError(hr, error, "pkcsAddCrossCertExtensions");
|
|
|
|
hr = pkcsCreateNewInternalCert(
|
|
prow,
|
|
&pCertInfoCATarget->Subject,
|
|
pCACross->pCAContextTarget->hProvCA, // for the public key
|
|
NULL, // pwszUserName
|
|
TRUE, // fCrossCert
|
|
pCACross->pCAContext, // signing context
|
|
pCACross->pCAContextTarget, // target context
|
|
pftNotBefore,
|
|
pftNotAfter,
|
|
&pCACross->pccCACross);
|
|
_JumpIfErrorStr(hr, error, "pkcsCreateNewInternalCert", wszIndex);
|
|
|
|
error:
|
|
if (NULL != prow)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = prow->CommitTransaction(TRUE);
|
|
_PrintIfError(hr2, "CommitTransaction");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
if (S_OK != hr2)
|
|
{
|
|
hr2 = prow->CommitTransaction(FALSE);
|
|
_PrintIfError(hr2, "CommitTransaction");
|
|
}
|
|
prow->Release();
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
CERT_CONTEXT const *
|
|
pkcsFindCertificateInStore(
|
|
IN HCERTSTORE hStore,
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
BYTE rgbHash[CBMAX_CRYPT_HASH_LEN];
|
|
CRYPT_DATA_BLOB HashBlob;
|
|
|
|
HashBlob.pbData = rgbHash;
|
|
HashBlob.cbData = sizeof(rgbHash);
|
|
|
|
if (!CertGetCertificateContextProperty(
|
|
pCert,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
rgbHash,
|
|
&HashBlob.cbData) ||
|
|
sizeof(rgbHash) != HashBlob.cbData)
|
|
{
|
|
return(NULL);
|
|
}
|
|
return(CertFindCertificateInStore(
|
|
hStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,// dwCertEncodingType
|
|
0, // dwFindFlags
|
|
CERT_FIND_SHA1_HASH,
|
|
(const void *) &HashBlob,
|
|
NULL)); // pPrevCertContext
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsWriteCertToStore(
|
|
IN WCHAR const *pwszStore,
|
|
IN BOOL fEnterprise,
|
|
IN BOOL fDelete,
|
|
IN CERT_CONTEXT const *pcc,
|
|
OUT LONG *plDisposition)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pccT;
|
|
|
|
*plDisposition = 0;
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
fEnterprise?
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE :
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
pwszStore);
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
pccT = pkcsFindCertificateInStore(hStore, pcc);
|
|
if (NULL != pccT)
|
|
{
|
|
if (fDelete)
|
|
{
|
|
CertDeleteCertificateFromStore(pccT);
|
|
*plDisposition = -1;
|
|
}
|
|
else
|
|
{
|
|
// already added; do nothing
|
|
|
|
CertFreeCertificateContext(pccT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!fDelete)
|
|
{
|
|
if (!CertAddCertificateContextToStore(
|
|
hStore,
|
|
pcc,
|
|
CERT_STORE_ADD_USE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
*plDisposition = 1;
|
|
}
|
|
else
|
|
{
|
|
// already deleted; do nothing
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// if necessary, publish to System32\CertSrv\CertEnroll directory
|
|
// if necessary, publish to HKLM CA cert store
|
|
// if necessary, publish to crossCertificatePair in this CA's AIA object
|
|
|
|
HRESULT
|
|
pkcsPublishCrossCert(
|
|
IN OUT CACROSSCTX *pCACross,
|
|
IN BOOL fDelete)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
WCHAR *pwszFile = NULL;
|
|
LONG lDisposition;
|
|
|
|
hr = pkcsGetCertFilename(
|
|
g_wszSanitizedName,
|
|
pCACross->pCAContext->iCert,
|
|
pCACross->pCAContextTarget->iCert,
|
|
&pwszFile);
|
|
_JumpIfError(hr, error, "myGetCertFilename");
|
|
|
|
hr = CRLWriteToLockedFile(
|
|
pCACross->pccCACross->pbCertEncoded,
|
|
pCACross->pccCACross->cbCertEncoded,
|
|
fDelete,
|
|
pwszFile);
|
|
_PrintIfError(hr, "CRLWriteToLockedFile");
|
|
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszCA_CERTSTORE,
|
|
FALSE, // fEnterprise
|
|
fDelete,
|
|
pCACross->pccCACross,
|
|
&lDisposition);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintIfError(hr2, "pkcsWriteCertToStore");
|
|
|
|
if (g_fUseDS)
|
|
{
|
|
// don't attempt to create the object, should already be there
|
|
|
|
hr = CorePublishCrossCertificate(
|
|
pCACross->ReqId,
|
|
pCACross->pccCACross,
|
|
FALSE, // fCreateDSObject
|
|
fDelete); // fDelete
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_JumpIfError(hr2, error, "CorePublishCrossCertificate");
|
|
}
|
|
_JumpIfError(hr, error, "pkcsPublishCrossCert");
|
|
|
|
error:
|
|
if (NULL != pwszFile)
|
|
{
|
|
LocalFree(pwszFile);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Verify the cert matches the expected Cross cert contents:
|
|
// 1) verify timestamps meet expectations
|
|
// 2) Issuer and Subject both match CA binary subject
|
|
// 3) it is a CrossCA cert (v1 CrossCA template extension)
|
|
// 4) IssuerNameId matches source CA cert key index
|
|
// 5) SubjectKeyId matches target CA cert SubjectKeyId
|
|
|
|
HRESULT
|
|
pkcsVerifyCrossCertificate(
|
|
IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN FILETIME const *pftNotBefore,
|
|
IN FILETIME const *pftNotAfter,
|
|
IN OUT CACROSSCTX *pCACross)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
CERT_NAME_BLOB const *pSubject;
|
|
CERT_EXTENSION const *pExtCross;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_NAME_VALUE *pName = NULL;
|
|
CERT_AUTHORITY_KEY_ID2_INFO *pInfo = NULL;
|
|
CERT_BLOB *pSKIBlob = NULL;
|
|
DWORD cb;
|
|
|
|
CSASSERT(NULL == pCACross->pccCACross);
|
|
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
// verify timestamps meet expectations
|
|
// verify Subject & Issuer match CA Subject
|
|
|
|
pSubject = &pCACross->pCAContext->pccCA->pCertInfo->Subject;
|
|
if (0 != CompareFileTime(pftNotBefore, &pcc->pCertInfo->NotBefore) ||
|
|
0 != CompareFileTime(pftNotAfter, &pcc->pCertInfo->NotAfter) ||
|
|
!pkcsCompareBlob(pSubject, &pcc->pCertInfo->Subject) ||
|
|
!pkcsCompareBlob(pSubject, &pcc->pCertInfo->Issuer))
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError2(hr, error, "NotBefore/NotAfter/Subject/Issuer", hr);
|
|
}
|
|
|
|
// verify v1 CrossCA template extension
|
|
|
|
pExtCross = CertFindExtension(
|
|
szOID_ENROLL_CERTTYPE_EXTENSION,
|
|
pcc->pCertInfo->cExtension,
|
|
pcc->pCertInfo->rgExtension);
|
|
if (NULL == pExtCross)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "No Template");
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
pExtCross->Value.pbData,
|
|
pExtCross->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pName,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "Policy:myDecodeObject");
|
|
}
|
|
if (0 != LSTRCMPIS(
|
|
(WCHAR const *) pName->Value.pbData,
|
|
wszCERTTYPE_CROSS_CA))
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "No Template");
|
|
}
|
|
|
|
// verify SKI matches target SKI
|
|
|
|
pExtCross = CertFindExtension(
|
|
szOID_SUBJECT_KEY_IDENTIFIER,
|
|
pcc->pCertInfo->cExtension,
|
|
pcc->pCertInfo->rgExtension);
|
|
pExt = CertFindExtension(
|
|
szOID_SUBJECT_KEY_IDENTIFIER,
|
|
pCACross->pCAContextTarget->pccCA->pCertInfo->cExtension,
|
|
pCACross->pCAContextTarget->pccCA->pCertInfo->rgExtension);
|
|
if (NULL == pExtCross ||
|
|
NULL == pExt ||
|
|
!pkcsCompareBlob(&pExtCross->Value, &pExt->Value))
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "SKI");
|
|
}
|
|
|
|
// verify AKI KeyId matches source SKI
|
|
|
|
pExtCross = CertFindExtension(
|
|
szOID_AUTHORITY_KEY_IDENTIFIER2,
|
|
pcc->pCertInfo->cExtension,
|
|
pcc->pCertInfo->rgExtension);
|
|
pExt = CertFindExtension(
|
|
szOID_SUBJECT_KEY_IDENTIFIER,
|
|
pCACross->pCAContext->pccCA->pCertInfo->cExtension,
|
|
pCACross->pCAContext->pccCA->pCertInfo->rgExtension);
|
|
if (NULL == pExtCross || NULL == pExt)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "SKI");
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_AUTHORITY_KEY_ID2,
|
|
pExtCross->Value.pbData,
|
|
pExtCross->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pInfo,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "Policy:myDecodeObject");
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pSKIBlob,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "Policy:myDecodeObject");
|
|
}
|
|
if (!pkcsCompareBlob(pSKIBlob, &pInfo->KeyId))
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "AKI");
|
|
}
|
|
pCACross->pccCACross = pcc;
|
|
pcc = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSKIBlob)
|
|
{
|
|
LocalFree(pSKIBlob);
|
|
}
|
|
if (NULL != pName)
|
|
{
|
|
LocalFree(pName);
|
|
}
|
|
if (NULL != pInfo)
|
|
{
|
|
LocalFree(pInfo);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Query for a cert matching the CA's Common Name AND
|
|
// IssuerNameId matches source CA cert key index
|
|
|
|
DWORD g_aColCross[] = {
|
|
|
|
#define ICOL_RAWCERTIFICATE 0
|
|
DTI_CERTIFICATETABLE | DTC_RAWCERTIFICATE,
|
|
};
|
|
|
|
HRESULT
|
|
pkcsLoadCrossCertFromDB(
|
|
IN OUT CACROSSCTX *pCACross,
|
|
IN FILETIME const *pftNotBefore,
|
|
IN FILETIME const *pftNotAfter)
|
|
{
|
|
HRESULT hr;
|
|
CERTVIEWRESTRICTION acvr[3];
|
|
CERTVIEWRESTRICTION *pcvr;
|
|
DWORD NameIdMin;
|
|
DWORD NameIdMax;
|
|
IEnumCERTDBRESULTROW *pView = NULL;
|
|
DWORD celtFetched;
|
|
DWORD i;
|
|
BOOL fEnd;
|
|
CERTDBRESULTROW aResult[1];
|
|
BOOL fResultActive = FALSE;
|
|
|
|
CSASSERT(NULL == pCACross->pccCACross);
|
|
|
|
// Set up restrictions as follows:
|
|
|
|
pcvr = acvr;
|
|
|
|
// CommonName == g_wszCommonName
|
|
|
|
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_COMMONNAME;
|
|
pcvr->SeekOperator = CVR_SEEK_EQ;
|
|
pcvr->SortOrder = CVR_SORT_ASCEND;
|
|
pcvr->pbValue = (BYTE *) g_wszCommonName;
|
|
pcvr->cbValue = sizeof(WCHAR) * (wcslen(g_wszCommonName) + 1);
|
|
pcvr++;
|
|
|
|
// NameId >= MAKECANAMEID(iCert == 0, iKey)
|
|
|
|
NameIdMin = MAKECANAMEID(0, pCACross->pCAContext->iKey);
|
|
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_CERTIFICATEISSUERNAMEID;
|
|
pcvr->SeekOperator = CVR_SEEK_GE;
|
|
pcvr->SortOrder = CVR_SORT_NONE;
|
|
pcvr->pbValue = (BYTE *) &NameIdMin;
|
|
pcvr->cbValue = sizeof(NameIdMin);
|
|
pcvr++;
|
|
|
|
// NameId <= MAKECANAMEID(iCert == _16BITMASK, iKey)
|
|
|
|
NameIdMax = MAKECANAMEID(_16BITMASK, pCACross->pCAContext->iKey);
|
|
pcvr->ColumnIndex = DTI_CERTIFICATETABLE | DTC_CERTIFICATEISSUERNAMEID;
|
|
pcvr->SeekOperator = CVR_SEEK_LE;
|
|
pcvr->SortOrder = CVR_SORT_NONE;
|
|
pcvr->pbValue = (BYTE *) &NameIdMax;
|
|
pcvr->cbValue = sizeof(NameIdMax);
|
|
pcvr++;
|
|
|
|
CSASSERT(ARRAYSIZE(acvr) == SAFE_SUBTRACT_POINTERS(pcvr, acvr));
|
|
|
|
celtFetched = 0;
|
|
hr = g_pCertDB->OpenView(
|
|
ARRAYSIZE(acvr),
|
|
acvr,
|
|
ARRAYSIZE(g_aColCross),
|
|
g_aColCross,
|
|
0, // no worker thread
|
|
&pView);
|
|
_JumpIfError(hr, error, "OpenView");
|
|
|
|
fEnd = FALSE;
|
|
while (!fEnd)
|
|
{
|
|
hr = pView->Next(NULL, ARRAYSIZE(aResult), aResult, &celtFetched);
|
|
if (S_FALSE == hr)
|
|
{
|
|
fEnd = TRUE;
|
|
if (0 == celtFetched)
|
|
{
|
|
break;
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
_JumpIfError(hr, error, "Next");
|
|
|
|
fResultActive = TRUE;
|
|
|
|
CSASSERT(ARRAYSIZE(aResult) >= celtFetched);
|
|
|
|
for (i = 0; i < celtFetched; i++)
|
|
{
|
|
CERTDBRESULTROW *pResult = &aResult[i];
|
|
|
|
CSASSERT(ARRAYSIZE(g_aColCross) == pResult->ccol);
|
|
|
|
if (NULL == pResult->acol[ICOL_RAWCERTIFICATE].pbValue)
|
|
{
|
|
continue;
|
|
}
|
|
CSASSERT(PROPTYPE_BINARY == (PROPTYPE_MASK & pResult->acol[ICOL_RAWCERTIFICATE].Type));
|
|
|
|
|
|
hr = pkcsVerifyCrossCertificate(
|
|
pResult->acol[ICOL_RAWCERTIFICATE].pbValue,
|
|
pResult->acol[ICOL_RAWCERTIFICATE].cbValue,
|
|
pftNotBefore,
|
|
pftNotAfter,
|
|
pCACross);
|
|
_PrintIfError2(hr, "pkcsVerifyCrossCertificate", hr);
|
|
if (S_OK == hr)
|
|
{
|
|
CSASSERT(NULL != pCACross->pccCACross);
|
|
fEnd = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
pView->ReleaseResultRow(celtFetched, aResult);
|
|
fResultActive = FALSE;
|
|
}
|
|
hr = NULL != pCACross->pccCACross? S_OK : CRYPT_E_NOT_FOUND;
|
|
_JumpIfError(hr, error, "pCACross->pccCACross");
|
|
|
|
error:
|
|
if (NULL != pView)
|
|
{
|
|
if (fResultActive)
|
|
{
|
|
pView->ReleaseResultRow(celtFetched, aResult);
|
|
}
|
|
pView->Release();
|
|
}
|
|
return(hr);
|
|
}
|
|
#undef ICOL_RAWCERTIFICATE
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCrossCertFromFile(
|
|
IN OUT CACROSSCTX *pCACross,
|
|
IN FILETIME const *pftNotBefore,
|
|
IN FILETIME const *pftNotAfter)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszFile = NULL;
|
|
BYTE *pbCert = NULL;
|
|
DWORD cbCert;
|
|
|
|
hr = pkcsGetCertFilename(
|
|
g_wszSanitizedName,
|
|
pCACross->pCAContext->iCert,
|
|
pCACross->pCAContextTarget->iCert,
|
|
&pwszFile);
|
|
_JumpIfError(hr, error, "myGetCertFilename");
|
|
|
|
hr = DecodeFileW(pwszFile, &pbCert, &cbCert, CRYPT_STRING_ANY);
|
|
_JumpIfError(hr, error, "DecodeFileW");
|
|
|
|
hr = pkcsVerifyCrossCertificate(
|
|
pbCert,
|
|
cbCert,
|
|
pftNotBefore,
|
|
pftNotAfter,
|
|
pCACross);
|
|
_JumpIfError(hr, error, "pkcsVerifyCrossCertificate");
|
|
|
|
error:
|
|
if (NULL != pwszFile)
|
|
{
|
|
LocalFree(pwszFile);
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsImportCAOrCrossOrKRACert(
|
|
IN CERT_CONTEXT const *pcc,
|
|
IN BOOL fCrossCert, // else CA or KRA cert
|
|
IN DWORD DBDisposition,
|
|
OPTIONAL IN CACTX const *pCAContext)
|
|
{
|
|
HRESULT hr;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
BSTR strHash = NULL;
|
|
ICertDBRow *prow = NULL;
|
|
WCHAR *pwszUserName = NULL;
|
|
DWORD cb;
|
|
BOOL fCommit = FALSE;
|
|
BOOL fCommitted = FALSE;
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
// Import Cert if it doesn't already exist in DB:
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_CERTHASH | PROPTABLE_REQCERT,
|
|
0,
|
|
strHash,
|
|
&prow);
|
|
if (S_OK != hr)
|
|
{
|
|
if (CERTSRV_E_PROPERTY_EMPTY != hr)
|
|
{
|
|
_JumpError(hr, error, "OpenRow");
|
|
}
|
|
|
|
hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, &prow);
|
|
_JumpIfError(hr, error, "OpenRow");
|
|
|
|
hr = PKCSParseImportedCertificate(
|
|
prow,
|
|
fCrossCert,
|
|
DBDisposition,
|
|
pCAContext,
|
|
pcc);
|
|
_JumpIfError(hr, error, "PKCSParseImportedCertificate");
|
|
|
|
fCommit = TRUE;
|
|
}
|
|
|
|
// Set requester name if missing
|
|
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequesterName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
NULL);
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr)
|
|
{
|
|
hr = myGetComputerObjectName(NameSamCompatible, &pwszUserName);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myGetComputerObjectName");
|
|
|
|
hr = myGetUserNameEx(NameSamCompatible, &pwszUserName);
|
|
_JumpIfError(hr, error, "myGetUserNameEx");
|
|
}
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropRequesterName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszUserName);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCallerName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
MAXDWORD,
|
|
(BYTE const *) pwszUserName);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
fCommit = TRUE;
|
|
}
|
|
if (DB_DISP_REVOKED == DBDisposition)
|
|
{
|
|
DWORD DispositionOld;
|
|
|
|
cb = sizeof(DispositionOld);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestDisposition,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &DispositionOld);
|
|
_JumpIfError(hr, error, "GetProperty(Disposition)");
|
|
|
|
CSASSERT(
|
|
DB_DISP_ISSUED == DispositionOld ||
|
|
DB_DISP_REVOKED == DispositionOld);
|
|
if (DB_DISP_ISSUED == DispositionOld)
|
|
{
|
|
fCommit = TRUE;
|
|
hr = pkcsSetRevocationFields(prow);
|
|
_JumpIfError(hr, error, "pkcsSetRevocationFields");
|
|
}
|
|
}
|
|
hr = prow->CommitTransaction(fCommit);
|
|
_JumpIfError(hr, error, "CommitTransaction");
|
|
|
|
fCommitted = TRUE;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != prow)
|
|
{
|
|
if (S_OK != hr && !fCommitted)
|
|
{
|
|
HRESULT hr2 = prow->CommitTransaction(FALSE);
|
|
_PrintIfError(hr2, "CommitTransaction");
|
|
}
|
|
prow->Release();
|
|
}
|
|
if (NULL != pwszUserName)
|
|
{
|
|
LocalFree(pwszUserName);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// If the row exists in the database, do not create a new Cross cert -- even if
|
|
// the attempt to create a cross cert failed. The row must be deleted to cause
|
|
// a new Cross cert creation attempt.
|
|
// If the cert exists in the row, load the cert context without validation.
|
|
|
|
HRESULT
|
|
pkcsLoadCrossCert(
|
|
IN OUT CACROSSCTX *pCACross,
|
|
IN FILETIME *pftNow,
|
|
IN BOOL fForward,
|
|
IN BOOL fRevoke)
|
|
{
|
|
HRESULT hr;
|
|
CACTX *pCAContextOld;
|
|
CACTX *pCAContextNew;
|
|
FILETIME const *pftNotBefore;
|
|
FILETIME const *pftNotAfter;
|
|
|
|
if (fForward)
|
|
{
|
|
pCAContextOld = pCACross->pCAContext;
|
|
pCAContextNew = pCACross->pCAContextTarget;
|
|
}
|
|
else
|
|
{
|
|
pCAContextOld = pCACross->pCAContextTarget;
|
|
pCAContextNew = pCACross->pCAContext;
|
|
}
|
|
|
|
if (NULL != pCAContextOld->pccCA && NULL != pCAContextNew->pccCA)
|
|
{
|
|
pftNotBefore = &pCAContextNew->pccCA->pCertInfo->NotBefore;
|
|
pftNotAfter = &pCAContextOld->pccCA->pCertInfo->NotAfter;
|
|
|
|
// Query for Cross cert -- see above criteria
|
|
|
|
hr = pkcsLoadCrossCertFromDB(pCACross, pftNotBefore, pftNotAfter);
|
|
_PrintIfError(hr, "pkcsLoadCrossCertFromDB");
|
|
|
|
// if not in DB, look in CertEnroll directory
|
|
|
|
if (NULL == pCACross->pccCACross)
|
|
{
|
|
hr = pkcsLoadCrossCertFromFile(pCACross, pftNotBefore, pftNotAfter);
|
|
_PrintIfError(hr, "pkcsLoadCrossCertFromFile");
|
|
}
|
|
|
|
// if not yet loaded or found, create a new one
|
|
|
|
if (NULL == pCACross->pccCACross)
|
|
{
|
|
// Only create a cross cert if:
|
|
// 1) neither CA cert is revoked -- if !fRevoke
|
|
// 2) old CA cert is not yet expired
|
|
// 3) overlap period exists
|
|
|
|
if (!fRevoke &&
|
|
0 > CompareFileTime(pftNow, pftNotAfter) &&
|
|
0 > CompareFileTime(pftNotBefore, pftNotAfter))
|
|
{
|
|
hr = pkcsCreateNewCrossCert(pCACross, pftNotBefore, pftNotAfter);
|
|
_JumpIfError(hr, error, "pkcsCreateNewCrossCert");
|
|
|
|
if (CERTLOG_TERSE <= g_dwLogLevel)
|
|
{
|
|
WCHAR const *apwsz[2];
|
|
WCHAR awc[2 * cwcDWORDSPRINTF + 2 + 1];
|
|
|
|
wsprintf(
|
|
awc,
|
|
L"(%u-%u)",
|
|
pCACross->pCAContext->iCert,
|
|
pCACross->pCAContextTarget->iCert);
|
|
apwsz[0] = g_wszCommonName;
|
|
apwsz[1] = awc;
|
|
|
|
LogEvent(
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
MSG_CREATED_CROSS_CERT,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if cert loaded:
|
|
|
|
if (NULL != pCACross->pccCACross)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
pkcsVerifyCACrossState(pCACross);
|
|
|
|
hr = pkcsImportCAOrCrossOrKRACert(
|
|
pCACross->pccCACross,
|
|
TRUE, // fCrossCert
|
|
fRevoke? DB_DISP_REVOKED : DB_DISP_ISSUED,
|
|
pCACross->pCAContext);
|
|
_PrintIfError(hr, "pkcsImportCAOrCrossOrKRACert");
|
|
|
|
hr2 = pkcsPublishCrossCert(pCACross, fRevoke);
|
|
_PrintIfError(hr2, "pkcsPublishCrossCert");
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
_JumpIfError(hr, error, "Import/Publish Cross Cert");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pkcsShouldDelete(
|
|
IN CACTX const *pCAContext,
|
|
IN BOOL fRevokedOnly)
|
|
{
|
|
BOOL fDelete = FALSE;
|
|
|
|
if ((CTXF_REVOKED & pCAContext->Flags) &&
|
|
0 == (CRLF_PRESERVE_REVOKED_CA_CERTS & g_dwCRLFlags))
|
|
{
|
|
fDelete = TRUE;
|
|
}
|
|
if (!fRevokedOnly &&
|
|
(CTXF_EXPIRED & pCAContext->Flags) &&
|
|
0 == (CRLF_PRESERVE_EXPIRED_CA_CERTS & g_dwCRLFlags))
|
|
{
|
|
fDelete = TRUE;
|
|
}
|
|
return(fDelete);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCrossCertArray()
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD i;
|
|
FILETIME ftNow;
|
|
|
|
g_aCACrossForward = (CACROSSCTX *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
g_cCACerts * sizeof(g_aCACrossForward[0]));
|
|
if (NULL == g_aCACrossForward)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
g_aCACrossBackward = (CACROSSCTX *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
g_cCACerts * sizeof(g_aCACrossBackward[0]));
|
|
if (NULL == g_aCACrossBackward)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
hr = S_OK;
|
|
for (i = 0; i < g_cCACerts - 1; i++)
|
|
{
|
|
if (g_aCAContext[i].iKey != g_aCAContext[i + 1].iKey)
|
|
{
|
|
BOOL fRevoke;
|
|
|
|
fRevoke = pkcsShouldDelete(&g_aCAContext[i], TRUE) ||
|
|
pkcsShouldDelete(&g_aCAContext[i + 1], TRUE);
|
|
|
|
g_aCACrossForward[i].pCAContext = &g_aCAContext[i];
|
|
g_aCACrossForward[i].pCAContextTarget = &g_aCAContext[i + 1];
|
|
if (fRevoke)
|
|
{
|
|
g_aCACrossForward[i].Flags |= CTXF_REVOKED;
|
|
}
|
|
hr2 = pkcsLoadCrossCert(
|
|
&g_aCACrossForward[i],
|
|
&ftNow,
|
|
TRUE, // fForward
|
|
fRevoke);
|
|
_PrintIfError(hr2, "pkcsLoadCrossCert");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
|
|
g_aCACrossBackward[i + 1].pCAContext = &g_aCAContext[i + 1];
|
|
g_aCACrossBackward[i + 1].pCAContextTarget = &g_aCAContext[i];
|
|
if (fRevoke)
|
|
{
|
|
g_aCACrossBackward[i + 1].Flags |= CTXF_REVOKED;
|
|
}
|
|
hr2 = pkcsLoadCrossCert(
|
|
&g_aCACrossBackward[i + 1],
|
|
&ftNow,
|
|
FALSE, // fForward
|
|
fRevoke);
|
|
_PrintIfError(hr2, "pkcsLoadCrossCert");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "pkcsLoadCrossCert");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsImportCAContext(
|
|
IN CACTX const *pCAContext)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD i;
|
|
WCHAR *pwszCertFile = NULL;
|
|
BOOL fDelete;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
LONG lDisposition;
|
|
|
|
hr = S_OK;
|
|
CSASSERT(NULL != pCAContext->pccCA);
|
|
for (i = 0; i < pCAContext->cCACertChain; i++)
|
|
{
|
|
pCert = pCAContext->apCACertChain[i];
|
|
|
|
// If missing, save the cert to the database.
|
|
|
|
hr2 = pkcsImportCAOrCrossOrKRACert(
|
|
pCert,
|
|
FALSE, // fCrossCert
|
|
0 == i? DB_DISP_CA_CERT : DB_DISP_CA_CERT_CHAIN,
|
|
pCAContext);
|
|
if (S_OK != hr2)
|
|
{
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintError(hr2, "pkcsImportCAOrCrossOrKRACert");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
hr2 = pkcsGetCertFilename(
|
|
g_wszSanitizedName,
|
|
pCAContext->iCert,
|
|
MAXDWORD, // iCertTarget
|
|
&pwszCertFile);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_JumpIfError(hr2, error, "myGetCertFilename");
|
|
|
|
fDelete = pkcsShouldDelete(pCAContext, FALSE);
|
|
|
|
// If necessary, save/delete the cert to/from the CertEnroll directory.
|
|
|
|
hr2 = CRLWriteToLockedFile(
|
|
pCAContext->pccCA->pbCertEncoded,
|
|
pCAContext->pccCA->cbCertEncoded,
|
|
fDelete,
|
|
pwszCertFile);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintIfError(hr2, "CRLWriteToLockedFile");
|
|
|
|
// If necessary, add/delete the cert to/from the HKLM Machine CA store.
|
|
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszCA_CERTSTORE,
|
|
FALSE, // fEnterprise
|
|
fDelete,
|
|
pCAContext->pccCA,
|
|
&lDisposition);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintIfError(hr2, "pkcsWriteCertToStore");
|
|
|
|
if (fDelete)
|
|
{
|
|
// If present, delete the cert from the HKLM Enterprise CA store.
|
|
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszCA_CERTSTORE,
|
|
TRUE, // fEnterprise
|
|
TRUE, // fDelete
|
|
pCAContext->pccCA,
|
|
&lDisposition);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintIfError(hr2, "pkcsWriteCertToStore");
|
|
|
|
if (IsRootCA(g_CAType))
|
|
{
|
|
// If present, delete the cert from the HKLM Machine Root store.
|
|
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszROOT_CERTSTORE,
|
|
FALSE, // fEnterprise
|
|
TRUE, // fDelete
|
|
pCAContext->pccCA,
|
|
&lDisposition);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
|
|
// If present, delete the cert from the HKLM Enterprise Root store.
|
|
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszROOT_CERTSTORE,
|
|
TRUE, // fEnterprise
|
|
TRUE, // fDelete
|
|
pCAContext->pccCA,
|
|
&lDisposition);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
_PrintIfError(hr2, "pkcsWriteCertToStore");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If executing with a DS available, expect group policy to supply the
|
|
// root cert. Log an event if we don't find the root cert in the HKLM
|
|
// Enterprise Root store.
|
|
|
|
if (g_fUseDS &&
|
|
NULL != pCert &&
|
|
CertCompareCertificateName(
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->Subject,
|
|
&pCert->pCertInfo->Issuer))
|
|
{
|
|
hr2 = pkcsWriteCertToStore(
|
|
wszROOT_CERTSTORE,
|
|
TRUE, // fEnterprise
|
|
FALSE, // fDelete
|
|
pCert,
|
|
&lDisposition);
|
|
_PrintIfError(hr2, "pkcsWriteCertToStore");
|
|
if (0 < lDisposition)
|
|
{
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
WCHAR const *apwsz[1];
|
|
|
|
// Was missing -- probably not being sucked down from Group
|
|
// Policy's Root CA object query. Complain loudly.
|
|
|
|
wsprintf(awc, L"%u", pCAContext->iCert);
|
|
apwsz[0] = awc;
|
|
LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_E_MISSING_POLICY_ROOT,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
}
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "pkcsImportCAContext");
|
|
|
|
error:
|
|
if (NULL != pwszCertFile)
|
|
{
|
|
LocalFree(pwszCertFile);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsImportCAContextArray()
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD i;
|
|
WCHAR *pwszCertDir = NULL;
|
|
WCHAR *pwsz;
|
|
|
|
hr = pkcsGetCertFilename(g_wszSanitizedName, 0, MAXDWORD, &pwszCertDir);
|
|
_JumpIfError(hr, error, "myGetCertFilename");
|
|
|
|
pwsz = wcsrchr(pwszCertDir, L'\\');
|
|
CSASSERT(NULL != pwsz);
|
|
*pwsz = L'\0';
|
|
CreateDirectory(pwszCertDir, NULL); // in case it's missing
|
|
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
CACTX *pCAContext = &g_aCAContext[i];
|
|
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
continue;
|
|
}
|
|
hr2 = pkcsImportCAContext(pCAContext);
|
|
if (S_OK != hr2)
|
|
{
|
|
_PrintError(hr2, "pkcsImportCAContext");
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2; // return first error
|
|
}
|
|
}
|
|
}
|
|
|
|
error:
|
|
if (NULL != pwszCertDir)
|
|
{
|
|
LocalFree(pwszCertDir);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsExpandURL(
|
|
IN WCHAR const *pwszURLTemplate,
|
|
IN BOOL fDSAttrib,
|
|
OUT WCHAR **ppwszURL)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppwszURL = NULL;
|
|
|
|
CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN);
|
|
hr = myFormatCertsrvStringArray(
|
|
FALSE, // fURL
|
|
g_pwszServerName, // pwszServerName_p1_2
|
|
g_wszSanitizedName, // pwszSanitizedName_p3_7
|
|
0, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
g_strDomainDN, // pwszDomainDN_p5
|
|
g_strConfigDN, // pwszConfigDN_p6
|
|
0, // iCRL_p8
|
|
FALSE, // fDeltaCRL_p9
|
|
fDSAttrib, // fDSAttrib_p10_11
|
|
1, // cStrings
|
|
&pwszURLTemplate, // apwszStringsIn
|
|
ppwszURL); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsObtainDSStore(
|
|
IN WCHAR const *pwszURLTemplate,
|
|
OUT HCERTSTORE *phCertStore)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hEnterpriseStore;
|
|
WCHAR *pwszURL = NULL;
|
|
|
|
if (NULL == pwszURLTemplate || NULL == phCertStore)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL parm");
|
|
}
|
|
*phCertStore = NULL;
|
|
|
|
hr = pkcsExpandURL(pwszURLTemplate, TRUE, &pwszURL);
|
|
_JumpIfError(hr, error, "pkcsExpandURL");
|
|
|
|
hEnterpriseStore = myUrlCertOpenStore(
|
|
CRYPT_WIRE_ONLY_RETRIEVAL |
|
|
CRYPT_RETRIEVE_MULTIPLE_OBJECTS |
|
|
CRYPT_LDAP_SIGN_RETRIEVAL,
|
|
pwszURL);
|
|
if (NULL == hEnterpriseStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myUrlCertOpenStore");
|
|
}
|
|
*phCertStore = hEnterpriseStore;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszURL)
|
|
{
|
|
LocalFree(pwszURL);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifyRootRevocationStatus(
|
|
IN OUT CACTX *pCAContext,
|
|
OUT BOOL *pfRevoked)
|
|
{
|
|
HRESULT hr;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cb;
|
|
BSTR strHash = NULL;
|
|
ICertDBRow *prow = NULL;
|
|
DWORD Disposition;
|
|
|
|
*pfRevoked = FALSE;
|
|
cb = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pCAContext->pccCA,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
hr = MultiByteIntegerToBstr(TRUE, cb, abHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY | PROPOPEN_CERTHASH | PROPTABLE_REQCERT,
|
|
0,
|
|
strHash,
|
|
&prow);
|
|
_JumpIfError(hr, error, "OpenRow(xchg cert)");
|
|
|
|
cb = sizeof(Disposition);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestDisposition,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &Disposition);
|
|
_JumpIfError(hr, error, "GetProperty(Disposition)");
|
|
|
|
if (DB_DISP_CA_CERT == Disposition)
|
|
{
|
|
DWORD Reason;
|
|
FILETIME ftRevoked;
|
|
FILETIME ftCurrent;
|
|
|
|
cb = sizeof(Reason);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRevokedReason,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &Reason);
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
_JumpIfError(hr, error, "GetProperty(Reason)");
|
|
|
|
cb = sizeof(ftRevoked);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRevokedEffectiveWhen,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &ftRevoked);
|
|
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
_JumpIfError(hr, error, "GetProperty(RevokedEffectiveWhen)");
|
|
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
if (0 >= CompareFileTime(&ftRevoked, &ftCurrent))
|
|
{
|
|
*pfRevoked = TRUE;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
if (NULL != prow)
|
|
{
|
|
prow->Release();
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifySignatureCertContext(
|
|
IN OUT CACTX *pCAContext,
|
|
IN HCERTSTORE hNTAuthStore)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
|
|
hr = myVerifyCertContext(
|
|
pCAContext->pccCA, // pCert
|
|
0, // dwFlags
|
|
0, // cUsageOids
|
|
NULL, // apszUsageOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
NULL, // hAdditionalStore
|
|
NULL); // ppwszMissingIssuer
|
|
_PrintIfError2(hr, "myVerifyCertContext", CRYPT_E_REVOCATION_OFFLINE);
|
|
|
|
if (IsRootCA(g_CAType) &&
|
|
(S_OK == hr ||
|
|
CERT_E_UNTRUSTEDROOT == hr ||
|
|
CRYPT_E_REVOCATION_OFFLINE == hr ||
|
|
CRYPT_E_NO_REVOCATION_CHECK == hr))
|
|
{
|
|
HRESULT hr2;
|
|
BOOL fRevoked;
|
|
|
|
hr2 = pkcsVerifyRootRevocationStatus(pCAContext, &fRevoked);
|
|
_PrintIfError2(hr2, "pkcsVerifyRootRevocationStatus", CERTSRV_E_PROPERTY_EMPTY);
|
|
if (S_OK == hr2 && fRevoked)
|
|
{
|
|
hr = CRYPT_E_REVOKED;
|
|
_PrintError(hr, "pkcsVerifyRootRevocationStatus");
|
|
}
|
|
else
|
|
{
|
|
FILETIME ftCurrent;
|
|
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
if (0 < CompareFileTime(
|
|
&ftCurrent,
|
|
&pCAContext->pccCA->pCertInfo->NotAfter))
|
|
{
|
|
hr = CERT_E_EXPIRED;
|
|
_PrintError(hr, "CA certificate is expired");
|
|
}
|
|
}
|
|
}
|
|
pCAContext->hrVerifyStatus = hr;
|
|
if (S_OK != hr)
|
|
{
|
|
DWORD LogMsg = MAXDWORD;
|
|
DWORD dwLogTypeOld = EVENTLOG_INFORMATION_TYPE;
|
|
DWORD dwLogTypeCurrent = EVENTLOG_ERROR_TYPE;
|
|
|
|
if (CERT_E_EXPIRED == hr)
|
|
{
|
|
DBGCODE(DWORD f = pCAContext->Flags);
|
|
|
|
pCAContext->Flags |= CTXF_EXPIRED;
|
|
|
|
if (0 == (CRLF_PUBLISH_EXPIRED_CERT_CRLS & g_dwCRLFlags))
|
|
{
|
|
pCAContext->Flags |= CTXF_SKIPCRL;
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"pkcsVerifySignatureCertContext(%u.%u) EXPIRED, f=%x->%x, hr=%x\n",
|
|
pCAContext->iCert,
|
|
pCAContext->iKey,
|
|
f,
|
|
pCAContext->Flags,
|
|
hr));
|
|
LogMsg = MSG_E_CA_CERT_EXPIRED;
|
|
}
|
|
else if (CRYPT_E_REVOKED == hr || CERT_E_REVOKED == hr)
|
|
{
|
|
pCAContext->Flags |= CTXF_REVOKED | CTXF_SKIPCRL;
|
|
LogMsg = MSG_E_CA_CERT_REVOKED;
|
|
dwLogTypeOld = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else if (CRYPT_E_REVOCATION_OFFLINE == hr)
|
|
{
|
|
HRESULT hr2;
|
|
DWORD dwState;
|
|
|
|
hr2 = GetSetupStatus(NULL, &dwState);
|
|
if ((S_OK != hr2 || 0 == (SETUP_CREATEDB_FLAG & dwState)) &&
|
|
CERTLOG_WARNING <= g_dwLogLevel)
|
|
{
|
|
LogMsg = MSG_E_CA_CERT_REVOCATION_OFFLINE;
|
|
dwLogTypeCurrent = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else if (CRYPT_E_NO_REVOCATION_CHECK == hr)
|
|
{
|
|
if (CERTLOG_VERBOSE <= g_dwLogLevel)
|
|
{
|
|
LogMsg = MSG_E_CA_CERT_REVOCATION_NOT_CHECKED;
|
|
dwLogTypeCurrent = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogMsg = MSG_E_CA_CHAIN;
|
|
}
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
WCHAR const *apwsz[3];
|
|
WCHAR awchr[cwcHRESULTSTRING];
|
|
|
|
CSASSERT(MAXDWORD != LogMsg);
|
|
wsprintf(awc, L"%u", pCAContext->iCert);
|
|
apwsz[0] = g_wszCommonName;
|
|
apwsz[1] = myGetErrorMessageText(hr, TRUE);
|
|
if (NULL == apwsz[1])
|
|
{
|
|
apwsz[1] = myHResultToString(awchr, hr);
|
|
}
|
|
apwsz[2] = awc;
|
|
|
|
LogEvent(
|
|
pCAContext->iCert + 1 < g_cCACerts?
|
|
dwLogTypeOld : dwLogTypeCurrent,
|
|
LogMsg,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
|
|
if (NULL != apwsz[1] && awchr != apwsz[1])
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(apwsz[1]));
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "myVerifyCertContext");
|
|
}
|
|
|
|
// The CA's certificate looks good. We verify the CA's certificate
|
|
// is in the NTAuth store
|
|
|
|
if (NULL != hNTAuthStore)
|
|
{
|
|
CERT_CONTEXT const *pCertContext;
|
|
|
|
pCertContext = pkcsFindCertificateInStore(
|
|
hNTAuthStore,
|
|
pCAContext->pccCA);
|
|
if (NULL == pCertContext)
|
|
{
|
|
WCHAR const *apwsz[2];
|
|
|
|
wsprintf(awc, L"%u", pCAContext->iCert);
|
|
apwsz[0] = awc;
|
|
apwsz[1] = g_wszCommonName;
|
|
|
|
LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_CA_CERT_NO_IN_AUTH,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
}
|
|
if (NULL != pCertContext)
|
|
{
|
|
CertFreeCertificateContext(pCertContext);
|
|
}
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifySignatureCertContextArray()
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
HCERTSTORE hNTAuthStore = NULL;
|
|
|
|
CSASSERT(0 != g_cCACerts);
|
|
|
|
// We need to verify CA's certificates should be in the NTAuth store
|
|
// if the certificate is not yet expired or revoked
|
|
|
|
if (IsEnterpriseCA(g_CAType))
|
|
{
|
|
hr = pkcsObtainDSStore(g_wszLDAPNTAuthURLTemplate, &hNTAuthStore);
|
|
_PrintIfError(hr, "pkcsObtainDSStore");
|
|
if (NULL == hNTAuthStore)
|
|
{
|
|
WCHAR const *pwszCommonName = g_wszCommonName;
|
|
|
|
LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_CA_CERT_NO_AUTH_STORE,
|
|
1,
|
|
&pwszCommonName);
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
CACTX *pCAContext = &g_aCAContext[i];
|
|
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Ignore all errors except for the current CA (last array entry)
|
|
|
|
hr = pkcsVerifySignatureCertContext(pCAContext, hNTAuthStore);
|
|
_PrintIfError(hr, "pkcsVerifySignatureCertContext");
|
|
}
|
|
|
|
//error:
|
|
if (NULL != hNTAuthStore)
|
|
{
|
|
CertCloseStore(hNTAuthStore, 0);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSVerifyCAState(
|
|
IN OUT CACTX *pCAContext)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (0 == (~CTXF_SKIPCRL & pCAContext->Flags) &&
|
|
NULL != pCAContext->pccCA)
|
|
{
|
|
hr = pkcsVerifySignatureCertContext(pCAContext, NULL);
|
|
_JumpIfError(hr, error, "pkcsVerifySignatureCertContext");
|
|
}
|
|
hr = pCAContext->hrVerifyStatus;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifyDSCACert(
|
|
IN LDAP *pld)
|
|
{
|
|
HRESULT hr;
|
|
HCAINFO hCAInfo = NULL;
|
|
CERT_CONTEXT const *pDSCertContext = NULL;
|
|
|
|
CSASSERT(NULL != g_pCAContextCurrent && NULL != g_pCAContextCurrent->pccCA);
|
|
|
|
hr = CAFindByName(
|
|
g_pwszSanitizedDSName,
|
|
(LPCWSTR) pld,
|
|
CA_FIND_LOCAL_SYSTEM |
|
|
CA_FIND_INCLUDE_UNTRUSTED | // skip CA cert checking
|
|
CA_FLAG_SCOPE_IS_LDAP_HANDLE,
|
|
&hCAInfo);
|
|
_JumpIfErrorStr(hr, error, "CAFindByName", g_wszSanitizedName);
|
|
|
|
hr = CAGetCACertificate(hCAInfo, &pDSCertContext);
|
|
_JumpIfError(hr, error, "CAGetCACertificate");
|
|
|
|
if (!pDSCertContext ||
|
|
pDSCertContext->cbCertEncoded !=
|
|
g_pCAContextCurrent->pccCA->cbCertEncoded ||
|
|
0 != memcmp(
|
|
pDSCertContext->pbCertEncoded,
|
|
g_pCAContextCurrent->pccCA->pbCertEncoded,
|
|
g_pCAContextCurrent->pccCA->cbCertEncoded))
|
|
{
|
|
// published cert is invalid or old, publish the current one
|
|
|
|
hr = CASetCACertificate(hCAInfo, g_pCAContextCurrent->pccCA);
|
|
_JumpIfError(hr, error, "CASetCACertificate");
|
|
|
|
hr = CAUpdateCA(hCAInfo);
|
|
_JumpIfError(hr, error, "CAUpdateCA");
|
|
|
|
{
|
|
CAuditEvent audit(SE_AUDITID_CERTSRV_PUBLISHCACERT, g_dwAuditFilter);
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
g_pCAContextCurrent->pccCA,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
|
|
// %1 Certificate Hash
|
|
|
|
hr = audit.AddData(abHash, cbHash);
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
hr = audit.AddData(g_pCAContextCurrent->pccCA->pCertInfo->NotBefore); // %2 Valid From
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
hr = audit.AddData(g_pCAContextCurrent->pccCA->pCertInfo->NotAfter); //%3 Valid To
|
|
_JumpIfError(hr, error, "CAuditEvent::AddData");
|
|
|
|
hr = audit.Report();
|
|
_JumpIfError(hr, error, "CAuditEvent::Report");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hCAInfo)
|
|
{
|
|
CACloseCA(hCAInfo);
|
|
}
|
|
if (NULL != pDSCertContext)
|
|
{
|
|
CertFreeCertificateContext(pDSCertContext);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Verify all of this CA's unexpired signature certs are in the DS.
|
|
// Republish any that aren't. Cleans up DS replication conflicts.
|
|
|
|
HRESULT
|
|
pkcsPublishCAContextArray(
|
|
IN LDAP *pld)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD i;
|
|
HCERTSTORE hAIAStore = NULL;
|
|
WCHAR *pwszDSError = NULL;
|
|
WCHAR *pwszURL = NULL;
|
|
WCHAR *pwszDN;
|
|
|
|
CSASSERT(0 != g_cCACerts);
|
|
|
|
// We need to verify each CA certificate is in the DS AIA store,
|
|
// if not yet expired or revoked
|
|
|
|
hr = pkcsObtainDSStore(g_wszzLDAPIssuerCertURLTemplate, &hAIAStore);
|
|
_JumpIfError(hr, error, "pkcsObtainDSStore");
|
|
|
|
CSASSERT(NULL != hAIAStore);
|
|
|
|
hr = pkcsExpandURL(g_wszzLDAPIssuerCertURLTemplate, FALSE, &pwszURL);
|
|
_JumpIfError(hr, error, "pkcsExpandURL");
|
|
|
|
pwszDN = pwszURL;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
pwszDN = wcschr(pwszDN, L'/');
|
|
if (NULL == pwszDN)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad pwszURL");
|
|
}
|
|
pwszDN++;
|
|
}
|
|
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
CACTX *pCAContext = &g_aCAContext[i];
|
|
CERT_CONTEXT const *pcc;
|
|
BOOL fDelete;
|
|
DWORD dwDisposition;
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
WCHAR const *apwsz[4];
|
|
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Ignore verify errors except for the current CA (last array entry)
|
|
|
|
fDelete = FALSE;
|
|
hr = PKCSVerifyCAState(pCAContext);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "PKCSVerifyCAState");
|
|
fDelete = pkcsShouldDelete(pCAContext, FALSE);
|
|
if (!fDelete)
|
|
{
|
|
continue; // don't publish/delete: (transient invalidity?)
|
|
}
|
|
}
|
|
|
|
pcc = pkcsFindCertificateInStore(hAIAStore, pCAContext->pccCA);
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
if (!fDelete)
|
|
{
|
|
continue; // don't publish if already published
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fDelete)
|
|
{
|
|
continue; // don't delete if already deleted
|
|
}
|
|
}
|
|
if (NULL != pwszDSError)
|
|
{
|
|
LocalFree(pwszDSError);
|
|
pwszDSError = NULL;
|
|
}
|
|
hr2 = myLdapPublishCertToDS(
|
|
pld,
|
|
pCAContext->pccCA,
|
|
pwszDN,
|
|
wszDSCACERTATTRIBUTE,
|
|
LPC_CAOBJECT | LPC_CREATEOBJECT,
|
|
fDelete,
|
|
&dwDisposition,
|
|
&pwszDSError);
|
|
|
|
wsprintf(awc, L"%u", pCAContext->iCert);
|
|
apwsz[0] = awc;
|
|
apwsz[1] = pwszDN;
|
|
|
|
if (S_OK != hr2)
|
|
{
|
|
WCHAR const *pwszError = NULL;
|
|
WCHAR awchr[cwcHRESULTSTRING];
|
|
|
|
apwsz[2] = pwszDSError;
|
|
pwszError = myGetErrorMessageText(hr2, TRUE);
|
|
apwsz[3] = pwszError;
|
|
if (NULL == apwsz[3])
|
|
{
|
|
apwsz[3] = myHResultToString(awchr, hr2);
|
|
}
|
|
LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
fDelete?
|
|
MSG_E_CANNOT_DELETE_INVALID_CA_CERT :
|
|
MSG_E_CANNOT_ADD_MISSING_CA_CERT,
|
|
ARRAYSIZE(apwsz),
|
|
apwsz);
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(pwszError));
|
|
}
|
|
|
|
_PrintErrorStr(dwDisposition, "myLdapPublishCertToDS", pwszDSError);
|
|
if (fDelete)
|
|
{
|
|
_PrintErrorStr(hr2, "myLdapPublishCertToDS", L"Delete");
|
|
}
|
|
_PrintErrorStr(hr2, "myLdapPublishCertToDS", pwszDN);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
fDelete?
|
|
"Deleted CA Cert[%u] from %ws\n" :
|
|
"Published CA Cert[%u] to %ws\n",
|
|
i,
|
|
pwszDN));
|
|
LogEvent(
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
fDelete?
|
|
MSG_E_DELETED_INVALID_CA_CERT :
|
|
MSG_E_ADDED_MISSING_CA_CERT,
|
|
min(ARRAYSIZE(apwsz), 2),
|
|
apwsz);
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "pkcsPublishCAContextArray");
|
|
|
|
error:
|
|
if (NULL != pwszURL)
|
|
{
|
|
LocalFree(pwszURL);
|
|
}
|
|
if (NULL != pwszDSError)
|
|
{
|
|
LocalFree(pwszDSError);
|
|
}
|
|
if (NULL != hAIAStore)
|
|
{
|
|
CertCloseStore(hAIAStore, 0);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseKRACertArray()
|
|
{
|
|
DWORD i;
|
|
|
|
if (NULL != g_aKRAContext)
|
|
{
|
|
for (i = 0; i < g_cKRACerts; i++)
|
|
{
|
|
if (NULL != g_aKRAContext[i].pccKRA)
|
|
{
|
|
CertFreeCertificateContext(g_aKRAContext[i].pccKRA);
|
|
}
|
|
if (NULL != g_aKRAContext[i].strKRAHash)
|
|
{
|
|
SysFreeString(g_aKRAContext[i].strKRAHash);
|
|
}
|
|
}
|
|
LocalFree(g_aKRAContext);
|
|
g_aKRAContext = NULL;
|
|
}
|
|
g_cKRACerts = 0;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadKRACertContext(
|
|
IN DWORD iHash,
|
|
IN OUT HCERTSTORE *phStore)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
BSTR strHash = NULL;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
DWORD LogMsg = 0;
|
|
BOOL fReloaded;
|
|
KRACTX *pKRAContext;
|
|
|
|
DBGPRINT((DBG_SS_CERTSRV, "Loading KRA Cert[%u]:\n", iHash));
|
|
|
|
fReloaded = FALSE;
|
|
for (;;)
|
|
{
|
|
hr = myFindCACertByHashIndex(
|
|
*phStore,
|
|
g_wszSanitizedName,
|
|
CSRH_CAKRACERT,
|
|
iHash,
|
|
NULL, // pNameId
|
|
&pcc);
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
if (fReloaded || CRYPT_E_NOT_FOUND != hr)
|
|
{
|
|
_JumpError(hr, error, "myFindCACertByHashIndex");
|
|
}
|
|
_PrintError(hr, "myFindCACertByHashIndex");
|
|
|
|
// The KRA cert is missing from the HKLM "kra" store -- look it up in
|
|
// the DB, and put it back in the store.
|
|
|
|
hr = pkcsReloadMissingCAOrKRACert(
|
|
g_wszSanitizedName,
|
|
CSRH_CAKRACERT,
|
|
iHash,
|
|
wszKRA_CERTSTORE);
|
|
_JumpIfError(hr, error, "pkcsReloadMissingCAOrKRACert");
|
|
|
|
CertCloseStore(*phStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
*phStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
|
CERT_STORE_READONLY_FLAG,
|
|
wszKRA_CERTSTORE);
|
|
if (NULL == *phStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
fReloaded = TRUE;
|
|
}
|
|
|
|
hr = pkcsImportCAOrCrossOrKRACert(pcc, FALSE, DB_DISP_KRA_CERT, NULL);
|
|
_JumpIfError(hr, error, "pkcsImportCAOrCrossOrKRACert");
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
pKRAContext = &g_aKRAContext[g_cKRACerts];
|
|
g_cKRACerts++;
|
|
|
|
pKRAContext->pccKRA = pcc;
|
|
pKRAContext->strKRAHash = strHash;
|
|
strHash = NULL;
|
|
pcc = NULL;
|
|
|
|
// Ignore failure from here on -- collected data is optional
|
|
|
|
hr = myVerifyKRACertContext(pKRAContext->pccKRA, g_dwVerifyCertFlags);
|
|
pKRAContext->hrVerifyStatus = hr;
|
|
if (S_OK != hr)
|
|
{
|
|
if (CERT_E_EXPIRED == hr)
|
|
{
|
|
pKRAContext->Flags |= CTXF_EXPIRED;
|
|
}
|
|
else
|
|
// Assume revoked for other errors
|
|
// if (CRYPT_E_REVOKED == hr || CERT_E_REVOKED == hr)
|
|
{
|
|
pKRAContext->Flags |= CTXF_REVOKED;
|
|
}
|
|
LogMsg = MSG_E_INVALID_KRA_CERT;
|
|
_JumpError(hr, error, "myVerifyKRACertContext");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
if (0 == LogMsg)
|
|
{
|
|
LogMsg = MSG_E_CANNOT_LOAD_KRA_CERT;
|
|
}
|
|
pkcsLogKRACertError(LogMsg, iHash, pcc, hr);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadKRACertArray()
|
|
{
|
|
HRESULT hr;
|
|
DWORD iHash;
|
|
DWORD cKRACerts;
|
|
HCERTSTORE hStore = NULL;
|
|
DWORD LogMsg = 0;
|
|
DWORD cKRACertsValid;
|
|
WCHAR wszDword0[cwcDWORDSPRINTF];
|
|
WCHAR wszDword1[cwcDWORDSPRINTF];
|
|
|
|
if (!g_fAdvancedServer)
|
|
{
|
|
LogMsg = MSG_E_KRA_NOT_ADVANCED_SERVER;
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
_JumpError(hr, error, "!g_fAdvancedServer");
|
|
}
|
|
|
|
wszDword0[0] = L'\0';
|
|
wszDword1[0] = L'\0';
|
|
|
|
// open KRA store
|
|
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
|
|
wszKRA_CERTSTORE);
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
// find & load KRA certs
|
|
|
|
hr = myGetCARegHashCount(g_wszSanitizedName, CSRH_CAKRACERT, &cKRACerts);
|
|
if (S_OK == hr && 0 == cKRACerts)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
_JumpIfError(hr, error, "myGetCARegHashCount");
|
|
|
|
g_aKRAContext = (KRACTX *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cKRACerts * sizeof(g_aKRAContext[0]));
|
|
if (NULL == g_aKRAContext)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
cKRACertsValid = 0;
|
|
for (iHash = 0; iHash < cKRACerts; iHash++)
|
|
{
|
|
hr = pkcsLoadKRACertContext(iHash, &hStore);
|
|
_PrintIfError(hr, "pkcsLoadKRACertContext");
|
|
if (S_OK == hr)
|
|
{
|
|
cKRACertsValid++;
|
|
}
|
|
}
|
|
if (0 == cKRACertsValid || g_cKRACertsRoundRobin > cKRACertsValid)
|
|
{
|
|
wsprintf(wszDword0, L"%u", cKRACertsValid);
|
|
wsprintf(wszDword1, L"%u", g_cKRACertsRoundRobin);
|
|
LogMsg = MSG_E_TOO_FEW_VALID_KRA_CERTS;
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "cKRACertsValid");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
if (MSG_E_TOO_FEW_VALID_KRA_CERTS == LogMsg)
|
|
{
|
|
WCHAR const *apwsz[2];
|
|
|
|
apwsz[0] = wszDword0;
|
|
apwsz[1] = wszDword1;
|
|
|
|
LogEvent(EVENTLOG_ERROR_TYPE, LogMsg, ARRAYSIZE(apwsz), apwsz);
|
|
}
|
|
else
|
|
{
|
|
if (0 == LogMsg)
|
|
{
|
|
LogMsg = MSG_E_LOADING_KRA_CERTS;
|
|
}
|
|
LogEventHResult(EVENTLOG_ERROR_TYPE, LogMsg, hr);
|
|
}
|
|
pkcsReleaseKRACertArray();
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsPatchDN(
|
|
IN HRESULT hrFail,
|
|
IN WCHAR const *pwszRegName,
|
|
IN OUT BSTR *pstrDSValue)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszRegValue = NULL;
|
|
|
|
hr = myGetCertRegStrValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
pwszRegName,
|
|
&pwszRegValue);
|
|
_PrintIfErrorStr(hr, "myGetCertRegStrValue", pwszRegName);
|
|
|
|
// If the DS DN was retrieved, make sure the registry matches
|
|
|
|
if (NULL != *pstrDSValue)
|
|
{
|
|
if (NULL == pwszRegValue || 0 != lstrcmp(*pstrDSValue, pwszRegValue))
|
|
{
|
|
// set reg value
|
|
|
|
hr = mySetCertRegStrValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
pwszRegName,
|
|
*pstrDSValue);
|
|
_PrintIfErrorStr(hr, "mySetCertRegStrValue", pwszRegName);
|
|
}
|
|
}
|
|
|
|
// Else if the registry DN was retrieved, just use it
|
|
|
|
else
|
|
if (NULL != pwszRegValue && L'\0' != *pwszRegValue)
|
|
{
|
|
if (!myConvertWszToBstr(pstrDSValue, pwszRegValue, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertWszToBstr");
|
|
}
|
|
}
|
|
|
|
// If neither DN was retrieved, fail if g_fUsedDS or alloc an empty string:
|
|
|
|
else
|
|
{
|
|
if (g_fUseDS)
|
|
{
|
|
hr = hrFail;
|
|
_JumpError(hr, error, "both DS and Reg NULL");
|
|
}
|
|
*pstrDSValue = SysAllocString(L"");
|
|
if (NULL == *pstrDSValue)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocString");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszRegValue)
|
|
{
|
|
LocalFree(pwszRegValue);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetAuthoritativeDomainDn(
|
|
IN WCHAR const *pwszCommonName,
|
|
OUT LDAP **ppld,
|
|
OUT BSTR *pstrDomainDN,
|
|
OUT BSTR *pstrConfigDN)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
|
|
// Get domain and config containers (%5, %6)
|
|
|
|
*ppld = NULL;
|
|
*pstrDomainDN = NULL;
|
|
*pstrConfigDN = NULL;
|
|
|
|
hr2 = S_OK;
|
|
if (g_fUseDS)
|
|
{
|
|
hr = myRobustLdapBindEx(
|
|
0, // dwFlags1
|
|
RLBF_REQUIRE_SECURE_LDAP, // dwFlags2
|
|
LDAP_VERSION2, // uVersion
|
|
NULL, // pwszDomainName
|
|
ppld,
|
|
NULL); // ppwszForestDNSName
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myRobustLdapBindEx");
|
|
}
|
|
else
|
|
{
|
|
hr = myGetAuthoritativeDomainDn(*ppld, pstrDomainDN, pstrConfigDN);
|
|
_PrintIfError(hr, "myGetAuthoritativeDomainDn");
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
LogEventStringHResult(
|
|
EVENTLOG_ERROR_TYPE,
|
|
MSG_E_DS_RETRY,
|
|
pwszCommonName,
|
|
hr);
|
|
}
|
|
hr2 = hr;
|
|
}
|
|
hr = pkcsPatchDN(hr2, wszREGDSCONFIGDN, pstrConfigDN);
|
|
_JumpIfError(hr, error, "pkcsPatchDN");
|
|
|
|
hr = pkcsPatchDN(hr2, wszREGDSDOMAINDN, pstrDomainDN);
|
|
_JumpIfError(hr, error, "pkcsPatchDN");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
PKCSSetup(
|
|
IN WCHAR const *pwszCommonName,
|
|
IN WCHAR const *pwszSanitizedName)
|
|
{
|
|
HRESULT hr;
|
|
LDAP *pld = NULL;
|
|
DWORD LogMsg = MAXDWORD;
|
|
BOOL fWarn = FALSE;
|
|
|
|
g_dwVerifyCertFlags = 0;
|
|
if (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags)
|
|
{
|
|
g_dwVerifyCertFlags |= CA_VERIFY_FLAGS_IGNORE_OFFLINE;
|
|
}
|
|
if (CRLF_REVCHECK_IGNORE_NOREVCHECK & g_dwCRLFlags)
|
|
{
|
|
g_dwVerifyCertFlags |= CA_VERIFY_FLAGS_IGNORE_NOREVCHECK;
|
|
}
|
|
// set crypt handles and load certificate chain
|
|
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&g_critsecCAXchg);
|
|
g_fcritsecCAXchg = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
hr = pkcsGetAuthoritativeDomainDn(
|
|
pwszCommonName,
|
|
&pld,
|
|
&g_strDomainDN,
|
|
&g_strConfigDN);
|
|
if (S_OK != hr)
|
|
{
|
|
LogMsg = MSG_E_NO_DS;
|
|
_JumpError(hr, error, "pkcsGetAuthoritativeDomainDn");
|
|
}
|
|
|
|
// get (multiple) CRL path templates
|
|
|
|
hr = pkcsLoadURLTemplates(
|
|
wszREGCRLPUBLICATIONURLS,
|
|
&g_paRevURL,
|
|
&g_caRevURL);
|
|
_PrintIfErrorStr(hr, "pkcsLoadURLTemplates", wszREGCRLPUBLICATIONURLS);
|
|
|
|
// get (multiple) CA Cert path templates
|
|
|
|
hr = pkcsLoadURLTemplates(
|
|
wszREGCACERTPUBLICATIONURLS,
|
|
&g_paCACertURL,
|
|
&g_caCACertURL);
|
|
_PrintIfErrorStr(hr, "pkcsLoadURLTemplates", wszREGCACERTPUBLICATIONURLS);
|
|
|
|
hr = DBOpen(pwszSanitizedName);
|
|
if (S_OK != hr)
|
|
{
|
|
LogMsg = MSG_E_DB_INIT_FAILED;
|
|
_JumpError(hr, error, "PKCSSetup:DBOpen");
|
|
}
|
|
|
|
hr = pkcsLoadCAContextArray(pwszSanitizedName);
|
|
if (S_OK != hr)
|
|
{
|
|
LogMsg = MSG_E_CA_CERT_INVALID;
|
|
_JumpError(hr, error, "pkcsLoadCAContextArray");
|
|
}
|
|
|
|
hr = pkcsVerifySignatureCertContextArray();
|
|
{
|
|
// Import the certs into the database even if there were errors.
|
|
// Import them after verifying them so we can delete revoked and
|
|
// expired CA certs from various locations.
|
|
|
|
HRESULT hr2 = pkcsImportCAContextArray();
|
|
_PrintIfError(hr2, "pkcsImportCAContextArray");
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
LogMsg = MSG_E_CA_CERT_INVALID;
|
|
_JumpError(hr, error, "pkcsVerifySignatureCertContextArray");
|
|
}
|
|
|
|
if (0 != g_cKRACertsRoundRobin)
|
|
{
|
|
hr = pkcsLoadKRACertArray();
|
|
_PrintIfError(hr, "pkcsLoadKRACertArray");
|
|
}
|
|
|
|
hr = pkcsExpandURL(
|
|
g_wszzLDAPKRACertURLTemplate,
|
|
FALSE,
|
|
&g_pwszKRAPublishURL);
|
|
_JumpIfError(hr, error, "pkcsExpandURL");
|
|
|
|
hr = pkcsExpandURL(
|
|
g_wszzLDAPIssuerCertURLTemplate,
|
|
FALSE,
|
|
&g_pwszAIACrossCertPublishURL);
|
|
_JumpIfError(hr, error, "pkcsExpandURL");
|
|
|
|
hr = pkcsExpandURL(
|
|
g_wszLDAPRootTrustURLTemplate,
|
|
FALSE,
|
|
&g_pwszRootTrustCrossCertPublishURL);
|
|
_JumpIfError(hr, error, "pkcsExpandURL");
|
|
|
|
if (IsRootCA(g_CAType) &&
|
|
0 == (CRLF_DISABLE_ROOT_CROSS_CERTS & g_dwCRLFlags))
|
|
{
|
|
hr = pkcsLoadCrossCertArray();
|
|
_PrintIfError(hr, "pkcsLoadCrossCertArray");
|
|
}
|
|
if (NULL != pld)
|
|
{
|
|
hr = pkcsVerifyDSCACert(pld);
|
|
_PrintIfError(hr, "pkcsVerifyDSCACert");
|
|
|
|
hr = pkcsPublishCAContextArray(pld);
|
|
_PrintIfError(hr, "pkcsPublishCAContextArray");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pld)
|
|
{
|
|
ldap_unbind(pld);
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
if (MAXDWORD == LogMsg)
|
|
{
|
|
LogMsg = MSG_E_GENERIC_STARTUP_FAILURE;
|
|
}
|
|
PKCSTerminate();
|
|
LogEventStringHResult(
|
|
EVENTLOG_ERROR_TYPE,
|
|
LogMsg,
|
|
pwszCommonName,
|
|
hr);
|
|
if (fWarn)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCACertificateChain(
|
|
CERT_CONTEXT const **apCACertChain,
|
|
DWORD cCACertChain)
|
|
{
|
|
DWORD i;
|
|
|
|
if (NULL != apCACertChain)
|
|
{
|
|
for (i = 0; i < cCACertChain; ++i)
|
|
{
|
|
CertFreeCertificateContext(apCACertChain[i]);
|
|
}
|
|
LocalFree(apCACertChain);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCAContext(
|
|
IN OUT CACTX *pCAContext)
|
|
{
|
|
pkcsReleaseCACertificateChain(
|
|
pCAContext->apCACertChain,
|
|
pCAContext->cCACertChain);
|
|
//pCAContext->apCACertChain = NULL;
|
|
//pCAContext->pccCA = NULL;
|
|
if (NULL != pCAContext->hProvCA)
|
|
{
|
|
CryptReleaseContext(pCAContext->hProvCA, 0);
|
|
}
|
|
if (NULL != pCAContext->IssuerKeyId.pbData)
|
|
{
|
|
LocalFree(pCAContext->IssuerKeyId.pbData);
|
|
}
|
|
if (NULL != pCAContext->pszObjIdSignatureAlgorithm)
|
|
{
|
|
LocalFree(pCAContext->pszObjIdSignatureAlgorithm);
|
|
}
|
|
if (NULL != pCAContext->KeyAuthority2Cert.pbData)
|
|
{
|
|
LocalFree(pCAContext->KeyAuthority2Cert.pbData);
|
|
}
|
|
if (NULL != pCAContext->KeyAuthority2CRL.pbData)
|
|
{
|
|
LocalFree(pCAContext->KeyAuthority2CRL.pbData);
|
|
}
|
|
if (NULL != pCAContext->CDPCert.pbData)
|
|
{
|
|
LocalFree(pCAContext->CDPCert.pbData);
|
|
}
|
|
if (NULL != pCAContext->CDPCRLFreshest.pbData)
|
|
{
|
|
LocalFree(pCAContext->CDPCRLFreshest.pbData);
|
|
}
|
|
if (NULL != pCAContext->CDPCRLBase.pbData)
|
|
{
|
|
LocalFree(pCAContext->CDPCRLBase.pbData);
|
|
}
|
|
if (NULL != pCAContext->CDPCRLDelta.pbData)
|
|
{
|
|
LocalFree(pCAContext->CDPCRLDelta.pbData);
|
|
}
|
|
if (NULL != pCAContext->AIACert.pbData)
|
|
{
|
|
LocalFree(pCAContext->AIACert.pbData);
|
|
}
|
|
if (NULL != pCAContext->pwszKeyContainerName)
|
|
{
|
|
LocalFree(pCAContext->pwszKeyContainerName);
|
|
}
|
|
if (NULL != pCAContext->papwszCRLFiles)
|
|
{
|
|
WCHAR **ppwsz;
|
|
|
|
for (ppwsz = pCAContext->papwszCRLFiles; NULL != *ppwsz; ppwsz++)
|
|
{
|
|
LocalFree(*ppwsz);
|
|
}
|
|
LocalFree(pCAContext->papwszCRLFiles);
|
|
}
|
|
if (NULL != pCAContext->papwszDeltaCRLFiles)
|
|
{
|
|
WCHAR **ppwsz;
|
|
|
|
for (ppwsz = pCAContext->papwszDeltaCRLFiles; NULL != *ppwsz; ppwsz++)
|
|
{
|
|
LocalFree(*ppwsz);
|
|
}
|
|
LocalFree(pCAContext->papwszDeltaCRLFiles);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCACrossContextArray(
|
|
IN OUT CACROSSCTX **prgCACross)
|
|
{
|
|
DWORD i;
|
|
CACROSSCTX *pCACross = *prgCACross;
|
|
|
|
if (NULL != pCACross)
|
|
{
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
if (NULL != pCACross[i].pccCACross)
|
|
{
|
|
CertFreeCertificateContext(pCACross[i].pccCACross);
|
|
}
|
|
}
|
|
LocalFree(pCACross);
|
|
*prgCACross = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCAContextArray()
|
|
{
|
|
DWORD i;
|
|
|
|
pkcsReleaseCACrossContextArray(&g_aCACrossForward);
|
|
pkcsReleaseCACrossContextArray(&g_aCACrossBackward);
|
|
if (NULL != g_aCAContext)
|
|
{
|
|
for (i = 0; i < g_cCACerts; i++)
|
|
{
|
|
pkcsReleaseCAContext(&g_aCAContext[i]);
|
|
}
|
|
LocalFree(g_aCAContext);
|
|
g_aCAContext = NULL;
|
|
}
|
|
g_cCACerts = 0;
|
|
g_pCAContextCurrent = NULL;
|
|
}
|
|
|
|
|
|
// Trim off leading and trailing whitespace and separator characters
|
|
|
|
WCHAR *
|
|
pkcsTrimToken(
|
|
IN WCHAR *pwszIn,
|
|
IN WCHAR wchSeparator)
|
|
{
|
|
WCHAR *pwsz;
|
|
|
|
while (wchSeparator == *pwszIn || iswspace(*pwszIn))
|
|
{
|
|
pwszIn++;
|
|
}
|
|
pwsz = &pwszIn[wcslen(pwszIn)];
|
|
while (--pwsz >= pwszIn &&
|
|
(wchSeparator == *pwsz || iswspace(*pwsz)))
|
|
{
|
|
*pwsz = L'\0';
|
|
}
|
|
if (L'\0' == *pwszIn)
|
|
{
|
|
pwszIn = NULL;
|
|
}
|
|
return(pwszIn);
|
|
}
|
|
|
|
|
|
WCHAR *
|
|
PKCSSplitToken(
|
|
IN OUT WCHAR **ppwszIn,
|
|
IN WCHAR *pwcSeparator,
|
|
OUT BOOL *pfSplit)
|
|
{
|
|
WCHAR *pwszOut = NULL;
|
|
WCHAR *pwszNext = NULL;
|
|
BOOL fSplit = FALSE;
|
|
WCHAR *pwszIn;
|
|
WCHAR *pwsz;
|
|
|
|
pwszIn = *ppwszIn;
|
|
if (NULL != pwszIn)
|
|
{
|
|
pwszOut = pwszIn;
|
|
if (NULL != pwcSeparator)
|
|
{
|
|
pwsz = wcschr(pwszIn, *pwcSeparator);
|
|
if (NULL != pwsz)
|
|
{
|
|
*pwsz = L'\0';
|
|
|
|
pwszNext = pkcsTrimToken(&pwsz[1], *pwcSeparator);
|
|
pwszOut = pkcsTrimToken(pwszOut, *pwcSeparator);
|
|
fSplit = TRUE;
|
|
}
|
|
}
|
|
}
|
|
*ppwszIn = pwszNext;
|
|
*pfSplit = fSplit;
|
|
return(pwszOut);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSSetSubjectTemplate(
|
|
IN WCHAR const *pwszzTemplate)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszz;
|
|
WCHAR const *pwszPropName;
|
|
SUBJECTTABLE const **ppSubject;
|
|
SUBJECTTABLE const **pps;
|
|
SUBJECTTABLE *pSubject;
|
|
DWORD dwIndex;
|
|
DWORD cchMax;
|
|
|
|
hr = E_INVALIDARG;
|
|
if (NULL == pwszzTemplate)
|
|
{
|
|
_JumpError(hr, error, "pwszzTemplate NULL");
|
|
}
|
|
ppSubject = pkcs_apSubject; // fill in this empty subject array with string matches
|
|
|
|
for (pwszz = pwszzTemplate; L'\0' != *pwszz; pwszz += wcslen(pwszz) + 1)
|
|
{
|
|
pwszPropName = PKCSMapAttributeName(pwszz, NULL, &dwIndex, &cchMax);
|
|
if (NULL == pwszPropName)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpErrorStr(hr, error, "PKCSMapAttributeName", pwszz);
|
|
}
|
|
|
|
for (pSubject = pkcs_subject; ; pSubject++)
|
|
{
|
|
if (NULL == pSubject->pwszPropName)
|
|
{
|
|
_JumpError(hr, error, "pkcs_subject lookup");
|
|
}
|
|
if (0 == mylstrcmpiS(pwszPropName, pSubject->pwszPropName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
for (pps = pkcs_apSubject; pps < ppSubject; pps++)
|
|
{
|
|
if (*pps == pSubject)
|
|
{
|
|
_JumpErrorStr(hr, error, "pkcs_subject duplicate", pwszz);
|
|
}
|
|
}
|
|
if (ppSubject >= &pkcs_apSubject[CSUBJECTTABLE])
|
|
{
|
|
_JumpError(hr, error, "pkcs_subject overflow");
|
|
}
|
|
pSubject->dwSubjectTemplateIndex = SAFE_SUBTRACT_POINTERS(
|
|
ppSubject,
|
|
pkcs_apSubject);
|
|
*ppSubject++ = pSubject;
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"Subject Template[%u] <== Subject[%u]: %hs -- %ws\n",
|
|
pSubject->dwSubjectTemplateIndex,
|
|
pSubject->dwSubjectTableIndex,
|
|
pSubject->pszObjId,
|
|
pSubject->pwszPropName));
|
|
}
|
|
CSASSERT(ppSubject <= &pkcs_apSubject[CSUBJECTTABLE]);
|
|
|
|
if (ppSubject == pkcs_apSubject)
|
|
{
|
|
_JumpError(hr, error, "pwszzTemplate empty");
|
|
}
|
|
pkcs_ppSubjectLast = ppSubject - 1;
|
|
pkcsfSubjectTemplate = TRUE;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSplitRDNComponents(
|
|
IN SUBJECTTABLE const *pSubjectTable,
|
|
IN OUT WCHAR *pwszRDN, // Parsing stomps string in-place
|
|
IN DWORD cAttrMax,
|
|
OUT DWORD *pcAttr,
|
|
OUT CERT_RDN_ATTR *rgAttr)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cAttr;
|
|
DWORD i;
|
|
DWORD cwc;
|
|
WCHAR *pwszRemain;
|
|
WCHAR const *pwszToken;
|
|
WCHAR *pwszT;
|
|
BOOL fSplit;
|
|
|
|
*pcAttr = 0;
|
|
cAttr = 0;
|
|
if (NULL != pwszRDN)
|
|
{
|
|
// Allocate memory for each RDN component filled in:
|
|
|
|
pwszRemain = pwszRDN;
|
|
for (;;)
|
|
{
|
|
pwszToken = PKCSSplitToken(
|
|
&pwszRemain,
|
|
wszNAMESEPARATORDEFAULT,
|
|
&fSplit);
|
|
if (NULL == pwszToken)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (cAttr >= cAttrMax)
|
|
{
|
|
hr = CERTSRV_E_BAD_REQUESTSUBJECT;
|
|
_JumpError(hr, error, "Subject RDN overflow");
|
|
}
|
|
|
|
cwc = wcslen(pwszToken);
|
|
if (g_fEnforceRDNNameLengths && cwc > pSubjectTable->cchMax)
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"RDN component too long: %u/%u: %ws[%u]=\"%ws\"\n",
|
|
cwc,
|
|
pSubjectTable->cchMax,
|
|
pSubjectTable->pwszPropName,
|
|
cAttr,
|
|
pwszToken));
|
|
hr = CERTSRV_E_BAD_REQUESTSUBJECT;
|
|
_JumpErrorStr(hr, error, "RDN component too long", pwszToken);
|
|
}
|
|
pwszT = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszT)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc(pwszToken)");
|
|
}
|
|
wcscpy(pwszT, pwszToken);
|
|
|
|
rgAttr[cAttr].pszObjId = (char *) pSubjectTable->pszObjId;
|
|
rgAttr[cAttr].dwValueType = CERT_RDN_ANY_TYPE; // 'best' encoding
|
|
rgAttr[cAttr].Value.pbData = (BYTE *) pwszT;
|
|
rgAttr[cAttr].Value.cbData = 0; // Indicate Unicode input
|
|
|
|
cAttr++;
|
|
}
|
|
}
|
|
*pcAttr = cAttr;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
for (i = 0; i < cAttr; i++)
|
|
{
|
|
LocalFree(rgAttr[i].Value.pbData);
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define CSUBJECTRDNMAX (4 * CSUBJECTTABLE)
|
|
|
|
HRESULT
|
|
pkcsEncodeSubjectName(
|
|
IN ICertDBRow *prow,
|
|
IN CERT_RDN_ATTR const *rgAttr,
|
|
IN DWORD cAttr,
|
|
OUT BYTE **ppbData,
|
|
OUT DWORD *pcbData)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD cbprop;
|
|
DWORD dwRequestFlags;
|
|
DWORD dwFlags;
|
|
CERT_RDN rgRDN[CSUBJECTRDNMAX];
|
|
CERT_NAME_INFO nameinfo;
|
|
|
|
CSASSERT(ARRAYSIZE(rgRDN) >= cAttr);
|
|
for (i = 0; i < cAttr; i++)
|
|
{
|
|
rgRDN[i].cRDNAttr = 1;
|
|
rgRDN[i].rgRDNAttr = (CERT_RDN_ATTR *) &rgAttr[i];
|
|
}
|
|
nameinfo.cRDN = cAttr;
|
|
nameinfo.rgRDN = rgRDN;
|
|
|
|
cbprop = sizeof(dwRequestFlags);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbprop,
|
|
(BYTE *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
CSASSERT(sizeof(dwRequestFlags) == cbprop);
|
|
dwFlags = 0;
|
|
if (CR_FLG_FORCETELETEX & dwRequestFlags)
|
|
{
|
|
dwFlags |= CERT_RDN_ENABLE_T61_UNICODE_FLAG;
|
|
}
|
|
if (CR_FLG_FORCEUTF8 & dwRequestFlags)
|
|
{
|
|
dwFlags |= CERT_RDN_ENABLE_UTF8_UNICODE_FLAG;
|
|
}
|
|
|
|
if (!myEncodeName(
|
|
X509_ASN_ENCODING,
|
|
&nameinfo,
|
|
dwFlags,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbData,
|
|
pcbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeName");
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildSubjectFromNamesTable(
|
|
IN ICertDBRow *prow,
|
|
OUT CERT_NAME_BLOB *pSubject)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbData = 0;
|
|
DWORD i;
|
|
DWORD cAttr;
|
|
DWORD cAttrT;
|
|
CERT_RDN_ATTR rgAttr[CSUBJECTRDNMAX];
|
|
SUBJECTTABLE const * const *ppSubject;
|
|
WCHAR *pwszData = NULL;
|
|
|
|
pSubject->pbData = NULL;
|
|
CSASSERT(NULL != pkcs_ppSubjectLast);
|
|
|
|
cAttr = 0;
|
|
for (
|
|
ppSubject = pkcs_ppSubjectLast;
|
|
ppSubject >= pkcs_apSubject;
|
|
ppSubject--)
|
|
{
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
(*ppSubject)->pwszPropName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cbData,
|
|
(BYTE **) &pwszData);
|
|
if (S_OK != hr)
|
|
{
|
|
continue;
|
|
}
|
|
if (0 != cbData)
|
|
{
|
|
// Allocates memory for each RDN component filled in:
|
|
|
|
hr = pkcsSplitRDNComponents(
|
|
*ppSubject,
|
|
pwszData,
|
|
ARRAYSIZE(rgAttr) - cAttr,
|
|
&cAttrT,
|
|
&rgAttr[cAttr]);
|
|
_JumpIfError(hr, error, "SplitRDNComponents");
|
|
|
|
cAttr += cAttrT;
|
|
}
|
|
LocalFree(pwszData);
|
|
pwszData = NULL;
|
|
}
|
|
|
|
// done building string of subject entries, time to encode
|
|
|
|
hr = pkcsEncodeSubjectName(
|
|
prow,
|
|
rgAttr,
|
|
cAttr,
|
|
&pSubject->pbData,
|
|
&pSubject->cbData);
|
|
_JumpIfError(hr, error, "pkcsEncodeSubjectName");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropSubjectRawName,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
pSubject->cbData,
|
|
pSubject->pbData);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
pkcsSetDistinguishedName(prow, PROPTABLE_CERTIFICATE, pSubject);
|
|
|
|
error:
|
|
for (i = 0; i < cAttr; i++)
|
|
{
|
|
LocalFree(rgAttr[i].Value.pbData);
|
|
}
|
|
if (NULL != pwszData)
|
|
{
|
|
LocalFree(pwszData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCheck7f(
|
|
IN ICertDBRow *prow,
|
|
IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
OUT BOOL *pfErrorLogged)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszSubject = NULL;
|
|
WCHAR const *pwszSubject2;
|
|
WCHAR wszDword[cwcDWORDSPRINTF];
|
|
WCHAR wszRequestId[cwcDWORDSPRINTF];
|
|
WORD cString = 0;
|
|
WCHAR const *apwsz[4];
|
|
|
|
DWORD State;
|
|
DWORD Index1;
|
|
DWORD Index2;
|
|
DWORD cwcField;
|
|
WCHAR wszField[128];
|
|
DWORD cwcObjectId;
|
|
WCHAR wszObjectId[128];
|
|
WCHAR const *pwszObjectIdDescription = NULL;
|
|
WCHAR *wszBuf = NULL;
|
|
const DWORD dwDefaultBufSize = 2048 * sizeof(WCHAR);
|
|
|
|
*pfErrorLogged = FALSE;
|
|
cwcField = sizeof(wszField)/sizeof(wszField[0]);
|
|
cwcObjectId = sizeof(wszObjectId)/sizeof(wszObjectId[0]);
|
|
hr = myCheck7f(
|
|
pbCert,
|
|
cbCert,
|
|
FALSE,
|
|
&State,
|
|
&Index1,
|
|
&Index2,
|
|
&cwcField,
|
|
wszField,
|
|
&cwcObjectId,
|
|
wszObjectId,
|
|
&pwszObjectIdDescription); // Static: do not free!
|
|
_JumpIfError(hr, error, "myCheck7f");
|
|
|
|
if (CHECK7F_NONE != State)
|
|
{
|
|
DWORD ReqId;
|
|
|
|
wszBuf = (WCHAR *) LocalAlloc(LMEM_FIXED, dwDefaultBufSize);
|
|
if (NULL == wszBuf)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
prow->GetRowId(&ReqId);
|
|
wsprintf(wszRequestId, L"%u", ReqId);
|
|
apwsz[cString++] = wszRequestId;
|
|
apwsz[cString++] = wszDword;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropSubjectDistinguishedName,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
NULL,
|
|
(BYTE **) &pwszSubject);
|
|
pwszSubject2 = pwszSubject;
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "GetProperty(DN)");
|
|
pwszSubject2 = g_pwszUnknownSubject;
|
|
}
|
|
apwsz[cString++] = pwszSubject2;
|
|
|
|
wcscpy(wszBuf, wszField);
|
|
if (0 != Index1)
|
|
{
|
|
wsprintf(
|
|
&wszBuf[wcslen(wszBuf)],
|
|
0 != Index2? L"[%u,%u]" : L"[%u]",
|
|
Index1 - 1,
|
|
Index2 - 1);
|
|
}
|
|
|
|
if (0 != cwcObjectId)
|
|
{
|
|
wcscat(wszBuf, L" ObjectId=");
|
|
wcscat(wszBuf, wszObjectId);
|
|
}
|
|
if (NULL != pwszObjectIdDescription)
|
|
{
|
|
// If buffer too small, reallocate enough space for old buffer,
|
|
// OID description, () and trailing zero
|
|
DWORD dwBufLen = (wcslen(wszBuf)+wcslen(pwszObjectIdDescription)+3)*
|
|
sizeof(WCHAR);
|
|
if (dwDefaultBufSize < dwBufLen)
|
|
{
|
|
WCHAR *pTempBuf = (WCHAR *) LocalReAlloc(
|
|
wszBuf,
|
|
dwBufLen,
|
|
LMEM_MOVEABLE);
|
|
if (NULL == pTempBuf)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalReAlloc");
|
|
}
|
|
wszBuf = pTempBuf;
|
|
}
|
|
wcscat(wszBuf, L" (");
|
|
wcscat(wszBuf, pwszObjectIdDescription);
|
|
wcscat(wszBuf, L")");
|
|
}
|
|
apwsz[cString++] = wszBuf;
|
|
|
|
hr = CERTSRV_E_ENCODING_LENGTH;
|
|
wsprintf(wszDword, L"0x%x", hr);
|
|
|
|
if (CERTLOG_ERROR <= g_dwLogLevel)
|
|
{
|
|
LogEvent(
|
|
EVENTLOG_ERROR_TYPE,
|
|
MSG_E_BADCERTLENGTHFIELD,
|
|
cString,
|
|
apwsz);
|
|
}
|
|
CONSOLEPRINT4((
|
|
DBG_SS_CERTSRV,
|
|
"CertSrv Request %u: rc=%x: Bad encoded length detected: %ws \"%ws\"\n",
|
|
ReqId,
|
|
hr,
|
|
wszBuf,
|
|
pwszSubject));
|
|
*pfErrorLogged = TRUE;
|
|
}
|
|
|
|
error:
|
|
if (NULL != pwszSubject)
|
|
{
|
|
LocalFree(pwszSubject);
|
|
}
|
|
if (NULL != wszBuf)
|
|
{
|
|
LocalFree(wszBuf);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCreateCertSerialNumber(
|
|
IN ICertDBRow *prow,
|
|
IN CACTX const *pCAContext,
|
|
OUT BSTR *pstrSerialNumber)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dw;
|
|
USHORT us;
|
|
BYTE abRandom[8];
|
|
BYTE abSerial[max(
|
|
sizeof(dw) + sizeof(us) + sizeof(dw),
|
|
sizeof(dw) + sizeof(us) + sizeof(abRandom) + sizeof(dw) + sizeof(BYTE))];
|
|
BSTR strHighSerial = NULL;
|
|
BSTR strSerialNumber = NULL;
|
|
DWORD cbSerial;
|
|
BYTE *pb;
|
|
BOOL fCritSecEntered = FALSE;
|
|
//#define TEST_SPECIAL_SERIAL_NUMBERS
|
|
#ifdef TEST_SPECIAL_SERIAL_NUMBERS
|
|
BOOL fAddZeroByte = FALSE;
|
|
#endif
|
|
|
|
*pstrSerialNumber = NULL;
|
|
pb = abSerial;
|
|
|
|
prow->GetRowId(&dw);
|
|
CopyMemory(pb, &dw, sizeof(dw));
|
|
pb += sizeof(dw);
|
|
|
|
us = (USHORT) pCAContext->iCert;
|
|
CopyMemory(pb, &us, sizeof(us));
|
|
pb += sizeof(us);
|
|
|
|
if (MAXDWORD == g_dwHighSerial)
|
|
{
|
|
EnterCriticalSection(&g_critsecCAXchg);
|
|
fCritSecEntered = TRUE;
|
|
|
|
if (NULL == g_pbHighSerial)
|
|
{
|
|
if (!CryptGenRandom(
|
|
g_pCAContextCurrent->hProvCA,
|
|
ARRAYSIZE(abRandom),
|
|
abRandom))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptGenRandom");
|
|
}
|
|
|
|
g_pbHighSerial = (BYTE *) LocalAlloc(LMEM_FIXED, sizeof(abRandom));
|
|
if (NULL == g_pbHighSerial)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
g_cbHighSerial = sizeof(abRandom);
|
|
CopyMemory(g_pbHighSerial, abRandom, sizeof(abRandom));
|
|
hr = MultiByteIntegerToBstr(
|
|
FALSE,
|
|
sizeof(abRandom),
|
|
abRandom,
|
|
&strHighSerial);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = mySetCertRegStrValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
g_wszRegHighSerial,
|
|
strHighSerial);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegStrValue", g_wszRegHighSerial);
|
|
|
|
g_dwHighSerial = 0;
|
|
}
|
|
if (fCritSecEntered)
|
|
{
|
|
LeaveCriticalSection(&g_critsecCAXchg);
|
|
fCritSecEntered = FALSE;
|
|
}
|
|
}
|
|
if (NULL != g_pbHighSerial)
|
|
{
|
|
DWORD cbLeft = sizeof(abSerial) - SAFE_SUBTRACT_POINTERS(pb, abSerial);
|
|
|
|
if (g_cbHighSerial > cbLeft)
|
|
{
|
|
g_cbHighSerial = cbLeft;
|
|
}
|
|
CopyMemory(pb, g_pbHighSerial, g_cbHighSerial);
|
|
pb += g_cbHighSerial;
|
|
}
|
|
else
|
|
if (0 != g_dwHighSerial)
|
|
{
|
|
if (!CryptGenRandom(
|
|
g_pCAContextCurrent->hProvCA,
|
|
ARRAYSIZE(abRandom),
|
|
abRandom))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptGenRandom");
|
|
memset(abRandom, g_dwHighSerial, sizeof(abRandom));
|
|
}
|
|
CopyMemory(pb, abRandom, sizeof(abRandom));
|
|
pb += sizeof(abRandom);
|
|
|
|
CopyMemory(pb, &dw, sizeof(dw));
|
|
pb += sizeof(dw);
|
|
|
|
*pb++ = (BYTE) g_dwHighSerial;
|
|
}
|
|
else
|
|
{
|
|
dw = GetTickCount();
|
|
CopyMemory(pb, &dw, sizeof(dw));
|
|
pb += sizeof(dw);
|
|
}
|
|
cbSerial = SAFE_SUBTRACT_POINTERS(pb, abSerial);
|
|
|
|
// Make sure the serial number doesn't overflow the buffer:
|
|
|
|
CSASSERT(sizeof(abSerial) >= cbSerial);
|
|
|
|
// IETF max serial number length is 20 bytes:
|
|
|
|
CSASSERT(20 >= cbSerial);
|
|
|
|
pb--;
|
|
if (0 == *pb)
|
|
{
|
|
*pb = 'a';
|
|
}
|
|
else if (0 == (0xf0 & *pb))
|
|
{
|
|
*pb |= 0x10; // make high nibble non-zero
|
|
}
|
|
*pb &= 0x7f; // Some clients can't handle negative serial numbers:
|
|
#ifdef TEST_SPECIAL_SERIAL_NUMBERS
|
|
if (1 & abSerial[0])
|
|
{
|
|
*pb |= 0x80; // Test negative serial numbers:
|
|
if (2 & abSerial[0])
|
|
{
|
|
*pb-- = 0; // Test high zero byte serial numbers:
|
|
*pb |= 0x80; // Test negative serial numbers:
|
|
fAddZeroByte = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hr = MultiByteIntegerToBstr(FALSE, cbSerial, abSerial, &strSerialNumber);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
#ifdef TEST_SPECIAL_SERIAL_NUMBERS
|
|
if (fAddZeroByte)
|
|
{
|
|
BSTR str = NULL;
|
|
|
|
str = SysAllocStringLen(NULL, 2 + wcslen(strSerialNumber));
|
|
if (NULL != str)
|
|
{
|
|
wcscpy(str, L"00");
|
|
wcscat(str, strSerialNumber);
|
|
SysFreeString(strSerialNumber);
|
|
strSerialNumber = str;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*pstrSerialNumber = strSerialNumber;
|
|
strSerialNumber = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (fCritSecEntered)
|
|
{
|
|
LeaveCriticalSection(&g_critsecCAXchg);
|
|
fCritSecEntered = FALSE;
|
|
}
|
|
if (NULL != strHighSerial)
|
|
{
|
|
SysFreeString(strHighSerial);
|
|
}
|
|
if (NULL != strSerialNumber)
|
|
{
|
|
SysFreeString(strSerialNumber);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSVerifySubjectRDN(
|
|
OPTIONAL IN ICertDBRow *prow,
|
|
IN OUT WCHAR const **ppwszPropertyName,
|
|
OPTIONAL IN WCHAR const *pwszPropertyValue,
|
|
OUT BOOL *pfSubjectDot)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwsz;
|
|
WCHAR const *pwszName = *ppwszPropertyName;
|
|
WCHAR wszPrefix[ARRAYSIZE(wszPROPSUBJECTDOT)];
|
|
SUBJECTTABLE const *pSubjectTable;
|
|
DWORD i;
|
|
DWORD cAttr = 0;
|
|
CERT_RDN_ATTR rgAttr[CSUBJECTRDNMAX];
|
|
WCHAR *pwszValue = NULL;
|
|
DWORD cbData;
|
|
BYTE *pbData = NULL;
|
|
|
|
hr = S_OK;
|
|
*pfSubjectDot = FALSE;
|
|
|
|
// Check to see if the request is for L"Subject.".
|
|
|
|
pwsz = wcschr(pwszName, L'.');
|
|
if (NULL != pwsz &&
|
|
SAFE_SUBTRACT_POINTERS(pwsz, pwszName) + 2 == ARRAYSIZE(wszPrefix))
|
|
{
|
|
pwsz++; // skip past L'.'
|
|
|
|
CopyMemory(
|
|
wszPrefix,
|
|
pwszName,
|
|
(SAFE_SUBTRACT_POINTERS(pwsz, pwszName) * sizeof(WCHAR)));
|
|
wszPrefix[ARRAYSIZE(wszPrefix) - 1] = L'\0';
|
|
|
|
if (0 == LSTRCMPIS(wszPrefix, wszPROPSUBJECTDOT))
|
|
{
|
|
pwszName = pwsz;
|
|
if (L'\0' == *pwszName)
|
|
{
|
|
*pfSubjectDot = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSubjectTable = NULL;
|
|
if (!*pfSubjectDot)
|
|
{
|
|
for (pSubjectTable = pkcs_subject; ; pSubjectTable++)
|
|
{
|
|
WCHAR const * const *ppwsz;
|
|
|
|
if (NULL == pSubjectTable->pwszPropName)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
// Check for matching full name without "Subject." prefix:
|
|
|
|
pwsz = wcschr(pSubjectTable->pwszPropName, L'.');
|
|
if (NULL != pwsz && 0 == mylstrcmpiS(pwszName, &pwsz[1]))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Check for matching OID or abbreviated name:
|
|
|
|
for (
|
|
ppwsz = pSubjectTable->apwszAttributeName;
|
|
NULL != *ppwsz;
|
|
ppwsz++)
|
|
{
|
|
if (0 == mylstrcmpiS(pwszName, *ppwsz))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL != *ppwsz)
|
|
{
|
|
*ppwszPropertyName = pSubjectTable->pwszPropName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// It's a valid Certificate Table Subject RDN. Call pkcsSplitRDNComponents
|
|
// to split the string into individual RDN components and optionally
|
|
// enforce each component is under the maximum length.
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"PKCSVerifySubjectRDN(%ws) --> '%ws'\n",
|
|
*ppwszPropertyName,
|
|
pwszName));
|
|
|
|
if (NULL != prow)
|
|
{
|
|
if (!*pfSubjectDot && NULL != pwszPropertyValue)
|
|
{
|
|
pwszValue = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(wcslen(pwszPropertyValue) + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszValue)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(pwszValue, pwszPropertyValue);
|
|
|
|
CSASSERT(NULL != pSubjectTable);
|
|
hr = pkcsSplitRDNComponents(
|
|
pSubjectTable,
|
|
pwszValue,
|
|
ARRAYSIZE(rgAttr),
|
|
&cAttr,
|
|
rgAttr);
|
|
_JumpIfError(hr, error, "SplitRDNComponents");
|
|
|
|
// Call myEncodeName merely to test for valid string data.
|
|
// Some RDN OIDs are restricted to IA5 strings.
|
|
|
|
hr = pkcsEncodeSubjectName(prow, rgAttr, cAttr, &pbData, &cbData);
|
|
_JumpIfError(hr, error, "pkcsEncodeSubjectName");
|
|
}
|
|
hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED);
|
|
_JumpIfError(hr, error, "PKCSSetRequestFlags");
|
|
}
|
|
|
|
error:
|
|
for (i = 0; i < cAttr; i++)
|
|
{
|
|
LocalFree(rgAttr[i].Value.pbData);
|
|
}
|
|
if (NULL != pbData)
|
|
{
|
|
LocalFree(pbData);
|
|
}
|
|
if (NULL != pwszValue)
|
|
{
|
|
LocalFree(pwszValue);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSDeleteAllSubjectRDNs(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD Flags)
|
|
{
|
|
HRESULT hr;
|
|
SUBJECTTABLE const *pSubjectTable;
|
|
|
|
for (pSubjectTable = pkcs_subject; ; pSubjectTable++)
|
|
{
|
|
if (NULL == pSubjectTable->pwszPropName)
|
|
{
|
|
break;
|
|
}
|
|
hr = prow->SetProperty(pSubjectTable->pwszPropName, Flags, 0, NULL);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifyEKUPolices(
|
|
IN CERT_CONTEXT const *pcc,
|
|
OPTIONAL IN WCHAR const *pwszzPolicies,
|
|
IN WCHAR const *pwszInvalidPoliciesPrefix,
|
|
OUT WCHAR **ppwszInvalidPolicies)
|
|
{
|
|
HRESULT hr;
|
|
CERT_ENHKEY_USAGE *pEKUs = NULL;
|
|
DWORD i;
|
|
WCHAR const *pwsz;
|
|
WCHAR *pwszObjId = NULL;
|
|
|
|
*ppwszInvalidPolicies = NULL;
|
|
if (NULL == pwszzPolicies)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
|
|
hr = myCertGetEnhancedKeyUsage(
|
|
pcc,
|
|
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
&pEKUs);
|
|
if (CRYPT_E_NOT_FOUND == hr)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
_JumpIfError(hr, error, "myCertGetEnhancedKeyUsage");
|
|
|
|
for (i = 0; i < pEKUs->cUsageIdentifier; i++)
|
|
{
|
|
CSASSERT(NULL == pwszObjId);
|
|
|
|
if (!myConvertSzToWsz(
|
|
&pwszObjId,
|
|
pEKUs->rgpszUsageIdentifier[i],
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(ObjId)");
|
|
}
|
|
for (pwsz = pwszzPolicies; ; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
if (L'\0' == *pwsz)
|
|
{
|
|
hr = myAppendString(pwszObjId, L", ", ppwszInvalidPolicies);
|
|
_JumpIfError(hr, error, "myAppendString");
|
|
|
|
hr = CERT_E_INVALID_POLICY;
|
|
_PrintErrorStr(hr, "Chain invalidates policy", pwszObjId);
|
|
break;
|
|
}
|
|
if (0 == lstrcmp(pwsz, pwszObjId))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
LocalFree(pwszObjId);
|
|
pwszObjId = NULL;
|
|
}
|
|
if (NULL != *ppwszInvalidPolicies)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = myPrependString(pwszInvalidPoliciesPrefix, L" ", ppwszInvalidPolicies);
|
|
_PrintIfError(hr2, "myPrependString");
|
|
}
|
|
_JumpIfErrorStr(
|
|
hr,
|
|
error,
|
|
"Chain invalidates policy",
|
|
*ppwszInvalidPolicies);
|
|
|
|
error:
|
|
if (NULL != pEKUs)
|
|
{
|
|
LocalFree(pEKUs);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsVerifyIssuedPolices(
|
|
IN CERT_CONTEXT const *pcc,
|
|
IN CHAR const *pszObjId,
|
|
OPTIONAL IN WCHAR const *pwszzPolicies,
|
|
IN WCHAR const *pwszInvalidPoliciesPrefix,
|
|
OUT WCHAR **ppwszInvalidPolicies)
|
|
{
|
|
HRESULT hr;
|
|
CERT_EXTENSION const *pExt;
|
|
CERT_POLICIES_INFO *pcpsi = NULL;
|
|
DWORD cb;
|
|
DWORD i;
|
|
WCHAR const *pwsz;
|
|
WCHAR *pwszObjId = NULL;
|
|
|
|
*ppwszInvalidPolicies = NULL;
|
|
if (NULL == pwszzPolicies)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
pExt = CertFindExtension(
|
|
pszObjId,
|
|
pcc->pCertInfo->cExtension,
|
|
pcc->pCertInfo->rgExtension);
|
|
if (NULL == pExt)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
goto error;
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_POLICIES,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcpsi,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
hr = S_OK;
|
|
for (i = 0; i < pcpsi->cPolicyInfo; i++)
|
|
{
|
|
CSASSERT(NULL == pwszObjId);
|
|
|
|
if (!myConvertSzToWsz(
|
|
&pwszObjId,
|
|
pcpsi->rgPolicyInfo[i].pszPolicyIdentifier,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertSzToWsz(ObjId)");
|
|
}
|
|
for (pwsz = pwszzPolicies; ; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
if (L'\0' == *pwsz)
|
|
{
|
|
hr = myAppendString(pwszObjId, L", ", ppwszInvalidPolicies);
|
|
_JumpIfError(hr, error, "myAppendString");
|
|
|
|
hr = CERT_E_INVALID_POLICY;
|
|
_PrintErrorStr(hr, "Chain invalidates policy", pwszObjId);
|
|
break;
|
|
}
|
|
if (0 == lstrcmp(pwsz, pwszObjId))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
LocalFree(pwszObjId);
|
|
pwszObjId = NULL;
|
|
}
|
|
if (NULL != *ppwszInvalidPolicies)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = myPrependString(pwszInvalidPoliciesPrefix, L" ", ppwszInvalidPolicies);
|
|
_PrintIfError(hr2, "myPrependString");
|
|
}
|
|
_JumpIfErrorStr(
|
|
hr,
|
|
error,
|
|
"Chain invalidates policy",
|
|
*ppwszInvalidPolicies);
|
|
|
|
error:
|
|
if (NULL != pcpsi)
|
|
{
|
|
LocalFree(pcpsi);
|
|
}
|
|
if (NULL != pwszObjId)
|
|
{
|
|
LocalFree(pwszObjId);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
int _cdecl
|
|
fnRDNSort(
|
|
IN VOID const *pvrdn1,
|
|
IN VOID const *pvrdn2)
|
|
{
|
|
CERT_RDN_ATTR const *prdna1 = &((CERT_RDN const *) pvrdn1)->rgRDNAttr[0];
|
|
CERT_RDN_ATTR const *prdna2 = &((CERT_RDN const *) pvrdn2)->rgRDNAttr[0];
|
|
int r;
|
|
|
|
r = strcmp(prdna1->pszObjId, prdna2->pszObjId);
|
|
if (0 == r)
|
|
{
|
|
r = prdna1->dwValueType - prdna2->dwValueType;
|
|
if (0 == r)
|
|
{
|
|
r = prdna1->Value.cbData - prdna2->Value.cbData;
|
|
if (0 == r)
|
|
{
|
|
r = mylstrcmpiL(
|
|
(WCHAR const *) prdna1->Value.pbData,
|
|
(WCHAR const *) prdna2->Value.pbData);
|
|
}
|
|
}
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsGetSortedName(
|
|
IN CERT_NAME_BLOB const *pName,
|
|
OUT WCHAR **ppwszName)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
CERT_NAME_INFO *pNameInfo = NULL;
|
|
CERT_NAME_INFO NameInfo;
|
|
CERT_RDN *prdn;
|
|
CERT_RDN_ATTR *rgrdna = NULL;
|
|
CERT_RDN_ATTR *prdna;
|
|
CERT_NAME_BLOB NameBlob;
|
|
DWORD cb;
|
|
|
|
*ppwszName = NULL;
|
|
NameInfo.rgRDN = NULL;
|
|
NameBlob.pbData = NULL;
|
|
if (!myDecodeName(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_NAME,
|
|
pName->pbData,
|
|
pName->cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pNameInfo,
|
|
&cb))
|
|
{
|
|
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeName");
|
|
}
|
|
NameInfo.cRDN = 0;
|
|
for (i = 0; i < pNameInfo->cRDN; i++)
|
|
{
|
|
NameInfo.cRDN += pNameInfo->rgRDN[i].cRDNAttr;
|
|
}
|
|
NameInfo.rgRDN = (CERT_RDN *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
NameInfo.cRDN * sizeof(NameInfo.rgRDN[0]));
|
|
if (NULL == NameInfo.rgRDN)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
rgrdna = (CERT_RDN_ATTR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
NameInfo.cRDN * sizeof(rgrdna[0]));
|
|
if (NULL == rgrdna)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
prdn = NameInfo.rgRDN;
|
|
prdna = rgrdna;
|
|
for (i = 0; i < pNameInfo->cRDN; i++)
|
|
{
|
|
CERT_RDN const *prdnT = &pNameInfo->rgRDN[i];
|
|
DWORD j;
|
|
|
|
for (j = 0; j < prdnT->cRDNAttr; j++)
|
|
{
|
|
prdn->cRDNAttr = 1;
|
|
prdn->rgRDNAttr = prdna;
|
|
*prdna = prdnT->rgRDNAttr[j];
|
|
prdn++;
|
|
prdna++;
|
|
}
|
|
}
|
|
qsort(NameInfo.rgRDN, NameInfo.cRDN, sizeof(NameInfo.rgRDN[0]), fnRDNSort);
|
|
|
|
if (!myEncodeName(
|
|
X509_ASN_ENCODING,
|
|
&NameInfo,
|
|
CERT_RDN_ENABLE_UTF8_UNICODE_FLAG,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&NameBlob.pbData,
|
|
&NameBlob.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeName");
|
|
}
|
|
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&NameBlob,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
ppwszName);
|
|
_JumpIfError(hr, error, "myCertNameToStr");
|
|
|
|
error:
|
|
if (NULL != NameBlob.pbData)
|
|
{
|
|
LocalFree(NameBlob.pbData);
|
|
}
|
|
if (NULL != NameInfo.rgRDN)
|
|
{
|
|
LocalFree(NameInfo.rgRDN);
|
|
}
|
|
if (NULL != rgrdna)
|
|
{
|
|
LocalFree(rgrdna);
|
|
}
|
|
if (NULL != pNameInfo)
|
|
{
|
|
LocalFree(pNameInfo);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsCompareNames(
|
|
IN CERT_NAME_BLOB const *pName1,
|
|
IN CERT_NAME_BLOB const *pName2,
|
|
OUT BOOL *pfMatch)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszName1 = NULL;
|
|
WCHAR *pwszName2 = NULL;
|
|
|
|
*pfMatch = FALSE;
|
|
|
|
hr = pkcsGetSortedName(pName1, &pwszName1);
|
|
_JumpIfError(hr, error, "pkcsGetSortedName");
|
|
|
|
hr = pkcsGetSortedName(pName2, &pwszName2);
|
|
_JumpIfError(hr, error, "pkcsGetSortedName");
|
|
|
|
if (0 == mylstrcmpiL(pwszName1, pwszName2))
|
|
{
|
|
*pfMatch = TRUE;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszName1)
|
|
{
|
|
LocalFree(pwszName1);
|
|
}
|
|
if (NULL != pwszName2)
|
|
{
|
|
LocalFree(pwszName2);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsEncodeSubjectCert(
|
|
IN ICertDBRow *prow,
|
|
IN CACTX const *pCAContext,
|
|
IN BOOL fCrossCert,
|
|
OUT BYTE **ppbEncoded, // CoTaskMem*
|
|
OUT DWORD *pcbEncoded,
|
|
OUT BOOL *pfErrorLogged,
|
|
OUT WCHAR **ppwszDispositionCreateCert)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
HRESULT hrValidate = S_OK;
|
|
BYTE *pbCertEncoded = NULL;
|
|
DWORD cbCertEncoded;
|
|
DWORD ExtFlags;
|
|
BSTR strSerialNumber = NULL;
|
|
IEnumCERTDBNAME *penum = NULL;
|
|
CERTDBNAME cdbn;
|
|
FILETIME ftNotBefore;
|
|
DWORD dwRequestFlags;
|
|
WCHAR *pwszSubject = NULL;
|
|
DWORD dwFlags;
|
|
|
|
CERT_INFO Cert;
|
|
CERT_EXTENSION *pExt = NULL;
|
|
DWORD cExt = INCREMENT_EXTENSIONS;
|
|
|
|
DWORD cbprop;
|
|
DWORD i;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
WCHAR *pwszzIssuancePolicies = NULL;
|
|
WCHAR *pwszzApplicationPolicies = NULL;
|
|
WCHAR *pwszInvalidIssuancePolicies = NULL;
|
|
WCHAR *pwszInvalidApplicationPolicies = NULL;
|
|
WCHAR *pwszExtendedErrorInfo = NULL;
|
|
CERT_TRUST_STATUS TrustStatus;
|
|
|
|
cdbn.pwszName = NULL;
|
|
*pfErrorLogged = FALSE;
|
|
*ppwszDispositionCreateCert = NULL;
|
|
|
|
// CERT
|
|
ZeroMemory(&Cert, sizeof(Cert));
|
|
pExt = (CERT_EXTENSION *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cExt * sizeof(*pExt));
|
|
if (NULL == pExt)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
Cert.dwVersion = CERT_V3;
|
|
|
|
hr = pkcsCreateCertSerialNumber(prow, pCAContext, &strSerialNumber);
|
|
_JumpIfError(hr, error, "pkcsCreateCertSerialNumber");
|
|
|
|
// convert to int
|
|
hr = WszToMultiByteInteger(
|
|
FALSE,
|
|
strSerialNumber,
|
|
&Cert.SerialNumber.cbData,
|
|
&Cert.SerialNumber.pbData);
|
|
_JumpIfError(hr, error, "WszToMultiByteInteger");
|
|
|
|
|
|
Cert.SignatureAlgorithm.pszObjId = pCAContext->pszObjIdSignatureAlgorithm;
|
|
Cert.Issuer = pCAContext->pccCA->pCertInfo->Subject;
|
|
|
|
|
|
cbprop = sizeof(Cert.NotBefore);
|
|
hr = prow->GetProperty(
|
|
g_wszPropCertificateNotBeforeDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
NULL,
|
|
&cbprop,
|
|
(BYTE *) &Cert.NotBefore);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
CSASSERT(sizeof(Cert.NotBefore) == cbprop);
|
|
|
|
cbprop = sizeof(Cert.NotAfter);
|
|
hr = prow->GetProperty(
|
|
g_wszPropCertificateNotAfterDate,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
NULL,
|
|
&cbprop,
|
|
(BYTE *) &Cert.NotAfter);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
CSASSERT(sizeof(Cert.NotAfter) == cbprop);
|
|
|
|
CSASSERT(NULL == Cert.Subject.pbData);
|
|
|
|
cbprop = sizeof(dwRequestFlags);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbprop,
|
|
(BYTE *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
if (!pkcsfSubjectTemplate ||
|
|
fCrossCert ||
|
|
((CRLF_REBUILD_MODIFIED_SUBJECT_ONLY & g_dwCRLFlags) &&
|
|
(CR_FLG_SUBJECTUNMODIFIED & dwRequestFlags)))
|
|
{
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropSubjectRawName,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
&Cert.Subject.cbData,
|
|
&Cert.Subject.pbData);
|
|
if (S_OK == hr &&
|
|
0 == Cert.Subject.cbData &&
|
|
NULL != Cert.Subject.pbData)
|
|
{
|
|
LocalFree(Cert.Subject.pbData);
|
|
Cert.Subject.pbData = NULL;
|
|
}
|
|
}
|
|
|
|
if (NULL == Cert.Subject.pbData)
|
|
{
|
|
hr = pkcsBuildSubjectFromNamesTable(prow, &Cert.Subject);
|
|
_JumpIfError(hr, error, "pkcsBuildSubjectFromNamesTable");
|
|
}
|
|
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&Cert.Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
&pwszSubject);
|
|
_JumpIfError(hr, error, "myCertNameToStr");
|
|
|
|
if (!fCrossCert)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = pkcsCompareNames(&Cert.Issuer, &Cert.Subject, &fMatch);
|
|
_JumpIfError(hr, error, "pkcsCompareNames");
|
|
|
|
if (fMatch)
|
|
{
|
|
hr = CERTSRV_E_BAD_REQUESTSUBJECT;
|
|
_JumpError(hr, error, "Subject string matches Issuer");
|
|
}
|
|
}
|
|
|
|
// if the subject is empty, Subject Alt Name extension must be critical:
|
|
|
|
if (L'\0' == *pwszSubject)
|
|
{
|
|
BYTE *pbAltName = NULL;
|
|
DWORD cbAltName;
|
|
|
|
hr = PropGetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
TEXT(szOID_SUBJECT_ALT_NAME2),
|
|
&ExtFlags,
|
|
&cbAltName,
|
|
&pbAltName);
|
|
_PrintIfErrorStr(hr, "PropGetExtension", TEXT(szOID_SUBJECT_ALT_NAME2));
|
|
|
|
// Empty subject: Require a non-empty, non-disabled Subject Alt Name 2
|
|
// extension. An empty extension with a single empty entry can be
|
|
// constructed in four bytes. We don't attempt to detect multiple
|
|
// empty name entries.
|
|
|
|
if (S_OK == hr &&
|
|
NULL != pbAltName &&
|
|
4 < cbAltName &&
|
|
0 == (EXTENSION_DISABLE_FLAG & ExtFlags))
|
|
{
|
|
if (0 == (EXTENSION_CRITICAL_FLAG & ExtFlags))
|
|
{
|
|
ExtFlags |= EXTENSION_CRITICAL_FLAG;
|
|
hr = PropSetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
TEXT(szOID_SUBJECT_ALT_NAME2),
|
|
ExtFlags,
|
|
cbAltName,
|
|
pbAltName);
|
|
}
|
|
LocalFree(pbAltName);
|
|
_JumpIfError(hr, error, "PropSetExtension");
|
|
}
|
|
else
|
|
{
|
|
hr = CERTSRV_E_BAD_REQUESTSUBJECT;
|
|
_JumpError(hr, error, "empty Subject+missing/disabled SubjectAltName");
|
|
}
|
|
}
|
|
|
|
hr = pkcsGetPublicKeyInfo(prow, &Cert.SubjectPublicKeyInfo);
|
|
_JumpIfError(hr, error, "pkcsGetPublicKeyInfo");
|
|
|
|
Cert.rgExtension = pExt;
|
|
i = 0;
|
|
|
|
hr = prow->EnumCertDBName(CIE_TABLE_EXTENSIONS, &penum);
|
|
_JumpIfError(hr, error, "EnumCertDBName");
|
|
|
|
hr = CERTSRV_E_PROPERTY_EMPTY;
|
|
for (;;)
|
|
{
|
|
ULONG celtFetched;
|
|
|
|
if (cExt == i)
|
|
{
|
|
CERT_EXTENSION *pExtT;
|
|
|
|
// reached max, increse size
|
|
cExt += INCREMENT_EXTENSIONS;
|
|
pExtT = (CERT_EXTENSION *) LocalReAlloc(
|
|
pExt,
|
|
cExt * sizeof(*pExt),
|
|
LMEM_ZEROINIT | LMEM_MOVEABLE);
|
|
if (NULL == pExtT)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalReAlloc");
|
|
}
|
|
pExt = pExtT;
|
|
Cert.rgExtension = pExt;
|
|
}
|
|
|
|
hr = penum->Next(1, &cdbn, &celtFetched);
|
|
if (S_FALSE == hr)
|
|
{
|
|
break;
|
|
}
|
|
_JumpIfError(hr, error, "Next");
|
|
|
|
CSASSERT(1 == celtFetched);
|
|
CSASSERT(NULL != cdbn.pwszName);
|
|
|
|
if (!myConvertWszToSz(
|
|
&Cert.rgExtension[i].pszObjId,
|
|
cdbn.pwszName,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "myConvertWszToSz(ExtObjId)");
|
|
}
|
|
|
|
hr = PropGetExtension(
|
|
prow,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER,
|
|
cdbn.pwszName,
|
|
&ExtFlags,
|
|
&Cert.rgExtension[i].Value.cbData,
|
|
&Cert.rgExtension[i].Value.pbData);
|
|
_JumpIfError(hr, error, "PropGetExtension");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRVI,
|
|
"pkcsEncodeSubjectCert: Ext=%ws, ExtFlags=%x, len=%x\n",
|
|
cdbn.pwszName,
|
|
ExtFlags,
|
|
Cert.rgExtension[i].Value.cbData));
|
|
|
|
Cert.rgExtension[i].fCritical =
|
|
(EXTENSION_CRITICAL_FLAG & ExtFlags)? TRUE : FALSE;
|
|
|
|
CoTaskMemFree(cdbn.pwszName);
|
|
cdbn.pwszName = NULL;
|
|
|
|
if (EXTENSION_DISABLE_FLAG & ExtFlags)
|
|
{
|
|
if (NULL != pExt[i].pszObjId)
|
|
{
|
|
LocalFree(pExt[i].pszObjId);
|
|
pExt[i].pszObjId = NULL;
|
|
}
|
|
if (NULL != pExt[i].Value.pbData)
|
|
{
|
|
LocalFree(pExt[i].Value.pbData);
|
|
pExt[i].Value.pbData = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
Cert.cExtension = i;
|
|
|
|
// encode the cert contents
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_TO_BE_SIGNED,
|
|
&Cert,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbCertEncoded,
|
|
&cbCertEncoded))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
|
|
// sign the cert, then encode the signed info
|
|
hr = myEncodeSignedContent(
|
|
pCAContext->hProvCA,
|
|
X509_ASN_ENCODING,
|
|
Cert.SignatureAlgorithm.pszObjId,
|
|
pbCertEncoded,
|
|
cbCertEncoded,
|
|
CERTLIB_USE_COTASKMEMALLOC,
|
|
ppbEncoded,
|
|
pcbEncoded); // use CoTaskMem*
|
|
_JumpIfError(hr, error, "myEncodeSignedContent");
|
|
|
|
pcc = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
*ppbEncoded,
|
|
*pcbEncoded);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
if (g_fCertEnrollCompatible)
|
|
{
|
|
hr = pkcsCheck7f(
|
|
prow,
|
|
*ppbEncoded,
|
|
*pcbEncoded,
|
|
pfErrorLogged);
|
|
if (S_OK != hr)
|
|
{
|
|
CoTaskMemFree(*ppbEncoded);
|
|
*ppbEncoded = NULL;
|
|
_JumpError(hr, error, "pkcsCheck7f");
|
|
}
|
|
}
|
|
|
|
ftNotBefore = pcc->pCertInfo->NotBefore;
|
|
myMakeExprDateTime(&ftNotBefore, g_dwClockSkewMinutes, ENUM_PERIOD_MINUTES);
|
|
|
|
dwFlags = g_dwVerifyCertFlags;
|
|
if (fCrossCert)
|
|
{
|
|
dwFlags |= CA_VERIFY_FLAGS_IGNORE_OFFLINE;
|
|
}
|
|
if (CRLF_IGNORE_INVALID_POLICIES & g_dwCRLFlags)
|
|
{
|
|
dwFlags |= CA_VERIFY_FLAGS_IGNORE_INVALID_POLICIES;
|
|
}
|
|
hr = myVerifyCertContextEx(
|
|
pcc, // pCert
|
|
dwFlags,
|
|
0, // dwmsTimeout
|
|
0, // cUsageOids
|
|
NULL, // apszUsageOids
|
|
0, // cIssuanceOids
|
|
NULL, // apszIssuanceOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
&ftNotBefore, // pft
|
|
NULL, // hAdditionalStore
|
|
NULL, // pfnCallback
|
|
NULL, // ppwszMissingIssuer
|
|
&pwszzIssuancePolicies,
|
|
&pwszzApplicationPolicies,
|
|
&pwszExtendedErrorInfo,
|
|
&TrustStatus);
|
|
_PrintIfError(hr, "myVerifyCertContextEx");
|
|
|
|
// Ignore old Crypt32 cross cert chain verification errors
|
|
|
|
if ((CERT_E_UNTRUSTEDROOT == hr || TRUST_E_CERT_SIGNATURE == hr) &&
|
|
(CRLF_IGNORE_CROSS_CERT_TRUST_ERROR & g_dwCRLFlags) &&
|
|
fCrossCert)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
hrValidate = hr;
|
|
|
|
if (S_OK == hrValidate)
|
|
{
|
|
hr = pkcsVerifyIssuedPolices(
|
|
pcc,
|
|
szOID_CERT_POLICIES,
|
|
pwszzIssuancePolicies,
|
|
g_pwszInvalidIssuancePolicies,
|
|
&pwszInvalidIssuancePolicies);
|
|
_PrintIfError(hr, "pkcsVerifyIssuedPolices");
|
|
if (CRYPT_E_NOT_FOUND == hr)
|
|
{
|
|
hr = S_OK; // ignore error if extension is not found
|
|
}
|
|
if (S_OK == hrValidate &&
|
|
0 == (CRLF_IGNORE_INVALID_POLICIES & g_dwCRLFlags) &&
|
|
(CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY & TrustStatus.dwInfoStatus))
|
|
{
|
|
hrValidate = hr;
|
|
}
|
|
|
|
hr = pkcsVerifyIssuedPolices(
|
|
pcc,
|
|
szOID_APPLICATION_CERT_POLICIES,
|
|
pwszzApplicationPolicies,
|
|
g_pwszInvalidApplicationPolicies,
|
|
&pwszInvalidApplicationPolicies);
|
|
_PrintIfError(hr, "pkcsVerifyIssuedPolices(szOID_APPLICATION_CERT_POLICIES)");
|
|
if (CRYPT_E_NOT_FOUND == hr)
|
|
{
|
|
// application policies extension empty, fail over to EKU
|
|
hr = pkcsVerifyEKUPolices(
|
|
pcc,
|
|
pwszzApplicationPolicies,
|
|
g_pwszInvalidApplicationPolicies,
|
|
&pwszInvalidApplicationPolicies);
|
|
_PrintIfError(hr, "pkcsVerifyIssuedPolices(szOID_ENHANCED_KEY_USAGE)");
|
|
}
|
|
if (S_OK == hrValidate &&
|
|
0 == (CRLF_IGNORE_INVALID_POLICIES & g_dwCRLFlags))
|
|
{
|
|
hrValidate = hr;
|
|
}
|
|
}
|
|
if (S_OK != hrValidate)
|
|
{
|
|
if (0 == (CRLF_SAVE_FAILED_CERTS & g_dwCRLFlags))
|
|
{
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
hr = pkcsSetCertAndKeyHashes(prow, pcc);
|
|
_JumpIfError(hr, error, "pkcsSetCertAndKeyHashes");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateIssuerNameID,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
sizeof(pCAContext->NameId),
|
|
(BYTE *) &pCAContext->NameId);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateSerialNumber,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE *) strSerialNumber);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
|
|
#ifdef TEST_SPECIAL_SERIAL_NUMBERS
|
|
if (L'0' == strSerialNumber[0] && L'0' == strSerialNumber[1])
|
|
{
|
|
hr = prow->SetProperty(
|
|
g_wszPropCertificateSerialNumber,
|
|
PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
MAXDWORD,
|
|
(BYTE *) &strSerialNumber[2]);
|
|
_JumpIfError(hr, error, "SetProperty");
|
|
}
|
|
#endif
|
|
|
|
error:
|
|
if (S_OK != hrValidate)
|
|
{
|
|
hr = hrValidate;
|
|
}
|
|
*ppwszDispositionCreateCert = pwszInvalidIssuancePolicies;
|
|
pwszInvalidIssuancePolicies = NULL;
|
|
if (NULL != pwszInvalidApplicationPolicies)
|
|
{
|
|
hr2 = myAppendString(
|
|
pwszInvalidApplicationPolicies,
|
|
L" ",
|
|
ppwszDispositionCreateCert);
|
|
_PrintIfError(hr2, "myAppendString");
|
|
}
|
|
if (NULL != pwszExtendedErrorInfo)
|
|
{
|
|
hr2 = myAppendString(
|
|
pwszExtendedErrorInfo,
|
|
L" ",
|
|
ppwszDispositionCreateCert);
|
|
_PrintIfError(hr2, "myAppendString");
|
|
}
|
|
if (NULL != pwszSubject)
|
|
{
|
|
LocalFree(pwszSubject);
|
|
}
|
|
if (NULL != pwszzIssuancePolicies)
|
|
{
|
|
LocalFree(pwszzIssuancePolicies);
|
|
}
|
|
if (NULL != pwszzApplicationPolicies)
|
|
{
|
|
LocalFree(pwszzApplicationPolicies);
|
|
}
|
|
if (NULL != pwszInvalidIssuancePolicies)
|
|
{
|
|
LocalFree(pwszInvalidIssuancePolicies);
|
|
}
|
|
if (NULL != pwszInvalidApplicationPolicies)
|
|
{
|
|
LocalFree(pwszInvalidApplicationPolicies);
|
|
}
|
|
if (NULL != pwszExtendedErrorInfo)
|
|
{
|
|
LocalFree(pwszExtendedErrorInfo);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
if (NULL != pExt)
|
|
{
|
|
i = 0;
|
|
if (NULL != cdbn.pwszName)
|
|
{
|
|
CoTaskMemFree(cdbn.pwszName);
|
|
}
|
|
while (cExt != i)
|
|
{
|
|
if (NULL != pExt[i].pszObjId)
|
|
{
|
|
LocalFree(pExt[i].pszObjId);
|
|
}
|
|
if (NULL != pExt[i].Value.pbData)
|
|
{
|
|
LocalFree(pExt[i].Value.pbData);
|
|
}
|
|
i++;
|
|
}
|
|
LocalFree(pExt);
|
|
}
|
|
if (NULL != penum)
|
|
{
|
|
penum->Release();
|
|
}
|
|
if (NULL != Cert.SerialNumber.pbData)
|
|
{
|
|
LocalFree(Cert.SerialNumber.pbData);
|
|
}
|
|
if (NULL != Cert.Subject.pbData)
|
|
{
|
|
LocalFree(Cert.Subject.pbData);
|
|
}
|
|
pkcsFreePublicKeyInfo(&Cert.SubjectPublicKeyInfo);
|
|
if (NULL != pbCertEncoded)
|
|
{
|
|
LocalFree(pbCertEncoded);
|
|
}
|
|
if (NULL != strSerialNumber)
|
|
{
|
|
SysFreeString(strSerialNumber);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsFreeCRLChain(
|
|
IN DWORD cCert,
|
|
OPTIONAL IN OUT CERT_BLOB *prgCertBlob,
|
|
OPTIONAL IN OUT CERT_CONTEXT const **rgCert,
|
|
IN DWORD cCRL,
|
|
OPTIONAL IN OUT CRL_BLOB *rgCRLBlob,
|
|
OPTIONAL IN OUT CRL_CONTEXT const **rgCRL)
|
|
{
|
|
DWORD i;
|
|
|
|
if (NULL != prgCertBlob)
|
|
{
|
|
LocalFree(prgCertBlob);
|
|
}
|
|
if (NULL != rgCert)
|
|
{
|
|
for (i = 0; i < cCert; i++)
|
|
{
|
|
if (NULL != rgCert[i])
|
|
{
|
|
CertFreeCertificateContext(rgCert[i]);
|
|
}
|
|
}
|
|
LocalFree(rgCert);
|
|
}
|
|
if (NULL != rgCRLBlob)
|
|
{
|
|
LocalFree(rgCRLBlob);
|
|
}
|
|
if (NULL != rgCRL)
|
|
{
|
|
for (i = 0; i < cCRL; i++)
|
|
{
|
|
if (NULL != rgCRL[i])
|
|
{
|
|
CertFreeCRLContext(rgCRL[i]);
|
|
}
|
|
}
|
|
LocalFree(rgCRL);
|
|
}
|
|
}
|
|
|
|
|
|
// Build the CA's cert chain and collect all paremt CA CRLs.
|
|
// Add in the optional passed leaf cert and the CA's CRLs.
|
|
// This ensures that the chain includes at least this CA's correct cert & CRLs.
|
|
|
|
|
|
HRESULT
|
|
pkcsBuildCRLChain(
|
|
OPTIONAL IN CACTX *pCAContext,
|
|
OPTIONAL IN BYTE const *pbCertLeaf,
|
|
IN DWORD cbCertLeaf,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT DWORD *pcCert,
|
|
OPTIONAL OUT CERT_BLOB **prgCertBlob,
|
|
OUT CERT_CONTEXT const ***prgCert,
|
|
OUT DWORD *pcCRLBlob,
|
|
OPTIONAL OUT CRL_BLOB **prgCRLBlob,
|
|
OUT CRL_CONTEXT const ***prgCRL)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CHAIN_PARA ChainParams;
|
|
CERT_CHAIN_CONTEXT const *pChainContext = NULL;
|
|
DWORD cElement;
|
|
CERT_CHAIN_ELEMENT **rgpElement;
|
|
DWORD cCert = 0;
|
|
CERT_CONTEXT const **rgpCert = NULL;
|
|
CERT_CONTEXT const *pccCertLeaf = NULL;
|
|
CERT_BLOB *rgCertBlob = NULL;
|
|
DWORD cCRL = 0;
|
|
CRL_CONTEXT const **rgpCRL = NULL;
|
|
CRL_BLOB *rgCRLBlob = NULL;
|
|
DWORD i;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
|
|
if (NULL != prgCertBlob)
|
|
{
|
|
*prgCertBlob = NULL;
|
|
}
|
|
*prgCert = NULL;
|
|
if (NULL != prgCRLBlob)
|
|
{
|
|
*prgCRLBlob = NULL;
|
|
}
|
|
*prgCRL = NULL;
|
|
|
|
if (NULL != pbCertLeaf)
|
|
{
|
|
pccCertLeaf = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbCertLeaf,
|
|
cbCertLeaf);
|
|
if (NULL == pccCertLeaf)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
}
|
|
CSASSERT(NULL != pCAContext || NULL != pccCertLeaf);
|
|
if (NULL == pCAContext || fIncludeCRLs)
|
|
{
|
|
// Get the CA cert chain and parent CA CRLs:
|
|
|
|
ZeroMemory(&ChainParams, sizeof(ChainParams));
|
|
ChainParams.cbSize = sizeof(ChainParams);
|
|
//ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
|
|
//ChainParams.RequestedUsage.Usage.cUsageIdentifier = 0;
|
|
//ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
NULL != pCAContext?
|
|
pCAContext->pccCA : pccCertLeaf,
|
|
NULL, // pTime
|
|
NULL, // hAdditionalStore
|
|
&ChainParams, // pChainPara
|
|
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
|
|
NULL, // pvReserved
|
|
&pChainContext)) // ppChainContext
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
if (0 == pChainContext->cChain ||
|
|
0 == pChainContext->rgpChain[0]->cElement)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "No chain");
|
|
}
|
|
cElement = pChainContext->rgpChain[0]->cElement;
|
|
rgpElement = pChainContext->rgpChain[0]->rgpElement;
|
|
}
|
|
else
|
|
{
|
|
cElement = pCAContext->cCACertChain;
|
|
rgpElement = NULL;
|
|
}
|
|
cCert = cElement;
|
|
cCRL = 2 * (cCert + 1); // Worst case. *Always* include this CA's CRLs
|
|
if (NULL != pbCertLeaf)
|
|
{
|
|
cCert++;
|
|
}
|
|
rgpCert = (CERT_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cCert * sizeof(rgpCert[0]));
|
|
if (NULL == rgpCert)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
if (fIncludeCRLs)
|
|
{
|
|
rgpCRL = (CRL_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cCRL * sizeof(rgpCRL[0]));
|
|
if (NULL == rgpCRL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
|
|
iCert = 0;
|
|
iCRL = 0;
|
|
|
|
// Add parent CA certs and CRLs:
|
|
|
|
if (NULL == pCAContext || fIncludeCRLs)
|
|
{
|
|
for (i = 0; i < cElement; i++)
|
|
{
|
|
CSASSERT(NULL != rgpElement);
|
|
CERT_CHAIN_ELEMENT const *pElement = rgpElement[i];
|
|
CERT_REVOCATION_INFO *pRevocationInfo;
|
|
|
|
rgpCert[iCert] = CertDuplicateCertificateContext(
|
|
pElement->pCertContext);
|
|
if (NULL != rgpCert[iCert])
|
|
{
|
|
iCert++;
|
|
}
|
|
pRevocationInfo = pElement->pRevocationInfo;
|
|
|
|
if (fIncludeCRLs &&
|
|
NULL != pRevocationInfo &&
|
|
CCSIZEOF_STRUCT(CERT_REVOCATION_INFO, pCrlInfo) <=
|
|
pRevocationInfo->cbSize &&
|
|
NULL != pRevocationInfo->pCrlInfo)
|
|
{
|
|
CERT_REVOCATION_CRL_INFO *pCrlInfo;
|
|
|
|
pCrlInfo = pRevocationInfo->pCrlInfo;
|
|
if (NULL != pCrlInfo)
|
|
{
|
|
if (NULL != pCrlInfo->pBaseCrlContext)
|
|
{
|
|
rgpCRL[iCRL] = CertDuplicateCRLContext(
|
|
pCrlInfo->pBaseCrlContext);
|
|
if (NULL != rgpCRL[iCRL])
|
|
{
|
|
iCRL++;
|
|
}
|
|
}
|
|
if (NULL != pCrlInfo->pDeltaCrlContext)
|
|
{
|
|
rgpCRL[iCRL] = CertDuplicateCRLContext(
|
|
pCrlInfo->pDeltaCrlContext);
|
|
if (NULL != rgpCRL[iCRL])
|
|
{
|
|
iCRL++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < pCAContext->cCACertChain; i++)
|
|
{
|
|
rgpCert[iCert] = CertDuplicateCertificateContext(
|
|
pCAContext->apCACertChain[i]);
|
|
if (NULL != rgpCert[iCert])
|
|
{
|
|
iCert++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != pCAContext)
|
|
{
|
|
// Add issued cert at the end -- optional Leaf cert:
|
|
|
|
if (NULL != pbCertLeaf)
|
|
{
|
|
for (i = 0; i < iCert; i++)
|
|
{
|
|
if (cbCertLeaf == rgpCert[i]->cbCertEncoded &&
|
|
0 == memcmp(
|
|
pbCertLeaf,
|
|
rgpCert[i]->pbCertEncoded,
|
|
cbCertLeaf))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == iCert) // if not found in existing array
|
|
{
|
|
rgpCert[iCert] = CertDuplicateCertificateContext(pccCertLeaf);
|
|
if (NULL != rgpCert[iCert])
|
|
{
|
|
iCert++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add current CA's Base and delta CRLs:
|
|
|
|
if (fIncludeCRLs)
|
|
{
|
|
hr = CRLGetCRL(
|
|
pCAContext->iKey,
|
|
FALSE, // fDelta
|
|
&rgpCRL[iCRL],
|
|
NULL); // pdwCRLPublishFlags
|
|
_JumpIfError(hr, error, "CRLGetCRL(base)"); // Base CRL must exist
|
|
|
|
iCRL++;
|
|
|
|
hr = CRLGetCRL(
|
|
pCAContext->iKey,
|
|
TRUE, // fDelta
|
|
&rgpCRL[iCRL],
|
|
NULL); // pdwCRLPublishFlags
|
|
_PrintIfError(hr, "CRLGetCRL(delta)"); // Delta CRL might not exist
|
|
if (S_OK == hr)
|
|
{
|
|
iCRL++;
|
|
}
|
|
}
|
|
}
|
|
CSASSERT(iCert <= cCert);
|
|
CSASSERT(iCRL <= cCRL);
|
|
|
|
if (NULL != prgCertBlob)
|
|
{
|
|
rgCertBlob = (CERT_BLOB *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
iCert * sizeof(rgCertBlob[0]));
|
|
if (NULL == rgCertBlob)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
for (i = 0; i < iCert; i++)
|
|
{
|
|
rgCertBlob[i].cbData = rgpCert[i]->cbCertEncoded;
|
|
rgCertBlob[i].pbData = rgpCert[i]->pbCertEncoded;
|
|
}
|
|
}
|
|
if (NULL != prgCRLBlob && 0 != iCRL)
|
|
{
|
|
rgCRLBlob = (CERT_BLOB *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
iCRL * sizeof(rgCRLBlob[0]));
|
|
if (NULL == rgCRLBlob)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
for (i = 0; i < iCRL; i++)
|
|
{
|
|
rgCRLBlob[i].cbData = rgpCRL[i]->cbCrlEncoded;
|
|
rgCRLBlob[i].pbData = rgpCRL[i]->pbCrlEncoded;
|
|
}
|
|
}
|
|
*pcCert = iCert;
|
|
*prgCert = rgpCert;
|
|
rgpCert = NULL;
|
|
if (NULL != prgCertBlob)
|
|
{
|
|
*prgCertBlob = rgCertBlob;
|
|
rgCertBlob = NULL;
|
|
}
|
|
|
|
*pcCRLBlob = iCRL;
|
|
*prgCRL = rgpCRL;
|
|
rgpCRL = NULL;
|
|
if (NULL != prgCRLBlob)
|
|
{
|
|
*prgCRLBlob = rgCRLBlob;
|
|
rgCRLBlob = NULL;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
pkcsFreeCRLChain(cCert, rgCertBlob, rgpCert, cCRL, rgCRLBlob, rgpCRL);
|
|
if (NULL != pccCertLeaf)
|
|
{
|
|
CertFreeCertificateContext(pccCertLeaf);
|
|
}
|
|
if (NULL != pChainContext)
|
|
{
|
|
CertFreeCertificateChain(pChainContext);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsRetrieveKeyHashFromRequest(
|
|
IN ICertDBRow *prow,
|
|
OUT BYTE **ppbKeyHashOut,
|
|
OUT DWORD *pcbKeyHashOut)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTMSG hMsg = NULL;
|
|
char *pszInnerContentObjId = NULL;
|
|
BYTE *pbRequest = NULL;
|
|
DWORD cbRequest;
|
|
BYTE *pbContent = NULL;
|
|
DWORD cbContent;
|
|
CMC_ADD_ATTRIBUTES_INFO *pcmcAttrib = NULL;
|
|
DWORD cb;
|
|
CRYPT_ATTRIBUTE const *pAttrib;
|
|
CRYPT_ATTRIBUTE const *pAttribEnd;
|
|
CRYPT_DATA_BLOB *pBlob = NULL;
|
|
CMC_DATA_INFO *pcmcData = NULL;
|
|
DWORD i;
|
|
DWORD DataReference = MAXDWORD; // nested CMC message Body Part Id
|
|
DWORD CertReference = MAXDWORD; // PKCS10 Cert Request Body Part Id
|
|
|
|
*ppbKeyHashOut = NULL;
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRequestRawRequest,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
&cbRequest,
|
|
(BYTE **) &pbRequest);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(xchg cert)");
|
|
|
|
hr = myDecodePKCS7(
|
|
pbRequest,
|
|
cbRequest,
|
|
&pbContent,
|
|
&cbContent,
|
|
NULL, // pdwMsgType
|
|
&pszInnerContentObjId,
|
|
NULL, // pcSigner
|
|
NULL, // pcRecipient
|
|
NULL, // phStore
|
|
&hMsg);
|
|
_JumpIfError(hr, error, "myDecodePKCS7");
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_DATA,
|
|
pbContent,
|
|
cbContent,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcmcData,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
if (1 == pcmcData->cTaggedRequest)
|
|
{
|
|
CertReference = pcmcData->rgTaggedRequest[0].pTaggedCertRequest->dwBodyPartID;
|
|
}
|
|
// Process extensions and attributes
|
|
|
|
for (i = 0; i < pcmcData->cTaggedAttribute; i++)
|
|
{
|
|
if (0 != strcmp(
|
|
szOID_CMC_ADD_ATTRIBUTES,
|
|
pcmcData->rgTaggedAttribute[i].Attribute.pszObjId))
|
|
{
|
|
continue;
|
|
}
|
|
// Decode CMC_ADD_ATTRIBUTES_INFO from Attribute Blob
|
|
|
|
if (NULL != pcmcAttrib)
|
|
{
|
|
LocalFree(pcmcAttrib);
|
|
pcmcAttrib = NULL;
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_ADD_ATTRIBUTES,
|
|
pcmcData->rgTaggedAttribute[i].Attribute.rgValue[0].pbData,
|
|
pcmcData->rgTaggedAttribute[i].Attribute.rgValue[0].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pcmcAttrib,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
if (!pkcsCMCReferenceMatch(
|
|
DataReference,
|
|
CertReference,
|
|
pcmcAttrib->dwCmcDataReference,
|
|
pcmcAttrib->cCertReference,
|
|
pcmcAttrib->rgdwCertReference))
|
|
{
|
|
continue;
|
|
}
|
|
pAttrib = pcmcAttrib->rgAttribute;
|
|
if (NULL == pAttrib)
|
|
{
|
|
continue;
|
|
}
|
|
pAttribEnd = &pAttrib[pcmcAttrib->cAttribute];
|
|
for ( ; pAttrib < pAttribEnd; pAttrib++)
|
|
{
|
|
if (0 != strcmp(pAttrib->pszObjId, szOID_ENCRYPTED_KEY_HASH))
|
|
{
|
|
continue;
|
|
}
|
|
if (NULL != pBlob)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Multiple encrypted key hashes");
|
|
}
|
|
if (1 != pAttrib->cValue)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Attribute Value count != 1");
|
|
}
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
pAttrib->rgValue[0].pbData,
|
|
pAttrib->rgValue[0].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pBlob,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
*ppbKeyHashOut = (BYTE *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
pBlob->cbData);
|
|
if (NULL == *ppbKeyHashOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(*ppbKeyHashOut, pBlob->pbData, pBlob->cbData);
|
|
*pcbKeyHashOut = pBlob->cbData;
|
|
}
|
|
}
|
|
if (NULL == pBlob)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "No encrypted key hash");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pBlob)
|
|
{
|
|
LocalFree(pBlob);
|
|
}
|
|
if (NULL != pcmcData)
|
|
{
|
|
LocalFree(pcmcData);
|
|
}
|
|
if (NULL != pcmcAttrib)
|
|
{
|
|
LocalFree(pcmcAttrib);
|
|
}
|
|
if (NULL != pbContent)
|
|
{
|
|
LocalFree(pbContent);
|
|
}
|
|
if (NULL != pbRequest)
|
|
{
|
|
LocalFree(pbRequest);
|
|
}
|
|
if (NULL != pszInnerContentObjId)
|
|
{
|
|
LocalFree(pszInnerContentObjId);
|
|
}
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Build a PKCS7 CMC response
|
|
|
|
HRESULT
|
|
PKCSEncodeFullResponse(
|
|
OPTIONAL IN ICertDBRow *prow,
|
|
IN CERTSRV_RESULT_CONTEXT *pResult,
|
|
IN HRESULT hrRequest,
|
|
IN WCHAR *pwszDispositionString,
|
|
OPTIONAL IN CACTX *pCAContext,
|
|
OPTIONAL IN BYTE const *pbCertLeaf,
|
|
IN DWORD cbCertLeaf,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT BYTE **ppbResponse, // CoTaskMem*
|
|
OUT DWORD *pcbResponse)
|
|
{
|
|
HRESULT hr;
|
|
CMC_RESPONSE_INFO Response;
|
|
CMC_STATUS_INFO Status;
|
|
BYTE *pbContent = NULL;
|
|
DWORD cbContent;
|
|
DWORD dwBodyPartIdOfRequest = 1;
|
|
DWORD dwCMCDataReference = 0;
|
|
DWORD dwBodyPartId = 1;
|
|
CMC_TAGGED_ATTRIBUTE aTaggedAttribute[5];
|
|
DWORD ita = 0;
|
|
CRYPT_ATTRIBUTE aAttr[2];
|
|
DWORD iAttr = 0;
|
|
CRYPT_ATTR_BLOB aAttrBlob[7];
|
|
DWORD iblob = 0;
|
|
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
|
|
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
|
|
CMC_PEND_INFO PendInfo;
|
|
DWORD ReqId;
|
|
DWORD dwRequestFlags;
|
|
DWORD cb;
|
|
DWORD i;
|
|
HCRYPTMSG hMsg = NULL;
|
|
CERT_CONTEXT const **prgCert = NULL;
|
|
CRL_CONTEXT const **prgCRL = NULL;
|
|
CHAR szNonce[(11 + 1) + (8 + 1) * 3];
|
|
|
|
ZeroMemory(aAttrBlob, sizeof(aAttrBlob));
|
|
ZeroMemory(&Status, sizeof(Status));
|
|
ZeroMemory(&Response, sizeof(Response));
|
|
|
|
ZeroMemory(&SignedMsgEncodeInfo, sizeof(SignedMsgEncodeInfo));
|
|
SignedMsgEncodeInfo.cbSize = sizeof(SignedMsgEncodeInfo);
|
|
SignedMsgEncodeInfo.cSigners = 1;
|
|
SignedMsgEncodeInfo.rgSigners = &SignerEncodeInfo;
|
|
//SignedMsgEncodeInfo.cCertEncoded = 0;
|
|
//SignedMsgEncodeInfo.rgCertEncoded = NULL;
|
|
//SignedMsgEncodeInfo.cCrlEncoded = 0;
|
|
//SignedMsgEncodeInfo.rgCrlEncoded = NULL;
|
|
|
|
Status.cBodyList = 1;
|
|
Status.dwOtherInfoChoice = CMC_OTHER_INFO_NO_CHOICE;
|
|
Status.rgdwBodyList = &dwBodyPartIdOfRequest;
|
|
Status.pwszStatusString = pwszDispositionString;
|
|
|
|
switch (*pResult->pdwDisposition)
|
|
{
|
|
case CR_DISP_ISSUED:
|
|
case CR_DISP_ISSUED_OUT_OF_BAND:
|
|
case CR_DISP_REVOKED: // map revoked to CMC_STATUS_FAILED?
|
|
Status.dwStatus = CMC_STATUS_SUCCESS;
|
|
break;
|
|
|
|
case CR_DISP_UNDER_SUBMISSION:
|
|
Status.dwStatus = CMC_STATUS_PENDING;
|
|
Status.dwOtherInfoChoice = CMC_OTHER_INFO_PEND_CHOICE;
|
|
Status.pPendInfo = &PendInfo;
|
|
|
|
CSASSERT(NULL != prow);
|
|
prow->GetRowId(&ReqId);
|
|
|
|
PendInfo.PendToken.cbData = sizeof(ReqId);
|
|
PendInfo.PendToken.pbData = (BYTE *) &ReqId;
|
|
|
|
cb = sizeof(PendInfo.PendTime);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestSubmittedWhen,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &PendInfo.PendTime);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
break;
|
|
|
|
//case CR_DISP_INCOMPLETE:
|
|
//case CR_DISP_ERROR:
|
|
//case CR_DISP_DENIED:
|
|
default:
|
|
Status.dwStatus = CMC_STATUS_FAILED;
|
|
if (NULL != prow)
|
|
{
|
|
cb = sizeof(hrRequest);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestStatusCode,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &hrRequest);
|
|
_JumpIfError(hr, error, "GetProperty(status code)");
|
|
}
|
|
switch (hrRequest)
|
|
{
|
|
case CERTSRV_E_BAD_REQUESTSUBJECT:
|
|
Status.dwFailInfo = CMC_FAIL_BAD_REQUEST;
|
|
Status.dwOtherInfoChoice = CMC_OTHER_INFO_FAIL_CHOICE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Encode control attributes for Status, Transaction Id, Sender and
|
|
// Recipient Nonces and Issued Cert Hash.
|
|
|
|
ZeroMemory(aTaggedAttribute, sizeof(aTaggedAttribute));
|
|
|
|
// Status:
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_STATUS,
|
|
&Status,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
|
|
aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++;
|
|
aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_STATUS_INFO;
|
|
aTaggedAttribute[ita].Attribute.cValue = 1;
|
|
aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
ita++;
|
|
|
|
// Transaction Id:
|
|
|
|
if (pResult->fTransactionId)
|
|
{
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
&pResult->dwTransactionId,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++;
|
|
aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_TRANSACTION_ID;
|
|
aTaggedAttribute[ita].Attribute.cValue = 1;
|
|
aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
ita++;
|
|
}
|
|
|
|
if (NULL != pResult->pbSenderNonce && 0 != pResult->cbSenderNonce)
|
|
{
|
|
CRYPT_DATA_BLOB Blob;
|
|
FILETIME ft;
|
|
DWORD dw;
|
|
DWORD cch;
|
|
|
|
// Recipient Nonce:
|
|
|
|
Blob.pbData = const_cast<BYTE *>(pResult->pbSenderNonce);
|
|
Blob.cbData = pResult->cbSenderNonce;
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&Blob,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++;
|
|
aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_RECIPIENT_NONCE;
|
|
aTaggedAttribute[ita].Attribute.cValue = 1;
|
|
aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
ita++;
|
|
|
|
// Sender Nonce:
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
dw = GetTickCount();
|
|
|
|
cch = sprintf(
|
|
szNonce,
|
|
"%u %08lx %08lx-%08lx",
|
|
*pResult->pdwRequestId,
|
|
dw,
|
|
ft.dwHighDateTime,
|
|
ft.dwLowDateTime);
|
|
CSASSERT(ARRAYSIZE(szNonce) > cch);
|
|
|
|
Blob.pbData = (BYTE *) szNonce;
|
|
Blob.cbData = cch;
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&Blob,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++;
|
|
aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_SENDER_NONCE;
|
|
aTaggedAttribute[ita].Attribute.cValue = 1;
|
|
aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
ita++;
|
|
}
|
|
|
|
// Issued Cert Hash:
|
|
|
|
if (NULL != pbCertLeaf)
|
|
{
|
|
CSASSERT(NULL != prow);
|
|
hr = pkcsGetHashAsOctet(
|
|
prow,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData);
|
|
_JumpIfError(hr, error, "pkcsGetHashAsOctet");
|
|
|
|
aAttr[iAttr].pszObjId = szOID_ISSUED_CERT_HASH;
|
|
aAttr[iAttr].cValue = 1;
|
|
aAttr[iAttr].rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
iAttr++;
|
|
}
|
|
|
|
dwRequestFlags = 0;
|
|
if (NULL != prow)
|
|
{
|
|
cb = sizeof(dwRequestFlags);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &dwRequestFlags);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "GetProperty");
|
|
dwRequestFlags = 0;
|
|
}
|
|
}
|
|
|
|
// Computed hash of private key encrypted to this CA, for client
|
|
// confirmation.
|
|
|
|
if (CR_FLG_VALIDENCRYPTEDKEYHASH & dwRequestFlags)
|
|
{
|
|
CRYPT_DATA_BLOB Blob;
|
|
|
|
if (NULL == pResult->pbKeyHashOut)
|
|
{
|
|
hr = pkcsRetrieveKeyHashFromRequest(
|
|
prow,
|
|
&pResult->pbKeyHashOut,
|
|
&pResult->cbKeyHashOut);
|
|
_JumpIfError(hr, error, "pkcsRetrieveKeyHashFromRequest");
|
|
}
|
|
Blob.pbData = pResult->pbKeyHashOut;
|
|
Blob.cbData = pResult->cbKeyHashOut;
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&Blob,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&aAttrBlob[iblob].pbData,
|
|
&aAttrBlob[iblob].cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
aAttr[iAttr].pszObjId = szOID_ENCRYPTED_KEY_HASH;
|
|
aAttr[iAttr].cValue = 1;
|
|
aAttr[iAttr].rgValue = &aAttrBlob[iblob];
|
|
iblob++;
|
|
iAttr++;
|
|
}
|
|
|
|
if (0 != iAttr)
|
|
{
|
|
hr = BuildCMCAttributes(
|
|
iAttr, // cAttribute
|
|
aAttr, // rgAttribute
|
|
dwCMCDataReference,
|
|
dwBodyPartIdOfRequest,
|
|
dwBodyPartId++,
|
|
&aTaggedAttribute[ita],
|
|
&aAttrBlob[iblob]);
|
|
_JumpIfError(hr, error, "BuildCMCAttributes");
|
|
|
|
iblob++;
|
|
ita++;
|
|
}
|
|
|
|
CSASSERT(ARRAYSIZE(aTaggedAttribute) >= ita);
|
|
CSASSERT(ARRAYSIZE(aAttr) >= iAttr);
|
|
CSASSERT(ARRAYSIZE(aAttrBlob) >= iblob);
|
|
|
|
Response.cTaggedAttribute = ita;
|
|
Response.rgTaggedAttribute = aTaggedAttribute;
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
CMC_RESPONSE,
|
|
&Response,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbContent,
|
|
&cbContent))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
|
|
if (NULL == pCAContext)
|
|
{
|
|
pCAContext = g_pCAContextCurrent;
|
|
}
|
|
ZeroMemory(&SignerEncodeInfo, sizeof(SignerEncodeInfo));
|
|
SignerEncodeInfo.cbSize = sizeof(SignerEncodeInfo);
|
|
SignerEncodeInfo.pCertInfo = pCAContext->pccCA->pCertInfo;
|
|
SignerEncodeInfo.hCryptProv = pCAContext->hProvCA;
|
|
SignerEncodeInfo.dwKeySpec = AT_SIGNATURE;
|
|
SignerEncodeInfo.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1;
|
|
//SignerEncodeInfo.pvHashAuxInfo = NULL;
|
|
//SignerEncodeInfo.cAuthAttr = 0;
|
|
//SignerEncodeInfo.rgAuthAttr = NULL;
|
|
//SignerEncodeInfo.cUnauthAttr = 0;
|
|
//SignerEncodeInfo.rgUnauthAttr = NULL;
|
|
|
|
if (NULL != pbCertLeaf)
|
|
{
|
|
hr = pkcsBuildCRLChain(
|
|
pCAContext,
|
|
pbCertLeaf,
|
|
cbCertLeaf,
|
|
fIncludeCRLs,
|
|
&SignedMsgEncodeInfo.cCertEncoded,
|
|
&SignedMsgEncodeInfo.rgCertEncoded,
|
|
&prgCert,
|
|
&SignedMsgEncodeInfo.cCrlEncoded,
|
|
&SignedMsgEncodeInfo.rgCrlEncoded,
|
|
&prgCRL);
|
|
_JumpIfError(hr, error, "pkcsBuildCRLChain");
|
|
}
|
|
|
|
hMsg = CryptMsgOpenToEncode(
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
CMSG_CMS_ENCAPSULATED_CONTENT_FLAG, // dwFlags
|
|
CMSG_SIGNED,
|
|
&SignedMsgEncodeInfo,
|
|
szOID_CT_PKI_RESPONSE,
|
|
NULL); // pStreamInfo
|
|
if (NULL == hMsg)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgOpenToEncode");
|
|
}
|
|
|
|
if (!CryptMsgUpdate(hMsg, pbContent, cbContent, TRUE))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptMsgUpdate");
|
|
}
|
|
|
|
// Return the encoded and signed content.
|
|
// Use CMSG_CONTENT_PARAM to get the signed message.
|
|
|
|
hr = myCryptMsgGetParam(
|
|
hMsg,
|
|
CMSG_CONTENT_PARAM,
|
|
0,
|
|
CERTLIB_USE_COTASKMEMALLOC,
|
|
(VOID **) ppbResponse,
|
|
pcbResponse);
|
|
_JumpIfError(hr, error, "myCryptMsgGetParam");
|
|
|
|
if (CRLF_LOG_FULL_RESPONSE & g_dwCRLFlags)
|
|
{
|
|
mydbgDumpHex(DBG_SS_ERROR, 0, *ppbResponse, *pcbResponse);
|
|
}
|
|
|
|
error:
|
|
pkcsFreeCRLChain(
|
|
SignedMsgEncodeInfo.cCertEncoded,
|
|
SignedMsgEncodeInfo.rgCertEncoded,
|
|
prgCert,
|
|
SignedMsgEncodeInfo.cCrlEncoded,
|
|
SignedMsgEncodeInfo.rgCrlEncoded,
|
|
prgCRL);
|
|
for (i = 0; i < ARRAYSIZE(aAttrBlob); i++)
|
|
{
|
|
if (NULL != aAttrBlob[i].pbData)
|
|
{
|
|
LocalFree(aAttrBlob[i].pbData);
|
|
}
|
|
}
|
|
if (NULL != hMsg)
|
|
{
|
|
CryptMsgClose(hMsg);
|
|
}
|
|
if (NULL != pbContent)
|
|
{
|
|
LocalFree(pbContent);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Build a PKCS7 NULL signature with encapsulated certs
|
|
|
|
HRESULT
|
|
pkcsEncodeCertChain(
|
|
OPTIONAL IN CACTX *pCAContext,
|
|
OPTIONAL IN BYTE const *pbCertLeaf,
|
|
IN DWORD cbCertLeaf,
|
|
IN BYTE const *pbToBeSigned,
|
|
IN DWORD cbToBeSigned,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT BYTE **ppbCertChain, // CoTaskMem*
|
|
OUT DWORD *pcbCertChain)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_SIGN_MESSAGE_PARA csmp;
|
|
CRYPT_ALGORITHM_IDENTIFIER DigestAlgorithm = { szOID_OIWSEC_sha1, 0, 0 };
|
|
|
|
// init csmp for empty signature
|
|
|
|
ZeroMemory(&csmp, sizeof(csmp));
|
|
csmp.cbSize = sizeof(csmp);
|
|
csmp.dwMsgEncodingType = PKCS_7_ASN_ENCODING;
|
|
//csmp.pSigningCert = NULL;
|
|
csmp.HashAlgorithm = DigestAlgorithm;
|
|
//csmp.cMsgCert = 0;
|
|
//csmp.rgpMsgCert = NULL;
|
|
//csmp.cMsgCrl = 0;
|
|
//csmp.rgpMsgCrl = NULL;
|
|
|
|
hr = pkcsBuildCRLChain(
|
|
pCAContext,
|
|
pbCertLeaf,
|
|
cbCertLeaf,
|
|
fIncludeCRLs,
|
|
&csmp.cMsgCert,
|
|
NULL,
|
|
&csmp.rgpMsgCert,
|
|
&csmp.cMsgCrl,
|
|
NULL,
|
|
&csmp.rgpMsgCrl);
|
|
_JumpIfError(hr, error, "pkcsBuildCRLChain");
|
|
|
|
if (!myCryptSignMessage(
|
|
&csmp,
|
|
pbToBeSigned,
|
|
cbToBeSigned,
|
|
CERTLIB_USE_COTASKMEMALLOC,
|
|
ppbCertChain,
|
|
pcbCertChain))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCryptSignMessage");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
pkcsFreeCRLChain(
|
|
csmp.cMsgCert,
|
|
NULL,
|
|
csmp.rgpMsgCert,
|
|
csmp.cMsgCrl,
|
|
NULL,
|
|
csmp.rgpMsgCrl);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCACert(
|
|
IN LONG PropId, // CR_PROP_*
|
|
IN DWORD iCert,
|
|
OUT BYTE **ppbCACert,
|
|
OUT DWORD *pcbCACert)
|
|
{
|
|
HRESULT hr;
|
|
DWORD State;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
CACROSSCTX *pCACross;
|
|
|
|
switch (PropId)
|
|
{
|
|
case CR_PROP_CAFORWARDCROSSCERT:
|
|
case CR_PROP_CABACKWARDCROSSCERT:
|
|
hr = pkcsMapCrossCertIndex(
|
|
CR_PROP_CAFORWARDCROSSCERT == PropId,
|
|
iCert,
|
|
&iCert,
|
|
&State);
|
|
_JumpIfError(hr, error, "pkcsMapCrossCertIndex");
|
|
|
|
// Now we know iCert is a valid Cross Cert Index:
|
|
|
|
pCACross = CR_PROP_CAFORWARDCROSSCERT == PropId?
|
|
g_aCACrossForward : g_aCACrossBackward;
|
|
if (NULL != pCACross)
|
|
{
|
|
pcc = pCACross[iCert].pccCACross;
|
|
}
|
|
break;
|
|
|
|
case CR_PROP_CASIGCERT:
|
|
hr = PKCSMapCertIndex(iCert, &iCert, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
// Now we know iCert is a valid Cert Index:
|
|
|
|
pcc = g_aCAContext[iCert].pccCA;
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "PropId");
|
|
|
|
break;
|
|
}
|
|
if (NULL == pcc)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid cert");
|
|
}
|
|
*pcbCACert = pcc->cbCertEncoded;
|
|
*ppbCACert = pcc->pbCertEncoded;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCAChain(
|
|
IN DWORD iCert,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT BYTE **ppbCAChain, // CoTaskMem*
|
|
OUT DWORD *pcbCAChain)
|
|
{
|
|
HRESULT hr;
|
|
DWORD State;
|
|
CACTX *pCAContext;
|
|
|
|
hr = PKCSMapCertIndex(iCert, &iCert, &State);
|
|
_JumpIfError(hr, error, "PKCSMapCertIndex");
|
|
|
|
// Now we know iCert is a valid Cert Index:
|
|
|
|
pCAContext = &g_aCAContext[iCert];
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid cert");
|
|
}
|
|
hr = pkcsEncodeCertChain(
|
|
pCAContext,
|
|
NULL, // pbCertLeaf
|
|
0, // cbCertLeaf
|
|
pCAContext->pccCA->pbCertEncoded, // pbToBeSigned
|
|
pCAContext->pccCA->cbCertEncoded, // cbToBeSigned
|
|
fIncludeCRLs,
|
|
ppbCAChain, // CoTaskMem*
|
|
pcbCAChain);
|
|
_JumpIfError(hr, error, "PKCSEncodeCertChain");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsAcquireKey(
|
|
OPTIONAL IN WCHAR const *pwszKeyContainer,
|
|
OUT HCRYPTPROV *phProv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*phProv = NULL;
|
|
if (!CryptAcquireContext(
|
|
phProv,
|
|
pwszKeyContainer,
|
|
g_pwszXchgProvName,
|
|
g_dwXchgProvType,
|
|
g_CryptSilent |
|
|
(g_fXchgMachineKeyset? CRYPT_MACHINE_KEYSET : 0)))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsSyncRegTimePeriod(
|
|
IN WCHAR const *pwszRegPeriodCount,
|
|
IN WCHAR const *pwszRegPeriodString,
|
|
IN OUT enum ENUM_PERIOD *penumCAXchgValidityPeriod,
|
|
IN OUT LONG *plCAXchgValidityPeriodCount,
|
|
IN FILETIME const *pft)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cPeriodUnits;
|
|
PERIODUNITS *rgPeriodUnits = NULL;
|
|
|
|
hr = caTranslateFileTimePeriodToPeriodUnits(
|
|
pft,
|
|
FALSE, // fExact
|
|
&cPeriodUnits,
|
|
&rgPeriodUnits);
|
|
_JumpIfError(hr, error, "caTranslateFileTimePeriodToPeriodUnits");
|
|
|
|
CSASSERT(0 < cPeriodUnits);
|
|
|
|
if (rgPeriodUnits[0].lCount != *plCAXchgValidityPeriodCount ||
|
|
rgPeriodUnits[0].enumPeriod != *penumCAXchgValidityPeriod)
|
|
{
|
|
WCHAR const *pwszPeriodString;
|
|
|
|
*plCAXchgValidityPeriodCount = rgPeriodUnits[0].lCount;
|
|
*penumCAXchgValidityPeriod = rgPeriodUnits[0].enumPeriod;
|
|
|
|
hr = myTranslateUnlocalizedPeriodString(
|
|
rgPeriodUnits[0].enumPeriod,
|
|
&pwszPeriodString);
|
|
_JumpIfError(hr, error, "myTranslateUnlocalizedPeriodString");
|
|
|
|
// CA xchg cert period string
|
|
|
|
hr = mySetCertRegStrValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
pwszRegPeriodString,
|
|
pwszPeriodString);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegStrValue", pwszRegPeriodString);
|
|
|
|
// CA xchg cert period count
|
|
|
|
hr = mySetCertRegDWValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
pwszRegPeriodCount,
|
|
*plCAXchgValidityPeriodCount);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegDWValue", pwszRegPeriodCount);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != rgPeriodUnits)
|
|
{
|
|
LocalFree(rgPeriodUnits);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Set validity & overlap periods from template; update registry if it differs.
|
|
// enum ENUM_PERIOD g_enumCAXchgValidityPeriod
|
|
// LONG g_lCAXchgValidityPeriodCount
|
|
//
|
|
// enum ENUM_PERIOD g_enumCAXchgOverlapPeriod
|
|
// LONG g_lCAXchgOverlapPeriodCount
|
|
|
|
HRESULT
|
|
PKCSUpdateXchgValidityPeriods(
|
|
OPTIONAL IN HCERTTYPE hCertType)
|
|
{
|
|
HRESULT hr;
|
|
HCERTTYPE hCertTypeT = NULL;
|
|
FILETIME ftExpiration;
|
|
FILETIME ftOverlap;
|
|
|
|
if (NULL == hCertType)
|
|
{
|
|
hr = CAFindCertTypeByName(
|
|
wszCERTTYPE_CA_EXCHANGE,
|
|
NULL,
|
|
CT_FIND_LOCAL_SYSTEM |
|
|
CT_ENUM_MACHINE_TYPES |
|
|
CT_ENUM_USER_TYPES |
|
|
CT_FLAG_NO_CACHE_LOOKUP,
|
|
&hCertTypeT);
|
|
_JumpIfErrorStr(hr, error, "CAFindCertTypeByName", wszCERTTYPE_CA_EXCHANGE);
|
|
|
|
hCertType = hCertTypeT;
|
|
}
|
|
hr = CAGetCertTypeExpiration(
|
|
hCertType,
|
|
&ftExpiration,
|
|
&ftOverlap);
|
|
_JumpIfError(hr, error, "CAGetCertTypeExpiration");
|
|
|
|
hr = pkcsSyncRegTimePeriod(
|
|
g_wszRegCAXchgValidityPeriodCount,
|
|
g_wszRegCAXchgValidityPeriodString,
|
|
&g_enumCAXchgValidityPeriod,
|
|
&g_lCAXchgValidityPeriodCount,
|
|
&ftExpiration);
|
|
_JumpIfError(hr, error, "pkcsSyncRegTimePeriod");
|
|
|
|
hr = pkcsSyncRegTimePeriod(
|
|
g_wszRegCAXchgOverlapPeriodCount,
|
|
g_wszRegCAXchgOverlapPeriodString,
|
|
&g_enumCAXchgOverlapPeriod,
|
|
&g_lCAXchgOverlapPeriodCount,
|
|
&ftOverlap);
|
|
_JumpIfError(hr, error, "pkcsSyncRegTimePeriod");
|
|
|
|
error:
|
|
if (NULL != hCertTypeT)
|
|
{
|
|
CACloseCertType(hCertTypeT);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCAXchgContext(
|
|
IN OUT CAXCHGCTX *pCAXchgContext)
|
|
{
|
|
if (NULL != pCAXchgContext->hProvCA)
|
|
{
|
|
CryptReleaseContext(pCAXchgContext->hProvCA, 0);
|
|
pCAXchgContext->hProvCA = NULL;
|
|
}
|
|
if (NULL != pCAXchgContext->pccCAXchg)
|
|
{
|
|
CertFreeCertificateContext(pCAXchgContext->pccCAXchg);
|
|
pCAXchgContext->pccCAXchg = NULL;
|
|
}
|
|
if (NULL != pCAXchgContext->pwszKeyContainerName)
|
|
{
|
|
LocalFree(pCAXchgContext->pwszKeyContainerName);
|
|
pCAXchgContext->pwszKeyContainerName = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pkcsReleaseCAXchgContextArray()
|
|
{
|
|
DWORD i;
|
|
|
|
if (NULL != g_aCAXchgContext)
|
|
{
|
|
for (i = 0; i < g_cCAXchgCerts; i++)
|
|
{
|
|
pkcsReleaseCAXchgContext(&g_aCAXchgContext[i]);
|
|
}
|
|
LocalFree(g_aCAXchgContext);
|
|
g_aCAXchgContext = NULL;
|
|
}
|
|
g_cCAXchgCerts = 0;
|
|
g_pCAContextCurrent = NULL;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCAXchgContext(
|
|
IN DWORD iHash)
|
|
{
|
|
HRESULT hr;
|
|
CAXCHGCTX *pCAXchgContext;
|
|
DWORD dwRequestFlags;
|
|
DWORD NameId;
|
|
HCRYPTPROV hProv = NULL;
|
|
WCHAR *pwszKeyContainer = NULL;
|
|
BYTE *pbHash = NULL;
|
|
DWORD cbHash;
|
|
BYTE *pbCert = NULL;
|
|
DWORD cbCert;
|
|
DWORD cb;
|
|
BSTR strHash = NULL;
|
|
ICertDBRow *prow = NULL;
|
|
DWORD dwRequestId;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
BOOL fDeleteKey = FALSE;
|
|
DWORD i;
|
|
|
|
hr = myGetCARegHash(
|
|
g_wszSanitizedName,
|
|
CSRH_CAXCHGCERT,
|
|
iHash,
|
|
&pbHash,
|
|
&cbHash);
|
|
_JumpIfError2(hr, error, "myGetCARegHash", S_FALSE);
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Reloading Xchg CAContext[%u]:\n %ws\n",
|
|
iHash,
|
|
strHash));
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY | PROPOPEN_CERTHASH | PROPTABLE_REQCERT,
|
|
0,
|
|
strHash,
|
|
&prow);
|
|
_JumpIfError(hr, error, "OpenRow(xchg cert)");
|
|
|
|
prow->GetRowId(&dwRequestId);
|
|
|
|
hr = pkcsFormXchgKeyContainerName(dwRequestId, &pwszKeyContainer);
|
|
_JumpIfError(hr, error, "pkcsFormXchgKeyContainerName");
|
|
|
|
cb = sizeof(dwRequestFlags);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestFlags,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &dwRequestFlags);
|
|
_JumpIfError(hr, error, "GetProperty(RequestFlags)");
|
|
|
|
if (0 == (CR_FLG_CAXCHGCERT & dwRequestFlags))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Not a CA Xchg cert");
|
|
}
|
|
|
|
cb = sizeof(NameId);
|
|
hr = prow->GetProperty(
|
|
g_wszPropCertificateIssuerNameID,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
NULL,
|
|
&cb,
|
|
(BYTE *) &NameId);
|
|
_JumpIfError(hr, error, "GetProperty");
|
|
|
|
hr = PKCSGetProperty(
|
|
prow,
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
&cbCert,
|
|
(BYTE **) &pbCert);
|
|
_JumpIfError(hr, error, "PKCSGetProperty(xchg cert)");
|
|
|
|
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
hr = pkcsAcquireKey(pwszKeyContainer, &hProv);
|
|
_PrintIfErrorStr(hr, "pkcsAcquireKey", g_pwszXchgProvName);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = myValidateKeyForEncrypting(
|
|
hProv,
|
|
&pcc->pCertInfo->SubjectPublicKeyInfo,
|
|
CALG_3DES);
|
|
_PrintIfErrorStr(hr, "myValidateKeyForEncrypting", g_pwszXchgProvName);
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
LogEventHResult(
|
|
NULL == g_pwszXchgProvName?
|
|
EVENTLOG_ERROR_TYPE :
|
|
EVENTLOG_WARNING_TYPE,
|
|
NULL == g_pwszXchgProvName?
|
|
MSG_E_BAD_DEFAULT_CA_XCHG_CSP :
|
|
MSG_E_BAD_REGISTRY_CA_XCHG_CSP,
|
|
hr);
|
|
if (0 != i || NULL == g_pwszXchgProvName)
|
|
{
|
|
fDeleteKey = TRUE;
|
|
_JumpError(hr, error, "pkcsAcquireKey/myValidateKeyForEncrypting");
|
|
}
|
|
pkcsLoadCAXchgCSPInfo(TRUE); // switch to default CSP
|
|
}
|
|
if (0 != i)
|
|
{
|
|
hr = LogEvent(
|
|
EVENTLOG_WARNING_TYPE,
|
|
MSG_E_USE_DEFAULT_CA_XCHG_CSP,
|
|
0, // cpwsz
|
|
NULL); // apwsz
|
|
_PrintIfError(hr, "LogEvent");
|
|
}
|
|
|
|
hr = pkcsVerifyCertContext(NULL, FALSE, pcc);
|
|
if (S_OK != hr)
|
|
{
|
|
fDeleteKey = TRUE;
|
|
_JumpErrorStr(hr, error, "pkcsVerifyCertContext", L"CAXchg cert invalid");
|
|
}
|
|
|
|
pCAXchgContext = &g_aCAXchgContext[g_cCAXchgCerts];
|
|
ZeroMemory(pCAXchgContext, sizeof(*pCAXchgContext));
|
|
|
|
pCAXchgContext->ReqId = dwRequestId;
|
|
|
|
pCAXchgContext->pccCAXchg = pcc;
|
|
pcc = NULL;
|
|
|
|
pCAXchgContext->hProvCA = hProv;
|
|
hProv = NULL;
|
|
|
|
pCAXchgContext->pwszKeyContainerName = pwszKeyContainer;
|
|
pwszKeyContainer = NULL;
|
|
|
|
pCAXchgContext->iCertSig = CANAMEIDTOICERT(NameId);
|
|
g_cCAXchgCerts++;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
if (fDeleteKey)
|
|
{
|
|
pkcsDeleteKey(pwszKeyContainer);
|
|
}
|
|
if (NULL != prow)
|
|
{
|
|
prow->Release();
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
if (NULL != pwszKeyContainer)
|
|
{
|
|
LocalFree(pwszKeyContainer);
|
|
}
|
|
if (NULL != pbHash)
|
|
{
|
|
LocalFree(pbHash);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsLoadCAXchgContextArray(
|
|
OUT BOOL *pfIncompleteLoad)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cCAXchgCerts;
|
|
DWORD iHash;
|
|
DWORD i;
|
|
|
|
// get provider name, etc.
|
|
|
|
pkcsLoadCAXchgCSPInfo(FALSE);
|
|
|
|
// find & load CA Xchg certs, etc.
|
|
|
|
*pfIncompleteLoad = TRUE;
|
|
hr = myGetCARegHashCount(
|
|
g_wszSanitizedName,
|
|
CSRH_CAXCHGCERT,
|
|
&cCAXchgCerts);
|
|
if (S_OK == hr && 0 == cCAXchgCerts)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
_JumpIfError(hr, error, "myGetCARegHashCount");
|
|
|
|
g_aCAXchgContext = (CAXCHGCTX *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cCAXchgCerts * sizeof(g_aCAXchgContext[0]));
|
|
if (NULL == g_aCAXchgContext)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
for (iHash = 0; iHash < cCAXchgCerts; iHash++)
|
|
{
|
|
hr = pkcsLoadCAXchgContext(iHash);
|
|
_PrintIfError(hr, "pkcsLoadCAXchgContext");
|
|
}
|
|
if (0 == g_cCAXchgCerts)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "g_cCAXchgCerts");
|
|
}
|
|
g_pCAXchgContextCurrent = &g_aCAXchgContext[0];
|
|
for (i = 1; i < g_cCAXchgCerts; i++)
|
|
{
|
|
if (0 < CompareFileTime(
|
|
&g_aCAXchgContext[i].pccCAXchg->pCertInfo->NotAfter,
|
|
&g_pCAXchgContextCurrent->pccCAXchg->pCertInfo->NotAfter))
|
|
{
|
|
g_pCAXchgContextCurrent = &g_aCAXchgContext[i];
|
|
}
|
|
}
|
|
if (cCAXchgCerts == g_cCAXchgCerts)
|
|
{
|
|
*pfIncompleteLoad = FALSE;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
if (NULL != g_aCAXchgContext)
|
|
{
|
|
LocalFree(g_aCAXchgContext);
|
|
g_aCAXchgContext = NULL;
|
|
}
|
|
g_cCAXchgCerts = 0;
|
|
g_pCAXchgContextCurrent = NULL;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
pkcsUpdateCAXchgStoreAndRegistry(
|
|
IN BOOL fUpdateRegistry)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD iHash;
|
|
CAXCHGCTX *pCAXchgContext;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_KEY_CONTEXT ckc;
|
|
CERT_CONTEXT const *pccStore = NULL;
|
|
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_MEMORY,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
0, // dwFlags
|
|
NULL); // pvPara
|
|
if (NULL == hStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
if (fUpdateRegistry)
|
|
{
|
|
hr = myDeleteCertRegValue(
|
|
g_wszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
g_wszRegCAXchgCertHash);
|
|
_PrintIfError(hr, "myDeleteCertRegValue");
|
|
}
|
|
|
|
ZeroMemory(&ckc, sizeof(ckc));
|
|
ckc.cbSize = sizeof(ckc);
|
|
ckc.dwKeySpec = AT_KEYEXCHANGE;
|
|
|
|
iHash = 0;
|
|
for (i = 0; i < g_cCAXchgCerts; i++)
|
|
{
|
|
pCAXchgContext = &g_aCAXchgContext[i];
|
|
if (CTXF_EXPIRED & pCAXchgContext->Flags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Add as encoded blob to avoid all properties, key prov info, etc.
|
|
|
|
if (!CertAddEncodedCertificateToStore(
|
|
hStore,
|
|
X509_ASN_ENCODING,
|
|
pCAXchgContext->pccCAXchg->pbCertEncoded,
|
|
pCAXchgContext->pccCAXchg->cbCertEncoded,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
&pccStore)) // ppCertContext
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddEncodedCertificateToStore");
|
|
}
|
|
ckc.hCryptProv = pCAXchgContext->hProvCA;
|
|
if (!CertSetCertificateContextProperty(
|
|
pccStore,
|
|
CERT_KEY_CONTEXT_PROP_ID,
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
|
&ckc))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertSetCertificateContextProperty");
|
|
}
|
|
CertFreeCertificateContext(pccStore);
|
|
pccStore = NULL;
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTSRV,
|
|
"Add to CA Xchg memory store: '%ws'\n",
|
|
pCAXchgContext->pwszKeyContainerName));
|
|
|
|
if (fUpdateRegistry)
|
|
{
|
|
hr = mySetCARegHash(
|
|
g_wszSanitizedName,
|
|
CSRH_CAXCHGCERT,
|
|
iHash,
|
|
pCAXchgContext->pccCAXchg);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "mySetCARegHash");
|
|
continue;
|
|
}
|
|
}
|
|
iHash++;
|
|
}
|
|
if (NULL != g_hStoreCAXchg)
|
|
{
|
|
CertCloseStore(g_hStoreCAXchg, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
g_hStoreCAXchg = hStore;
|
|
hStore = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pccStore)
|
|
{
|
|
CertFreeCertificateContext(pccStore);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSIsRevoked(
|
|
IN DWORD RequestId,
|
|
OPTIONAL IN WCHAR const *pwszSerialNumber,
|
|
OUT LONG *pRevocationReason,
|
|
OUT LONG *pDisposition)
|
|
{
|
|
HRESULT hr;
|
|
ICertDBRow *prow = NULL;
|
|
BYTE *pbHash = NULL;
|
|
DWORD cbHash;
|
|
BSTR strHash = NULL;
|
|
DWORD Disposition;
|
|
DWORD cbProp;
|
|
FILETIME ftRevoked;
|
|
FILETIME ftCurrent;
|
|
|
|
*pRevocationReason = CRL_REASON_UNSPECIFIED;
|
|
*pDisposition = CA_DISP_INVALID;
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY | PROPTABLE_REQCERT,
|
|
RequestId,
|
|
pwszSerialNumber,
|
|
&prow);
|
|
_PrintIfErrorStr2(hr, "OpenRow", pwszSerialNumber, CERTSRV_E_PROPERTY_EMPTY);
|
|
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr && NULL != pwszSerialNumber)
|
|
{
|
|
_PrintErrorStr2(
|
|
hr,
|
|
"OpenRow(serial)",
|
|
pwszSerialNumber,
|
|
CERTSRV_E_PROPERTY_EMPTY);
|
|
|
|
hr = WszToMultiByteInteger(TRUE, pwszSerialNumber, &cbHash, &pbHash);
|
|
_JumpIfError(hr, error, "WszToMultiByteInteger");
|
|
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
|
|
hr = g_pCertDB->OpenRow(
|
|
PROPOPEN_READONLY |
|
|
PROPOPEN_CERTHASH |
|
|
PROPTABLE_REQCERT,
|
|
RequestId,
|
|
strHash,
|
|
&prow);
|
|
_PrintIfErrorStr2(hr, "OpenRow", strHash, CERTSRV_E_PROPERTY_EMPTY);
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr)
|
|
{
|
|
hr = S_OK; // disposition indicates cert is invalid
|
|
}
|
|
goto error;
|
|
}
|
|
|
|
cbProp = sizeof(Disposition);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestDisposition,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbProp,
|
|
(BYTE *) &Disposition);
|
|
_JumpIfError(hr, error, "GetProperty(Disposition)");
|
|
|
|
if (DB_DISP_ISSUED == Disposition ||
|
|
(DB_DISP_CA_CERT == Disposition && IsRootCA(g_CAType)))
|
|
{
|
|
*pDisposition = CA_DISP_VALID;
|
|
goto error;
|
|
}
|
|
|
|
if (DB_DISP_REVOKED != Disposition)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
cbProp = sizeof(ftRevoked);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRevokedEffectiveWhen,
|
|
PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbProp,
|
|
(BYTE *) &ftRevoked);
|
|
|
|
if (CERTSRV_E_PROPERTY_EMPTY == hr)
|
|
{
|
|
*pDisposition = CA_DISP_VALID;
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
_JumpIfError(hr, error, "GetProperty(RevokedEffectiveWhen)");
|
|
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
if (0 < CompareFileTime(&ftRevoked, &ftCurrent))
|
|
{
|
|
*pDisposition = CA_DISP_VALID;
|
|
goto error;
|
|
}
|
|
|
|
cbProp = sizeof(*pRevocationReason);
|
|
hr = prow->GetProperty(
|
|
g_wszPropRequestRevokedReason,
|
|
PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST,
|
|
NULL,
|
|
&cbProp,
|
|
(BYTE *) pRevocationReason);
|
|
_JumpIfError(hr, error, "GetProperty(RevokedReason)");
|
|
|
|
*pDisposition = CA_DISP_REVOKED;
|
|
|
|
error:
|
|
if (NULL != pbHash)
|
|
{
|
|
LocalFree(pbHash);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
if (NULL != prow)
|
|
{
|
|
prow->Release();
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCAXchgCert(
|
|
IN DWORD iCert,
|
|
IN WCHAR const *pwszUserName,
|
|
OUT DWORD *piCertSig,
|
|
OUT BYTE **ppbCACert,
|
|
OUT DWORD *pcbCACert)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fNewCert = FALSE;
|
|
BOOL fIncompleteLoad = FALSE;
|
|
FILETIME ft;
|
|
BOOL fCritSecEntered = FALSE;
|
|
|
|
*ppbCACert = NULL;
|
|
if (MAXDWORD != iCert && 0 != iCert)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad Xchg CertIndex");
|
|
}
|
|
EnterCriticalSection(&g_critsecCAXchg);
|
|
fCritSecEntered = TRUE;
|
|
|
|
hr = S_OK;
|
|
__try
|
|
{
|
|
if (NULL == g_pCAXchgContextCurrent ||
|
|
NULL == g_pCAXchgContextCurrent->pccCAXchg)
|
|
{
|
|
hr = pkcsLoadCAXchgContextArray(&fIncompleteLoad);
|
|
_PrintIfError(hr, "pkcsLoadCAXchgContextArray");
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
fNewCert = TRUE;
|
|
}
|
|
}
|
|
if (NULL != g_pCAXchgContextCurrent &&
|
|
NULL != g_pCAXchgContextCurrent->pccCAXchg)
|
|
{
|
|
CERT_INFO const *pCertInfo = g_pCAXchgContextCurrent->pccCAXchg->pCertInfo;
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
if (0 < CompareFileTime(&ft, &pCertInfo->NotAfter))
|
|
{
|
|
g_pCAXchgContextCurrent->Flags |= CTXF_EXPIRED;
|
|
hr = CERT_E_EXPIRED;
|
|
_PrintError(hr, "CA Xchg certificate is expired -- delete key");
|
|
|
|
pkcsDeleteKey(g_pCAXchgContextCurrent->pwszKeyContainerName);
|
|
fNewCert = TRUE;
|
|
}
|
|
else
|
|
if (0 > CompareFileTime(&ft, &pCertInfo->NotBefore))
|
|
{
|
|
hr = CERT_E_EXPIRED;
|
|
_PrintError(hr, "CA Xchg certificate not yet valid");
|
|
fNewCert = TRUE;
|
|
}
|
|
else
|
|
{
|
|
myMakeExprDateTime(
|
|
&ft,
|
|
g_lCAXchgOverlapPeriodCount,
|
|
g_enumCAXchgOverlapPeriod);
|
|
|
|
if (0 < CompareFileTime(&ft, &pCertInfo->NotAfter))
|
|
{
|
|
hr = CERT_E_EXPIRED;
|
|
_PrintError(hr, "CA Xchg certificate expires too soon");
|
|
fNewCert = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hr = pkcsVerifyCertIssuer(
|
|
g_pCAXchgContextCurrent->pccCAXchg,
|
|
g_pCAContextCurrent);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "CA Xchg cert not issued by current CA");
|
|
fNewCert = TRUE;
|
|
}
|
|
else
|
|
{
|
|
LONG RevocationReason;
|
|
LONG Disposition;
|
|
|
|
hr = PKCSIsRevoked(
|
|
g_pCAXchgContextCurrent->ReqId,
|
|
NULL, // pwszSerialNumber
|
|
&RevocationReason,
|
|
&Disposition);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "PKCSIsRevoked");
|
|
fNewCert = TRUE;
|
|
}
|
|
else
|
|
if (CA_DISP_VALID != Disposition)
|
|
{
|
|
hr = CRYPT_E_REVOKED;
|
|
_PrintError(hr, "revoked or bad CA Xchg certificate");
|
|
fNewCert = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (fNewCert)
|
|
{
|
|
hr = pkcsCreateNewCAXchgCert(pwszUserName);
|
|
_LeaveIfError(hr, "pkcsCreateNewCAXchgCert");
|
|
}
|
|
hr = pkcsUpdateCAXchgStoreAndRegistry(fNewCert || fIncompleteLoad);
|
|
_LeaveIfError(hr, "pkcsUpdateCAXchgStoreAndRegistry");
|
|
|
|
// It's safe to return the cert blob memory pointer because the CA
|
|
// Exchange cert contexts aren't released until shutdown.
|
|
|
|
*piCertSig = g_pCAXchgContextCurrent->iCertSig;
|
|
*pcbCACert = g_pCAXchgContextCurrent->pccCAXchg->cbCertEncoded;
|
|
*ppbCACert = g_pCAXchgContextCurrent->pccCAXchg->pbCertEncoded;
|
|
hr = S_OK;
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
error:
|
|
if (fCritSecEntered)
|
|
{
|
|
LeaveCriticalSection(&g_critsecCAXchg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetCAXchgChain(
|
|
IN DWORD iCert,
|
|
IN WCHAR const *pwszUserName,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT BYTE **ppbCAChain, // CoTaskMem*
|
|
OUT DWORD *pcbCAChain)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbCACert;
|
|
DWORD cbCACert;
|
|
CACTX *pCAContext;
|
|
|
|
hr = PKCSGetCAXchgCert(iCert, pwszUserName, &iCert, &pbCACert, &cbCACert);
|
|
_JumpIfError(hr, error, "PKCSGetCAXchgCert");
|
|
|
|
// iCert now indexes the signature cert that signed the current Xchg cert
|
|
|
|
pCAContext = &g_aCAContext[iCert];
|
|
if (NULL == pCAContext->pccCA)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid cert");
|
|
}
|
|
hr = pkcsEncodeCertChain(
|
|
pCAContext,
|
|
pbCACert, // pbCertLeaf
|
|
cbCACert, // cbCertLeaf
|
|
pbCACert, // pbToBeSigned
|
|
cbCACert, // cbToBeSigned
|
|
fIncludeCRLs,
|
|
ppbCAChain, // CoTaskMem*
|
|
pcbCAChain);
|
|
_JumpIfError(hr, error, "PKCSEncodeCertChain");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
PKCSTerminate(VOID)
|
|
{
|
|
pkcsReleaseCAContextArray();
|
|
pkcsReleaseCAXchgContextArray();
|
|
if (NULL != g_hStoreCAXchg)
|
|
{
|
|
CertCloseStore(g_hStoreCAXchg, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
g_hStoreCAXchg = NULL;
|
|
}
|
|
pkcsLoadCAXchgCSPInfo(TRUE);
|
|
pkcsReleaseKRACertArray();
|
|
|
|
pkcsFreeTemplates(&g_paRevURL, &g_caRevURL);
|
|
pkcsFreeTemplates(&g_paCACertURL, &g_caCACertURL);
|
|
if (NULL != g_pwszKRAPublishURL)
|
|
{
|
|
LocalFree(g_pwszKRAPublishURL);
|
|
g_pwszKRAPublishURL = NULL;
|
|
}
|
|
if (NULL != g_pwszAIACrossCertPublishURL)
|
|
{
|
|
LocalFree(g_pwszAIACrossCertPublishURL);
|
|
g_pwszAIACrossCertPublishURL = NULL;
|
|
}
|
|
if (NULL != g_pwszRootTrustCrossCertPublishURL)
|
|
{
|
|
LocalFree(g_pwszRootTrustCrossCertPublishURL);
|
|
g_pwszRootTrustCrossCertPublishURL = NULL;
|
|
}
|
|
if (NULL != g_strDomainDN)
|
|
{
|
|
SysFreeString(g_strDomainDN);
|
|
g_strDomainDN = NULL;
|
|
}
|
|
if (NULL != g_strConfigDN)
|
|
{
|
|
SysFreeString(g_strConfigDN);
|
|
g_strConfigDN = NULL;
|
|
}
|
|
if (g_fcritsecCAXchg)
|
|
{
|
|
DeleteCriticalSection(&g_critsecCAXchg);
|
|
g_fcritsecCAXchg = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// PKCSCreateCertificate -- Create certificate & build PKCS 7 or Full Response.
|
|
//
|
|
// If pResult->pctbCert is non-NULL and pResult->pctbCert->pb is NULL:
|
|
// CR_IN_NEW:
|
|
// Build, store and return cert
|
|
// Use current CA Context
|
|
// Build and return PKCS 7 or Full Response
|
|
//
|
|
// If pResult->pctbCert is non-NULL and pResult->pctbCert->pb is non-NULL:
|
|
// CR_IN_RETRIEVEPENDING:
|
|
// Use passed cert
|
|
// Find matching CA Context
|
|
// Build and return PKCS 7 or Full Response
|
|
//
|
|
// If pResult->pctbCert is NULL:
|
|
// CR_IN_RESUBMIT:
|
|
// Build and store cert -- don't return cert
|
|
// Use current CA Context
|
|
// Don't build or return PKCS 7 or Full Response
|
|
|
|
|
|
HRESULT
|
|
PKCSCreateCertificate(
|
|
IN ICertDBRow *prow,
|
|
IN DWORD Disposition,
|
|
IN BOOL fIncludeCRLs,
|
|
IN BOOL fCrossCert,
|
|
OPTIONAL IN CACTX *pCAContext, // signing CACTX
|
|
OUT BOOL *pfErrorLogged,
|
|
OPTIONAL OUT CACTX **ppCAContext,
|
|
OPTIONAL OUT WCHAR **ppwszDispositionCreateCert,
|
|
IN OUT CERTSRV_RESULT_CONTEXT *pResult) // CoTaskMem*
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbCert = NULL;
|
|
DWORD cbCert;
|
|
BYTE *pbCertChain = NULL;
|
|
DWORD cbCertChain;
|
|
BOOL fCreated = FALSE;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
|
|
*pfErrorLogged = FALSE;
|
|
if (NULL != ppCAContext)
|
|
{
|
|
*ppCAContext = NULL;
|
|
}
|
|
if (NULL != ppwszDispositionCreateCert)
|
|
{
|
|
*ppwszDispositionCreateCert = NULL;
|
|
}
|
|
CSASSERT(NULL == pResult->pctbCertChain || NULL == pResult->pctbCertChain->pb);
|
|
CSASSERT(NULL == pResult->pctbFullResponse || NULL == pResult->pctbFullResponse->pb);
|
|
|
|
cbCertChain = 0;
|
|
if (NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb)
|
|
{
|
|
CSASSERT(NULL == pCAContext);
|
|
CSASSERT(NULL == ppwszDispositionCreateCert);
|
|
pbCert = pResult->pctbCert->pb;
|
|
cbCert = pResult->pctbCert->cb;
|
|
|
|
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
pCAContext = NULL;
|
|
if (DB_DISP_CA_CERT != Disposition &&
|
|
DB_DISP_CA_CERT_CHAIN != Disposition)
|
|
{
|
|
hr = PKCSVerifyIssuedCertificate(pcc, &pCAContext);
|
|
_JumpIfError(hr, error, "PKCSVerifyIssuedCertificate");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CSASSERT(NULL != ppwszDispositionCreateCert);
|
|
if (NULL == pCAContext)
|
|
{
|
|
pCAContext = g_pCAContextCurrent;
|
|
}
|
|
cbCert = 0;
|
|
hr = pkcsEncodeSubjectCert(
|
|
prow,
|
|
pCAContext,
|
|
fCrossCert,
|
|
&pbCert, // CoTaskMem*
|
|
&cbCert,
|
|
pfErrorLogged,
|
|
ppwszDispositionCreateCert);
|
|
if (S_OK == hr ||
|
|
((CRLF_SAVE_FAILED_CERTS & g_dwCRLFlags) &&
|
|
NULL != pbCert &&
|
|
0 != cbCert))
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = prow->SetProperty(
|
|
g_wszPropRawCertificate,
|
|
PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE,
|
|
cbCert,
|
|
pbCert);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = hr2;
|
|
}
|
|
_JumpIfError(hr2, error, "SetProperty");
|
|
}
|
|
_JumpIfError(hr, error, "pkcsEncodeSubjectCert");
|
|
|
|
fCreated = TRUE;
|
|
}
|
|
|
|
if (NULL != pResult->pctbCertChain)
|
|
{
|
|
hr = pkcsEncodeCertChain(
|
|
pCAContext,
|
|
pbCert, // pbCertLeaf
|
|
cbCert, // cbCertLeaf
|
|
pbCert, // pbToBeSigned
|
|
cbCert, // cbToBeSigned
|
|
fIncludeCRLs,
|
|
&pbCertChain, // CoTaskMem*
|
|
&cbCertChain);
|
|
_JumpIfError(hr, error, "pkcsEncodeCertChain");
|
|
}
|
|
if (fCreated && NULL != pResult->pctbCert)
|
|
{
|
|
pResult->pctbCert->pb = pbCert;
|
|
pResult->pctbCert->cb = cbCert;
|
|
pbCert = NULL;
|
|
}
|
|
if (NULL != pResult->pctbCertChain)
|
|
{
|
|
pResult->pctbCertChain->pb = pbCertChain;
|
|
pResult->pctbCertChain->cb = cbCertChain;
|
|
pbCertChain = NULL;
|
|
}
|
|
if (NULL != ppCAContext)
|
|
{
|
|
*ppCAContext = pCAContext;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
if (fCreated && NULL != pbCert)
|
|
{
|
|
CoTaskMemFree(pbCert);
|
|
}
|
|
if (fCreated && NULL != pbCertChain)
|
|
{
|
|
CoTaskMemFree(pbCertChain);
|
|
}
|
|
CSASSERT(
|
|
NULL == pResult->pctbCertChain ||
|
|
((S_OK == hr) ^ (NULL == pResult->pctbCertChain->pb)));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PKCSGetKRACert(
|
|
IN DWORD iCert,
|
|
OUT BYTE **ppbCert,
|
|
OUT DWORD *pcbCert)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (MAXDWORD == iCert)
|
|
{
|
|
iCert = g_cKRACerts - 1;
|
|
}
|
|
if (iCert >= g_cKRACerts || NULL == g_aKRAContext)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad CertIndex");
|
|
}
|
|
|
|
*pcbCert = g_aKRAContext[iCert].pccKRA->cbCertEncoded;
|
|
*ppbCert = g_aKRAContext[iCert].pccKRA->pbCertEncoded;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|