Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2251 lines
52 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2000
//
// File: request.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 "cspelog.h"
#include "pollog.h"
#include "csprop.h"
#include "csldap.h"
#include "csdisp.h"
#include "policy.h"
#include "cainfop.h"
#define __dwFILE__ __dwFILE_POLICY_DEFAULT_REQUEST_CPP__
LDAP **g_rgGCCache = NULL;
LONG g_cGCCacheCur;
LONG g_cGCCacheMax;
CRITICAL_SECTION g_GCCacheCriticalSection;
BOOL g_fGCCacheCriticalSection = FALSE;
VOID
myLdapUnbind(
IN OUT LDAP **ppld)
{
if (NULL != *ppld)
{
ldap_unbind(*ppld);
*ppld = NULL;
}
}
HRESULT
myLdapBind(
IN DWORD Flags,
OPTIONAL IN WCHAR const *pwszClientDC, // require GC unless non-NULL
IN OUT LDAP **ppld)
{
HRESULT hr;
ULONG ldaperr;
DWORD GetDSNameFlags;
WCHAR *pwszDomainControllerName;
BOOL fGC = NULL == pwszClientDC;
BOOL fRediscover = FALSE;
LDAP *pld = *ppld;
GetDSNameFlags = DS_RETURN_DNS_NAME;
if (fGC)
{
// We want to talk to a GC, so grab the GC name. Get the GC location.
GetDSNameFlags |= DS_GC_SERVER_REQUIRED;
}
while (TRUE)
{
if (NULL != *ppld)
{
break;
}
// Clean up from previous loop execution
if (NULL != pld)
{
ldap_unbind(pld);
pld = NULL;
}
// Grab an LDAP handle for use during this instantiation
pld = ldap_init(
const_cast<WCHAR *>(pwszClientDC),
fGC? LDAP_GC_PORT : LDAP_PORT);
if (NULL == pld)
{
hr = myHLdapLastError(NULL, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_init", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_init");
}
if (fRediscover && NULL == pwszClientDC)
{
GetDSNameFlags |= DS_FORCE_REDISCOVERY;
}
ldaperr = ldap_set_option(
pld,
LDAP_OPT_GETDSNAME_FLAGS,
(VOID *) &GetDSNameFlags);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_set_option", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_set_option");
}
if (NULL != pwszClientDC)
{
DWORD SSPIFlags;
// Turn on mutual authentication -- just to make sure we can trust
// the client-supplied DC name.
ldaperr = ldap_get_option(
pld,
LDAP_OPT_SSPI_FLAGS,
(VOID *) &SSPIFlags);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_get_option", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_get_option");
}
SSPIFlags |= ISC_REQ_MUTUAL_AUTH;
ldaperr = ldap_set_option(
pld,
LDAP_OPT_SSPI_FLAGS,
(VOID *) &GetDSNameFlags);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_set_option", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_set_option");
}
}
ldaperr = ldap_set_option(pld, LDAP_OPT_SIGN, LDAP_OPT_ON);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_set_option", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_set_option");
}
if (0 == (EDITF_ENABLELDAPREFERRALS & Flags) || NULL != pwszClientDC)
{
ldaperr = ldap_set_option(pld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_set_option LDAP_OPT_REFERRALS", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_set_option LDAP_OPT_REFERRALS");
}
}
ldaperr = ldap_bind_s(pld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pld, ldaperr, NULL);
if (!fRediscover)
{
_PrintError2(hr, "Policy:ldap_bind_s", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:ldap_bind_s");
}
hr = myLdapGetDSHostName(pld, &pwszDomainControllerName);
if (S_OK != hr)
{
if (!fRediscover)
{
_PrintError2(hr, "Policy:myLdapGetDSHostName", hr);
fRediscover = TRUE;
continue;
}
_JumpError(hr, error, "Policy:myLdapGetDSHostName");
}
DBGPRINT((
DBG_SS_CERTPOLI,
"DC name = %ws\n",
pwszDomainControllerName));
break;
}
hr = S_OK;
error:
if (S_OK != hr)
{
myLdapUnbind(&pld);
}
*ppld = pld;
return(hr);
}
HRESULT
reqGetLdapGC(
IN DWORD Flags,
OUT LDAP **ppldGC,
OUT BOOL *pfCached)
{
HRESULT hr;
LDAP *pldGC;
*pfCached = TRUE;
myLdapUnbind(ppldGC);
if (!g_fGCCacheCriticalSection || NULL == g_rgGCCache)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
pldGC = NULL;
EnterCriticalSection(&g_GCCacheCriticalSection);
if (0 != g_cGCCacheCur)
{
pldGC = g_rgGCCache[--g_cGCCacheCur];
}
LeaveCriticalSection(&g_GCCacheCriticalSection);
if (NULL == pldGC)
{
hr = myLdapBind(Flags, NULL, &pldGC);
_JumpIfError(hr, error, "myLdapBind");
*pfCached = FALSE;
}
*ppldGC = pldGC;
hr = S_OK;
error:
return(hr);
}
VOID
reqReleaseLdapGC(
IN OUT LDAP **ppldGC)
{
LDAP *pldGC = *ppldGC;
if (NULL != pldGC)
{
*ppldGC = NULL;
EnterCriticalSection(&g_GCCacheCriticalSection);
CSASSERT(0 != g_cGCCacheMax);
if (g_cGCCacheCur < g_cGCCacheMax)
{
g_rgGCCache[g_cGCCacheCur++] = pldGC;
pldGC = NULL;
}
LeaveCriticalSection(&g_GCCacheCriticalSection);
myLdapUnbind(&pldGC);
}
}
WCHAR *
reqCombineTemplates(
OPTIONAL IN WCHAR const *pwszTemplateName,
OPTIONAL IN WCHAR const *pwszTemplateObjId,
OPTIONAL IN WCHAR const *pwszTemplateRA)
{
HRESULT hr;
WCHAR const *apwszTemplate[3];
WCHAR const *apwszDisplayName[3];
DWORD i;
DWORD cwc;
WCHAR *pwszList = NULL;
apwszTemplate[0] = pwszTemplateName;
apwszTemplate[1] = pwszTemplateObjId;
apwszTemplate[2] = pwszTemplateRA;
ZeroMemory(apwszDisplayName, sizeof(apwszDisplayName));
cwc = 0;
for (i = 0; i < ARRAYSIZE(apwszTemplate); i++)
{
if (NULL != apwszTemplate[i])
{
if (0 != cwc)
{
cwc++;
}
cwc += wcslen(apwszTemplate[i]);
hr = myVerifyObjId(apwszTemplate[i]);
if (S_OK == hr)
{
WCHAR const *pwszDisplay = NULL;
pwszDisplay = myGetOIDName(apwszTemplate[i]); // Static: do not free!
if (NULL != pwszDisplay && L'\0' != *pwszDisplay)
{
apwszDisplayName[i] = pwszDisplay;
cwc += 2 + wcslen(pwszDisplay);
}
}
}
}
if (0 != cwc)
{
pwszList = (WCHAR *) LocalAlloc(LMEM_FIXED, (1 + cwc) * sizeof(WCHAR));
if (NULL == pwszList)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:LocalAlloc");
}
pwszList[0] = L'\0';
for (i = 0; i < ARRAYSIZE(apwszTemplate); i++)
{
if (NULL != apwszTemplate[i])
{
if (L'\0' != pwszList[0])
{
wcscat(pwszList, L"/");
}
wcscat(pwszList, apwszTemplate[i]);
if (NULL != apwszDisplayName[i])
{
wcscat(pwszList, wszLPAREN);
wcscat(pwszList, apwszDisplayName[i]);
wcscat(pwszList, wszRPAREN);
}
}
}
CSASSERT(wcslen(pwszList) == cwc);
}
error:
return(pwszList);
}
// begin_sdksample
HRESULT
ReqInitialize(
IN ICertServerPolicy *pServer)
{
HRESULT hr;
// end_sdksample
hr = S_OK;
__try
{
InitializeCriticalSection(&g_GCCacheCriticalSection);
g_fGCCacheCriticalSection = TRUE;
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
_JumpIfError(hr, error, "InitializeCriticalSection");
hr = polGetCertificateLongProperty(
pServer,
wszPROPSESSIONCOUNT,
&g_cGCCacheMax);
if (S_OK != hr)
{
g_cGCCacheMax = DBSESSIONCOUNTDEFAULT;
}
g_rgGCCache = (LDAP **) LocalAlloc(
LMEM_FIXED | LMEM_ZEROINIT,
g_cGCCacheMax * sizeof(g_rgGCCache[0]));
if (NULL == g_rgGCCache)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:LocalAlloc");
}
g_cGCCacheCur = 0;
// begin_sdksample
hr = S_OK;
error: // no_sdksample
return(hr);
}
VOID
ReqCleanup()
{
// end_sdksample
if (g_fGCCacheCriticalSection)
{
EnterCriticalSection(&g_GCCacheCriticalSection);
if (NULL != g_rgGCCache)
{
while (0 != g_cGCCacheCur)
{
myLdapUnbind(&g_rgGCCache[--g_cGCCacheCur]);
}
LocalFree(g_rgGCCache);
}
LeaveCriticalSection(&g_GCCacheCriticalSection);
DeleteCriticalSection(&g_GCCacheCriticalSection);
}
// begin_sdksample
}
CRequestInstance::~CRequestInstance()
{
_Cleanup();
}
VOID
CRequestInstance::_Cleanup()
{
if (NULL != m_strTemplateName)
{
SysFreeString(m_strTemplateName);
m_strTemplateName = NULL;
}
if (NULL != m_strTemplateObjId)
{
SysFreeString(m_strTemplateObjId);
m_strTemplateObjId = NULL;
}
// end_sdksample
//+--------------------------------------
_ReleasePrincipalObject();
if (NULL != m_hToken)
{
CloseHandle(m_hToken);
m_hToken = NULL;
}
if (NULL != m_strUserDN)
{
SysFreeString(m_strUserDN);
m_strUserDN = NULL;
}
if (NULL != m_pwszUPN)
{
LocalFree(m_pwszUPN);
m_pwszUPN = NULL;
}
delete m_pTemplate;
m_pTemplate = NULL;
if (NULL != m_pCreateErrorInfo)
{
m_pCreateErrorInfo->Release();
m_pCreateErrorInfo = NULL;
}
//+--------------------------------------
// begin_sdksample
}
// end_sdksample
VOID
CRequestInstance::SaveErrorInfo(
OPTIONAL IN ICreateErrorInfo *pCreateErrorInfo)
{
if (NULL != pCreateErrorInfo)
{
if (NULL != m_pCreateErrorInfo)
{
m_pCreateErrorInfo->Release();
}
m_pCreateErrorInfo = pCreateErrorInfo;
}
}
HRESULT
CRequestInstance::SetErrorInfo()
{
HRESULT hr;
if (NULL != m_pCreateErrorInfo)
{
hr = SetModuleErrorInfo(m_pCreateErrorInfo);
_JumpIfError(hr, error, "Policy:SetErrorInfo");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
CRequestInstance::BuildErrorInfo(
IN HRESULT hrLog,
IN DWORD dwLogId,
OPTIONAL IN WCHAR const * const *ppwszInsert)
{
HRESULT hr;
hr = polBuildErrorInfo(
hrLog,
dwLogId,
m_pPolicy->GetPolicyDescription(),
ppwszInsert,
&m_pCreateErrorInfo);
_JumpIfError(hr, error, "polBuildErrorInfo");
error:
return(hr);
}
// begin_sdksample
static WCHAR const *s_apwszCATypes[] =
{
wszCERTTYPE_SUBORDINATE_CA,
wszCERTTYPE_CROSS_CA,
};
//+--------------------------------------------------------------------------
// CRequestInstance::Initialize
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::Initialize(
IN CCertPolicyEnterprise *pPolicy,
IN BOOL fEnterpriseCA, // no_sdksample
IN BOOL bNewRequest, // no_sdksample
IN ICertServerPolicy *pServer,
OUT BOOL *pfEnableEnrolleeExtensions)
{
HRESULT hr;
HRESULT hrTemplate = S_OK;
CERT_TEMPLATE_EXT *pTemplate = NULL;
CERT_NAME_VALUE *pName = NULL;
BSTR strTemplateObjId = NULL; // from V2 template extension
BSTR strTemplateName = NULL; // from V1 template extension
BSTR strTemplateRA = NULL; // from request attributes
WCHAR const *pwszTemplateName;
WCHAR const *pwszTemplateObjId;
WCHAR const *pwszV1TemplateClass;
VARIANT varValue;
DWORD cbType;
DWORD i;
BOOL fConflict;
BOOL f;
BOOL fTemplateMissing;
BOOL fRAObjId = FALSE;
CTemplatePolicy *ptp = NULL; // no_sdksample
WCHAR *pwszTemplateList = NULL; // no_sdksample
VariantInit(&varValue);
*pfEnableEnrolleeExtensions = TRUE
&& !fEnterpriseCA // no_sdksample
;
m_pPolicy = pPolicy;
m_fCA = FALSE;
m_fNewRequest = bNewRequest; // no_sdksample
// end_sdksample
//+--------------------------------------
m_fUser = TRUE;
m_fEnterpriseCA = fEnterpriseCA;
if (m_fEnterpriseCA && bNewRequest)
{
hr = _InitToken(pServer);
_JumpIfError(hr, error, "Policy:_InitToken");
}
hr = _InitClientOSVersionInfo(pServer);
_JumpIfError(hr, error, "Policy:_InitClientOSVersionInfo");
//+--------------------------------------
// begin_sdksample
// Retrieve the template ObjId from the V2 cert template info extension
m_dwTemplateMajorVersion = 0;
m_dwTemplateMinorVersion = 0;
hr = polGetCertificateExtension(
pServer,
TEXT(szOID_CERTIFICATE_TEMPLATE),
PROPTYPE_BINARY,
&varValue);
_PrintIfErrorStr2(
hr,
"Policy:polGetCertificateExtension",
TEXT(szOID_CERTIFICATE_TEMPLATE),
CERTSRV_E_PROPERTY_EMPTY);
if (S_OK == hr)
{
// There was a cert type indicator.
// varValue points to an encoded string
if (VT_BSTR != varValue.vt)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Policy:varValue.vt");
}
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_CERTIFICATE_TEMPLATE,
(BYTE *) varValue.bstrVal,
SysStringByteLen(varValue.bstrVal),
CERTLIB_USE_LOCALALLOC,
(VOID **) &pTemplate,
&cbType))
{
hr = myHLastError();
_JumpError(hr, error, "Policy:myDecodeObject");
}
if (!myConvertSzToBstr(&strTemplateObjId, pTemplate->pszObjId, -1))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:myConvertSzToBstr");
}
m_dwTemplateMajorVersion = pTemplate->dwMajorVersion;
m_dwTemplateMinorVersion = pTemplate->dwMinorVersion;
DBGPRINT((
DBG_SS_CERTPOL,
pTemplate->fMinorVersion?
"Extension Template Info: %ws V%u.%u\n" :
"Extension Template Info: %ws V%u%\n",
strTemplateObjId,
m_dwTemplateMajorVersion,
m_dwTemplateMinorVersion));
}
VariantClear(&varValue);
// Retrieve template Name from the V1 cert template name extension
hr = polGetCertificateExtension(
pServer,
TEXT(szOID_ENROLL_CERTTYPE_EXTENSION),
PROPTYPE_BINARY,
&varValue);
_PrintIfErrorStr2(
hr,
"Policy:polGetCertificateExtension",
TEXT(szOID_ENROLL_CERTTYPE_EXTENSION),
CERTSRV_E_PROPERTY_EMPTY);
if (S_OK == hr)
{
// There was a cert type indicator.
// varValue points to an encoded string
if (VT_BSTR != varValue.vt)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Policy:varValue.vt");
}
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(BYTE *) varValue.bstrVal,
SysStringByteLen(varValue.bstrVal),
CERTLIB_USE_LOCALALLOC,
(VOID **) &pName,
&cbType))
{
hr = myHLastError();
_JumpError(hr, error, "Policy:myDecodeObject");
}
strTemplateName = SysAllocString((WCHAR *) pName->Value.pbData);
if (NULL == strTemplateName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
DBGPRINT((DBG_SS_CERTPOL, "Extension Template: %ws\n", strTemplateName));
}
fConflict = FALSE;
fTemplateMissing = FALSE;
// Retrieve the template from the request attributes
hr = polGetRequestAttribute(pServer, wszPROPCERTTEMPLATE, &strTemplateRA);
if (S_OK != hr)
{
_PrintErrorStr2(
hr,
"Policy:polGetRequestAttribute",
wszPROPCERTTEMPLATE,
CERTSRV_E_PROPERTY_EMPTY);
hr = S_OK;
// end_sdksample
if (m_fEnterpriseCA &&
NULL == strTemplateObjId &&
NULL == strTemplateName)
{
hrTemplate = CERTSRV_E_NO_CERT_TYPE;
_PrintError(hrTemplate, "Policy:Request contains no template name");
}
// begin_sdksample
}
else
{
DBGPRINT((DBG_SS_CERTPOL, "Attribute Template: %ws\n", strTemplateRA));
if (NULL != strTemplateObjId &&
!_TemplateNamesMatch(strTemplateObjId, strTemplateRA, &f))
{
fConflict = TRUE;
if (f)
{
fTemplateMissing = TRUE;
}
}
if (NULL != strTemplateName &&
!_TemplateNamesMatch(strTemplateName, strTemplateRA, &f))
{
fConflict = TRUE;
if (f)
{
fTemplateMissing = TRUE;
}
}
hr = myVerifyObjId(strTemplateRA);
fRAObjId = S_OK == hr;
}
if (NULL != strTemplateObjId &&
NULL != strTemplateName &&
!_TemplateNamesMatch(strTemplateObjId, strTemplateName, &f))
{
fConflict = TRUE;
if (f)
{
fTemplateMissing = TRUE;
}
}
if (fConflict)
{
hrTemplate = CERTSRV_E_TEMPLATE_CONFLICT;
if (NULL != strTemplateObjId)
{
_PrintErrorStr(
hrTemplate,
"Policy:Extension Template ObjId",
strTemplateObjId);
}
if (NULL != strTemplateName)
{
_PrintErrorStr(
hrTemplate,
"Policy:Extension Template Name",
strTemplateName);
}
if (NULL != strTemplateRA)
{
_PrintErrorStr(
hrTemplate,
"Policy:Attribute Template",
strTemplateRA);
}
}
pwszTemplateName = strTemplateName;
pwszTemplateObjId = strTemplateObjId;
if (fRAObjId)
{
if (NULL == pwszTemplateObjId)
{
pwszTemplateObjId = strTemplateRA;
}
}
else
{
if (NULL == pwszTemplateName)
{
pwszTemplateName = strTemplateRA;
}
}
// end_sdksample
if (m_fEnterpriseCA)
{
DWORD dwFlags;
hr = m_pPolicy->FindTemplate(
pwszTemplateName,
pwszTemplateObjId,
&ptp);
if (S_OK != hr)
{
_PrintErrorStr(
hr,
"FindTemplate",
NULL != pwszTemplateName? pwszTemplateName : pwszTemplateObjId);
if (S_OK == hrTemplate || fTemplateMissing)
{
hrTemplate = hr;
}
}
else
{
hr = ptp->GetFlags(CERTTYPE_GENERAL_FLAG, &dwFlags);
_JumpIfError(hr, error, "Policy:GetFlags");
if ((CT_FLAG_IS_CA | CT_FLAG_IS_CROSS_CA) & dwFlags)
{
m_fCA = TRUE;
}
hr = ptp->GetFlags(CERTTYPE_ENROLLMENT_FLAG, &dwFlags);
_JumpIfError(hr, error, "Policy:GetFlags");
hr = _SetFlagsProperty(
pServer,
wszPROPCERTIFICATEENROLLMENTFLAGS,
dwFlags);
_JumpIfError(hr, error, "Policy:_SetFlagsProperty");
hr = ptp->GetFlags(CERTTYPE_GENERAL_FLAG, &dwFlags);
_JumpIfError(hr, error, "Policy:GetFlags");
hr = _SetFlagsProperty(
pServer,
wszPROPCERTIFICATEGENERALFLAGS,
dwFlags);
_JumpIfError(hr, error, "Policy:_SetFlagsProperty");
if (CT_FLAG_MACHINE_TYPE & dwFlags)
{
m_fUser = FALSE;
}
pwszTemplateName = ptp->GetTemplateName();
pwszTemplateObjId = ptp->GetTemplateObjId();
}
}
else
// begin_sdksample
{
if (NULL != pwszTemplateName)
{
for (i = 0; i < ARRAYSIZE(s_apwszCATypes); i++)
{
if (0 == mylstrcmpiL(s_apwszCATypes[i], pwszTemplateName))
{
m_fCA = TRUE;
break;
}
}
}
}
hr = SetTemplateName(pServer, pwszTemplateName, pwszTemplateObjId);
_JumpIfError(hr, error, "Policy:SetTemplateName");
pwszV1TemplateClass = pwszTemplateName;
// end_sdksample
if (NULL != ptp)
{
DWORD dwSubjectNameFlags;
// on resubmitted requests we don't have the requester's token
if (bNewRequest)
{
CSASSERT(NULL != m_hToken);
hr = ptp->AccessCheck(m_hToken);
_JumpIfError(hr, error, "Policy:AccessCheck");
}
hr = ptp->GetV1TemplateClass(&pwszV1TemplateClass);
_JumpIfError(hr, error, "AddTemplateNameExtension");
hr = ptp->GetFlags(CERTTYPE_SUBJECT_NAME_FLAG, &dwSubjectNameFlags);
_JumpIfError(hr, error, "GetFlags");
if (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT & dwSubjectNameFlags)
{
*pfEnableEnrolleeExtensions = TRUE;
}
hr = ptp->Clone(&m_pTemplate);
_JumpIfError(hr, error, "Clone");
}
// begin_sdksample
hr = pPolicy->AddV1TemplateNameExtension(pServer, pwszV1TemplateClass);
_JumpIfError(hr, error, "AddTemplateNameExtension");
error:
if (S_OK != hrTemplate)
{
hr = hrTemplate; // override secondary errors
// end_sdksample
WCHAR const *apwsz[2];
DWORD cpwsz = 0;
DWORD LogMsg;
switch (hrTemplate)
{
default:
case CERTSRV_E_NO_CERT_TYPE:
LogMsg = MSG_MISSING_CERT_TYPE;
apwsz[cpwsz++] = wszPROPCERTTEMPLATE;
break;
// The request specifies conflicting certificate templates: %1.
case CERTSRV_E_TEMPLATE_CONFLICT:
LogMsg = MSG_CONFLICTING_CERT_TYPE;
break;
// The request was for a certificate template that is not
// supported by the Certificate Services policy: %1.
case CERTSRV_E_UNSUPPORTED_CERT_TYPE:
LogMsg = MSG_UNSUPPORTED_CERT_TYPE;
break;
}
if (0 == cpwsz)
{
WCHAR const *pwsz;
pwszTemplateList = reqCombineTemplates(
strTemplateName,
strTemplateObjId,
strTemplateRA);
pwsz = pwszTemplateList;
if (NULL == pwsz)
{
pwsz = strTemplateName;
if (NULL == pwsz)
{
pwsz = strTemplateObjId;
if (NULL == pwsz)
{
pwsz = strTemplateRA;
if (NULL == pwsz)
{
pwsz = L"???";
}
}
}
}
apwsz[cpwsz++] = pwsz;
}
CSASSERT(ARRAYSIZE(apwsz) > cpwsz);
apwsz[cpwsz] = NULL;
BuildErrorInfo(hr, LogMsg, apwsz);
// begin_sdksample
}
VariantClear(&varValue);
// end_sdksample
if (NULL != pwszTemplateList)
{
LocalFree(pwszTemplateList);
}
// begin_sdksample
if (NULL != pName)
{
LocalFree(pName);
}
if (NULL != pTemplate)
{
LocalFree(pTemplate);
}
if (NULL != strTemplateObjId)
{
SysFreeString(strTemplateObjId);
}
if (NULL != strTemplateName)
{
SysFreeString(strTemplateName);
}
if (NULL != strTemplateRA)
{
SysFreeString(strTemplateRA);
}
return(hr);
}
// end_sdksample
//+--------------------------------------------------------------------------
// CRequestInstance::ApplyTemplate
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::ApplyTemplate(
IN ICertServerPolicy *pServer,
OUT BOOL *pfReenroll,
OUT DWORD *pdwEnrollmentFlags,
OUT DWORD *pcCriticalExtensions,
OUT WCHAR const * const **papwszCriticalExtensions)
{
HRESULT hr;
*pdwEnrollmentFlags = 0;
*pfReenroll = FALSE;
*pcCriticalExtensions = 0;
*papwszCriticalExtensions = NULL;
if (NULL == m_pTemplate)
{
hr = CERTSRV_E_UNSUPPORTED_CERT_TYPE;
_JumpErrorStr(
hr,
error,
"Policy:ApplyTemplate:no cert template",
m_strTemplateName);
}
hr = m_pTemplate->Apply(pServer, this, pfReenroll);
_JumpIfError(hr, error, "Apply");
hr = m_pTemplate->GetFlags(CERTTYPE_ENROLLMENT_FLAG, pdwEnrollmentFlags);
_JumpIfError(hr, error, "GetFlags");
hr = m_pTemplate->GetCriticalExtensions(
pcCriticalExtensions,
papwszCriticalExtensions);
_JumpIfError(hr, error, "GetCriticalExtension");
error:
DBGPRINT((DBG_SS_CERTPOLI, "Policy:_ApplyTemplate: %x\n", hr));
return(hr);
}
// begin_sdksample
BOOL
CRequestInstance::_TemplateNamesMatch(
IN WCHAR const *pwszTemplateName1,
IN WCHAR const *pwszTemplateName2,
OUT BOOL *pfTemplateMissing)
{
HRESULT hr1;
HRESULT hr2;
BOOL fMatch = TRUE;
*pfTemplateMissing = FALSE;
if (0 == mylstrcmpiL(pwszTemplateName1, pwszTemplateName2))
{
goto done; // identical names
}
// end_sdksample
if (m_fEnterpriseCA)
{
CTemplatePolicy *pTemplate1;
CTemplatePolicy *pTemplate2;
hr1 = m_pPolicy->FindTemplate(pwszTemplateName1, NULL, &pTemplate1);
hr2 = m_pPolicy->FindTemplate(pwszTemplateName2, NULL, &pTemplate2);
if (S_OK == hr1 && S_OK == hr2)
{
if (pTemplate1 == pTemplate2)
{
goto done;
}
}
else
{
*pfTemplateMissing = TRUE;
}
}
else
// begin_sdksample
{
hr1 = myVerifyObjId(pwszTemplateName1);
hr2 = myVerifyObjId(pwszTemplateName2);
if ((S_OK == hr1) ^ (S_OK == hr2))
{
goto done;
}
}
fMatch = FALSE;
done:
return(fMatch);
}
//+--------------------------------------------------------------------------
// CRequestInstance::SetTemplateName
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::SetTemplateName(
IN ICertServerPolicy *pServer,
IN OPTIONAL WCHAR const *pwszTemplateName,
IN OPTIONAL WCHAR const *pwszTemplateObjId)
{
HRESULT hr;
BSTR strProp = NULL;
BSTR strTemplateName = NULL;
if (NULL != pwszTemplateName)
{
m_strTemplateName = SysAllocString(pwszTemplateName);
if (NULL == m_strTemplateName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
strTemplateName = m_strTemplateName;
}
if (NULL != pwszTemplateObjId)
{
m_strTemplateObjId = SysAllocString(pwszTemplateObjId);
if (NULL == m_strTemplateObjId)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
strTemplateName = m_strTemplateObjId;
}
if (NULL != strTemplateName)
{
VARIANT var;
strProp = SysAllocString(wszPROPCERTIFICATETEMPLATE);
if (NULL == strProp)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
var.vt = VT_BSTR;
var.bstrVal = strTemplateName;
hr = pServer->SetCertificateProperty(strProp, PROPTYPE_STRING, &var);
_JumpIfError(hr, error, "Policy:SetCertificateProperty");
}
hr = S_OK;
error:
if (NULL != strProp)
{
SysFreeString(strProp);
}
return(hr);
}
// end_sdksample
HRESULT
CRequestInstance::_SetFlagsProperty(
IN ICertServerPolicy *pServer,
IN WCHAR const *pwszPropName,
IN DWORD dwFlags)
{
HRESULT hr;
BSTR strPropName = NULL;
VARIANT var;
strPropName = SysAllocString(pwszPropName);
if (NULL == strPropName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
var.vt = VT_I4;
var.lVal = dwFlags;
hr = pServer->SetCertificateProperty(strPropName, PROPTYPE_LONG, &var);
_JumpIfError(hr, error, "Policy:SetCertificateProperty");
error:
if (NULL != strPropName)
{
SysFreeString(strPropName);
}
return(hr);
}
VOID
CRequestInstance::GetTemplateVersion(
OUT DWORD *pdwTemplateMajorVersion,
OUT DWORD *pdwTemplateMinorVersion)
{
*pdwTemplateMajorVersion = m_dwTemplateMajorVersion;
*pdwTemplateMinorVersion = m_dwTemplateMinorVersion;
}
//+--------------------------------------------------------------------------
// CRequestInstance::_InitToken
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::_InitToken(
IN ICertServerPolicy *pServer)
{
HRESULT hr;
VARIANT varValue;
HANDLE hToken;
VariantInit(&varValue);
hr = polGetProperty(
pServer,
FALSE, // fRequest
wszPROPREQUESTERTOKEN,
PROPTYPE_BINARY,
&varValue);
if(CERTSRV_E_PROPERTY_EMPTY == hr)
{
_PrintIfError(hr, "polGetProperty(PROPREQUESTERTOKEN)");
hr = S_OK;
}
_JumpIfError(hr, error, "polGetProperty(PROPREQUESTERTOKEN)");
// Got a token value
if (sizeof(hToken) != SysStringByteLen(varValue.bstrVal) ||
NULL == *(HANDLE *) varValue.bstrVal)
{
hr = E_HANDLE;
BuildErrorInfo(hr, MSG_NO_REQUESTER_TOKEN, NULL);
_JumpError(hr, error, "Policy:Token/Length");
}
hToken = *(HANDLE *) varValue.bstrVal;
if (!DuplicateToken(hToken, SecurityIdentification, &m_hToken))
{
hr = myHLastError();
BuildErrorInfo(hr, MSG_NO_REQUESTER_TOKEN, NULL);
_JumpError(hr, error, "Policy:DuplicateToken");
}
hr = S_OK;
error:
VariantClear(&varValue);
return(hr);
}
//+--------------------------------------------------------------------------
// CRequestInstance::_InitClientOSVersionInfo
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::_InitClientOSVersionInfo(
IN ICertServerPolicy *pServer)
{
HRESULT hr;
VARIANT varValue;
DWORD dwFormat = 0;
LONG l;
BSTR strVersionInfo = NULL;
BSTR strCSPProvider = NULL;
VariantInit(&varValue);
// In the following code, we also attempt to determine if the
// request came from an xenroll.dll, so we know whether to put
// the UPN in the subject name. We put the UPN in the subject name
// for old xenroll requests, as we know that autoenrollment on those
// machines will need it to prevent enrollment loops.
// Get the optional OS version information. Ignore failure.
hr = polGetRequestAttribute(pServer, wszPROPREQUESTOSVERSION, &strVersionInfo);
if (S_OK == hr && NULL != strVersionInfo)
{
DWORD dwMajor, dwMinor, dwBuild, dwPlatform;
if (4 == swscanf(
strVersionInfo,
L"%d.%d.%d.%d",
&dwMajor,
&dwMinor,
&dwBuild,
&dwPlatform))
{
m_RequestOsVersion.dwMajorVersion = dwMajor;
m_RequestOsVersion.dwMinorVersion = dwMinor;
m_RequestOsVersion.dwBuildNumber = dwBuild;
m_RequestOsVersion.dwPlatformId = dwPlatform;
}
// We know this is an xenroll request,
// as it has a OSVERSIONINFO property
m_fIsXenrollRequest = TRUE;
m_fClientVersionSpecified = TRUE;
}
hr = polGetRequestLongProperty(pServer, wszPROPREQUESTTYPE, &l);
if (S_OK == hr)
{
dwFormat = CR_IN_FORMATMASK & l;
}
if (dwFormat == CR_IN_KEYGEN)
{
// KEYGEN requests only come from netscape, not xenroll,
// so we know it's not an xenroll request.
m_fIsXenrollRequest = FALSE;
}
else if (!m_fIsXenrollRequest)
{
hr = polGetRequestAttribute(
pServer,
wszPROPREQUESTCSPPROVIDER,
&strCSPProvider);
if (S_OK == hr && NULL != strCSPProvider)
{
// xenroll includes a CSPPROVIDER attribute
m_fIsXenrollRequest = TRUE;
}
}
hr = S_OK;
//error:
if (NULL != strVersionInfo)
{
SysFreeString(strVersionInfo);
}
if (NULL != strCSPProvider)
{
SysFreeString(strCSPProvider);
}
VariantClear(&varValue);
return(hr);
}
//+--------------------------------------------------------------------------
// CRequestInstance::_LoadPrincipalObject
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
HRESULT
CRequestInstance::_LoadPrincipalObject(
IN ICertServerPolicy *pServer,
IN CTemplatePolicy *pTemplate,
IN BOOL fDNSNameRequired)
{
HRESULT hr;
BSTR strProp = NULL;
LPWSTR *awszUPN = NULL;
BSTR strSamName = NULL;
BSTR strClientDC = NULL;
WCHAR *pwszUserName;
DWORD dwFlags;
VARIANT var;
VariantInit(&var);
// Get the name of the user or machine
hr = polGetRequestStringProperty(
pServer,
wszPROPREQUESTERNAME,
&strSamName);
_JumpIfErrorStr(
hr,
error,
"Policy:polGetRequestStringProperty",
wszPROPREQUESTERNAME);
if (L'\0' == *strSamName)
{
// can't have a zero length name
hr = E_ACCESSDENIED;
_JumpError(hr, error, "Policy:zero length name");
}
// See if there's a domain, as well
pwszUserName = wcschr(strSamName, L'\\');
if (NULL == pwszUserName)
{
WCHAR wszDN[MAX_PATH];
DWORD cwc = ARRAYSIZE(wszDN);
// No domain portion, so assume part of the current domain.
if (GetUserNameEx(NameSamCompatible, wszDN, &cwc))
{
pwszUserName = wcschr(wszDN, L'\\');
if (NULL != pwszUserName)
{
BSTR strT;
DWORD cwcT;
pwszUserName[1] = L'\0';
cwcT = wcslen(wszDN) + wcslen(strSamName);
strT = SysAllocStringLen(NULL, cwcT);
if (NULL == strT)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
wcscpy(strT, wszDN);
wcscat(strT, strSamName);
CSASSERT(wcslen(strT) == cwcT);
CSASSERT(SysStringLen(strT) == cwcT);
SysFreeString(strSamName);
strSamName = strT;
}
}
}
pwszUserName = wcschr(strSamName, L'\\');
if (NULL == pwszUserName)
{
pwszUserName = strSamName;
}
else
{
pwszUserName++;
}
DBGPRINT((DBG_SS_CERTPOL, "pwszUserName = %ws\n", pwszUserName));
DBGPRINT((DBG_SS_CERTPOL, "strSamName = %ws\n", strSamName));
// If the user name ends in $, it's a hint that this is a machine account.
if (pwszUserName[wcslen(pwszUserName) - 1] == L'$')
{
if (m_fUser)
{
DBGPRINT((
DBG_SS_CERTPOL,
"USER TEMPLATE w/ '$': %ws\n",
pwszUserName));
}
}
else
{
if (!m_fUser)
{
DBGPRINT((
DBG_SS_CERTPOL,
"MACHINE TEMPLATE w/o '$': %ws\n",
pwszUserName));
}
}
hr = polGetCertificateStringProperty(pServer, wszPROPUSERDN, &m_strUserDN);
_JumpIfErrorStr(
hr,
error,
"Policy:polGetCertificateStringProperty",
wszPROPUSERDN);
hr = _GetDSObject(pServer, fDNSNameRequired, NULL);
// If we couldn't find the DS object or the DNS name is missing or out of
// date, it is a machine object and the client specified his DC name, chase
// the client supplied DC in hopes of finding more current information.
if ((CERTSRV_E_SUBJECT_DNS_REQUIRED == hr ||
HRESULT_FROM_WIN32(DNS_ERROR_NAME_DOES_NOT_EXIST) == hr ||
(HRESULT) ERROR_DS_OBJ_NOT_FOUND == hr ||
HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND) == hr) &&
fDNSNameRequired &&
(EDITF_ENABLECHASECLIENTDC & m_pPolicy->GetEditFlags()) &&
!m_fUser)
{
HRESULT hr2;
_PrintError(hr, "_GetDSObject");
hr2 = polGetRequestAttribute(pServer, wszPROPCLIENTDCDNS, &strClientDC);
_PrintIfErrorStr(
hr2,
"Policy:polGetRequestAttribute",
wszPROPCLIENTDCDNS);
if (S_OK == hr2 && NULL != strClientDC)
{
_PrintErrorStr(hr, "_GetDSObject", strClientDC);
hr = _GetDSObject(pServer, TRUE, strClientDC);
_JumpIfErrorStr(hr, error, "_GetDSObject", strClientDC);
}
}
_JumpIfError(hr, error, "_GetDSObject");
hr = pTemplate->GetFlags(CERTTYPE_GENERAL_FLAG, &dwFlags);
_JumpIfError(hr, error, "Policy:GetFlags");
if (!m_fUser ^ (0 != (CT_FLAG_MACHINE_TYPE & dwFlags)))
{
// if m_fUser state no longer agrees with the template machine flag,
// toggle the flag and store the corrected value in the database.
dwFlags ^= CT_FLAG_MACHINE_TYPE;
hr = _SetFlagsProperty(
pServer,
wszPROPCERTIFICATEGENERALFLAGS,
dwFlags);
_JumpIfError(hr, error, "Policy:_SetFlagsProperty");
}
// Build the UPN value.
// If a machine, the UPN must be the DNS name.
hr = _GetValues(m_fUser? DS_ATTR_UPN : DS_ATTR_DNS_NAME, &awszUPN);
if (S_OK == hr && NULL != awszUPN && NULL != awszUPN[0])
{
hr = myDupString(awszUPN[0], &m_pwszUPN);
_JumpIfError(hr, error, "myDupString");
}
else
{
if (m_fUser)
{
WCHAR **awszExplodedDN;
WCHAR **ppwszCurrent;
DWORD cwcT;
// Build a UPN from the username -- without the SAM domain.
// Get a buffer that will be big enough.
cwcT = wcslen(pwszUserName) + 1 + wcslen(m_strUserDN);
m_pwszUPN = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(1 + cwcT) * sizeof(WCHAR));
if (NULL == m_pwszUPN)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:LocalAlloc");
}
wcscpy(m_pwszUPN, pwszUserName);
awszExplodedDN = ldap_explode_dn(m_strUserDN, 0);
if (NULL != awszExplodedDN)
{
wcscat(m_pwszUPN, L"@");
for (ppwszCurrent = awszExplodedDN;
NULL != *ppwszCurrent;
ppwszCurrent++)
{
WCHAR wszDC[4];
wcsncpy(wszDC, *ppwszCurrent, ARRAYSIZE(wszDC) - 1);
wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
if (0 == LSTRCMPIS(wszDC, L"DC="))
{
wcscat(
m_pwszUPN,
(*ppwszCurrent) + ARRAYSIZE(wszDC) - 1);
wcscat(m_pwszUPN, L".");
CSASSERT(wcslen(m_pwszUPN) < cwcT);
}
}
// remove the trailing '.' or "@" if there was no DC=
m_pwszUPN[wcslen(m_pwszUPN) - 1] = L'\0';
// We're done referencing awszExplodedDN, so free it.
// ldap_value_free frees the ldap_explode_dn return value
ldap_value_free(awszExplodedDN);
}
}
else
{
if (CERTSRV_E_PROPERTY_EMPTY == hr || S_OK == hr)
{
hr = CERTSRV_E_SUBJECT_DNS_REQUIRED;
}
BuildErrorInfo(hr, MSG_NO_DNS_NAME, &m_strUserDN);
_JumpErrorStr(hr, error, "No DNS Name", m_strUserDN);
}
}
DBGPRINT((DBG_SS_CERTPOL, "m_pwszUPN = %ws\n", m_pwszUPN));
strProp = SysAllocString(wszPROPCERTIFICATEUPN);
if (NULL == strProp)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocString");
}
var.bstrVal = NULL;
if (!myConvertWszToBstr(&var.bstrVal, m_pwszUPN, -1))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:myConvertWszToBstr");
}
var.vt = VT_BSTR;
hr = pServer->SetCertificateProperty(strProp, PROPTYPE_STRING, &var);
_JumpIfError(hr, error, "Policy:SetCertificateProperty");
error:
if (NULL != strClientDC)
{
SysFreeString(strClientDC);
}
if (NULL != strSamName)
{
SysFreeString(strSamName);
}
if (NULL != awszUPN)
{
_FreeValues(awszUPN);
}
if (NULL != strProp)
{
SysFreeString(strProp);
}
VariantClear(&var);
return(hr);
}
VOID
CRequestInstance::_ReleasePrincipalObject()
{
if (NULL != m_pldGC)
{
if (NULL != m_SearchResult)
{
ldap_msgfree(m_SearchResult);
m_SearchResult = NULL;
}
reqReleaseLdapGC(&m_pldGC);
myLdapUnbind(&m_pldClientDC);
}
}
#define wszHOSTPREFIX L"HOST/"
#define DS_ATTR_SPN L"servicePrincipalName"
#define DS_ATTR_BACKLINK L"serverReferenceBL"
#define wszSEARCHUSER L"(objectCategory=user)"
#define wszSEARCHCOMPUTER L"(objectCategory=computer)"
#define wszSEARCHNTDSDSA L"(objectCategory=nTDSDSA)"
#define wszSEARCHUSERCOMPUTER L"(|" wszSEARCHUSER wszSEARCHCOMPUTER L")"
#define wszSEARCHSPN L"(" DS_ATTR_SPN L"=" wszHOSTPREFIX L"%ws)"
#define wszSEARCHCOMPUTERSPN L"(&" wszSEARCHCOMPUTER wszSEARCHSPN L")"
#define wszDSOBJECTCATEGORYATTRIBUTE L"objectCategory"
WCHAR *s_apwszAttrsClientDC[] = {
DS_ATTR_DNS_NAME,
DS_ATTR_SPN,
DS_ATTR_BACKLINK,
NULL,
};
// Use the ldap GC handle to verify the client-supplied DC DNS name is a valid
// DC in our forest. If it is, bind to the client supplied DC.
HRESULT
reqFindClientDC(
IN LDAP *pldGC,
IN WCHAR const *pwszClientDC,
OUT LDAP **ppldClientDC)
{
HRESULT hr;
ULONG ldaperr;
struct l_timeval timeout;
WCHAR *pwszSearch = NULL;
DWORD cwc;
WCHAR *pwszError = NULL;
LDAPMessage *pSearchResult = NULL;
LDAPMessage *pEntry = NULL;
WCHAR **ppwszValues = NULL;
WCHAR *pwszServiceDN = NULL;
myLdapUnbind(ppldClientDC);
cwc = WSZARRAYSIZE(wszSEARCHCOMPUTERSPN) + wcslen(pwszClientDC);
pwszSearch = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
if (NULL == pwszSearch)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocStringByteLen");
}
wsprintf(pwszSearch, wszSEARCHCOMPUTERSPN, pwszClientDC);
CSASSERT(wcslen(pwszSearch) <= cwc);
timeout.tv_sec = csecLDAPTIMEOUT;
timeout.tv_usec = 0;
// ldap_search the GC for a Computer object with matching SPN.
// Fetch the service object back link attribute from the Computer object.
// Make sure the service object has a child object of class NTDSDSA
ldaperr = ldap_search_ext_s(
pldGC,
NULL,
LDAP_SCOPE_SUBTREE,
pwszSearch,
s_apwszAttrsClientDC,
0,
NULL,
NULL,
&timeout,
10000,
&pSearchResult);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pldGC, ldaperr, &pwszError);
_PrintErrorStr(hr, "Policy:ldap_search_ext_s", pwszError);
_JumpErrorStr(hr, error, "Policy:ldap_search_ext_s", pwszSearch);
}
if (0 == ldap_count_entries(pldGC, pSearchResult))
{
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
_JumpError(hr, error, "Policy:ldap_count_entries");
}
pEntry = ldap_first_entry(pldGC, pSearchResult);
if (NULL == pEntry)
{
hr = myHLdapLastError(pldGC, NULL);
_JumpError(hr, error, "Policy:ldap_first_entry");
}
ppwszValues = ldap_get_values(pldGC, pEntry, DS_ATTR_BACKLINK);
if (NULL == ppwszValues || NULL == ppwszValues[0])
{
hr = CERTSRV_E_PROPERTY_EMPTY;
_JumpErrorStr(hr, error, "Policy:ldap_get_values", DS_ATTR_BACKLINK);
}
hr = myDupString(ppwszValues[0], &pwszServiceDN);
_JumpIfError(hr, error, "myDupString");
if (NULL != ppwszValues)
{
ldap_value_free(ppwszValues);
ppwszValues = NULL;
}
if (NULL != pSearchResult)
{
ldap_msgfree(pSearchResult);
pSearchResult = NULL;
}
if (NULL != pwszError)
{
LocalFree(pwszError);
pwszError = NULL;
}
// ldap_search the GC for a child of the Service object with class NTDSDSA.
ldaperr = ldap_search_ext_s(
pldGC,
pwszServiceDN,
LDAP_SCOPE_ONELEVEL,
wszSEARCHNTDSDSA,
NULL,
0,
NULL,
NULL,
&timeout,
10000,
&pSearchResult);
if (LDAP_SUCCESS != ldaperr)
{
hr = myHLdapError(pldGC, ldaperr, &pwszError);
_PrintErrorStr(hr, "Policy:ldap_search_ext_s", pwszError);
_JumpErrorStr(hr, error, "Policy:ldap_search_ext_s", pwszServiceDN);
}
if (0 == ldap_count_entries(pldGC, pSearchResult))
{
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
_JumpError(hr, error, "Policy:ldap_count_entries");
}
hr = myLdapBind(0, pwszClientDC, ppldClientDC);
_JumpIfError(hr, error, "myLdapBind");
error:
if (NULL != ppwszValues)
{
ldap_value_free(ppwszValues);
}
if (NULL != pSearchResult)
{
ldap_msgfree(pSearchResult);
}
if (NULL != pwszError)
{
LocalFree(pwszError);
}
if (NULL != pwszServiceDN)
{
LocalFree(pwszServiceDN);
}
if (NULL != pwszSearch)
{
LocalFree(pwszSearch);
}
return(hr);
}
WCHAR *s_apwszAttrs[] = {
wszDSOBJECTCLASSATTRIBUTE,
//wszDSOBJECTCATEGORYATTRIBUTE,
DS_ATTR_COMMON_NAME,
DS_ATTR_DNS_NAME,
DS_ATTR_EMAIL_ADDR,
DS_ATTR_OBJECT_GUID,
DS_ATTR_UPN,
NULL,
};
HRESULT
CRequestInstance::_GetDSObject(
IN ICertServerPolicy *pServer,
IN BOOL fDNSNameRequired,
OPTIONAL IN WCHAR const *pwszClientDC)
{
HRESULT hr;
ULONG ldaperr;
struct l_timeval timeout;
WCHAR **ppwszValues = NULL;
BOOL fUser;
LONG cRetry;
WCHAR *pwszError = NULL;
BSTR strDNS = NULL;
timeout.tv_sec = csecLDAPTIMEOUT;
timeout.tv_usec = 0;
cRetry = 0;
while (TRUE)
{
BOOL fCached;
if (NULL != m_SearchResult)
{
ldap_msgfree(m_SearchResult);
m_SearchResult = NULL;
}
hr = reqGetLdapGC(m_pPolicy->GetEditFlags(), &m_pldGC, &fCached);
_JumpIfError(hr, error, "reqGetLdapGC");
m_pldT = m_pldGC;
if (NULL != pwszClientDC)
{
hr = reqFindClientDC(m_pldGC, pwszClientDC, &m_pldClientDC);
_JumpIfError(hr, error, "reqFindClientDC");
m_pldT = m_pldClientDC;
}
ldaperr = ldap_search_ext_s(
m_pldT,
m_strUserDN,
LDAP_SCOPE_BASE,
wszSEARCHUSERCOMPUTER,
s_apwszAttrs,
0,
NULL,
NULL,
&timeout,
10000,
&m_SearchResult);
if (LDAP_SUCCESS != ldaperr)
{
if (NULL != pwszError)
{
LocalFree(pwszError);
pwszError = NULL;
}
hr = myHLdapError(m_pldT, ldaperr, &pwszError);
// only retry for cached GC handle & when hr != object not found
if (fCached &&
(HRESULT) ERROR_DS_OBJ_NOT_FOUND != hr &&
NULL == pwszClientDC &&
cRetry++ < g_cGCCacheMax)
{
// get rid of GC handle we have, it might be stale
_PrintError2(hr, "Policy:ldap_search_ext_s", hr);
myLdapUnbind(&m_pldGC);
myLdapUnbind(&m_pldClientDC);
continue;
}
_JumpErrorStr(hr, error, "Policy:ldap_search_ext_s", m_strUserDN);
}
break;
}
if (0 == ldap_count_entries(m_pldT, m_SearchResult))
{
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
_JumpError(hr, error, "Policy:ldap_count_entries");
}
m_PrincipalAttributes = ldap_first_entry(m_pldT, m_SearchResult);
if (NULL == m_PrincipalAttributes)
{
hr = myHLdapLastError(m_pldT, NULL);
_JumpError(hr, error, "Policy:ldap_first_entry");
}
#if DBG_CERTSRV
{
DWORD i;
for (i = 0; NULL != s_apwszAttrs[i]; i++)
{
if (0 == LSTRCMPIS(s_apwszAttrs[i], DS_ATTR_OBJECT_GUID))
{
BSTR strGuid = NULL;
hr = _GetObjectGUID(&strGuid);
if (S_OK == hr)
{
WCHAR *pwsz;
hr = myCLSIDToWsz((CLSID const *) strGuid, &pwsz);
if (S_OK == hr)
{
DBGPRINT((
DBG_SS_CERTPOL,
"%ws = %ws\n",
s_apwszAttrs[i],
pwsz));
LocalFree(pwsz);
}
SysFreeString(strGuid);
}
}
else
{
hr = _GetValues(s_apwszAttrs[i], &ppwszValues);
if (S_OK == hr)
{
DWORD j;
for (j = 0; NULL != ppwszValues[j]; j++)
{
DBGPRINT((
DBG_SS_CERTPOL,
"%ws[%u] = %ws\n",
s_apwszAttrs[i],
j,
ppwszValues[j]));
}
_FreeValues(ppwszValues);
ppwszValues = NULL;
}
}
if (S_OK != hr)
{
DBGPRINT((DBG_SS_CERTPOL, "%ws = NULL\n", s_apwszAttrs[i]));
}
}
}
#endif
hr = _GetValues(wszDSOBJECTCLASSATTRIBUTE, &ppwszValues);
_JumpIfErrorStr(hr, error, "Policy:_GetValues", wszDSOBJECTCLASSATTRIBUTE);
fUser = TRUE;
if (NULL != ppwszValues)
{
DWORD i;
for (i = 0; NULL != ppwszValues[i]; i++)
{
DBGPRINT((
DBG_SS_CERTPOLI,
"%ws[%u] = %ws\n",
wszDSOBJECTCLASSATTRIBUTE,
i,
ppwszValues[i]));
if (0 == LSTRCMPIS(ppwszValues[i], L"computer"))
{
fUser = FALSE;
break;
}
}
}
if (fUser != m_fUser)
{
DBGPRINT((
DBG_SS_CERTPOL,
fUser? "MACHINE -> USER: %ws\n" : "USER -> MACHINE: %ws\n",
m_strUserDN));
m_fUser = fUser;
}
if (!m_fUser && fDNSNameRequired)
{
if (NULL != ppwszValues)
{
_FreeValues(ppwszValues);
ppwszValues = NULL;
}
hr = _GetValues(DS_ATTR_DNS_NAME, &ppwszValues);
if (S_OK != hr || NULL == ppwszValues || NULL == ppwszValues[0])
{
_PrintIfError(hr, "Policy:_GetValues");
hr = CERTSRV_E_SUBJECT_DNS_REQUIRED;
}
_JumpIfErrorStr(hr, error, "Policy:_GetValues", DS_ATTR_DNS_NAME);
hr = polGetRequestAttribute(pServer, wszPROPREQUESTMACHINEDNS, &strDNS);
_PrintIfErrorStr(
hr,
"Policy:polGetRequestAttribute",
wszPROPREQUESTMACHINEDNS);
if (S_OK == hr &&
NULL != strDNS &&
0 != mylstrcmpiL(ppwszValues[0], strDNS))
{
hr = HRESULT_FROM_WIN32(DNS_ERROR_NAME_DOES_NOT_EXIST);
_PrintErrorStr(hr, "Policy:DNS name changed", ppwszValues[0]);
_JumpErrorStr(hr, error, "Policy:DNS name changed", strDNS);
}
}
hr = S_OK;
error:
if (S_OK != hr && NULL != pwszError)
{
WCHAR *apwsz[2];
apwsz[0] = m_strUserDN;
apwsz[1] = pwszError;
BuildErrorInfo(
hr,
HRESULT_FROM_WIN32(ERROR_DS_REFERRAL) == hr?
MSG_DS_REFERRAL :
MSG_DS_SEARCH_ERROR,
apwsz);
}
if (NULL != strDNS)
{
SysFreeString(strDNS);
}
if (NULL != pwszError)
{
LocalFree(pwszError);
}
if (NULL != ppwszValues)
{
_FreeValues(ppwszValues);
}
return(hr);
}
HRESULT
CRequestInstance::_GetValues(
IN WCHAR const *pwszName,
OUT WCHAR ***pppwszValues)
{
HRESULT hr;
WCHAR **ppwszValues = NULL;
ppwszValues = ldap_get_values(
m_pldT,
m_PrincipalAttributes,
const_cast<WCHAR *>(pwszName));
if (NULL == ppwszValues || NULL == ppwszValues[0])
{
hr = CERTSRV_E_PROPERTY_EMPTY;
_JumpErrorStr2(hr, error, "Policy:ldap_get_values", pwszName, hr);
}
*pppwszValues = ppwszValues;
ppwszValues = NULL;
hr = S_OK;
error:
if (NULL != ppwszValues)
{
ldap_value_free(ppwszValues);
}
return(hr);
}
HRESULT
CRequestInstance::_GetObjectGUID(
OUT BSTR *pstrGuid)
{
struct berval **pGuidVal = NULL;
HRESULT hr;
*pstrGuid = NULL;
pGuidVal = ldap_get_values_len(
m_pldT,
m_PrincipalAttributes,
DS_ATTR_OBJECT_GUID);
if (NULL == pGuidVal || NULL == pGuidVal[0])
{
hr = CERTSRV_E_PROPERTY_EMPTY;
_JumpError2(hr, error, "Policy:ldap_get_values_len", hr);
}
*pstrGuid = SysAllocStringByteLen(
pGuidVal[0]->bv_val,
~(sizeof(WCHAR) - 1) & pGuidVal[0]->bv_len);
if (NULL == *pstrGuid)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocStringByteLen");
}
hr = S_OK;
error:
if (NULL != pGuidVal)
{
ldap_value_free_len(pGuidVal);
}
return(hr);
}
HRESULT
CRequestInstance::_FreeValues(
IN WCHAR **ppwszValues)
{
if (NULL != ppwszValues)
{
ldap_value_free(ppwszValues);
}
return(S_OK);
}
HRESULT
CRequestInstance::_GetValueString(
IN WCHAR const *pwszName,
OUT BSTRC *pstrValue)
{
HRESULT hr;
WCHAR **ppwszValues = NULL;
BSTR strReturn;
DWORD i;
DWORD cwc;
*pstrValue = NULL;
hr = _GetValues(pwszName, &ppwszValues);
_JumpIfErrorStr(hr, error, "Policy:_GetValues", pwszName);
if (NULL == ppwszValues || NULL == ppwszValues[0])
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpErrorStr(hr, error, "Policy:ppwszValues", pwszName);
}
cwc = 0;
for (i = 0; NULL != ppwszValues[i]; i++)
{
if (0 != i)
{
cwc++;
}
cwc += wcslen(ppwszValues[i]);
}
strReturn = SysAllocStringLen(NULL, cwc);
if (NULL == strReturn)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Policy:SysAllocStringLen");
}
strReturn[0] = L'\0';
for (i = 0; NULL != ppwszValues[i]; i++)
{
if (0 != i)
{
wcscat(strReturn, L",");
}
wcscat(strReturn, ppwszValues[i]);
}
CSASSERT(SysStringByteLen(strReturn) == cwc * sizeof(WCHAR));
CSASSERT(wcslen(strReturn) == cwc);
*pstrValue = strReturn;
error:
if (NULL != ppwszValues)
{
_FreeValues(ppwszValues);
}
return(hr);
}