|
|
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2000
//
// File: template.cpp
//
// Contents: Cert Server Policy Module implementation
//
//---------------------------------------------------------------------------
#include "pch.cpp"
#pragma hdrstop
#include <ntdsapi.h>
#include <lm.h>
#include <winldap.h>
#include <security.h>
#include <ntdsapi.h>
#include "cspelog.h"
#include "pollog.h"
#include "csprop.h"
#include "csldap.h"
#include "csdisp.h"
#include "csber.h"
#include "policy.h"
#include "cainfop.h"
#define __dwFILE__ __dwFILE_POLICY_DEFAULT_TEMPLATE_CPP__
// Versions of NT earlier than this build have an auto-enrollment loop problem
// with not having the basic constraints extension, so we must put one in certs
// for these builds. This build marks when certcli started return no BC
// extension for templates that were not CA's.
#define VERSION_AUTOENROLLMENT_BC_AWARE 2036
// Versions of NT earlier than this build have an auto-enrollment loop problem
// with having the UPN anywhere but the CN. Certs for these builds must have
// the UPN in the common name.
#define VERSION_AUTOENROLLMENT_UPN_AWARE 2090
#define VERSION_WIN2K_XENROLL_CLIENT (2195 + 1)
// All of the "known" Key Usage bits currently defined:
#define dwKNOWN_KEY_USAGE_BITS \
(CERT_DIGITAL_SIGNATURE_KEY_USAGE | \ CERT_NON_REPUDIATION_KEY_USAGE | \ CERT_KEY_ENCIPHERMENT_KEY_USAGE | \ CERT_DATA_ENCIPHERMENT_KEY_USAGE | \ CERT_KEY_AGREEMENT_KEY_USAGE | \ CERT_KEY_CERT_SIGN_KEY_USAGE | \ CERT_OFFLINE_CRL_SIGN_KEY_USAGE | \ CERT_CRL_SIGN_KEY_USAGE | \ CERT_ENCIPHER_ONLY_KEY_USAGE | \ (CERT_DECIPHER_ONLY_KEY_USAGE << 8))
// Mask to turn off all the "known" Key Usage bits that aren't expclitly valid:
#define dwKUMASK(dwValid) ((DWORD) ((dwValid) | ~dwKNOWN_KEY_USAGE_BITS))
typedef struct _KEYUSAGEMASK { WCHAR const * const *apwszAlg; DWORD dwMask1; // 1: CA mask (2: if any of these bits are set)
DWORD dwMask2; // 1: EE mask (2: clear these bits)
} KEYUSAGEMASK;
WCHAR const * const s_apwszRSA[] = // RSA public key
{ TEXT(szOID_RSA_RSA), TEXT(szOID_OIWSEC_rsaXchg), NULL };
WCHAR const * const s_apwszDSA[] = // DSA public key
{ TEXT(szOID_X957_DSA), TEXT(szOID_OIWSEC_dsa), TEXT(szOID_INFOSEC_mosaicKMandUpdSig), NULL };
WCHAR const * const s_apwszDH[] = // DH public key
{ TEXT(szOID_ANSI_X942_DH), TEXT(szOID_RSA_DH), NULL };
KEYUSAGEMASK g_aKeyUsageMask1[] = { { s_apwszRSA,
// Valid CA cert Key Usage bits -> mask = 0xffff7ff6
dwKUMASK( CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE | CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE), // same as CERT_OFFLINE_CRL_SIGN_KEY_USAGE
// Valid EE cert Key Usage bits -> mask = 0xffff7ff0
dwKUMASK( CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE), },
{ s_apwszDSA,
// Valid CA cert Key Usage bits -> mask = 0xffff7fc6
dwKUMASK( CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE), // same as CERT_OFFLINE_CRL_SIGN_KEY_USAGE
// Valid EE cert Key Usage bits -> mask = 0xffff7fc0
dwKUMASK( CERT_DIGITAL_SIGNATURE_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE), },
{ s_apwszDH,
// Valid CA cert Key Usage bits -> mask = 0xffff7f09
dwKUMASK( CERT_KEY_AGREEMENT_KEY_USAGE | CERT_ENCIPHER_ONLY_KEY_USAGE),
// Valid EE cert Key Usage bits -> mask = 0xffff7f09
dwKUMASK( CERT_KEY_AGREEMENT_KEY_USAGE | CERT_ENCIPHER_ONLY_KEY_USAGE), }, };
KEYUSAGEMASK g_aKeyUsageMask2[] = { { s_apwszRSA,
// CERT_KEY_ENCIPHERMENT_KEY_USAGE(SHOULD not be set w/Cert,CRL sign)
// CERT_DATA_ENCIPHERMENT_KEY_USAGE (SHOULD not be set w/Cert,CRL sign)
CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE, // same as CERT_OFFLINE_CRL_SIGN_KEY_USAGE
CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE, }, };
CRITICAL_SECTION g_DSCacheCriticalSection; BOOL g_fDSCacheCriticalSection = FALSE;
HRESULT TPInitialize( IN ICertServerPolicy *) // pServer
{ return(S_OK); }
VOID TPCleanup() { }
HRESULT tpCAGetCertTypeProperty( IN HCERTTYPE hCertType, IN WCHAR const *pwszPropName, WCHAR ***papwszValues) { HRESULT hr; *papwszValues = NULL; hr = CAGetCertTypeProperty(hCertType, pwszPropName, papwszValues); _PrintIfErrorStr2( hr, "Policy:CAGetCertTypeProperty", pwszPropName, HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); if (S_OK != hr) { *papwszValues = NULL; if (HRESULT_FROM_WIN32(ERROR_NOT_FOUND) != hr) { goto error; } hr = S_OK; }
error: return(hr); }
HRESULT tpCAGetCertTypeStringProperty( IN HCERTTYPE hCertType, IN WCHAR const *pwszPropName, OUT WCHAR **ppwszValue) { HRESULT hr; WCHAR **apwszValues = NULL; *ppwszValue = NULL; hr = CAGetCertTypeProperty(hCertType, pwszPropName, &apwszValues); if (S_OK != hr) { apwszValues = NULL; _JumpErrorStr(hr, error, "CAGetCertTypeProperty", pwszPropName); } if (NULL == apwszValues || NULL == apwszValues[0]) { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); _JumpErrorStr(hr, error, "CAGetCertTypeProperty", pwszPropName); } hr = myDupString(apwszValues[0], ppwszValue); _JumpIfError(hr, error, "myDupString");
error: if (NULL != apwszValues) { CAFreeCertTypeProperty(hCertType, apwszValues); } return(hr); }
HRESULT tpCAGetCertTypeObjectIdList( IN HCERTTYPE hCertType, IN WCHAR const *pwszPropName, OUT OBJECTIDLIST *prgPolicies) { HRESULT hr; WCHAR **apwsz;
prgPolicies->cObjId = 0; prgPolicies->rgpwszObjId = NULL;
hr = tpCAGetCertTypeProperty( hCertType, pwszPropName, &prgPolicies->rgpwszObjId); _JumpIfErrorStr( hr, error, "CTemplatePolicy:tpCAGetCertTypeProperty", pwszPropName);
apwsz = prgPolicies->rgpwszObjId; if (NULL != apwsz) { while (NULL != *apwsz++) { prgPolicies->cObjId++; } } hr = S_OK;
error: return(hr); }
CTemplatePolicy::CTemplatePolicy() { m_hCertType = NULL; ZeroMemory(&m_tp, sizeof(m_tp)); m_pwszTemplateName = NULL; m_pwszTemplateObjId = NULL; m_pExtensions = NULL; ZeroMemory(&m_CriticalExtensions, sizeof(m_CriticalExtensions)); ZeroMemory(&m_PoliciesIssuance, sizeof(m_PoliciesIssuance)); ZeroMemory(&m_PoliciesApplication, sizeof(m_PoliciesApplication)); m_pPolicy = NULL; }
CTemplatePolicy::~CTemplatePolicy() { _Cleanup(); }
VOID CTemplatePolicy::_Cleanup() { ZeroMemory(&m_tp, sizeof(m_tp)); if (NULL != m_hCertType) { CACloseCertType(m_hCertType); m_hCertType = NULL; } if (NULL != m_pwszTemplateName) { LocalFree(m_pwszTemplateName); m_pwszTemplateName = NULL; } if (NULL != m_pwszTemplateObjId) { LocalFree(m_pwszTemplateObjId); m_pwszTemplateObjId = NULL; } if (NULL != m_pExtensions) { LocalFree(m_pExtensions); m_pExtensions = NULL; } if (NULL != m_CriticalExtensions.rgpwszObjId) { LocalFree(m_CriticalExtensions.rgpwszObjId); } ZeroMemory(&m_CriticalExtensions, sizeof(m_CriticalExtensions));
if (NULL != m_PoliciesIssuance.rgpwszObjId) { LocalFree(m_PoliciesIssuance.rgpwszObjId); } ZeroMemory(&m_PoliciesIssuance, sizeof(m_PoliciesIssuance));
if (NULL != m_PoliciesApplication.rgpwszObjId) { LocalFree(m_PoliciesApplication.rgpwszObjId); } ZeroMemory(&m_PoliciesApplication, sizeof(m_PoliciesApplication));
m_pPolicy = NULL; }
HRESULT CTemplatePolicy::_LogLoadResult( IN CCertPolicyEnterprise *pPolicy, IN ICertServerPolicy *pServer, IN HRESULT hrLoad) { HRESULT hr; WCHAR const *pwszError = NULL; WCHAR const *apwsz[2]; DWORD level; DWORD MsgId; WCHAR const *pwszLogProp; WCHAR *pwszNameAndVersion = NULL; #define wszFORMATVERSION L"(v%u.%u): V%u"
WCHAR wszVersion[ARRAYSIZE(wszFORMATVERSION) + 3 * cwcDWORDSPRINTF]; WCHAR const *pwszTemplate;
if (S_OK != hrLoad) { pwszError = myGetErrorMessageText(hrLoad, TRUE); level = CERTLOG_WARNING; MsgId = MSG_LOAD_TEMPLATE; pwszLogProp = wszPROPEVENTLOGWARNING; } else { level = CERTLOG_VERBOSE; MsgId = MSG_LOAD_TEMPLATE_SUCCEEDED; pwszLogProp = wszPROPEVENTLOGVERBOSE; } if (level > pPolicy->GetLogLevel()) { hr = S_OK; goto error; } wsprintf( wszVersion, wszFORMATVERSION, m_tp.dwTemplateMajorVersion, m_tp.dwTemplateMinorVersion, m_tp.dwSchemaVersion); CSASSERT(wcslen(wszVersion) < ARRAYSIZE(wszVersion));
pwszTemplate = m_pwszTemplateName; if (NULL == pwszTemplate) { pwszTemplate = L"???"; } pwszNameAndVersion = (WCHAR *) LocalAlloc( LMEM_FIXED, (wcslen(pwszTemplate) + wcslen(wszVersion) + 1) * sizeof(WCHAR)); if (NULL == pwszNameAndVersion) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } wcscpy(pwszNameAndVersion, pwszTemplate); wcscat(pwszNameAndVersion, wszVersion);
apwsz[0] = pwszNameAndVersion; apwsz[1] = pwszError;
hr = LogPolicyEvent( g_hInstance, S_OK, MsgId, pServer, pwszLogProp, apwsz); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:LogPolicyEvent");
error: if (NULL != pwszNameAndVersion) { LocalFree(pwszNameAndVersion); } if (NULL != pwszError) { LocalFree(const_cast<WCHAR *>(pwszError)); } return(hr); }
HRESULT CTemplatePolicy::_CloneExtensions( IN CERT_EXTENSIONS const *pExtensionsIn, OUT CERT_EXTENSIONS **ppExtensionsOut) { HRESULT hr; DWORD cb; CERT_EXTENSION *pExt; CERT_EXTENSION *pExtEnd; CERT_EXTENSION *pExtOut; BYTE *pbOut;
*ppExtensionsOut = NULL; cb = sizeof(CERT_EXTENSIONS) + pExtensionsIn->cExtension * sizeof(pExtensionsIn->rgExtension[0]);
pExtEnd = &pExtensionsIn->rgExtension[pExtensionsIn->cExtension]; for (pExt = pExtensionsIn->rgExtension; pExt < pExtEnd; pExt++) { cb += DWORDROUND(strlen(pExt->pszObjId) + 1); cb += DWORDROUND(pExt->Value.cbData); } *ppExtensionsOut = (CERT_EXTENSIONS *) LocalAlloc(LMEM_FIXED, cb); if (NULL == *ppExtensionsOut) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } (*ppExtensionsOut)->cExtension = pExtensionsIn->cExtension; pExtOut = (CERT_EXTENSION *) &(*ppExtensionsOut)[1]; (*ppExtensionsOut)->rgExtension = pExtOut;
pbOut = (BYTE *) &pExtOut[pExtensionsIn->cExtension]; for (pExt = pExtensionsIn->rgExtension; pExt < pExtEnd; pExt++, pExtOut++) { pExtOut->pszObjId = (char *) pbOut; strcpy(pExtOut->pszObjId, pExt->pszObjId); pbOut += DWORDROUND(strlen(pExt->pszObjId) + 1);
pExtOut->fCritical = pExt->fCritical; pExtOut->Value.cbData = pExt->Value.cbData;
pExtOut->Value.pbData = pbOut; CopyMemory( pExtOut->Value.pbData, pExt->Value.pbData, pExt->Value.cbData); pbOut += DWORDROUND(pExt->Value.cbData); } CSASSERT(Add2Ptr(*ppExtensionsOut, cb) == pbOut); hr = S_OK;
error: return(hr); }
HRESULT CTemplatePolicy::_CloneObjectIdList( IN OBJECTIDLIST const *pObjectIdListIn, OUT OBJECTIDLIST *pObjectIdListOut) { HRESULT hr; ZeroMemory(pObjectIdListOut, sizeof(*pObjectIdListOut)); pObjectIdListOut->cObjId = pObjectIdListIn->cObjId; if (0 != pObjectIdListIn->cObjId) { DWORD cb = (pObjectIdListIn->cObjId + 1) * sizeof(pObjectIdListIn->rgpwszObjId); WCHAR const * const *ppwsz; WCHAR const * const *ppwszEnd; WCHAR **ppwszOut; BYTE *pbOut;
ppwszEnd = &pObjectIdListIn->rgpwszObjId[pObjectIdListIn->cObjId]; for (ppwsz = pObjectIdListIn->rgpwszObjId; ppwsz < ppwszEnd; ppwsz++) { cb += DWORDROUND((wcslen(*ppwsz) + 1) * sizeof(WCHAR)); } pObjectIdListOut->rgpwszObjId = (WCHAR **) LocalAlloc(LMEM_FIXED, cb); if (NULL == pObjectIdListOut->rgpwszObjId) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } ppwszOut = pObjectIdListOut->rgpwszObjId; pbOut = (BYTE *) &ppwszOut[pObjectIdListOut->cObjId + 1]; for ( ppwsz = pObjectIdListIn->rgpwszObjId; ppwsz < ppwszEnd; ppwsz++, ppwszOut++) { *ppwszOut = (WCHAR *) pbOut; wcscpy(*ppwszOut, *ppwsz); pbOut += DWORDROUND((wcslen(*ppwszOut) + 1) * sizeof(WCHAR)); } } hr = S_OK;
error: return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::Initialize
// Populate the CTemplatePolicy object from the registry
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::Initialize( IN HCERTTYPE hCertType, IN ICertServerPolicy *pServer, IN CCertPolicyEnterprise *pPolicy) { HRESULT hr; CERT_EXTENSIONS *pExtensions = NULL; OBJECTIDLIST CriticalExtensions; OBJECTIDLIST PoliciesIssuance; OBJECTIDLIST PoliciesApplication;
ZeroMemory(&CriticalExtensions, sizeof(CriticalExtensions)); ZeroMemory(&PoliciesIssuance, sizeof(PoliciesIssuance)); ZeroMemory(&PoliciesApplication, sizeof(PoliciesApplication)); _Cleanup(); CSASSERT(0 == m_tp.dwTemplateMajorVersion); CSASSERT(0 == m_tp.dwTemplateMinorVersion); CSASSERT(0 == m_tp.dwSchemaVersion); CSASSERT(0 == m_tp.dwMinKeyLength);
hr = tpCAGetCertTypeStringProperty( hCertType, CERTTYPE_PROP_DN, &m_pwszTemplateName); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:tpCAGetCertTypeStringProperty", CERTTYPE_PROP_DN);
hr = CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_REVISION, &m_tp.dwTemplateMajorVersion); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:CAGetCertTypePropertyEx", CERTTYPE_PROP_REVISION);
hr = CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_SCHEMA_VERSION, &m_tp.dwSchemaVersion); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:CAGetCertTypePropertyEx", CERTTYPE_PROP_SCHEMA_VERSION);
if (CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion) { hr = tpCAGetCertTypeStringProperty( hCertType, CERTTYPE_PROP_OID, &m_pwszTemplateObjId); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:tpCAGetCertTypeStringProperty", CERTTYPE_PROP_OID);
hr = CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_MINOR_REVISION, &m_tp.dwTemplateMinorVersion); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:CAGetCertTypePropertyEx", CERTTYPE_PROP_MINOR_REVISION);
hr = CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_MIN_KEY_SIZE, &m_tp.dwMinKeyLength); _JumpIfErrorStr( hr, error, "CTemplatePolicy:Initialize:CAGetCertTypePropertyEx", CERTTYPE_PROP_MIN_KEY_SIZE); }
if (!FIsAdvancedServer() && CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion) { // V2 templates require Advanced Server
hr = HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED); _JumpError(hr, error, "CTemplatePolicy:Initialize:m_tp.dwSchemaVersion"); } m_pPolicy = pPolicy;
hr = CAGetCertTypeFlagsEx( hCertType, CERTTYPE_ENROLLMENT_FLAG, &m_tp.dwEnrollmentFlags); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:CAGetCertTypeFlagsEx");
hr = CAGetCertTypeFlagsEx( hCertType, CERTTYPE_SUBJECT_NAME_FLAG, &m_tp.dwSubjectNameFlags); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:CAGetCertTypeFlagsEx");
hr = CAGetCertTypeFlagsEx( hCertType, CERTTYPE_PRIVATE_KEY_FLAG, &m_tp.dwPrivateKeyFlags); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:CAGetCertTypeFlagsEx");
hr = CAGetCertTypeFlagsEx( hCertType, CERTTYPE_GENERAL_FLAG, &m_tp.dwGeneralFlags); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:CAGetCertTypeFlagsEx");
hr = CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_RA_SIGNATURE, &m_tp.dwcSignatureRequired); _PrintIfErrorStr2( hr, "CTemplatePolicy:Initialize:CAGetCertTypePropertyEx", CERTTYPE_PROP_RA_SIGNATURE, HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); if (S_OK != hr) { if (HRESULT_FROM_WIN32(ERROR_NOT_FOUND) != hr) { goto error; } m_tp.dwcSignatureRequired = 0; } hr = CAGetCertTypeExpiration( hCertType, &m_tp.llftExpirationPeriod.ft, &m_tp.llftOverlapPeriod.ft); _JumpIfError(hr, error, "Policy:CAGetCertTypeExpiration");
hr = CAGetCertTypeExtensions(hCertType, &pExtensions); _JumpIfError(hr, error, "CTemplatePolicy:Initialize:CAGetCertTypeExtensions");
hr = _CloneExtensions(pExtensions, &m_pExtensions); _JumpIfError(hr, error, "CTemplatePolicy:_CloneExtensions");
hr = tpCAGetCertTypeObjectIdList( hCertType, CERTTYPE_PROP_CRITICAL_EXTENSIONS, &CriticalExtensions); _JumpIfErrorStr( hr, error, "CTemplatePolicy:tpCAGetCertTypePolicies", CERTTYPE_PROP_CRITICAL_EXTENSIONS);
hr = _CloneObjectIdList(&CriticalExtensions, &m_CriticalExtensions); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
hr = tpCAGetCertTypeObjectIdList( hCertType, CERTTYPE_PROP_RA_POLICY, &PoliciesIssuance); _JumpIfErrorStr( hr, error, "CTemplatePolicy:tpCAGetCertTypePolicies", CERTTYPE_PROP_RA_POLICY);
hr = _CloneObjectIdList(&PoliciesIssuance, &m_PoliciesIssuance); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
hr = tpCAGetCertTypeObjectIdList( hCertType, CERTTYPE_PROP_RA_APPLICATION_POLICY, &PoliciesApplication); _JumpIfErrorStr( hr, error, "CTemplatePolicy:tpCAGetCertTypePolicies", CERTTYPE_PROP_RA_APPLICATION_POLICY);
hr = _CloneObjectIdList(&PoliciesApplication, &m_PoliciesApplication); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
m_hCertType = hCertType; // Transfer ownership only on success
error: if (NULL != pExtensions) { CAFreeCertTypeExtensions(hCertType, pExtensions); } if (NULL != CriticalExtensions.rgpwszObjId) { CAFreeCertTypeProperty(hCertType, CriticalExtensions.rgpwszObjId); } if (NULL != PoliciesIssuance.rgpwszObjId) { CAFreeCertTypeProperty(hCertType, PoliciesIssuance.rgpwszObjId); } if (NULL != PoliciesApplication.rgpwszObjId) { CAFreeCertTypeProperty(hCertType, PoliciesApplication.rgpwszObjId); } DBGPRINT(( DBG_SS_CERTPOL, "Policy:Template:Initialize(%ws, v%u.%u): V%u hr=%x\n", NULL != m_pwszTemplateName? m_pwszTemplateName : L"", m_tp.dwTemplateMajorVersion, m_tp.dwTemplateMinorVersion, m_tp.dwSchemaVersion, hr)); _LogLoadResult(pPolicy, pServer, hr); return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::Clone
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::Clone( OUT CTemplatePolicy **ppTemplate) { HRESULT hr; CTemplatePolicy *pTemplateClone = NULL;
*ppTemplate = NULL; pTemplateClone = new CTemplatePolicy; if (NULL == pTemplateClone) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:Clone:new"); } //pTemplateClone->m_hCertType = m_hCertType;
pTemplateClone->m_tp = m_tp;
if (NULL != m_pwszTemplateName) { hr = myDupString( m_pwszTemplateName, &pTemplateClone->m_pwszTemplateName); _JumpIfError(hr, error, "myDupString"); } if (NULL != m_pwszTemplateObjId) { hr = myDupString( m_pwszTemplateObjId, &pTemplateClone->m_pwszTemplateObjId); _JumpIfError(hr, error, "myDupString"); }
hr = _CloneExtensions(m_pExtensions, &pTemplateClone->m_pExtensions); _JumpIfError(hr, error, "CTemplatePolicy:_CloneExtensions");
hr = _CloneObjectIdList( &m_CriticalExtensions, &pTemplateClone->m_CriticalExtensions); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
hr = _CloneObjectIdList( &m_PoliciesIssuance, &pTemplateClone->m_PoliciesIssuance); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
hr = _CloneObjectIdList( &m_PoliciesApplication, &pTemplateClone->m_PoliciesApplication); _JumpIfError(hr, error, "CTemplatePolicy:_CloneObjectIdList");
pTemplateClone->m_pPolicy = m_pPolicy;
*ppTemplate = pTemplateClone; pTemplateClone = NULL; hr = S_OK;
error: if (NULL != pTemplateClone) { delete pTemplateClone; } return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::AccessCheck
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::AccessCheck( IN HANDLE hToken) { HRESULT hr;
hr = CACertTypeAccessCheck(m_hCertType, hToken); if (E_ACCESSDENIED == hr) { // map E_ACCESSDENIED to a more meaningful error
hr = CERTSRV_E_TEMPLATE_DENIED; } _JumpIfError(hr, error, "Policy:CACertTypeAccessCheck");
error: return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::Apply
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::Apply( IN ICertServerPolicy *pServer, IN CRequestInstance *pRequest, OUT BOOL *pfReenroll) { HRESULT hr; DWORD dwRequestTemplateMinimumMajorVersion; DWORD dwRequestTemplateMinimumMinorVersion; BOOL fEnrollOnBehalfOf = FALSE;
*pfReenroll = FALSE;
pRequest->GetTemplateVersion( &dwRequestTemplateMinimumMajorVersion, &dwRequestTemplateMinimumMinorVersion); CONSOLEPRINT5(( DBG_SS_CERTPOL, "Request cert type: %ws(v%u.%u/v%u.%u)\n", pRequest->GetTemplateName(), dwRequestTemplateMinimumMajorVersion, dwRequestTemplateMinimumMinorVersion, m_tp.dwTemplateMajorVersion, m_tp.dwTemplateMinorVersion));
hr = _AddBasicConstraintsExtension(pRequest, pServer); _JumpIfError(hr, error, "Policy:_AddBasicConstraintsExtension");
hr = _AddKeyUsageExtension(pServer, pRequest); _JumpIfError(hr, error, "Policy:_AddKeyUsageExtension");
hr = _AddTemplateExtensionArray(pServer); _JumpIfError(hr, error, "Policy:_AddTemplateExtensionArray");
if (0 == (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT & m_tp.dwSubjectNameFlags)) { hr = pRequest->_LoadPrincipalObject( pServer, this, 0 != ((CT_FLAG_SUBJECT_REQUIRE_COMMON_NAME | CT_FLAG_SUBJECT_REQUIRE_DNS_AS_CN | CT_FLAG_SUBJECT_ALT_REQUIRE_DNS) & m_tp.dwSubjectNameFlags)); _JumpIfError(hr, error, "_LoadPrincipalObject");
hr = _AddSubjectName(pServer, pRequest); _JumpIfError(hr, error, "Policy:_AddSubjectName"); } hr = _AddAltSubjectName(pServer, pRequest); _JumpIfError(hr, error, "Policy:_AddAltSubjectName");
pRequest->_ReleasePrincipalObject();
hr = _ApplyExpirationTime(pServer, pRequest); _JumpIfError(hr, error, "Policy:_ApplyExpirationTime");
hr = _EnforceKeySizePolicy(pServer); _JumpIfError(hr, error, "Policy:_EnforceKeySizePolicy");
hr = _EnforceKeyArchivalPolicy(pServer); _JumpIfError(hr, error, "Policy:_EnforceKeyArchivalPolicy");
hr = _EnforceSymmetricAlgorithms(pServer); _JumpIfError(hr, error, "Policy:_EnforceSymmetricAlgorithms");
hr = _EnforceMinimumTemplateVersion(pRequest); _JumpIfError(hr, error, "Policy:_EnforceMinimumTemplateVersion");
hr = _EnforceEnrollOnBehalfOfAllowed(pServer, &fEnrollOnBehalfOf); _JumpIfError(hr, error, "Policy:_EnforceEnrollOnBehalfOfAllowed");
hr = S_FALSE; if (CT_FLAG_PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT & m_tp.dwEnrollmentFlags) { hr = _EnforceReenrollment(pServer, pRequest); _PrintIfError(hr, "Policy:_EnforceReenrollment"); if (S_OK == hr) { *pfReenroll = TRUE; } } if (S_OK != hr) { hr = _EnforceSignaturePolicy(pServer, pRequest, fEnrollOnBehalfOf); _JumpIfError(hr, error, "Policy:_EnforceSignaturePolicy"); } CSASSERT(S_OK == hr);
error: pRequest->_ReleasePrincipalObject(); return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::_AddBasicConstraintsExtension
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddBasicConstraintsExtension( IN CRequestInstance *pRequest, IN ICertServerPolicy *pServer) { HRESULT hr; CERT_EXTENSION const *pExt; CERT_EXTENSION BasicConstraintsExtension;
BasicConstraintsExtension.Value.pbData = NULL;
pExt = CertFindExtension( szOID_BASIC_CONSTRAINTS2, m_pExtensions->cExtension, m_pExtensions->rgExtension); if (NULL == pExt) { if (pRequest->_IsNTClientOlder( 5, 0, VERSION_AUTOENROLLMENT_BC_AWARE, VER_PLATFORM_WIN32_NT)) {
CERT_BASIC_CONSTRAINTS2_INFO OldBasicConstraints = { FALSE, FALSE, 0};
// Older autoenrollment clients don't know how to deal with
// having no basic constraints extension, so they might loop.
// For an old client, we must fabricate a basic constraints.
if (!myEncodeObject( X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS2, &OldBasicConstraints, 0, CERTLIB_USE_LOCALALLOC, &BasicConstraintsExtension.Value.pbData, &BasicConstraintsExtension.Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myEncodeObject"); } BasicConstraintsExtension.fCritical = TRUE; BasicConstraintsExtension.pszObjId = szOID_BASIC_CONSTRAINTS2; pExt = &BasicConstraintsExtension; } } hr = m_pPolicy->AddBasicConstraintsCommon( pServer, pExt, FALSE, // fCA only enabled for standalone
NULL != pExt); _JumpIfError(hr, error, "Policy:AddBasicConstraintsCommon");
error: if (NULL != BasicConstraintsExtension.Value.pbData) { LocalFree(BasicConstraintsExtension.Value.pbData); } return(hr); }
KEYUSAGEMASK const * FindKeyUsage( IN WCHAR const *pwszAlg, IN KEYUSAGEMASK const *pKeyUsageMask, IN DWORD cKeyUsageMask) { KEYUSAGEMASK const *pKeyUsageMaskRet = NULL; KEYUSAGEMASK const *pKeyUsageMaskEnd;
for ( pKeyUsageMaskEnd = &pKeyUsageMask[cKeyUsageMask]; pKeyUsageMask < pKeyUsageMaskEnd; pKeyUsageMask++) { DWORD i; for (i = 0; NULL != pKeyUsageMask->apwszAlg[i]; i++) { if (0 == wcscmp(pwszAlg, pKeyUsageMask->apwszAlg[i])) { pKeyUsageMaskRet = pKeyUsageMask; break; } } if (NULL != pKeyUsageMaskRet) { break; } } return(pKeyUsageMaskRet); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::_AddKeyUsageExtension
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddKeyUsageExtension( IN ICertServerPolicy *pServer, IN CRequestInstance *pRequest) { HRESULT hr = S_OK; BSTR strExtension = NULL; VARIANT varExtension; BSTR strAlg = NULL; CERT_EXTENSION const *pExt; CRYPT_BIT_BLOB *pKeyUsage = NULL; BYTE *pbKeyUsage = NULL; DWORD cbKeyUsage; DWORD dwKU; KEYUSAGEMASK const *pKeyUsageMask;
VariantInit(&varExtension);
pExt = CertFindExtension( szOID_KEY_USAGE, m_pExtensions->cExtension, m_pExtensions->rgExtension); if (NULL == pExt) { hr = S_OK; goto error; }
if (!myDecodeObject( X509_ASN_ENCODING, X509_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pKeyUsage, &cbKeyUsage)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myDecodeObject"); }
hr = polGetCertificateStringProperty( pServer, wszPROPCERTIFICATEPUBLICKEYALGORITHM, &strAlg); _JumpIfError(hr, error, "Policy:polGetCertificateStringProperty");
// Mask out any illegal bits
dwKU = 0; CopyMemory(&dwKU, pKeyUsage->pbData, min(sizeof(dwKU), pKeyUsage->cbData)); pKeyUsageMask = FindKeyUsage( strAlg, g_aKeyUsageMask1, ARRAYSIZE(g_aKeyUsageMask1)); if (NULL != pKeyUsageMask) { dwKU &= pRequest->IsCARequest()? pKeyUsageMask->dwMask1 : pKeyUsageMask->dwMask2; }
pKeyUsageMask = FindKeyUsage( strAlg, g_aKeyUsageMask2, ARRAYSIZE(g_aKeyUsageMask2)); if (NULL != pKeyUsageMask && (dwKU & pKeyUsageMask->dwMask1)) { dwKU &= ~pKeyUsageMask->dwMask2; }
CopyMemory(pKeyUsage->pbData, &dwKU, min(sizeof(dwKU), pKeyUsage->cbData));
if (!myEncodeObject( X509_ASN_ENCODING, X509_KEY_USAGE, pKeyUsage, 0, CERTLIB_USE_LOCALALLOC, &pbKeyUsage, &cbKeyUsage)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myEncodeObject"); }
if (!myConvertWszToBstr( &strExtension, (WCHAR const *) pbKeyUsage, cbKeyUsage)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:myConvertWszToBstr"); }
varExtension.vt = VT_BSTR; varExtension.bstrVal = strExtension; strExtension = NULL; hr = polSetCertificateExtension( pServer, TEXT(szOID_KEY_USAGE), PROPTYPE_BINARY, pExt->fCritical? EXTENSION_CRITICAL_FLAG : 0, &varExtension); _JumpIfError(hr, error, "Policy:polSetCertificateExtension");
error: VariantClear(&varExtension); if (NULL != strAlg) { SysFreeString(strAlg); } if (NULL != strExtension) { SysFreeString(strExtension); } if (NULL != pbKeyUsage) { LocalFree(pbKeyUsage); } if (NULL != pKeyUsage) { LocalFree(pKeyUsage); } return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::_AddTemplateExtensionArray
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddTemplateExtensionArray( IN ICertServerPolicy *pServer) { HRESULT hr; DWORD i;
for (i = 0; i < m_pExtensions->cExtension; i++) { CERT_EXTENSION const *pExt = &m_pExtensions->rgExtension[i];
// Skip extensions that have special handling code.
if (0 == strcmp(szOID_BASIC_CONSTRAINTS2, pExt->pszObjId) || 0 == strcmp(szOID_KEY_USAGE, pExt->pszObjId)) { continue; } hr = _AddTemplateExtension(pServer, pExt); _JumpIfError(hr, error, "Policy:_AddTemplateExtension"); } hr = S_OK;
error: return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::_AddTemplateExtension
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddTemplateExtension( IN ICertServerPolicy *pServer, IN CERT_EXTENSION const *pExt) { HRESULT hr = S_OK; BSTR strExtension = NULL; BSTR strObjId = NULL; VARIANT varExtension;
if (!myConvertSzToBstr(&strObjId, pExt->pszObjId, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:myConvertSzToBstr"); }
if (!myConvertWszToBstr( &strExtension, (WCHAR const *) pExt->Value.pbData, pExt->Value.cbData)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:myConvertWszToBstr"); }
varExtension.vt = VT_BSTR; varExtension.bstrVal = strExtension; hr = pServer->SetCertificateExtension( strObjId, PROPTYPE_BINARY, pExt->fCritical? EXTENSION_CRITICAL_FLAG : 0, &varExtension); _JumpIfErrorStr(hr, error, "Policy:polSetCertificateExtension", strObjId);
error: if (NULL != strObjId) { SysFreeString(strObjId); } if (NULL != strExtension) { SysFreeString(strExtension); } return(hr); }
//+--------------------------------------------------------------------------
// CTemplatePolicy::_AddSubjectName
//
// Build the subject name and add it to the cert, if required
//
// The subject name consists of:
// machine: the CN is set to the UPN (machineDNSName)
// old user: the CN is set to the UPN (Kerberos name)
// user: the CN is set to the DS_ATTR_COMMON_NAME
//
// both: E= indicates an e-mail name
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddSubjectName( IN ICertServerPolicy *pServer, CRequestInstance *pRequest) { HRESULT hr; BSTRC strCN = NULL; BSTRC strEMail = NULL; BSTR strPropEMail = NULL; VARIANT varValue; BSTR strSubjectDot = NULL; BSTR strSubjectCommonName = NULL;
varValue.vt = VT_NULL;
strSubjectDot = SysAllocString(wszPROPSUBJECTDOT); strSubjectCommonName = SysAllocString(wszPROPSUBJECTCOMMONNAME); if (NULL == strSubjectDot || NULL == strSubjectCommonName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocString"); }
// Clear out any existing subject name info
hr = pServer->SetCertificateProperty( strSubjectDot, PROPTYPE_STRING, &varValue); _JumpIfError(hr, error, "Policy:SetCertificateProperty");
if (CT_FLAG_SUBJECT_REQUIRE_DIRECTORY_PATH & m_tp.dwSubjectNameFlags) { hr = _AddDSDistinguishedName(pServer, pRequest); _JumpIfError(hr, error, "Policy:_AddDSDistinguishedName"); } else if ((CT_FLAG_SUBJECT_REQUIRE_COMMON_NAME | CT_FLAG_SUBJECT_REQUIRE_DNS_AS_CN) & m_tp.dwSubjectNameFlags) { if (!pRequest->_IsUser() || (pRequest->_IsXenrollRequest() && pRequest->_IsNTClientOlder( 5, 0, VERSION_AUTOENROLLMENT_UPN_AWARE, VER_PLATFORM_WIN32_NT)))
{ // The UPN will be either a user UPN or the machine DNS name
if (NULL == pRequest->m_pwszUPN) { hr = E_POINTER; // We should never get this
_JumpError(hr, error, "Policy:NULL UPN"); } strCN = SysAllocString(pRequest->m_pwszUPN); if (NULL == strCN) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocString"); } } else { // We are talking to an advanced client which can deal with a UPN
// in a SubjectAltName extension. Put the DS CN in the cert.
hr = pRequest->_GetValueString(DS_ATTR_COMMON_NAME, &strCN); _JumpIfErrorStr( hr, error, "Policy:_GetValueString", DS_ATTR_COMMON_NAME); } CSASSERT(NULL != strCN); varValue.vt = VT_BSTR; varValue.bstrVal = const_cast<BSTR>(strCN);
hr = pServer->SetCertificateProperty( strSubjectCommonName, PROPTYPE_STRING, &varValue); _JumpIfError(hr, error, "Policy:SetCertificateProperty"); }
if (CT_FLAG_SUBJECT_REQUIRE_EMAIL & m_tp.dwSubjectNameFlags) { hr = pRequest->_GetValueString(DS_ATTR_EMAIL_ADDR, &strEMail); if (S_OK != hr) { if (CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion && 0 == (EDITF_EMAILOPTIONAL & m_pPolicy->GetEditFlags())) { hr = CERTSRV_E_SUBJECT_EMAIL_REQUIRED; _JumpError(hr, error, "Policy:_AddSubjectName:EMail"); } pRequest->BuildErrorInfo( hr, MSG_NO_EMAIL_NAME, &pRequest->m_strUserDN); } else { varValue.vt = VT_BSTR; varValue.bstrVal = const_cast<BSTR>(strEMail);
strPropEMail = SysAllocString(wszPROPSUBJECTEMAIL); if (NULL == strPropEMail) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocStringLen"); }
hr = pServer->SetCertificateProperty( strPropEMail, PROPTYPE_STRING, &varValue); _JumpIfError(hr, error, "Policy:SetCertificateProperty"); } } hr = S_OK;
error: if (NULL != strPropEMail) { SysFreeString(strPropEMail); } if (NULL != strCN) { SysFreeString(const_cast<BSTR>(strCN)); } if (NULL != strEMail) { SysFreeString(const_cast<BSTR>(strEMail)); } if (NULL != strSubjectDot) { SysFreeString(strSubjectDot); } if (NULL != strSubjectCommonName) { SysFreeString(strSubjectCommonName); } return(hr); }
HRESULT tpDsUnquoteRdnValue( IN DWORD cwcVal, IN WCHAR const *pwcVal, OUT WCHAR **ppwszVal) { HRESULT hr; DWORD cwc;
*ppwszVal = NULL; cwc = 0; while (TRUE) { hr = DsUnquoteRdnValue(cwcVal, pwcVal, &cwc, *ppwszVal); if (S_OK != hr) { hr = myHError(hr); if (NULL == *ppwszVal) { if (HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) == hr) { hr = S_OK; } } else { LocalFree(*ppwszVal); *ppwszVal = NULL; } _JumpIfError(hr, error, "Policy:DsUnquoteRdnValue"); } if (NULL != *ppwszVal) { (*ppwszVal)[cwc] = L'\0'; break; } *ppwszVal = (WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * (cwc + 1)); if (NULL == *ppwszVal) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } } hr = S_OK;
error: return(hr); }
HRESULT tpSetRequestRDN( IN ICertServerPolicy *pServer, IN WCHAR const *pwcKey, IN DWORD cwcKey, IN WCHAR const *pwcVal, IN DWORD cwcVal) { HRESULT hr; WCHAR *pwszVal = NULL; BSTR strRDNName = NULL; BSTR strValueOld = NULL; BSTR strValue = NULL; VARIANT varNew; hr = tpDsUnquoteRdnValue(cwcVal, pwcVal, &pwszVal); _JumpIfError(hr, error, "Policy:tpDsUnquoteRdnValue");
strRDNName = SysAllocStringLen( NULL, WSZARRAYSIZE(wszPROPSUBJECTDOT) + cwcKey); if (NULL == strRDNName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocStringLen"); } wcscpy(strRDNName, wszPROPSUBJECTDOT); CopyMemory( &strRDNName[WSZARRAYSIZE(wszPROPSUBJECTDOT)], pwcKey, sizeof(WCHAR) * cwcKey); strRDNName[WSZARRAYSIZE(wszPROPSUBJECTDOT) + cwcKey] = L'\0'; CSASSERT(SysStringByteLen(strRDNName) == wcslen(strRDNName) * sizeof(WCHAR));
hr = polGetCertificateStringProperty(pServer, strRDNName, &strValueOld); if (S_OK == hr) { strValue = SysAllocStringLen( NULL, wcslen(pwszVal) + WSZARRAYSIZE(wszNAMESEPARATORDEFAULT) + wcslen(strValueOld)); if (NULL == strValue) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocStringLen"); } wcscpy(strValue, pwszVal); wcscat(strValue, wszNAMESEPARATORDEFAULT); wcscat(strValue, strValueOld); } else { strValue = SysAllocString(pwszVal); if (NULL == strValue) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocStringLen"); } } CSASSERT(SysStringByteLen(strValue) == wcslen(strValue) * sizeof(WCHAR)); varNew.vt = VT_BSTR; varNew.bstrVal = strValue; hr = pServer->SetCertificateProperty(strRDNName, PROPTYPE_STRING, &varNew); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; } _JumpIfError(hr, error, "Policy:SetCertificateProperty");
error: if (NULL != pwszVal) { LocalFree(pwszVal); } if (NULL != strRDNName) { SysFreeString(strRDNName); } if (NULL != strValueOld) { SysFreeString(strValueOld); } if (NULL != strValue) { SysFreeString(strValue); } return(hr); }
DWORD s_myDsGetRdnW( IN OUT WCHAR const **ppwcDN, IN OUT DWORD *pcwcDN, OUT WCHAR const **ppwcKey, OUT DWORD *pcwcKey, OUT WCHAR const **ppwcVal, OUT DWORD *pcwcVal) { HRESULT hr; WCHAR const *pwc = *ppwcDN; DWORD cwc = *pcwcDN;
if (0 < cwc && L',' == *pwc) { pwc++; cwc--; } hr = ERROR_DS_NAME_UNPARSEABLE;
*ppwcKey = pwc; while (TRUE) { if (0 == cwc) { _JumpError(hr, error, "no key"); } if (L'=' == *pwc) { break; } pwc++; cwc--; } *pcwcKey = SAFE_SUBTRACT_POINTERS(pwc, *ppwcKey); pwc++; cwc--;
*ppwcVal = pwc; if (0 == cwc) { _JumpError(hr, error, "no value"); } while (TRUE) { if (0 == cwc || L',' == *pwc) { break; } pwc++; cwc--; } *pcwcVal = SAFE_SUBTRACT_POINTERS(pwc, *ppwcVal); *ppwcDN = pwc; *pcwcDN = cwc; hr = S_OK;
error: return(hr); }
typedef DWORD (WINAPI FNDSGETRDNW)( IN OUT LPCWCH *ppDN, IN OUT DWORD *pcDN, OUT LPCWCH *ppKey, OUT DWORD *pcKey, OUT LPCWCH *ppVal, OUT DWORD *pcVal);
DWORD myDsGetRdn( IN OUT WCHAR const **ppwcDN, IN OUT DWORD *pcwcDN, OUT WCHAR const **ppwcKey, OUT DWORD *pcwcKey, OUT WCHAR const **ppwcVal, OUT DWORD *pcwcVal) { HRESULT hr; HMODULE hModule; static FNDSGETRDNW *s_pfn = NULL;
if (NULL == s_pfn) { hModule = GetModuleHandle(TEXT("ntdsapi.dll")); if (NULL == hModule) { hr = GetLastError(); goto error; }
// load system function
s_pfn = (FNDSGETRDNW *) GetProcAddress(hModule, "DsGetRdnW"); if (NULL == s_pfn) { hr = GetLastError(); _PrintError(hr, "ntdsapi.dll!DsGetRdnW");
s_pfn = s_myDsGetRdnW; } } hr = (*s_pfn)(ppwcDN, pcwcDN, ppwcKey, pcwcKey, ppwcVal, pcwcVal); _JumpIfError(hr, error, "Policy:DsGetRdnW");
error: return(hr); }
HRESULT CTemplatePolicy::_AddDSDistinguishedName( IN ICertServerPolicy *pServer, CRequestInstance *pRequest) { HRESULT hr; WCHAR const *pwcDN; DWORD cwcDN; DWORD cRDN;
cRDN = 0; pwcDN = pRequest->m_strUserDN; if (NULL != pwcDN) { WCHAR const *pwcKey; WCHAR const *pwcVal; DWORD cwcKey; DWORD cwcVal;
cwcDN = wcslen(pwcDN); while (0 != cwcDN) { hr = myDsGetRdn(&pwcDN, &cwcDN, &pwcKey, &cwcKey, &pwcVal, &cwcVal); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "Policy:myDsGetRdn"); } if (0 != cwcKey && 0 != cwcVal) { hr = tpSetRequestRDN( pServer, pwcKey, cwcKey, pwcVal, cwcVal); _JumpIfError(hr, error, "Policy:tpSetRequestRDN");
cRDN++; } } } if (0 == cRDN) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "Policy:m_strUserDN"); } hr = S_OK;
error: return(hr); }
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_AddAltSubjectName
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_AddAltSubjectName( IN ICertServerPolicy *pServer, IN OUT CRequestInstance *pRequest) { HRESULT hr = S_OK; DWORD cbExtension; VARIANT varExtension; DWORD iNameEntry; WCHAR **ppwszCurName; CERT_OTHER_NAME objectGuidOtherName; CERT_OTHER_NAME upnOtherName;
WCHAR **apwszMailNames = NULL; WCHAR **apwszMachineNames = NULL; BYTE *pbExtension= NULL; BSTR strobjectGuid = NULL; CERT_ALT_NAME_INFO AltName;
AltName.cAltEntry = 0; AltName.rgAltEntry = NULL; objectGuidOtherName.Value.pbData = NULL; objectGuidOtherName.Value.cbData = 0; upnOtherName.Value.pbData = NULL; upnOtherName.Value.cbData = 0;
VariantInit(&varExtension);
// If this cert template doesn't set the alt-subject-name, then enable
// whatever alt subject name that was in the request.
if (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME & m_tp.dwSubjectNameFlags) { LONG ExtFlags;
hr = polGetCertificateExtension( pServer, TEXT(szOID_SUBJECT_ALT_NAME2), PROPTYPE_BINARY, &varExtension); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = CERTSRV_E_SUBJECT_ALT_NAME_REQUIRED; } _JumpIfError(hr, error, "Policy:polGetCertificateExtension");
hr = pServer->GetCertificateExtensionFlags(&ExtFlags); _JumpIfError(hr, error, "Policy:GetCertificateExtensionFlags");
if (EXTENSION_DISABLE_FLAG & ExtFlags) { ExtFlags &= ~EXTENSION_DISABLE_FLAG; hr = polSetCertificateExtension( pServer, TEXT(szOID_SUBJECT_ALT_NAME2), PROPTYPE_BINARY, ExtFlags, &varExtension); _JumpIfError(hr, error, "Policy:polSetCertificateExtension"); } CSASSERT(S_OK == hr); goto error; }
// We do alt name entries for
// UPN/SPN
// rfc822 (mail name)
// DNSname
// DS location
if ((CT_FLAG_SUBJECT_ALT_REQUIRE_UPN | CT_FLAG_SUBJECT_ALT_REQUIRE_SPN) & m_tp.dwSubjectNameFlags) { // Add the UPN
if (NULL == pRequest->m_pwszUPN) { hr = CERTSRV_E_SUBJECT_UPN_REQUIRED; _JumpError(hr, error, "Policy:_AddAltSubjectName:UPN"); } AltName.cAltEntry++; }
if (CT_FLAG_SUBJECT_ALT_REQUIRE_EMAIL & m_tp.dwSubjectNameFlags) { hr = pRequest->_GetValues(DS_ATTR_EMAIL_ADDR, &apwszMailNames); if (S_OK != hr || NULL == apwszMailNames || NULL == apwszMailNames[0]) { _PrintIfError(hr, "_GetValues(email)"); if (CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion && 0 == (EDITF_EMAILOPTIONAL & m_pPolicy->GetEditFlags())) { hr = CERTSRV_E_SUBJECT_EMAIL_REQUIRED; _JumpError(hr, error, "Policy:_AddAltSubjectName:EMail"); } pRequest->BuildErrorInfo( hr, MSG_NO_EMAIL_NAME, &pRequest->m_strUserDN); } else { ppwszCurName = apwszMailNames; while (NULL != *ppwszCurName) { AltName.cAltEntry++; ppwszCurName++; } } }
if (CT_FLAG_SUBJECT_ALT_REQUIRE_DIRECTORY_GUID & m_tp.dwSubjectNameFlags) { hr = pRequest->_GetObjectGUID(&strobjectGuid); if (S_OK != hr || NULL == strobjectGuid) { _PrintIfError(hr, "_GetObjectGUID"); hr = CERTSRV_E_SUBJECT_DIRECTORY_GUID_REQUIRED; _JumpError(hr, error, "Policy:_AddAltSubjectName:GUID"); } AltName.cAltEntry++; } if (CT_FLAG_SUBJECT_ALT_REQUIRE_DNS & m_tp.dwSubjectNameFlags) { hr = pRequest->_GetValues(DS_ATTR_DNS_NAME, &apwszMachineNames); if (S_OK != hr || NULL == apwszMachineNames || NULL == apwszMachineNames[0]) { _PrintIfError(hr, "_GetValues(dns)"); hr = CERTSRV_E_SUBJECT_DNS_REQUIRED; _JumpError(hr, error, "Policy:_AddAltSubjectName:DNS"); }
ppwszCurName = apwszMachineNames; while (NULL != *ppwszCurName) { AltName.cAltEntry++; ppwszCurName++; } }
if (AltName.cAltEntry == 0) { hr = S_OK; goto error; }
AltName.rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc( LMEM_FIXED, sizeof(CERT_ALT_NAME_ENTRY) * AltName.cAltEntry); if (NULL == AltName.rgAltEntry) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } iNameEntry = 0;
if ((CT_FLAG_SUBJECT_ALT_REQUIRE_UPN | CT_FLAG_SUBJECT_ALT_REQUIRE_SPN) & m_tp.dwSubjectNameFlags) { // Add the UPN
CERT_NAME_VALUE nameUpn;
nameUpn.dwValueType = CERT_RDN_UTF8_STRING; nameUpn.Value.pbData = (BYTE *) pRequest->m_pwszUPN; nameUpn.Value.cbData = wcslen(pRequest->m_pwszUPN) * sizeof(WCHAR);
if (!myEncodeObject( X509_ASN_ENCODING, X509_UNICODE_ANY_STRING, &nameUpn, 0, CERTLIB_USE_LOCALALLOC, &upnOtherName.Value.pbData, &upnOtherName.Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myEncodeObject"); }
upnOtherName.pszObjId = szOID_NT_PRINCIPAL_NAME; AltName.rgAltEntry[iNameEntry].dwAltNameChoice= CERT_ALT_NAME_OTHER_NAME; AltName.rgAltEntry[iNameEntry++].pOtherName = &upnOtherName; }
// Now do strobjectGuid
if (CT_FLAG_SUBJECT_ALT_REQUIRE_DIRECTORY_GUID & m_tp.dwSubjectNameFlags) { CRYPT_DATA_BLOB blobGuid;
CSASSERT(NULL != strobjectGuid); blobGuid.pbData = (BYTE *) strobjectGuid; blobGuid.cbData = SysStringByteLen(strobjectGuid);
objectGuidOtherName.pszObjId = szOID_NTDS_REPLICATION;
if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &blobGuid, 0, CERTLIB_USE_LOCALALLOC, &objectGuidOtherName.Value.pbData, &objectGuidOtherName.Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myEncodeObject"); } AltName.rgAltEntry[iNameEntry].dwAltNameChoice= CERT_ALT_NAME_OTHER_NAME; AltName.rgAltEntry[iNameEntry++].pOtherName = &objectGuidOtherName; }
// Now do rfc822
if (CT_FLAG_SUBJECT_ALT_REQUIRE_EMAIL & m_tp.dwSubjectNameFlags) { ppwszCurName = apwszMailNames; if (NULL != ppwszCurName) { while (NULL != *ppwszCurName) { AltName.rgAltEntry[iNameEntry].dwAltNameChoice= CERT_ALT_NAME_RFC822_NAME; AltName.rgAltEntry[iNameEntry++].pwszRfc822Name = *ppwszCurName; ppwszCurName++; } } }
// Now do DNS
if (CT_FLAG_SUBJECT_ALT_REQUIRE_DNS & m_tp.dwSubjectNameFlags) { ppwszCurName = apwszMachineNames; if (NULL != ppwszCurName) { while (NULL != *ppwszCurName) { AltName.rgAltEntry[iNameEntry].dwAltNameChoice= CERT_ALT_NAME_DNS_NAME; AltName.rgAltEntry[iNameEntry++].pwszRfc822Name = *ppwszCurName; ppwszCurName++; } } }
if (!myEncodeObject( X509_ASN_ENCODING, X509_ALTERNATE_NAME, &AltName, 0, CERTLIB_USE_LOCALALLOC, &pbExtension, &cbExtension)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myEncodeObject"); }
varExtension.vt = VT_BSTR; varExtension.bstrVal = SysAllocStringByteLen( (char const *) pbExtension, cbExtension); if (NULL == varExtension.bstrVal) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocStringByteLen"); } hr = polSetCertificateExtension( pServer, TEXT(szOID_SUBJECT_ALT_NAME2), PROPTYPE_BINARY, 0, &varExtension); _JumpIfError(hr, error, "Policy:polSetCertificateExtension");
error: if (NULL != pbExtension) { LocalFree(pbExtension); } if (NULL != strobjectGuid) { SysFreeString(strobjectGuid); } if (NULL != AltName.rgAltEntry) { // clean each entry in array
for (DWORD i = 0; i < AltName.cAltEntry; i++) { switch (AltName.rgAltEntry[i].dwAltNameChoice) { case CERT_ALT_NAME_DIRECTORY_NAME: { if (NULL != AltName.rgAltEntry[i].DirectoryName.pbData) { LocalFree(AltName.rgAltEntry[i].DirectoryName.pbData); AltName.rgAltEntry[i].DirectoryName.pbData = NULL; } break; } case CERT_ALT_NAME_OTHER_NAME: // points to objectGuidOtherName or bstrUpn, which are
// freed separately
break;
case CERT_ALT_NAME_RFC822_NAME: // points to apwszMailNames, freed later
break;
case CERT_ALT_NAME_DNS_NAME: // points to apwszMachineNames, freed later
break; } }
// free array
LocalFree(AltName.rgAltEntry); } if (NULL != objectGuidOtherName.Value.pbData) { LocalFree(objectGuidOtherName.Value.pbData); } if (NULL != upnOtherName.Value.pbData) { LocalFree(upnOtherName.Value.pbData); } if (NULL != pRequest) { if (NULL != apwszMailNames) { pRequest->_FreeValues(apwszMailNames); } if (NULL != apwszMachineNames) { pRequest->_FreeValues(apwszMachineNames); } } VariantClear(&varExtension); return(hr); }
#ifdef DBG_CERTSRV_DEBUG_PRINT
# define DBGPRINTTIMEORPERIOD(pszDesc, pft) \
policyDbgPrintTimeOrPeriod((pszDesc), (pft))
VOID policyDbgPrintTimeOrPeriod( IN char const *pszDesc, IN FILETIME const *pft) { HRESULT hr; WCHAR *pwszTime = NULL; WCHAR awc[1];
if (0 <= (LONG) pft->dwHighDateTime) { hr = myGMTFileTimeToWszLocalTime(pft, TRUE, &pwszTime); _PrintIfError(hr, "myGMTFileTimeToWszLocalTime"); } else { hr = myFileTimePeriodToWszTimePeriod(pft, TRUE, &pwszTime); _PrintIfError(hr, "myFileTimePeriodToWszTimePeriod"); } if (S_OK != hr) { awc[0] = L'\0'; pwszTime = awc; } DBGPRINT((DBG_SS_CERTPOL, "%hs: %ws\n", pszDesc, pwszTime));
//error:
if (NULL != pwszTime && awc != pwszTime) { LocalFree(pwszTime); } } #else // DBG_CERTSRV_DEBUG_PRINT
# define DBGPRINTTIMEORPERIOD(pszDesc, pft)
#endif // DBG_CERTSRV_DEBUG_PRINT
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_ApplyExpirationTime
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_ApplyExpirationTime( IN ICertServerPolicy *pServer, IN OUT CRequestInstance *pRequest) { HRESULT hr = S_OK; VARIANT varValue; BSTR strNameNotBefore = NULL; BSTR strNameNotAfter = NULL; SYSTEMTIME SystemTime; LLFILETIME llftNotAfter; LLFILETIME llftNotAfterCalc; LLFILETIME llftNotBefore; LLFILETIME llftNotAfterOverlap; WCHAR const *pwszTemplate;
VariantInit(&varValue);
pwszTemplate = m_pwszTemplateName; if (NULL == pwszTemplate) { pwszTemplate = m_pwszTemplateObjId; }
strNameNotBefore = SysAllocString(wszPROPCERTIFICATENOTBEFOREDATE); if (NULL == strNameNotBefore) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocString"); } hr = pServer->GetCertificateProperty( strNameNotBefore, PROPTYPE_DATE, &varValue); _JumpIfError(hr, error, "Policy:GetCertificateProperty");
if (VT_DATE != varValue.vt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Policy:varValue.vt"); }
if (!VariantTimeToSystemTime(varValue.date, &SystemTime)) { hr = myHLastError(); _JumpError(hr, error, "Policy:VariantTimeToSystemTime"); }
if (!SystemTimeToFileTime(&SystemTime, &llftNotBefore.ft)) { hr = myHLastError(); _JumpError(hr, error, "Policy:SystemTimeToFileTime"); }
strNameNotAfter = SysAllocString(wszPROPCERTIFICATENOTAFTERDATE); if (NULL == strNameNotAfter) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocString"); } VariantClear(&varValue); hr = pServer->GetCertificateProperty( strNameNotAfter, PROPTYPE_DATE, &varValue); _JumpIfError(hr, error, "Policy:GetCertificateProperty");
if (VT_DATE != varValue.vt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Policy:varValue.vt"); }
if (!VariantTimeToSystemTime(varValue.date, &SystemTime)) { hr = myHLastError(); _JumpError(hr, error, "Policy:VariantTimeToSystemTime"); }
if (!SystemTimeToFileTime(&SystemTime, &llftNotAfter.ft)) { hr = myHLastError(); _JumpError(hr, error, "Policy:SystemTimeToFileTime"); } if (0 > m_tp.llftExpirationPeriod.ll) { llftNotAfterCalc.ll = llftNotBefore.ll - m_tp.llftExpirationPeriod.ll; } else { llftNotAfterCalc.ll = m_tp.llftExpirationPeriod.ll; }
if (llftNotAfterCalc.ll > llftNotAfter.ll) { // truncated!
llftNotAfterCalc.ll = llftNotAfter.ll;
pRequest->BuildErrorInfo( S_OK, MSG_TEMPLATE_VALIDITY_TOO_LONG, &pwszTemplate); }
if (!FileTimeToSystemTime(&llftNotAfterCalc.ft, &SystemTime)) { hr = myHLastError(); _JumpError(hr, error, "Policy:FileTimeToSystemTime"); } if (!SystemTimeToVariantTime(&SystemTime, &varValue.date)) { hr = myHLastError(); _JumpError(hr, error, "Policy:SystemTimeToVariantTime"); }
hr = pServer->SetCertificateProperty( strNameNotAfter, PROPTYPE_DATE, &varValue); if (S_OK != hr) { DBGPRINTTIMEORPERIOD(" Old NotBefore", &llftNotBefore.ft); DBGPRINTTIMEORPERIOD(" Old NotAfter", &llftNotAfter.ft); DBGPRINTTIMEORPERIOD("Template Period", &m_tp.llftExpirationPeriod.ft); DBGPRINTTIMEORPERIOD(" New NotAfter", &llftNotAfterCalc.ft); } _JumpIfError(hr, error, "Policy:SetCertificateProperty");
// Verify new cert will be valid for at least as long as the overlap period
if (0 > m_tp.llftOverlapPeriod.ll) { llftNotAfterOverlap.ll = llftNotBefore.ll - m_tp.llftOverlapPeriod.ll; } else { llftNotAfterOverlap.ll = m_tp.llftOverlapPeriod.ll; }
if (llftNotAfterOverlap.ll > llftNotAfter.ll) { LONG RequestFlags;
hr = polGetRequestLongProperty( pServer, wszPROPREQUESTFLAGS, &RequestFlags); _JumpIfErrorStr( hr, error, "Policy:polGetRequestLongProperty", wszPROPREQUESTFLAGS);
if (CR_FLG_RENEWAL & RequestFlags) { // too little life left, log and fail!
pRequest->BuildErrorInfo( S_OK, MSG_TEMPLATE_OVERLAP_TOO_LONG, &pwszTemplate); hr = CERTSRV_E_CERT_TYPE_OVERLAP; _JumpError(hr, error, "Overlap too long"); } } CSASSERT(S_OK == hr);
error: VariantClear(&varValue); if (NULL != strNameNotBefore) { SysFreeString(strNameNotBefore); } if (NULL != strNameNotAfter) { SysFreeString(strNameNotAfter); } return(hr); }
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_EnforceKeySizePolicy
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_EnforceKeySizePolicy( IN ICertServerPolicy *pServer) { HRESULT hr;
if (0 != m_tp.dwMinKeyLength) { LONG KeyLength;
hr = polGetCertificateLongProperty( pServer, wszPROPCERTIFICATEPUBLICKEYLENGTH, &KeyLength); _JumpIfErrorStr( hr, error, "Policy:polGetCertificateLongProperty", wszPROPCERTIFICATEPUBLICKEYLENGTH);
if (m_tp.dwMinKeyLength > (DWORD) KeyLength) { DBGPRINT(( DBG_SS_ERROR, "Key Length %u, expected minimum %u\n", KeyLength, m_tp.dwMinKeyLength));
hr = CERTSRV_E_KEY_LENGTH; _JumpError(hr, error, "Key too small"); } } hr = S_OK;
error: return(hr); }
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_EnforceKeyArchivalPolicy
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_EnforceKeyArchivalPolicy( IN ICertServerPolicy *pServer) { HRESULT hr; LONG fKeyArchived;
hr = polGetCertificateLongProperty( pServer, wszPROPKEYARCHIVED, &fKeyArchived);
_JumpIfErrorStr( hr, error, "Policy:polGetCertificateLongProperty", wszPROPKEYARCHIVED);
if (CT_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL & m_tp.dwPrivateKeyFlags) { if (!fKeyArchived) { hr = CERTSRV_E_ARCHIVED_KEY_REQUIRED; _JumpError(hr, error, "missing archived key"); } } else { if (fKeyArchived) { hr = CERTSRV_E_ARCHIVED_KEY_UNEXPECTED; _JumpError(hr, error, "unexpected archived key"); } } hr = S_OK;
error: return(hr); }
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_EnforceSymmetricAlgorithms
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_EnforceSymmetricAlgorithms( IN ICertServerPolicy *pServer) { HRESULT hr; VARIANT varExtension; BOOL fSetNeeded = FALSE; LONG ExtFlags = 0; BSTR strCSPProvider = NULL;
VariantInit(&varExtension); if (CT_FLAG_INCLUDE_SYMMETRIC_ALGORITHMS & m_tp.dwEnrollmentFlags) { hr = polGetCertificateExtension( pServer, TEXT(szOID_RSA_SMIMECapabilities), PROPTYPE_BINARY, &varExtension); if (CERTSRV_E_PROPERTY_EMPTY != hr) { _JumpIfError(hr, error, "Policy:GetCertificateExtension");
hr = pServer->GetCertificateExtensionFlags(&ExtFlags); _JumpIfError(hr, error, "Policy:GetCertificateExtensionFlags");
if (EXTENSION_DISABLE_FLAG & ExtFlags) { ExtFlags &= ~EXTENSION_DISABLE_FLAG; fSetNeeded = TRUE; } } else { BYTE const *pbSMIME; DWORD cbSMIME; pbSMIME = m_pPolicy->GetSMIME(&cbSMIME); if (0 == (EDITF_ENABLEDEFAULTSMIME & m_pPolicy->GetEditFlags()) || NULL == pbSMIME) { hr = CERTSRV_E_SMIME_REQUIRED; _JumpError(hr, error, "Policy:GetCertificateExtension"); }
varExtension.bstrVal = NULL; if (!myConvertWszToBstr( &varExtension.bstrVal, (WCHAR const *) pbSMIME, cbSMIME)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:myConvertWszToBstr"); } varExtension.vt = VT_BSTR; fSetNeeded = TRUE; } } if (fSetNeeded) { hr = polSetCertificateExtension( pServer, TEXT(szOID_RSA_SMIMECapabilities), PROPTYPE_BINARY, ExtFlags, &varExtension); _JumpIfError(hr, error, "Policy:SetCertificateExtension"); } hr = S_OK;
error: if (NULL != strCSPProvider) { SysFreeString(strCSPProvider); } VariantClear(&varExtension); return(hr); }
#ifdef CERTSRV_EOBO_DCR_APPROVED
// Fail the request if:
// the request is for a V2 template
// AND
// RequesterName is not the same as the CallerName (enroll-on-behalf-of)
// AND
// CT_FLAG_ALLOW_ENROLL_ON_BEHALF_OF is not set in the template flags
// OR
// RequesterName *is* the same as the CallerName (not enroll-on-behalf-of)
// AND
// CT_FLAG_ALLOW_ENROLL_ON_BEHALF_OF *is* set in the template flags
#else
// Fail the request if all of the following are true:
// 1: the request is for a V2 template
// 2: no signatures are required by the template
// 3: the RequesterName is not the same as the CallerName (enroll-on-behalf-of)
#endif
HRESULT CTemplatePolicy::_EnforceEnrollOnBehalfOfAllowed( IN ICertServerPolicy *pServer, OUT BOOL *pfEnrollOnBehalfOf) { HRESULT hr; BSTR strRequester = NULL; BSTR strCaller = NULL;
*pfEnrollOnBehalfOf = FALSE;
hr = polGetRequestStringProperty( pServer, wszPROPREQUESTERNAME, &strRequester); _JumpIfErrorStr( hr, error, "Policy:polGetRequestStringProperty", wszPROPREQUESTERNAME);
hr = polGetRequestStringProperty( pServer, wszPROPCALLERNAME, &strCaller); _JumpIfErrorStr( hr, error, "Policy:polGetRequestStringProperty", wszPROPCALLERNAME);
*pfEnrollOnBehalfOf = 0 != mylstrcmpiL(strRequester, strCaller);
if (CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion #ifdef CERTSRV_EOBO_DCR_APPROVED
#else
&& 0 == m_tp.dwcSignatureRequired #endif
) { #ifdef CERTSRV_EOBO_DCR_APPROVED
if (!*pfEnrollOnBehalfOf ^ (0 != (CT_FLAG_ALLOW_ENROLL_ON_BEHALF_OF & m_tp.dwEnrollmentFlags))) #else
if (*pfEnrollOnBehalfOf) #endif
{ hr = CERTSRV_E_BAD_RENEWAL_SUBJECT; _JumpError(hr, error, "EnrollOnBehalfOf allowed/disallowed"); } } hr = S_OK; // caller matches requester
error: if (NULL != strRequester) { SysFreeString(strRequester); } if (NULL != strCaller) { SysFreeString(strCaller); } return(hr); }
HRESULT CTemplatePolicy::_EnforceMinimumTemplateVersion( IN CRequestInstance *pRequest) { HRESULT hr; DWORD dwRequestTemplateMinimumMajorVersion; DWORD dwRequestTemplateMinimumMinorVersion;
pRequest->GetTemplateVersion( &dwRequestTemplateMinimumMajorVersion, &dwRequestTemplateMinimumMinorVersion);
if (m_tp.dwTemplateMajorVersion < dwRequestTemplateMinimumMajorVersion || (m_tp.dwTemplateMajorVersion == dwRequestTemplateMinimumMajorVersion && m_tp.dwTemplateMinorVersion < dwRequestTemplateMinimumMinorVersion)) { DBGPRINT(( DBG_SS_ERROR, "Requested template version %u.%u, Loaded version %u.%u\n", dwRequestTemplateMinimumMajorVersion, dwRequestTemplateMinimumMinorVersion, m_tp.dwTemplateMajorVersion, m_tp.dwTemplateMinorVersion)); hr = CERTSRV_E_BAD_TEMPLATE_VERSION; _JumpError(hr, error, "Policy:_EnforceMinimumTemplateVersion"); } hr = S_OK;
error: return(hr); }
HRESULT tpSplitPolicies( IN BSTR strPolicies, OUT DWORD *pcPolicies, OUT OBJECTIDLIST **pprgPolicies) { HRESULT hr; WCHAR const *pwszT; WCHAR *pwszOut; DWORD cPolicies; OBJECTIDLIST *prgPolicies; DWORD cObjId; WCHAR **rgpwszObjId; BOOL fNew;
*pprgPolicies = NULL;
pwszT = strPolicies; cPolicies = 1; // plus one per newline separator
cObjId = 0; while (L'\0' != *pwszT) { int ichar = wcscspn(pwszT, L",\n"); if (ichar == 0) // neither of these, look for end-of-string
ichar = wcslen(pwszT); pwszT += ichar;
switch (*pwszT) { case L'\n': cPolicies++; // plus one per newline separator
pwszT++; // step over the newline
break;
case L',': cObjId++; // plus one per comma separator
pwszT++; // step over the comma
break;
case L'\0': default: CSASSERT(L'\0' == *pwszT); break; } } cObjId += cPolicies; // plus one per signature
prgPolicies = (OBJECTIDLIST *) LocalAlloc( LMEM_FIXED, cPolicies * sizeof(prgPolicies[0]) + cObjId * sizeof(WCHAR *) + (wcslen(strPolicies) + 1) * sizeof(WCHAR)); if (NULL == prgPolicies) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:SysAllocString"); } *pprgPolicies = prgPolicies; *pcPolicies = cPolicies;
rgpwszObjId = (WCHAR **) &prgPolicies[cPolicies]; pwszOut = (WCHAR *) &rgpwszObjId[cObjId]; wcscpy(pwszOut, strPolicies);
fNew = TRUE; while (L'\0' != *pwszOut) { if (fNew) { prgPolicies->cObjId = 0; prgPolicies->rgpwszObjId = rgpwszObjId; } prgPolicies->cObjId++; *rgpwszObjId = pwszOut; rgpwszObjId++; // next array entry
fNew = FALSE;
int ichar = wcscspn(pwszOut , L",\n"); if (ichar == 0) // neither of these, look for end-of-string
ichar = wcslen(pwszOut); pwszOut += ichar;
switch (*pwszOut) { case L'\n': prgPolicies++; fNew = TRUE; // FALLTHROUGH
case L',': *pwszOut++ = L'\0'; break;
case L'\0': default: CSASSERT(L'\0' == *pwszOut); break; } } hr = S_OK;
error: return(hr); }
HRESULT CTemplatePolicy::_LoadSignaturePolicies( IN ICertServerPolicy *pServer, IN WCHAR const *pwszPropNameRequest, OUT DWORD *pcSignaturePolicies, OUT OBJECTIDLIST **pprgSignaturePolicies) // from the signing cert(s)
{ HRESULT hr; BSTR strSignaturePolicies = NULL;
*pcSignaturePolicies = 0; *pprgSignaturePolicies = NULL;
hr = polGetRequestStringProperty( pServer, pwszPropNameRequest, &strSignaturePolicies); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = CERTSRV_E_SIGNATURE_POLICY_REQUIRED; } _JumpIfErrorStr( hr, error, "Policy:polGetRequestStringProperty", pwszPropNameRequest);
hr = tpSplitPolicies( strSignaturePolicies, pcSignaturePolicies, pprgSignaturePolicies); _JumpIfError(hr, error, "Policy:tpSplitPolicies");
error: if (NULL != strSignaturePolicies) { SysFreeString(strSignaturePolicies); } return(hr); }
HRESULT CTemplatePolicy::_EnforceReenrollment( IN ICertServerPolicy *pServer, IN CRequestInstance *pRequest) { HRESULT hr; VARIANT var; CERT_CONTEXT const *pOldCert = NULL; CERT_EXTENSION const *pExt; CERT_TEMPLATE_EXT *pTemplateExt = NULL; WCHAR *pwszObjId = NULL; CERT_ALT_NAME_INFO *pAltName = NULL; CERT_NAME_VALUE *pName = NULL; DWORD cb; DWORD i;
VariantInit(&var);
if (NULL == m_pwszTemplateObjId) { hr = CERTSRV_E_UNSUPPORTED_CERT_TYPE; _JumpError(hr, error, "Policy:No template ObjId"); } hr = polGetProperty( pServer, TRUE, // fRequest
wszPROPREQUESTRAWOLDCERTIFICATE, PROPTYPE_BINARY, &var); _JumpIfError(hr, error, "Policy:polGetProperty");
if (VT_BSTR != var.vt || NULL == var.bstrVal) { hr = CERTSRV_E_PROPERTY_EMPTY; _JumpError(hr, error, "Policy:polGetProperty"); } var.bstrVal;
pOldCert = CertCreateCertificateContext( X509_ASN_ENCODING, (BYTE *) var.bstrVal, SysStringByteLen(var.bstrVal)); if (NULL == pOldCert) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "CertCreateCertificateContext"); } pExt = CertFindExtension( szOID_CERTIFICATE_TEMPLATE, pOldCert->pCertInfo->cExtension, pOldCert->pCertInfo->rgExtension); if (NULL == pExt) { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); _JumpError(hr, error, "CertFindExtension(Template)"); }
if (!myDecodeObject( X509_ASN_ENCODING, X509_CERTIFICATE_TEMPLATE, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pTemplateExt, &cb)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myDecodeObject"); } if (!myConvertSzToWsz(&pwszObjId, pTemplateExt->pszObjId, MAXDWORD)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:myConvertSzToWsz"); } if (0 != lstrcmp(pwszObjId, m_pwszTemplateObjId)) { hr = CERTSRV_E_UNSUPPORTED_CERT_TYPE; _JumpError(hr, error, "Policy:different cert type"); }
if (NULL == pRequest->m_pwszUPN) { hr = E_POINTER; // We should never get this
_JumpError(hr, error, "Policy:NULL UPN"); } pExt = CertFindExtension( szOID_SUBJECT_ALT_NAME2, pOldCert->pCertInfo->cExtension, pOldCert->pCertInfo->rgExtension); if (NULL == pExt) { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); _JumpError(hr, error, "CertFindExtension(SubjectAltName)"); }
if (!myDecodeObject( X509_ASN_ENCODING, X509_ALTERNATE_NAME, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pAltName, &cb)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myDecodeObject"); } for (i = 0; i < pAltName->cAltEntry; i++) { CERT_ALT_NAME_ENTRY *pAltEntry = &pAltName->rgAltEntry[i];
if (CERT_ALT_NAME_OTHER_NAME != pAltEntry->dwAltNameChoice) { continue; } if (0 != strcmp( pAltEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) { continue; } if (!myDecodeObject( X509_ASN_ENCODING, X509_UNICODE_ANY_STRING, pAltEntry->pOtherName->Value.pbData, pAltEntry->pOtherName->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pName, &cb)) { hr = myHLastError(); _JumpError(hr, error, "Policy:myDecodeObject"); } if (CERT_RDN_UTF8_STRING != pName->dwValueType || 0 != mylstrcmpiL( pRequest->m_pwszUPN, (WCHAR const *) pName->Value.pbData)) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpError(hr, error, "UPN doesn't match renewal UPN"); } } if (NULL == pName) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpError(hr, error, "missing renewal UPN"); } hr = S_OK;
error: if (NULL != pName) { LocalFree(pName); } if (NULL != pAltName) { LocalFree(pAltName); } if (NULL != pwszObjId) { LocalFree(pwszObjId); } if (NULL != pTemplateExt) { LocalFree(pTemplateExt); } if (NULL != pOldCert) { CertFreeCertificateContext(pOldCert); } VariantClear(&var); return(hr); }
//+--------------------------------------------------------------------------
// CCertPolicyEnterprise::_EnforceSignaturePolicy
//
// Fetch required lists of Issuance and Application ObjIds from the template.
// Fetch signing certificates' lists of Issuance and Application ObjIds from
// the cert server.
//
// Reject signatures that don't include all of the required Application ObjIds.
// Reject signatures that don't include at least one of the required Issuance
// ObjIds.
//
// The count of accepted signatures must be equal to or greater than the
// template-specified required signature count.
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT CTemplatePolicy::_EnforceSignaturePolicy( IN ICertServerPolicy *pServer, IN CRequestInstance *pRequest, IN BOOL fEnrollOnBehalfOf) { HRESULT hr; DWORD cSignatureAccepted; DWORD cSignatureRejected; DWORD *prgdwRefCount = NULL; DWORD i; WCHAR const *awszStrings[3]; WCHAR wszCountAccepted[50]; WCHAR wszCountRequired[50]; WCHAR *pwszMissing = NULL; OBJECTIDLIST *pPoliciesApplication;
DWORD cSignatureIssuance; OBJECTIDLIST *prgSignatureIssuance = NULL;
DWORD cSignatureApplication; OBJECTIDLIST *prgSignatureApplication = NULL;
static WCHAR *s_pwszObjIdEA = TEXT(szOID_ENROLLMENT_AGENT); static OBJECTIDLIST s_PoliciesApplicationEA = { 1, &s_pwszObjIdEA };
pPoliciesApplication = &m_PoliciesApplication; if (0 == m_tp.dwcSignatureRequired) { if (!fEnrollOnBehalfOf || CERTTYPE_SCHEMA_VERSION_2 <= m_tp.dwSchemaVersion) { hr = S_OK; goto error; }
// V1 template with fEnrollOnBehalfOf request.
// Enforce szOID_ENROLLMENT_AGENT
pPoliciesApplication = &s_PoliciesApplicationEA; } hr = _LoadSignaturePolicies( pServer, wszPROPSIGNERPOLICIES, &cSignatureIssuance, // from the signing cert(s)
&prgSignatureIssuance); _JumpIfErrorStr( hr, error, "CTemplatePolicy:_LoadSignaturePolicies", CERTTYPE_PROP_RA_POLICY);
hr = _LoadSignaturePolicies( pServer, wszPROPSIGNERAPPLICATIONPOLICIES, &cSignatureApplication, // from the signing cert(s)
&prgSignatureApplication); _JumpIfErrorStr( hr, error, "CTemplatePolicy:_LoadSignaturePolicies", CERTTYPE_PROP_RA_APPLICATION_POLICY);
if (0 == m_PoliciesIssuance.cObjId && 0 == pPoliciesApplication->cObjId) { hr = CERTSRV_E_TEMPLATE_POLICY_REQUIRED; _JumpIfError(hr, error, "no template policies"); }
if (cSignatureIssuance != cSignatureApplication) { hr = NTE_BAD_SIGNATURE; // must be an internal server problem
_JumpError(hr, error, "Policy:bad request policies counts"); } if (0 != m_PoliciesIssuance.cObjId) { prgdwRefCount = (DWORD *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, m_PoliciesIssuance.cObjId * sizeof(DWORD)); if (NULL == prgdwRefCount) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Policy:LocalAlloc"); } }
// Loop through each signature's Application and Issuance Policy OIDs
cSignatureAccepted = 0; cSignatureRejected = 0; for (i = 0; i < cSignatureIssuance; i++) { BOOL fReject = FALSE; WCHAR **rgpwszObjId; DWORD cObjId; DWORD j; DWORD idx;
// This signature must include ALL required Application Policy OIDs
// If no Application Policy OIDs are required, all signatures are OK.
rgpwszObjId = prgSignatureApplication[i].rgpwszObjId; cObjId = prgSignatureApplication[i].cObjId; for (j = 0; j < pPoliciesApplication->cObjId; j++) { idx = polFindObjIdInList( pPoliciesApplication->rgpwszObjId[j], cObjId, rgpwszObjId); if (MAXDWORD == idx) { fReject = TRUE; _PrintErrorStr( S_OK, "Policy:missing Application Policy", pPoliciesApplication->rgpwszObjId[j]); } } if (!fReject) { DWORD cFound = 0;
// For each Issuance Policy OID in this signature that also exists
// in the required Issuance Policy OIDs, increment the ref count to
// show the OID was referenced by an accepted signature.
// Reject the signature if it doesn't reference any required OID.
rgpwszObjId = prgSignatureIssuance[i].rgpwszObjId; cObjId = prgSignatureIssuance[i].cObjId; for (j = 0; j < cObjId; j++) { // "*" means the signing cert is good for *all* policies.
if (0 == wcscmp(L"*", rgpwszObjId[j])) { for (idx = 0; idx < m_PoliciesIssuance.cObjId; idx++) { prgdwRefCount[idx]++; cFound++; } } else { idx = polFindObjIdInList( rgpwszObjId[j], m_PoliciesIssuance.cObjId, m_PoliciesIssuance.rgpwszObjId); if (MAXDWORD != idx) { prgdwRefCount[idx]++; cFound++; } } }
// If no Issuance Policy OIDs are required, all signatures are OK.
if (0 != m_PoliciesIssuance.cObjId && 0 == cFound) { fReject = TRUE; } } if (fReject) { cSignatureRejected++; } else { cSignatureAccepted++; } DBGPRINT(( DBG_SS_CERTPOL, "Sig[%u]: %ws\n", i, fReject? L"Rejected" : L"Accepted")); }
awszStrings[0] = m_pwszTemplateName; if (NULL == awszStrings[0]) { awszStrings[0] = m_pwszTemplateObjId; }
hr = S_OK; if (cSignatureAccepted < m_tp.dwcSignatureRequired) { hr = CERTSRV_E_SIGNATURE_COUNT; if (0 != cSignatureRejected) { hr = CERTSRV_E_SIGNATURE_REJECTED; } _PrintError(hr, "Policy:not enough signatures");
// The %1 Certificate Template requires %2 signatures,
// but only %3 were accepted.
wsprintf(wszCountRequired, L"%u", m_tp.dwcSignatureRequired); awszStrings[1] = wszCountRequired;
wsprintf(wszCountAccepted, L"%u", cSignatureAccepted); awszStrings[2] = wszCountAccepted;
pRequest->BuildErrorInfo(hr, MSG_SIGNATURE_COUNT, awszStrings); } DBGPRINT(( S_OK != hr? DBG_SS_CERTPOLI : DBG_SS_CERTPOL, "Signatures: %u needed, %u accepted\n", m_tp.dwcSignatureRequired, cSignatureAccepted)); for (i = 0; i < m_PoliciesIssuance.cObjId; i++) { if (0 == prgdwRefCount[i]) { hr = CERTSRV_E_ISSUANCE_POLICY_REQUIRED; _PrintErrorStr( hr, "Policy:missing Issuance Policy", m_PoliciesIssuance.rgpwszObjId[i]); myAppendString( m_PoliciesIssuance.rgpwszObjId[i], L", ", &pwszMissing); } } if (NULL != pwszMissing) { // The %1 Certificate Template requires the following issuance
// policies that signing certificates did not include: %2.
awszStrings[1] = pwszMissing; pRequest->BuildErrorInfo( hr, MSG_SIGNATURE_ISSUANCE_POLICY, awszStrings); } _JumpIfError(hr, error, "Policy:missing Policy/Signature");
error: if (NULL != pwszMissing) { LocalFree(pwszMissing); } if (NULL != prgSignatureIssuance) { LocalFree(prgSignatureIssuance); } if (NULL != prgSignatureApplication) { LocalFree(prgSignatureApplication); } if (NULL != prgdwRefCount) { LocalFree(prgdwRefCount); } return(hr); }
HRESULT CTemplatePolicy::GetFlags( IN DWORD dwOption, OUT DWORD *pdwFlags) { HRESULT hr;
switch (dwOption) { case CERTTYPE_ENROLLMENT_FLAG: *pdwFlags = m_tp.dwEnrollmentFlags; break;
case CERTTYPE_SUBJECT_NAME_FLAG: *pdwFlags = m_tp.dwSubjectNameFlags; break;
case CERTTYPE_PRIVATE_KEY_FLAG: *pdwFlags = m_tp.dwPrivateKeyFlags; break;
case CERTTYPE_GENERAL_FLAG: *pdwFlags = m_tp.dwGeneralFlags; break;
default: hr = E_INVALIDARG; _JumpError(hr, error, "Policy:bad dwOption");
} hr = S_OK;
error: return(hr); }
HRESULT CTemplatePolicy::GetCriticalExtensions( OUT DWORD *pcCriticalExtensions, OUT WCHAR const * const **papwszCriticalExtensions) { HRESULT hr;
*pcCriticalExtensions = m_CriticalExtensions.cObjId; *papwszCriticalExtensions = m_CriticalExtensions.rgpwszObjId; hr = S_OK; return(hr); }
HRESULT CTemplatePolicy::GetV1TemplateClass( OUT WCHAR const **ppwszV1TemplateClass) { WCHAR const *pwsz = NULL;
*ppwszV1TemplateClass = NULL; if (CERTTYPE_SCHEMA_VERSION_2 > m_tp.dwSchemaVersion) { pwsz = m_pwszTemplateName; } else if (CT_FLAG_IS_CROSS_CA & m_tp.dwGeneralFlags) { pwsz = wszCERTTYPE_CROSS_CA; } *ppwszV1TemplateClass = pwsz; return(S_OK); }
// IsRequestedTemplate - determine if the request specifies this template
BOOL CTemplatePolicy::IsRequestedTemplate( OPTIONAL IN WCHAR const *pwszTemplateName, OPTIONAL IN WCHAR const *pwszTemplateObjId) { HRESULT hr = CERTSRV_E_UNSUPPORTED_CERT_TYPE;
if (NULL != pwszTemplateName) { if ((NULL != m_pwszTemplateName && 0 == mylstrcmpiL(m_pwszTemplateName, pwszTemplateName)) || (NULL != m_pwszTemplateObjId && 0 == lstrcmp(m_pwszTemplateObjId, pwszTemplateName))) { hr = S_OK; } } if (S_OK != hr && NULL != pwszTemplateObjId) { if ((NULL != m_pwszTemplateName && 0 == mylstrcmpiL(m_pwszTemplateName, pwszTemplateObjId)) || (NULL != m_pwszTemplateObjId && 0 == lstrcmp(m_pwszTemplateObjId, pwszTemplateObjId))) { hr = S_OK; } } _JumpIfErrorStr2( hr, error, "Policy:wrong CertType", m_pwszTemplateName, hr);
error: return(S_OK == hr); }
|