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.
3184 lines
77 KiB
3184 lines
77 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: ldap.cpp
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <pch.cpp>
|
|
|
|
#pragma hdrstop
|
|
|
|
#undef LdapMapErrorToWin32
|
|
#include <winldap.h>
|
|
#define LdapMapErrorToWin32 Use_myHLdapError_Instead_Of_LdapMapErrorToWin32
|
|
|
|
#include <ntldap.h>
|
|
|
|
#include "csldap.h"
|
|
#include "certacl.h"
|
|
#include "certtype.h"
|
|
#include "cainfop.h"
|
|
#include "csber.h"
|
|
#include "tptrlist.h"
|
|
|
|
#define __dwFILE__ __dwFILE_CERTLIB_LDAP_CPP__
|
|
|
|
|
|
static CHAR s_sdBerValue[] = {
|
|
BER_SEQUENCE,
|
|
3 * sizeof(BYTE), // three byte sequence
|
|
BER_INTEGER,
|
|
1 * sizeof(BYTE), // of one-byte integer
|
|
DACL_SECURITY_INFORMATION
|
|
//OWNER_SECURITY_INFORMATION |
|
|
//GROUP_SECURITY_INFORMATION
|
|
};
|
|
|
|
static LDAPControl s_se_info_control =
|
|
{
|
|
LDAP_SERVER_SD_FLAGS_OID_W,
|
|
{ ARRAYSIZE(s_sdBerValue), s_sdBerValue },
|
|
TRUE
|
|
};
|
|
LDAPControl *g_rgLdapControls[2] = { &s_se_info_control, NULL };
|
|
|
|
|
|
// Revocation templates
|
|
|
|
WCHAR const g_wszHTTPRevocationURLTemplate[] = // Fetch CRL via http:
|
|
L"http://"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"/CertEnroll/"
|
|
wszFCSAPARM_SANITIZEDCANAME
|
|
wszFCSAPARM_CRLFILENAMESUFFIX
|
|
wszFCSAPARM_CRLDELTAFILENAMESUFFIX
|
|
L".crl";
|
|
|
|
WCHAR const g_wszFILERevocationURLTemplate[] = // Fetch CRL via file:
|
|
L"file://\\\\"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"\\CertEnroll\\"
|
|
wszFCSAPARM_SANITIZEDCANAME
|
|
wszFCSAPARM_CRLFILENAMESUFFIX
|
|
wszFCSAPARM_CRLDELTAFILENAMESUFFIX
|
|
L".crl";
|
|
|
|
WCHAR const g_wszASPRevocationURLTemplate[] = // ASP revocation check via https:
|
|
L"https://"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"/CertEnroll/nsrev_"
|
|
wszFCSAPARM_SANITIZEDCANAME
|
|
L".asp";
|
|
|
|
#define wszCDPDNTEMPLATE \
|
|
L"CN=" \
|
|
wszFCSAPARM_SANITIZEDCANAMEHASH \
|
|
wszFCSAPARM_CRLFILENAMESUFFIX \
|
|
L"," \
|
|
L"CN=" \
|
|
wszFCSAPARM_SERVERSHORTNAME \
|
|
L"," \
|
|
L"CN=CDP," \
|
|
L"CN=Public Key Services," \
|
|
L"CN=Services," \
|
|
wszFCSAPARM_CONFIGDN
|
|
|
|
WCHAR const g_wszzLDAPRevocationURLTemplate[] = // Fetch CRL via ldap:
|
|
L"ldap:///"
|
|
wszCDPDNTEMPLATE
|
|
wszFCSAPARM_DSCRLATTRIBUTE
|
|
L"\0";
|
|
|
|
// Publish CRL via ldap:
|
|
WCHAR const g_wszCDPDNTemplate[] = wszCDPDNTEMPLATE;
|
|
|
|
|
|
// AIA templates
|
|
|
|
WCHAR const g_wszHTTPIssuerCertURLTemplate[] = // Fetch CA Cert via http:
|
|
L"http://"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"/CertEnroll/"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"_"
|
|
wszFCSAPARM_SANITIZEDCANAME
|
|
wszFCSAPARM_CERTFILENAMESUFFIX
|
|
L".crt"
|
|
L"\0";
|
|
|
|
WCHAR const g_wszFILEIssuerCertURLTemplate[] = // Fetch CA Cert via http:
|
|
L"file://\\\\"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"\\CertEnroll\\"
|
|
wszFCSAPARM_SERVERDNSNAME
|
|
L"_"
|
|
wszFCSAPARM_SANITIZEDCANAME
|
|
wszFCSAPARM_CERTFILENAMESUFFIX
|
|
L".crt"
|
|
L"\0";
|
|
|
|
#define wszAIADNTEMPLATE \
|
|
L"CN=" \
|
|
wszFCSAPARM_SANITIZEDCANAMEHASH \
|
|
L"," \
|
|
L"CN=AIA," \
|
|
L"CN=Public Key Services," \
|
|
L"CN=Services," \
|
|
wszFCSAPARM_CONFIGDN
|
|
|
|
WCHAR const g_wszzLDAPIssuerCertURLTemplate[] = // Fetch CA Cert via ldap:
|
|
L"ldap:///"
|
|
wszAIADNTEMPLATE
|
|
wszFCSAPARM_DSCACERTATTRIBUTE
|
|
L"\0";
|
|
|
|
// Publish CA Cert via ldap:
|
|
WCHAR const g_wszAIADNTemplate[] = wszAIADNTEMPLATE;
|
|
|
|
|
|
#define wszNTAUTHDNTEMPLATE \
|
|
L"CN=NTAuthCertificates," \
|
|
L"CN=Public Key Services," \
|
|
L"CN=Services," \
|
|
wszFCSAPARM_CONFIGDN
|
|
|
|
WCHAR const g_wszLDAPNTAuthURLTemplate[] = // Fetch NTAuth Certs via ldap:
|
|
L"ldap:///"
|
|
wszNTAUTHDNTEMPLATE
|
|
wszFCSAPARM_DSCACERTATTRIBUTE;
|
|
|
|
|
|
#define wszROOTTRUSTDNTEMPLATE \
|
|
L"CN=" \
|
|
wszFCSAPARM_SANITIZEDCANAMEHASH \
|
|
L"," \
|
|
L"CN=Certification Authorities," \
|
|
L"CN=Public Key Services," \
|
|
L"CN=Services," \
|
|
wszFCSAPARM_CONFIGDN
|
|
|
|
WCHAR const g_wszLDAPRootTrustURLTemplate[] = // Fetch Root Certs via ldap:
|
|
L"ldap:///"
|
|
wszROOTTRUSTDNTEMPLATE
|
|
wszFCSAPARM_DSCACERTATTRIBUTE;
|
|
|
|
|
|
#define wszKRADNTEMPLATE \
|
|
L"CN=" \
|
|
wszFCSAPARM_SANITIZEDCANAMEHASH \
|
|
L"," \
|
|
L"CN=KRA," \
|
|
L"CN=Public Key Services," \
|
|
L"CN=Services," \
|
|
wszFCSAPARM_CONFIGDN
|
|
|
|
WCHAR const g_wszzLDAPKRACertURLTemplate[] = // Fetch KRA Cert via ldap:
|
|
L"ldap:///"
|
|
wszKRADNTEMPLATE
|
|
wszFCSAPARM_DSKRACERTATTRIBUTE
|
|
L"\0";
|
|
|
|
// Publish KRA Certs via ldap:
|
|
WCHAR const g_wszKRADNTemplate[] = wszKRADNTEMPLATE;
|
|
|
|
|
|
DWORD
|
|
myGetLDAPFlags()
|
|
{
|
|
HRESULT hr;
|
|
DWORD LDAPFlags;
|
|
|
|
hr = myGetCertRegDWValue(NULL, NULL, NULL, wszREGLDAPFLAGS, &LDAPFlags);
|
|
_PrintIfErrorStr2(
|
|
hr,
|
|
"myGetCertRegDWValue",
|
|
wszREGLDAPFLAGS,
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
|
if (S_OK != hr)
|
|
{
|
|
LDAPFlags = 0;
|
|
}
|
|
return(LDAPFlags);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Routine Description:
|
|
// This routine simply queries the operational attributes for the
|
|
// domaindn and configdn.
|
|
//
|
|
// The strings returned by this routine must be freed by the caller
|
|
// using SysFreeString
|
|
//
|
|
// Parameters:
|
|
// pld -- a valid handle to an ldap session
|
|
// pstrDomainDN -- a pointer to a string to be allocated in this routine
|
|
// pstrConfigDN -- a pointer to a string to be allocated in this routine
|
|
//
|
|
// Return Values:
|
|
// HRESULT for operation error.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
myGetAuthoritativeDomainDn(
|
|
IN LDAP *pld,
|
|
OPTIONAL OUT BSTR *pstrDomainDN,
|
|
OPTIONAL OUT BSTR *pstrConfigDN)
|
|
{
|
|
HRESULT hr;
|
|
LDAPMessage *pSearchResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
LDAP_TIMEVAL timeval;
|
|
WCHAR *pwszAttrName;
|
|
BerElement *pber;
|
|
WCHAR **rgpwszValues;
|
|
WCHAR *apwszAttrArray[3];
|
|
WCHAR *pwszDefaultNamingContext = LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W;
|
|
WCHAR *pwszConfigurationNamingContext = LDAP_OPATT_CONFIG_NAMING_CONTEXT_W;
|
|
BSTR strDomainDN = NULL;
|
|
BSTR strConfigDN = NULL;
|
|
|
|
// Set the OUT parameters to NULL
|
|
|
|
if (NULL != pstrConfigDN)
|
|
{
|
|
*pstrConfigDN = NULL;
|
|
}
|
|
if (NULL != pstrDomainDN)
|
|
{
|
|
*pstrDomainDN = NULL;
|
|
}
|
|
|
|
// Query for the ldap server oerational attributes to obtain the default
|
|
// naming context.
|
|
|
|
apwszAttrArray[0] = pwszDefaultNamingContext;
|
|
apwszAttrArray[1] = pwszConfigurationNamingContext;
|
|
apwszAttrArray[2] = NULL; // this is the sentinel
|
|
|
|
timeval.tv_sec = csecLDAPTIMEOUT;
|
|
timeval.tv_usec = 0;
|
|
|
|
hr = ldap_search_st(
|
|
pld,
|
|
NULL, // base
|
|
LDAP_SCOPE_BASE,
|
|
L"objectClass=*",
|
|
apwszAttrArray,
|
|
FALSE, // attrsonly
|
|
&timeval,
|
|
&pSearchResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_st");
|
|
|
|
pEntry = ldap_first_entry(pld, pSearchResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = myHLdapLastError(pld, NULL);
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
pwszAttrName = ldap_first_attribute(pld, pEntry, &pber);
|
|
while (NULL != pwszAttrName)
|
|
{
|
|
BSTR *pstr = NULL;
|
|
|
|
if (NULL != pstrDomainDN &&
|
|
0 == mylstrcmpiS(pwszAttrName, pwszDefaultNamingContext))
|
|
{
|
|
pstr = &strDomainDN;
|
|
}
|
|
else
|
|
if (NULL != pstrConfigDN &&
|
|
0 == mylstrcmpiS(pwszAttrName, pwszConfigurationNamingContext))
|
|
{
|
|
pstr = &strConfigDN;
|
|
}
|
|
if (NULL != pstr && NULL == *pstr)
|
|
{
|
|
rgpwszValues = ldap_get_values(pld, pEntry, pwszAttrName);
|
|
if (NULL != rgpwszValues)
|
|
{
|
|
if (NULL != rgpwszValues[0])
|
|
{
|
|
*pstr = SysAllocString(rgpwszValues[0]);
|
|
if (NULL == *pstr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocString");
|
|
}
|
|
}
|
|
ldap_value_free(rgpwszValues);
|
|
}
|
|
}
|
|
ldap_memfree(pwszAttrName);
|
|
pwszAttrName = ldap_next_attribute(pld, pEntry, pber);
|
|
}
|
|
if ((NULL != pstrDomainDN && NULL == strDomainDN) ||
|
|
(NULL != pstrConfigDN && NULL == strConfigDN))
|
|
{
|
|
// We couldn't get default domain info - bail out
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
|
|
_JumpError(hr, error, "missing domain info");
|
|
}
|
|
if (NULL != pstrDomainDN)
|
|
{
|
|
*pstrDomainDN = strDomainDN;
|
|
strDomainDN = NULL;
|
|
}
|
|
if (NULL != pstrConfigDN)
|
|
{
|
|
*pstrConfigDN = strConfigDN;
|
|
strConfigDN = NULL;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSearchResult)
|
|
{
|
|
ldap_msgfree(pSearchResult);
|
|
}
|
|
myLdapClose(NULL, strDomainDN, strConfigDN);
|
|
return(myHError(hr));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myDomainFromDn(
|
|
IN WCHAR const *pwszDN,
|
|
OPTIONAL OUT WCHAR **ppwszDomainDNS)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cwcOut;
|
|
WCHAR *pwszOut;
|
|
WCHAR **ppwszExplodedDN = NULL;
|
|
WCHAR **ppwsz;
|
|
WCHAR wszDC[4];
|
|
WCHAR *pwsz;
|
|
|
|
*ppwszDomainDNS = NULL;
|
|
ppwszExplodedDN = ldap_explode_dn(const_cast<WCHAR *>(pwszDN), 0);
|
|
if (NULL == ppwszExplodedDN)
|
|
{
|
|
hr = myHLdapLastError(NULL, NULL);
|
|
_JumpError(hr, error, "ldap_explode_dn");
|
|
}
|
|
|
|
cwcOut = 0;
|
|
for (ppwsz = ppwszExplodedDN; NULL != *ppwsz; ppwsz++)
|
|
{
|
|
pwsz = *ppwsz;
|
|
|
|
wcsncpy(wszDC, pwsz, ARRAYSIZE(wszDC) - 1);
|
|
wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
|
|
if (0 == LSTRCMPIS(wszDC, L"DC="))
|
|
{
|
|
pwsz += ARRAYSIZE(wszDC) - 1;
|
|
if (0 != cwcOut)
|
|
{
|
|
cwcOut++;
|
|
}
|
|
cwcOut += wcslen(pwsz);
|
|
}
|
|
}
|
|
|
|
pwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwcOut + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
*ppwszDomainDNS = pwszOut;
|
|
|
|
for (ppwsz = ppwszExplodedDN; NULL != *ppwsz; ppwsz++)
|
|
{
|
|
pwsz = *ppwsz;
|
|
|
|
wcsncpy(wszDC, pwsz, ARRAYSIZE(wszDC) - 1);
|
|
wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
|
|
if (0 == LSTRCMPIS(wszDC, L"DC="))
|
|
{
|
|
pwsz += ARRAYSIZE(wszDC) - 1;
|
|
if (pwszOut != *ppwszDomainDNS)
|
|
{
|
|
*pwszOut++ = L'.';
|
|
}
|
|
wcscpy(pwszOut, pwsz);
|
|
pwszOut += wcslen(pwsz);
|
|
}
|
|
}
|
|
CSASSERT(wcslen(*ppwszDomainDNS) == cwcOut);
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != ppwszExplodedDN)
|
|
{
|
|
ldap_value_free(ppwszExplodedDN);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapOpen(
|
|
OPTIONAL IN WCHAR const *pwszDomainName,
|
|
IN DWORD dwFlags, // RLBF_*
|
|
OUT LDAP **ppld,
|
|
OPTIONAL OUT BSTR *pstrDomainDN,
|
|
OPTIONAL OUT BSTR *pstrConfigDN)
|
|
{
|
|
HRESULT hr;
|
|
LDAP *pld = NULL;
|
|
|
|
*ppld = NULL;
|
|
CSASSERT(NULL == pstrConfigDN || NULL == *pstrConfigDN);
|
|
CSASSERT(NULL == pstrDomainDN || NULL == *pstrDomainDN);
|
|
|
|
hr = myRobustLdapBindEx(
|
|
(RLBF_REQUIRE_GC & dwFlags)? RLBF_TRUE : 0, // dwFlags1 (was fGC)
|
|
~RLBF_TRUE & dwFlags, // dwFlags2
|
|
LDAP_VERSION2,
|
|
pwszDomainName,
|
|
&pld,
|
|
NULL); // ppwszForestDNSName
|
|
_JumpIfError(hr, error, "myRobustLdapBindEx");
|
|
|
|
// domain and config containers (%5, %6)
|
|
|
|
hr = myGetAuthoritativeDomainDn(pld, pstrDomainDN, pstrConfigDN);
|
|
if (S_OK != hr)
|
|
{
|
|
hr = myHError(hr);
|
|
_JumpError(hr, error, "myGetAuthoritativeDomainDn");
|
|
}
|
|
*ppld = pld;
|
|
pld = NULL;
|
|
|
|
error:
|
|
if (NULL != pld)
|
|
{
|
|
ldap_unbind(pld);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
myLdapClose(
|
|
OPTIONAL IN LDAP *pld,
|
|
OPTIONAL IN BSTR strDomainDN,
|
|
OPTIONAL IN BSTR strConfigDN)
|
|
{
|
|
if (NULL != strDomainDN)
|
|
{
|
|
SysFreeString(strDomainDN);
|
|
}
|
|
if (NULL != strConfigDN)
|
|
{
|
|
SysFreeString(strConfigDN);
|
|
}
|
|
if (NULL != pld)
|
|
{
|
|
ldap_unbind(pld);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
myLdapRebindRequired(
|
|
IN ULONG ldaperrParm,
|
|
OPTIONAL IN LDAP *pld)
|
|
{
|
|
BOOL fRebindRequired = FALSE;
|
|
|
|
if (LDAP_SERVER_DOWN == ldaperrParm ||
|
|
LDAP_UNAVAILABLE == ldaperrParm ||
|
|
LDAP_TIMEOUT == ldaperrParm ||
|
|
NULL == pld)
|
|
{
|
|
fRebindRequired = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ULONG ldaperr;
|
|
VOID *pvReachable = NULL; // clear high bits for 64-bit machines
|
|
|
|
ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_REACHABLE, &pvReachable);
|
|
if (LDAP_SUCCESS != ldaperr || LDAP_OPT_ON != pvReachable)
|
|
{
|
|
fRebindRequired = TRUE;
|
|
}
|
|
}
|
|
return(fRebindRequired);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapGetDSHostName(
|
|
IN LDAP *pld,
|
|
OUT WCHAR **ppwszHostName)
|
|
{
|
|
HRESULT hr;
|
|
ULONG ldaperr;
|
|
|
|
ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_NAME, ppwszHostName);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
*ppwszHostName = NULL;
|
|
}
|
|
hr = myHLdapError(pld, ldaperr, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapCreateContainer(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN BOOL fSkipObject, // Does the DN contain a leaf object name
|
|
IN DWORD cMaxLevel, // create this many nested containers as needed
|
|
IN PSECURITY_DESCRIPTOR pContainerSD,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwsz = pwszDN;
|
|
LDAPMod objectClass;
|
|
LDAPMod advancedView;
|
|
LDAPMod securityDescriptor;
|
|
WCHAR *papwszshowInAdvancedViewOnly[2] = { L"TRUE", NULL };
|
|
WCHAR *objectClassVals[3];
|
|
LDAPMod *mods[4];
|
|
struct berval *sdVals[2];
|
|
struct berval sdberval;
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
mods[0] = &objectClass;
|
|
mods[1] = &advancedView;
|
|
mods[2] = &securityDescriptor;
|
|
mods[3] = NULL;
|
|
|
|
objectClass.mod_op = LDAP_MOD_ADD;
|
|
objectClass.mod_type = TEXT("objectclass");
|
|
objectClass.mod_values = objectClassVals;
|
|
|
|
advancedView.mod_op = LDAP_MOD_ADD;
|
|
advancedView.mod_type = TEXT("showInAdvancedViewOnly");
|
|
advancedView.mod_values = papwszshowInAdvancedViewOnly;
|
|
|
|
objectClassVals[0] = TEXT("top");
|
|
objectClassVals[1] = TEXT("container");
|
|
objectClassVals[2] = NULL;
|
|
|
|
securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
|
|
securityDescriptor.mod_bvalues = sdVals;
|
|
sdVals[0] = &sdberval;
|
|
sdVals[1] = NULL;
|
|
|
|
if (IsValidSecurityDescriptor(pContainerSD))
|
|
{
|
|
sdberval.bv_len = GetSecurityDescriptorLength(pContainerSD);
|
|
sdberval.bv_val = (char *)pContainerSD;
|
|
}
|
|
else
|
|
{
|
|
sdberval.bv_len = 0;
|
|
sdberval.bv_val = NULL;
|
|
}
|
|
|
|
// If the DN passed in was for the full object that goes in the container
|
|
// (and not the container itself), skip past the CN for the leaf object.
|
|
|
|
if (fSkipObject)
|
|
{
|
|
// Look for the CN of the container for this object.
|
|
pwsz = wcsstr(&pwsz[3], L"CN=");
|
|
if (NULL == pwsz)
|
|
{
|
|
// If there was no CN, then we are contained in an OU or DC,
|
|
// and we don't need to do the create.
|
|
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
}
|
|
if (0 != wcsncmp(pwsz, L"CN=", 3))
|
|
{
|
|
// We're not pointing to a simple container, so don't create this DN.
|
|
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
|
|
pwszDN = pwsz;
|
|
if (0 != cMaxLevel)
|
|
{
|
|
pwsz = wcsstr(&pwsz[3], L"CN=");
|
|
if (NULL != pwsz)
|
|
{
|
|
// The remaining DN is a container, so try to create it.
|
|
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
pwsz,
|
|
FALSE,
|
|
cMaxLevel - 1,
|
|
pContainerSD,
|
|
ppwszError);
|
|
// ignore access denied errors to allow delegation
|
|
if (E_ACCESSDENIED != hr &&
|
|
HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
|
|
{
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateContainer", pwsz);
|
|
}
|
|
if (NULL != ppwszError && NULL != *ppwszError)
|
|
{
|
|
LocalFree(ppwszError);
|
|
*ppwszError = NULL;
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating DS Container: '%ws'\n", pwszDN));
|
|
|
|
// Create the container
|
|
|
|
hr = ldap_add_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
mods,
|
|
g_rgLdapControls,
|
|
NULL);
|
|
_PrintIfErrorStr2(
|
|
hr,
|
|
"ldap_add_ext_s(container)",
|
|
pwszDN,
|
|
LDAP_ALREADY_EXISTS);
|
|
if ((HRESULT) LDAP_SUCCESS != hr && (HRESULT) LDAP_ALREADY_EXISTS != hr)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpIfErrorStr(hr, error, "ldap_add_ext_s(container)", pwszDN);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
|
|
if(S_OK==hr && ppwszError && *ppwszError)
|
|
{
|
|
LocalFree(ppwszError);
|
|
*ppwszError = NULL;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
TrimURLDN(
|
|
IN WCHAR const *pwszIn,
|
|
OPTIONAL OUT WCHAR **ppwszSuffix,
|
|
OUT WCHAR **ppwszDN)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cSlash;
|
|
WCHAR *pwsz;
|
|
|
|
if (NULL != ppwszSuffix)
|
|
{
|
|
*ppwszSuffix = NULL;
|
|
}
|
|
*ppwszDN = NULL;
|
|
pwsz = wcschr(pwszIn, L':');
|
|
if (NULL != pwsz)
|
|
{
|
|
pwszIn = &pwsz[1];
|
|
}
|
|
cSlash = 0;
|
|
while (L'/' == *pwszIn)
|
|
{
|
|
pwszIn++;
|
|
cSlash++;
|
|
}
|
|
if (2 == cSlash)
|
|
{
|
|
while (L'\0' != *pwszIn && L'/' != *pwszIn)
|
|
{
|
|
pwszIn++;
|
|
}
|
|
if (L'\0' != *pwszIn)
|
|
{
|
|
pwszIn++;
|
|
}
|
|
}
|
|
hr = myDupString(pwszIn, ppwszDN);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
pwsz = wcschr(*ppwszDN, L'?');
|
|
if (NULL != pwsz)
|
|
{
|
|
*pwsz++ = L'\0';
|
|
if (NULL != ppwszSuffix)
|
|
{
|
|
*ppwszSuffix = pwsz;
|
|
}
|
|
}
|
|
CSASSERT(S_OK == hr);
|
|
|
|
error:
|
|
if (S_OK != hr && NULL != *ppwszDN)
|
|
{
|
|
LocalFree(*ppwszDN);
|
|
*ppwszDN = NULL;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CreateCertObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN DWORD dwObjectType, // LPC_*
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
PSECURITY_DESCRIPTOR pContainerSD = NULL;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
// get default DS CA security descriptor
|
|
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
// get default DS AIA security descriptor
|
|
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
if (LPC_CREATECONTAINER & dwObjectType)
|
|
{
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
pwszDN,
|
|
TRUE,
|
|
0,
|
|
pContainerSD,
|
|
ppwszError);
|
|
if (E_ACCESSDENIED != hr &&
|
|
HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
|
|
{
|
|
_JumpIfError(hr, error, "myLdapCreateContainer");
|
|
}
|
|
if (NULL != ppwszError && NULL != *ppwszError)
|
|
{
|
|
LocalFree(ppwszError);
|
|
*ppwszError = NULL;
|
|
}
|
|
}
|
|
|
|
if (LPC_CREATEOBJECT & dwObjectType)
|
|
{
|
|
if (NULL != ppwszError && NULL != *ppwszError)
|
|
{
|
|
LocalFree(*ppwszError);
|
|
*ppwszError = NULL;
|
|
}
|
|
switch (LPC_OBJECTMASK & dwObjectType)
|
|
{
|
|
case LPC_CAOBJECT:
|
|
hr = myLdapCreateCAObject(
|
|
pld,
|
|
pwszDN,
|
|
NULL,
|
|
0,
|
|
pSD,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszDN);
|
|
break;
|
|
|
|
case LPC_KRAOBJECT:
|
|
case LPC_USEROBJECT:
|
|
case LPC_MACHINEOBJECT:
|
|
hr = myLdapCreateUserObject(
|
|
pld,
|
|
pwszDN,
|
|
NULL,
|
|
0,
|
|
pSD,
|
|
dwObjectType,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateUserObject", pwszDN);
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "dwObjectType");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
if (NULL != pContainerSD)
|
|
{
|
|
LocalFree(pContainerSD);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AddCertToAttribute(
|
|
IN LDAP *pld,
|
|
IN CERT_CONTEXT const *pccPublish,
|
|
IN WCHAR const *pwszDN,
|
|
IN WCHAR const *pwszAttribute,
|
|
IN BOOL fDelete,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cres;
|
|
DWORD cber;
|
|
DWORD iber;
|
|
DWORD i;
|
|
LDAP_TIMEVAL timeval;
|
|
LDAPMessage *pmsg = NULL;
|
|
LDAPMessage *pres;
|
|
WCHAR *apwszAttrs[2];
|
|
struct berval **ppberval = NULL;
|
|
struct berval **prgpberVals = NULL;
|
|
FILETIME ft;
|
|
BOOL fDeleteExpiredCert = FALSE;
|
|
BOOL fFoundCert = FALSE;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
|
|
apwszAttrs[1] = NULL;
|
|
|
|
timeval.tv_sec = csecLDAPTIMEOUT;
|
|
timeval.tv_usec = 0;
|
|
|
|
hr = ldap_search_st(
|
|
pld, // ld
|
|
const_cast<WCHAR *>(pwszDN), // base
|
|
LDAP_SCOPE_BASE, // scope
|
|
NULL, // filter
|
|
apwszAttrs, // attrs
|
|
FALSE, // attrsonly
|
|
&timeval, // timeout
|
|
&pmsg); // res
|
|
if (S_OK != hr)
|
|
{
|
|
*pdwDisposition = hr;
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
|
|
}
|
|
cres = ldap_count_entries(pld, pmsg);
|
|
if (0 == cres)
|
|
{
|
|
// No entries were found.
|
|
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_count_entries");
|
|
}
|
|
|
|
pres = ldap_first_entry(pld, pmsg);
|
|
if (NULL == pres)
|
|
{
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
ppberval = ldap_get_values_len(
|
|
pld,
|
|
pres,
|
|
const_cast<WCHAR *>(pwszAttribute));
|
|
cber = 0;
|
|
if (NULL != ppberval)
|
|
{
|
|
while (NULL != ppberval[cber])
|
|
{
|
|
cber++;
|
|
}
|
|
}
|
|
prgpberVals = (struct berval **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cber + 2) * sizeof(prgpberVals[0]));
|
|
if (NULL == prgpberVals)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// Delete any certs that are at least one day old
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
myMakeExprDateTime(&ft, -1, ENUM_PERIOD_DAYS);
|
|
|
|
iber = 0;
|
|
if (NULL != ppberval)
|
|
{
|
|
for (i = 0; NULL != ppberval[i]; i++)
|
|
{
|
|
BOOL fCopyBER = TRUE;
|
|
struct berval *pberval = ppberval[i];
|
|
|
|
if (pberval->bv_len == 1 && pberval->bv_val[0] == 0)
|
|
{
|
|
fCopyBER = FALSE; // remove zero byte placeholder value
|
|
}
|
|
else
|
|
if (pccPublish->cbCertEncoded == pberval->bv_len &&
|
|
0 == memcmp(
|
|
pberval->bv_val,
|
|
pccPublish->pbCertEncoded,
|
|
pccPublish->cbCertEncoded))
|
|
{
|
|
fCopyBER = FALSE; // remove duplicates to avoid ldap error
|
|
fFoundCert = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CERT_CONTEXT const *pcc;
|
|
|
|
pcc = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
(BYTE *) pberval->bv_val,
|
|
pberval->bv_len);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CertCreateCertificateContext");
|
|
}
|
|
else
|
|
{
|
|
if (0 > CompareFileTime(&pcc->pCertInfo->NotAfter, &ft))
|
|
{
|
|
fCopyBER = FALSE;
|
|
fDeleteExpiredCert = TRUE;
|
|
DBGPRINT((DBG_SS_CERTLIB, "Deleting expired cert %u\n", i));
|
|
}
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
}
|
|
if (fCopyBER)
|
|
{
|
|
prgpberVals[iber++] = pberval;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set disposition assuming there's nothing to do:
|
|
|
|
*pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
|
|
|
|
if ((!fFoundCert ^ fDelete) || fDeleteExpiredCert)
|
|
{
|
|
struct berval certberval;
|
|
LDAPMod *mods[2];
|
|
LDAPMod certmod;
|
|
BYTE bZero = 0;
|
|
|
|
mods[0] = &certmod;
|
|
mods[1] = NULL;
|
|
|
|
certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
|
|
certmod.mod_bvalues = prgpberVals;
|
|
|
|
if (fDelete)
|
|
{
|
|
if (0 == iber)
|
|
{
|
|
certberval.bv_val = (char *) &bZero;
|
|
certberval.bv_len = sizeof(bZero);
|
|
prgpberVals[iber++] = &certberval;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
certberval.bv_val = (char *) pccPublish->pbCertEncoded;
|
|
certberval.bv_len = pccPublish->cbCertEncoded;
|
|
prgpberVals[iber++] = &certberval;
|
|
}
|
|
prgpberVals[iber] = NULL;
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = hr;
|
|
if (hr != S_OK)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpError(hr, error, "ldap_modify_ext_s");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != prgpberVals)
|
|
{
|
|
LocalFree(prgpberVals);
|
|
}
|
|
if (NULL != ppberval)
|
|
{
|
|
ldap_value_free_len(ppberval);
|
|
}
|
|
if (NULL != pmsg)
|
|
{
|
|
ldap_msgfree(pmsg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AddCRLToAttribute(
|
|
IN LDAP *pld,
|
|
IN CRL_CONTEXT const *pCRLPublish,
|
|
IN WCHAR const *pwszDN,
|
|
IN WCHAR const *pwszAttribute,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cres;
|
|
LDAP_TIMEVAL timeval;
|
|
LDAPMessage *pmsg = NULL;
|
|
LDAPMessage *pres;
|
|
WCHAR *apwszAttrs[2];
|
|
struct berval **ppberval = NULL;
|
|
LDAPMod crlmod;
|
|
LDAPMod *mods[2];
|
|
struct berval *crlberVals[2];
|
|
struct berval crlberval;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
|
|
apwszAttrs[1] = NULL;
|
|
|
|
timeval.tv_sec = csecLDAPTIMEOUT;
|
|
timeval.tv_usec = 0;
|
|
|
|
hr = ldap_search_st(
|
|
pld, // ld
|
|
const_cast<WCHAR *>(pwszDN), // base
|
|
LDAP_SCOPE_BASE, // scope
|
|
NULL, // filter
|
|
apwszAttrs, // attrs
|
|
FALSE, // attrsonly
|
|
&timeval, // timeout
|
|
&pmsg); // res
|
|
if (S_OK != hr)
|
|
{
|
|
*pdwDisposition = hr;
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
|
|
}
|
|
cres = ldap_count_entries(pld, pmsg);
|
|
if (0 == cres)
|
|
{
|
|
// No entries were found.
|
|
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_count_entries");
|
|
}
|
|
|
|
pres = ldap_first_entry(pld, pmsg);
|
|
if (NULL == pres)
|
|
{
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
ppberval = ldap_get_values_len(
|
|
pld,
|
|
pres,
|
|
const_cast<WCHAR *>(pwszAttribute));
|
|
|
|
if (NULL != ppberval &&
|
|
NULL != ppberval[0] &&
|
|
pCRLPublish->cbCrlEncoded == ppberval[0]->bv_len &&
|
|
0 == memcmp(
|
|
ppberval[0]->bv_val,
|
|
pCRLPublish->pbCrlEncoded,
|
|
pCRLPublish->cbCrlEncoded))
|
|
{
|
|
// set disposition assuming there's nothing to do:
|
|
|
|
*pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
mods[0] = &crlmod;
|
|
mods[1] = NULL;
|
|
|
|
crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
crlmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
|
|
crlmod.mod_bvalues = crlberVals;
|
|
|
|
crlberVals[0] = &crlberval;
|
|
crlberVals[1] = NULL;
|
|
|
|
crlberval.bv_val = (char *) pCRLPublish->pbCrlEncoded;
|
|
crlberval.bv_len = pCRLPublish->cbCrlEncoded;
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = hr;
|
|
if (hr != S_OK)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpError(hr, error, "ldap_modify_ext_s");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != ppberval)
|
|
{
|
|
ldap_value_free_len(ppberval);
|
|
}
|
|
if (NULL != pmsg)
|
|
{
|
|
ldap_msgfree(pmsg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapPublishCertToDS(
|
|
IN LDAP *pld,
|
|
IN CERT_CONTEXT const *pccPublish,
|
|
IN WCHAR const *pwszURL,
|
|
IN WCHAR const *pwszAttribute,
|
|
IN DWORD dwObjectType, // LPC_*
|
|
IN BOOL fDelete,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrCreate = S_OK;
|
|
WCHAR *pwszDN = NULL;
|
|
WCHAR *pwszSuffix;
|
|
WCHAR *pwszCreateError = NULL;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
|
|
_JumpIfError(hr, error, "TrimURLDN");
|
|
|
|
if (0 == LSTRCMPIS(pwszAttribute, wszDSUSERCERTATTRIBUTE) ||
|
|
0 == LSTRCMPIS(pwszAttribute, wszDSKRACERTATTRIBUTE))
|
|
{
|
|
if (LPC_CAOBJECT == (LPC_OBJECTMASK & dwObjectType))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
if (0 == LSTRCMPIS(pwszAttribute, wszDSCACERTATTRIBUTE) ||
|
|
0 == LSTRCMPIS(pwszAttribute, wszDSCROSSCERTPAIRATTRIBUTE))
|
|
{
|
|
if (LPC_CAOBJECT != (LPC_OBJECTMASK & dwObjectType))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
_JumpIfErrorStr(hr, error, "Bad Cert Attribute", pwszAttribute);
|
|
|
|
*pdwDisposition = LDAP_SUCCESS;
|
|
if ((LPC_CREATECONTAINER | LPC_CREATEOBJECT) & dwObjectType)
|
|
{
|
|
hr = CreateCertObject(
|
|
pld,
|
|
pwszDN,
|
|
dwObjectType,
|
|
pdwDisposition,
|
|
&pwszCreateError);
|
|
hrCreate = hr;
|
|
if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
|
|
{
|
|
_JumpIfError(hr, error, "CreateCertObject");
|
|
}
|
|
}
|
|
|
|
hr = AddCertToAttribute(
|
|
pld,
|
|
pccPublish,
|
|
pwszDN,
|
|
pwszAttribute,
|
|
fDelete,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "AddCertToAttribute");
|
|
|
|
CSASSERT(NULL == ppwszError || NULL == *ppwszError);
|
|
|
|
error:
|
|
if (HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND) == hr &&
|
|
HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hrCreate)
|
|
{
|
|
hr = hrCreate;
|
|
}
|
|
if (NULL != pwszCreateError)
|
|
{
|
|
if (S_OK != hr && NULL != ppwszError)
|
|
{
|
|
if (NULL != *ppwszError)
|
|
{
|
|
myPrependString(pwszCreateError, L"", ppwszError);
|
|
}
|
|
else
|
|
{
|
|
*ppwszError = pwszCreateError;
|
|
pwszCreateError = NULL;
|
|
}
|
|
}
|
|
if (NULL != pwszCreateError)
|
|
{
|
|
LocalFree(pwszCreateError);
|
|
}
|
|
}
|
|
if (NULL != pwszDN)
|
|
{
|
|
LocalFree(pwszDN);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapPublishCRLToDS(
|
|
IN LDAP *pld,
|
|
IN CRL_CONTEXT const *pCRLPublish,
|
|
IN WCHAR const *pwszURL,
|
|
IN WCHAR const *pwszAttribute,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszDN = NULL;
|
|
WCHAR *pwszSuffix;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
PSECURITY_DESCRIPTOR pContainerSD = NULL;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
|
|
_JumpIfError(hr, error, "TrimURLDN");
|
|
|
|
if (0 == LSTRCMPIS(pwszAttribute, wszDSBASECRLATTRIBUTE))
|
|
{
|
|
}
|
|
else if (0 == LSTRCMPIS(pwszAttribute, wszDSDELTACRLATTRIBUTE))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpErrorStr(hr, error, "Bad CRL Attribute", pwszAttribute);
|
|
}
|
|
|
|
// get default DS CDP security descriptor
|
|
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CDP_DS_SECURITY, SDDL_CERT_SERV_ADMINISTRATORS, &pSD);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myGetSDFromTemplate");
|
|
pSD = NULL;
|
|
}
|
|
|
|
// get default DS AIA security descriptor
|
|
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
hr = myLdapCreateContainer(pld, pwszDN, TRUE, 1, pContainerSD, ppwszError);
|
|
if (E_ACCESSDENIED != hr &&
|
|
HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
|
|
{
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateContainer", pwszDN);
|
|
}
|
|
if (NULL != ppwszError && NULL != *ppwszError)
|
|
{
|
|
LocalFree(ppwszError);
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
hr = myLdapCreateCDPObject(
|
|
pld,
|
|
pwszDN,
|
|
NULL != pSD? pSD : pContainerSD,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCDPObject", pwszDN);
|
|
|
|
hr = AddCRLToAttribute(
|
|
pld,
|
|
pCRLPublish,
|
|
pwszDN,
|
|
pwszAttribute,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "AddCRLToAttribute");
|
|
|
|
error:
|
|
if (NULL != pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
if (NULL != pContainerSD)
|
|
{
|
|
LocalFree(pContainerSD);
|
|
}
|
|
if (NULL != pwszDN)
|
|
{
|
|
LocalFree(pwszDN);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DNExists(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN)
|
|
{
|
|
ULONG ldaperr;
|
|
BOOL fExists = FALSE;
|
|
LPWSTR pwszAttrArray[2];
|
|
struct l_timeval timeout;
|
|
LDAPMessage *pResult = NULL;
|
|
|
|
pwszAttrArray[0] = L"cn";
|
|
pwszAttrArray[1] = NULL;
|
|
|
|
timeout.tv_sec = csecLDAPTIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
|
|
ldaperr = ldap_search_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
LDAP_SCOPE_BASE,
|
|
L"objectClass=*",
|
|
pwszAttrArray,
|
|
1,
|
|
g_rgLdapControls,
|
|
NULL,
|
|
&timeout,
|
|
0,
|
|
&pResult);
|
|
if (NULL != pResult)
|
|
{
|
|
fExists = LDAP_SUCCESS == ldaperr &&
|
|
1 == ldap_count_entries(pld, pResult);
|
|
ldap_msgfree(pResult);
|
|
}
|
|
return(fExists);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CreateOrUpdateDSObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN LDAPMod **prgmodsCreate,
|
|
OPTIONAL IN LDAPMod **prgmodsUpdate,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
ULONG ldaperr;
|
|
WCHAR *pwszError = NULL;
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
ldaperr = ldap_add_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
prgmodsCreate,
|
|
g_rgLdapControls,
|
|
NULL);
|
|
*pdwDisposition = ldaperr;
|
|
_PrintIfErrorStr2(ldaperr, "ldap_add_ext_s", pwszDN, LDAP_ALREADY_EXISTS);
|
|
|
|
if (LDAP_ALREADY_EXISTS == ldaperr || LDAP_INSUFFICIENT_RIGHTS == ldaperr)
|
|
{
|
|
if (NULL == prgmodsUpdate)
|
|
{
|
|
if (LDAP_INSUFFICIENT_RIGHTS == ldaperr)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, &pwszError);
|
|
_PrintErrorStr(hr, "ldap_add_ext_s", pwszError);
|
|
|
|
if (!DNExists(pld, pwszDN))
|
|
{
|
|
*ppwszError = pwszError;
|
|
pwszError = NULL;
|
|
_JumpErrorStr(hr, error, "ldap_add_ext_s", *ppwszError);
|
|
}
|
|
}
|
|
ldaperr = LDAP_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ldaperr = ldap_modify_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
prgmodsUpdate,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = ldaperr;
|
|
_PrintIfErrorStr2(
|
|
ldaperr,
|
|
"ldap_modify_ext_s",
|
|
pwszDN,
|
|
LDAP_ATTRIBUTE_OR_VALUE_EXISTS);
|
|
if (LDAP_ATTRIBUTE_OR_VALUE_EXISTS == ldaperr)
|
|
{
|
|
ldaperr = LDAP_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
if (ldaperr != LDAP_SUCCESS)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, ppwszError);
|
|
_JumpError(hr, error, "Add/Update DS");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(pwszError);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapCreateCAObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
OPTIONAL IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
BYTE ZeroByte = 0;
|
|
|
|
LDAPMod objectClass;
|
|
LDAPMod securityDescriptor;
|
|
LDAPMod crlmod;
|
|
LDAPMod arlmod;
|
|
LDAPMod certmod;
|
|
|
|
struct berval sdberval;
|
|
struct berval crlberval;
|
|
struct berval arlberval;
|
|
struct berval certberval;
|
|
|
|
WCHAR *objectClassVals[3];
|
|
struct berval *sdVals[2];
|
|
struct berval *crlVals[2];
|
|
struct berval *arlVals[2];
|
|
struct berval *certVals[2];
|
|
|
|
LDAPMod *mods[6];
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
mods[0] = &objectClass;
|
|
mods[1] = &securityDescriptor;
|
|
mods[2] = &crlmod;
|
|
mods[3] = &arlmod;
|
|
mods[4] = &certmod; // must be last!
|
|
mods[5] = NULL;
|
|
|
|
objectClass.mod_op = LDAP_MOD_ADD;
|
|
objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
|
|
objectClass.mod_values = objectClassVals;
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSCACLASSNAME;
|
|
objectClassVals[2] = NULL;
|
|
|
|
securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
|
|
securityDescriptor.mod_bvalues = sdVals;
|
|
sdVals[0] = &sdberval;
|
|
sdVals[1] = NULL;
|
|
sdberval.bv_len = 0;
|
|
sdberval.bv_val = NULL;
|
|
if (IsValidSecurityDescriptor(pSD))
|
|
{
|
|
sdberval.bv_len = GetSecurityDescriptorLength(pSD);
|
|
sdberval.bv_val = (char *) pSD;
|
|
}
|
|
|
|
crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
crlmod.mod_type = wszDSBASECRLATTRIBUTE;
|
|
crlmod.mod_bvalues = crlVals;
|
|
crlVals[0] = &crlberval;
|
|
crlVals[1] = NULL;
|
|
crlberval.bv_len = sizeof(ZeroByte);
|
|
crlberval.bv_val = (char *) &ZeroByte;
|
|
|
|
arlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
arlmod.mod_type = wszDSAUTHORITYCRLATTRIBUTE;
|
|
arlmod.mod_bvalues = arlVals;
|
|
arlVals[0] = &arlberval;
|
|
arlVals[1] = NULL;
|
|
arlberval.bv_len = sizeof(ZeroByte);
|
|
arlberval.bv_val = (char *) &ZeroByte;
|
|
|
|
certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
certmod.mod_type = wszDSCACERTATTRIBUTE;
|
|
certmod.mod_bvalues = certVals;
|
|
certVals[0] = &certberval;
|
|
certVals[1] = NULL;
|
|
certberval.bv_len = sizeof(ZeroByte);
|
|
certberval.bv_val = (char *) &ZeroByte;
|
|
if (NULL != pbCert)
|
|
{
|
|
certberval.bv_len = cbCert;
|
|
certberval.bv_val = (char *) pbCert;
|
|
}
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CA Object: '%ws'\n", pwszDN));
|
|
|
|
CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
|
|
hr = CreateOrUpdateDSObject(
|
|
pld,
|
|
pwszDN,
|
|
mods,
|
|
NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "CreateOrUpdateDSObject(CA object)");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapCreateUserObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
OPTIONAL IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN DWORD dwObjectType, // LPC_* (but LPC_CREATE* is ignored)
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
BYTE ZeroByte = 0;
|
|
|
|
LDAPMod objectClass;
|
|
LDAPMod securityDescriptor;
|
|
LDAPMod certmod;
|
|
|
|
struct berval sdberval;
|
|
struct berval certberval;
|
|
|
|
WCHAR *objectClassVals[6];
|
|
struct berval *sdVals[2];
|
|
struct berval *certVals[2];
|
|
|
|
LDAPMod *mods[4];
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
mods[0] = &objectClass;
|
|
mods[1] = &securityDescriptor;
|
|
mods[2] = &certmod; // must be last!
|
|
mods[3] = NULL;
|
|
|
|
securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
|
|
securityDescriptor.mod_bvalues = sdVals;
|
|
sdVals[0] = &sdberval;
|
|
sdVals[1] = NULL;
|
|
sdberval.bv_len = 0;
|
|
sdberval.bv_val = NULL;
|
|
if (IsValidSecurityDescriptor(pSD))
|
|
{
|
|
sdberval.bv_len = GetSecurityDescriptorLength(pSD);
|
|
sdberval.bv_val = (char *) pSD;
|
|
}
|
|
|
|
objectClass.mod_op = LDAP_MOD_ADD;
|
|
objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
|
|
objectClass.mod_values = objectClassVals;
|
|
|
|
DBGCODE(WCHAR const *pwszObjectType);
|
|
switch (LPC_OBJECTMASK & dwObjectType)
|
|
{
|
|
case LPC_CAOBJECT:
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSCACLASSNAME;
|
|
objectClassVals[2] = NULL;
|
|
DBGCODE(pwszObjectType = L"CA");
|
|
break;
|
|
|
|
case LPC_KRAOBJECT:
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSKRACLASSNAME;
|
|
objectClassVals[2] = NULL;
|
|
DBGCODE(pwszObjectType = L"KRA");
|
|
break;
|
|
|
|
case LPC_USEROBJECT:
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSPERSONCLASSNAME;
|
|
objectClassVals[2] = wszDSORGPERSONCLASSNAME;
|
|
objectClassVals[3] = wszDSUSERCLASSNAME;
|
|
objectClassVals[4] = NULL;
|
|
DBGCODE(pwszObjectType = L"User");
|
|
break;
|
|
|
|
case LPC_MACHINEOBJECT:
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSPERSONCLASSNAME;
|
|
objectClassVals[2] = wszDSORGPERSONCLASSNAME;
|
|
objectClassVals[3] = wszDSUSERCLASSNAME;
|
|
objectClassVals[4] = wszDSMACHINECLASSNAME;
|
|
objectClassVals[5] = NULL;
|
|
DBGCODE(pwszObjectType = L"Machine");
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "dwObjectType");
|
|
}
|
|
|
|
certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
certmod.mod_type = wszDSUSERCERTATTRIBUTE;
|
|
certmod.mod_bvalues = certVals;
|
|
certVals[0] = &certberval;
|
|
certVals[1] = NULL;
|
|
certberval.bv_len = sizeof(ZeroByte);
|
|
certberval.bv_val = (char *) &ZeroByte;
|
|
if (NULL != pbCert)
|
|
{
|
|
certberval.bv_len = cbCert;
|
|
certberval.bv_val = (char *) pbCert;
|
|
}
|
|
|
|
DBGPRINT((
|
|
DBG_SS_CERTLIBI,
|
|
"Creating DS %ws Object: '%ws'\n",
|
|
pwszObjectType,
|
|
pwszDN));
|
|
|
|
CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
|
|
hr = CreateOrUpdateDSObject(
|
|
pld,
|
|
pwszDN,
|
|
mods,
|
|
NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "CreateOrUpdateDSObject(KRA object)");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapCreateCDPObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
BYTE ZeroByte = 0;
|
|
|
|
LDAPMod objectClass;
|
|
LDAPMod securityDescriptor;
|
|
LDAPMod crlmod;
|
|
LDAPMod drlmod;
|
|
|
|
struct berval sdberval;
|
|
struct berval crlberval;
|
|
struct berval drlberval;
|
|
|
|
WCHAR *objectClassVals[3];
|
|
struct berval *sdVals[2];
|
|
struct berval *crlVals[2];
|
|
struct berval *drlVals[2];
|
|
|
|
LDAPMod *mods[5];
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
mods[0] = &objectClass;
|
|
mods[1] = &securityDescriptor;
|
|
mods[2] = &crlmod;
|
|
mods[3] = &drlmod;
|
|
mods[4] = NULL;
|
|
|
|
objectClass.mod_op = LDAP_MOD_ADD;
|
|
objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
|
|
objectClass.mod_values = objectClassVals;
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSCDPCLASSNAME;
|
|
objectClassVals[2] = NULL;
|
|
|
|
securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
|
|
securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
|
|
securityDescriptor.mod_bvalues = sdVals;
|
|
sdVals[0] = &sdberval;
|
|
sdVals[1] = NULL;
|
|
sdberval.bv_len = 0;
|
|
sdberval.bv_val = NULL;
|
|
if (IsValidSecurityDescriptor(pSD))
|
|
{
|
|
sdberval.bv_len = GetSecurityDescriptorLength(pSD);
|
|
sdberval.bv_val = (char *) pSD;
|
|
}
|
|
|
|
crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
crlmod.mod_type = wszDSBASECRLATTRIBUTE;
|
|
crlmod.mod_bvalues = crlVals;
|
|
crlVals[0] = &crlberval;
|
|
crlVals[1] = NULL;
|
|
crlberval.bv_val = (char *) &ZeroByte;
|
|
crlberval.bv_len = sizeof(ZeroByte);
|
|
|
|
drlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
drlmod.mod_type = wszDSDELTACRLATTRIBUTE;
|
|
drlmod.mod_bvalues = drlVals;
|
|
drlVals[0] = &drlberval;
|
|
drlVals[1] = NULL;
|
|
drlberval.bv_val = (char *) &ZeroByte;
|
|
drlberval.bv_len = sizeof(ZeroByte);
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CDP Object: '%ws'\n", pwszDN));
|
|
|
|
hr = CreateOrUpdateDSObject(
|
|
pld,
|
|
pwszDN,
|
|
mods,
|
|
NULL,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "CreateOrUpdateDSObject(CDP object)");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapCreateOIDObject(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN DWORD dwType,
|
|
IN WCHAR const *pwszObjId,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR awcType[22];
|
|
|
|
LDAPMod objectClass;
|
|
LDAPMod typemod;
|
|
LDAPMod oidmod;
|
|
|
|
WCHAR *objectClassVals[3];
|
|
WCHAR *typeVals[2];
|
|
WCHAR *oidVals[2];
|
|
|
|
LDAPMod *mods[4];
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
mods[0] = &objectClass;
|
|
mods[1] = &typemod;
|
|
mods[2] = &oidmod;
|
|
mods[3] = NULL;
|
|
CSASSERT(CSExpr(4 == ARRAYSIZE(mods)));
|
|
|
|
objectClass.mod_op = LDAP_MOD_ADD;
|
|
objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
|
|
objectClass.mod_values = objectClassVals;
|
|
objectClassVals[0] = wszDSTOPCLASSNAME;
|
|
objectClassVals[1] = wszDSOIDCLASSNAME;
|
|
objectClassVals[2] = NULL;
|
|
CSASSERT(CSExpr(3 == ARRAYSIZE(objectClassVals)));
|
|
|
|
typemod.mod_op = LDAP_MOD_ADD;
|
|
typemod.mod_type = OID_PROP_TYPE;
|
|
typemod.mod_values = typeVals;
|
|
wsprintf(awcType, L"%u", dwType);
|
|
typeVals[0] = awcType;
|
|
typeVals[1] = NULL;
|
|
CSASSERT(CSExpr(2 == ARRAYSIZE(typeVals)));
|
|
|
|
oidmod.mod_op = LDAP_MOD_ADD;
|
|
oidmod.mod_type = OID_PROP_OID;
|
|
oidmod.mod_values = oidVals;
|
|
oidVals[0] = const_cast<WCHAR *>(pwszObjId);
|
|
oidVals[1] = NULL;
|
|
CSASSERT(CSExpr(2 == ARRAYSIZE(oidVals)));
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating DS OID Object: '%ws'\n", pwszDN));
|
|
|
|
hr = CreateOrUpdateDSObject(
|
|
pld,
|
|
pwszDN,
|
|
mods,
|
|
NULL,
|
|
pdwDisposition,
|
|
ppwszError);
|
|
_JumpIfError(hr, error, "CreateOrUpdateDSObject(OID object)");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapOIDIsMatchingLangId(
|
|
IN WCHAR const *pwszDisplayName,
|
|
IN DWORD dwLanguageId,
|
|
OUT BOOL *pfLangIdExists)
|
|
{
|
|
DWORD DisplayLangId = _wtoi(pwszDisplayName);
|
|
|
|
*pfLangIdExists = FALSE;
|
|
if (iswdigit(*pwszDisplayName) &&
|
|
NULL != wcschr(pwszDisplayName, L',') &&
|
|
DisplayLangId == dwLanguageId)
|
|
{
|
|
*pfLangIdExists = TRUE;
|
|
}
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLdapAddOrDeleteOIDDisplayNameToAttribute(
|
|
IN LDAP *pld,
|
|
OPTIONAL IN WCHAR **ppwszOld,
|
|
IN DWORD dwLanguageId,
|
|
OPTIONAL IN WCHAR const *pwszDisplayName,
|
|
IN WCHAR const *pwszDN,
|
|
IN WCHAR const *pwszAttribute,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cname;
|
|
DWORD iname;
|
|
DWORD i;
|
|
WCHAR **ppwszNew = NULL;
|
|
WCHAR *pwszNew = NULL;
|
|
BOOL fDeleteOldName = FALSE;
|
|
BOOL fNewNameMissing;
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
if (NULL != pwszDisplayName)
|
|
{
|
|
pwszNew = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwcDWORDSPRINTF + 1 + wcslen(pwszDisplayName) + 1) *
|
|
sizeof(WCHAR));
|
|
if (NULL == pwszNew)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
wsprintf(pwszNew, L"%u,%ws", dwLanguageId, pwszDisplayName);
|
|
}
|
|
|
|
cname = 0;
|
|
if (NULL != ppwszOld)
|
|
{
|
|
while (NULL != ppwszOld[cname])
|
|
{
|
|
cname++;
|
|
}
|
|
}
|
|
ppwszNew = (WCHAR **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cname + 2) * sizeof(ppwszNew[0]));
|
|
if (NULL == ppwszNew)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// Delete any display names with matching dwLanguageId
|
|
|
|
iname = 0;
|
|
fNewNameMissing = NULL != pwszNew? TRUE : FALSE;
|
|
if (NULL != ppwszOld)
|
|
{
|
|
for (i = 0; NULL != ppwszOld[i]; i++)
|
|
{
|
|
BOOL fCopy = TRUE;
|
|
WCHAR *pwsz = ppwszOld[i];
|
|
|
|
// case-sensitive compare:
|
|
|
|
if (NULL != pwszNew && 0 == lstrcmp(pwszNew, ppwszOld[i]))
|
|
{
|
|
fCopy = FALSE; // remove duplicates to avoid ldap error
|
|
fNewNameMissing = FALSE;
|
|
}
|
|
else
|
|
{
|
|
BOOL fLangIdExists;
|
|
|
|
hr = myLdapOIDIsMatchingLangId(
|
|
pwsz,
|
|
dwLanguageId,
|
|
&fLangIdExists);
|
|
_PrintIfError(hr, "myLdapOIDIsMatchingLangId");
|
|
if (S_OK != hr || fLangIdExists)
|
|
{
|
|
fCopy = FALSE;
|
|
fDeleteOldName = TRUE;
|
|
DBGPRINT((DBG_SS_CERTLIB, "Deleting %ws\n", pwsz));
|
|
}
|
|
}
|
|
if (fCopy)
|
|
{
|
|
ppwszNew[iname++] = pwsz;
|
|
}
|
|
}
|
|
}
|
|
CSASSERT(iname <= cname);
|
|
|
|
// set disposition assuming there's nothing to do:
|
|
|
|
*pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
|
|
|
|
if (fNewNameMissing || fDeleteOldName)
|
|
{
|
|
LDAPMod *mods[2];
|
|
LDAPMod namemod;
|
|
|
|
mods[0] = &namemod;
|
|
mods[1] = NULL;
|
|
|
|
namemod.mod_op = LDAP_MOD_REPLACE;
|
|
namemod.mod_type = const_cast<WCHAR *>(pwszAttribute);
|
|
namemod.mod_values = ppwszNew;
|
|
|
|
ppwszNew[iname++] = pwszNew;
|
|
ppwszNew[iname] = NULL;
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
const_cast<WCHAR *>(pwszDN),
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = hr;
|
|
if (hr != S_OK)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpError(hr, error, "ldap_modify_ext_s");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszNew)
|
|
{
|
|
LocalFree(pwszNew);
|
|
}
|
|
if (NULL != ppwszNew)
|
|
{
|
|
LocalFree(ppwszNew);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myHLdapError3(
|
|
OPTIONAL IN LDAP *pld,
|
|
IN ULONG ldaperrParm,
|
|
IN ULONG ldaperrParmQuiet,
|
|
IN ULONG ldaperrParmQuiet2,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
if (LDAP_SUCCESS != ldaperrParm)
|
|
{
|
|
BOOL fXlat = TRUE;
|
|
ULONG ldaperr;
|
|
WCHAR *pwszError = NULL;
|
|
|
|
if (NULL != pld)
|
|
{
|
|
ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_ERROR, &pwszError);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
_PrintError(ldaperr, "ldap_get_option(server error)");
|
|
pwszError = NULL;
|
|
}
|
|
|
|
ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_EXT_ERROR, &hr);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
_PrintError2(
|
|
ldaperr,
|
|
"ldap_get_option(server extended error)",
|
|
ldaperr);
|
|
}
|
|
else
|
|
{
|
|
fXlat = FALSE;
|
|
}
|
|
}
|
|
if (fXlat)
|
|
{
|
|
#undef LdapMapErrorToWin32
|
|
hr = LdapMapErrorToWin32(ldaperrParm);
|
|
#define LdapMapErrorToWin32 Use_myHLdapError_Instead_Of_LdapMapErrorToWin32
|
|
}
|
|
hr = myHError(hr);
|
|
_PrintErrorStr3(
|
|
ldaperrParm,
|
|
"ldaperr",
|
|
pwszError,
|
|
ldaperrParmQuiet,
|
|
ldaperrParmQuiet2);
|
|
if (NULL != ppwszError && NULL != pwszError)
|
|
{
|
|
WCHAR awc[32];
|
|
DWORD cwc;
|
|
|
|
wsprintf(awc, L"ldap: 0x%x: ", ldaperrParm);
|
|
cwc = wcslen(awc) + wcslen(pwszError);
|
|
*ppwszError = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == *ppwszError)
|
|
{
|
|
_PrintError(E_OUTOFMEMORY, "LocalAlloc");
|
|
}
|
|
else
|
|
{
|
|
wcscpy(*ppwszError, awc);
|
|
wcscat(*ppwszError, pwszError);
|
|
}
|
|
}
|
|
if (NULL != pwszError)
|
|
{
|
|
ldap_memfree(pwszError);
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myHLdapError2(
|
|
OPTIONAL IN LDAP *pld,
|
|
IN ULONG ldaperrParm,
|
|
IN ULONG ldaperrParmQuiet,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
return(myHLdapError3(
|
|
pld,
|
|
ldaperrParm,
|
|
ldaperrParmQuiet,
|
|
LDAP_SUCCESS,
|
|
ppwszError));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myHLdapError(
|
|
OPTIONAL IN LDAP *pld,
|
|
IN ULONG ldaperrParm,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
return(myHLdapError3(
|
|
pld,
|
|
ldaperrParm,
|
|
LDAP_SUCCESS,
|
|
LDAP_SUCCESS,
|
|
ppwszError));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myHLdapLastError(
|
|
OPTIONAL IN LDAP *pld,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = myHLdapError3(
|
|
pld,
|
|
LdapGetLastError(),
|
|
LDAP_SUCCESS,
|
|
LDAP_SUCCESS,
|
|
ppwszError);
|
|
// must return error
|
|
if (hr == S_OK)
|
|
return E_UNEXPECTED;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLDAPSetStringAttribute(
|
|
IN LDAP *pld,
|
|
IN WCHAR const *pwszDN,
|
|
IN WCHAR const *pwszAttribute,
|
|
IN WCHAR const *pwszValue,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
LDAPMod *mods[2];
|
|
LDAPMod certmod;
|
|
const WCHAR *ppwszVals[2];
|
|
CAutoLPWSTR pwszDNOnly;
|
|
WCHAR *pwszSuffix; // no free
|
|
|
|
hr = TrimURLDN(pwszDN, &pwszSuffix, &pwszDNOnly);
|
|
_JumpIfErrorStr(hr, error, "TrimURLDN", pwszDN);
|
|
|
|
mods[0] = &certmod;
|
|
mods[1] = NULL;
|
|
|
|
ppwszVals[0] = pwszValue;
|
|
ppwszVals[1] = NULL;
|
|
|
|
certmod.mod_op = LDAP_MOD_REPLACE;
|
|
certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
|
|
certmod.mod_values = const_cast<PWCHAR *>(ppwszVals);
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
pwszDNOnly,
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = hr;
|
|
if (hr != S_OK)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpError(hr, error, "ldap_modify_ext_s");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CurrentUserCanInstallCA(
|
|
bool& fCanInstall)
|
|
{
|
|
HRESULT hr;
|
|
HANDLE hThread = NULL; // no free
|
|
HANDLE hAccessToken = NULL, hDupToken = NULL;
|
|
LDAP *pld = NULL;
|
|
BSTR bstrConfigDN = NULL;
|
|
LPWSTR pwszPKIContainerFilter =
|
|
L"(&(objectClass=container)(CN=Public Key Services))";
|
|
LPWSTR pwszSDAttr = L"nTSecurityDescriptor";
|
|
LPWSTR pwszAttrArray[3];
|
|
LDAPMessage* pResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
struct berval **bervalSD = NULL;
|
|
PSECURITY_DESCRIPTOR pSD; // no free
|
|
GENERIC_MAPPING mapping;
|
|
PRIVILEGE_SET PrivilegeSet;
|
|
DWORD cPrivilegeSet = sizeof(PrivilegeSet);
|
|
DWORD dwGrantedAccess;
|
|
BOOL fAccess = FALSE;
|
|
struct l_timeval timeout;
|
|
CHAR sdBerValue[] = {0x30, 0x03, 0x02, 0x01,
|
|
DACL_SECURITY_INFORMATION |
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION};
|
|
|
|
LDAPControl se_info_control =
|
|
{
|
|
LDAP_SERVER_SD_FLAGS_OID_W,
|
|
{
|
|
5, sdBerValue
|
|
},
|
|
TRUE
|
|
};
|
|
|
|
PLDAPControl server_controls[2] =
|
|
{
|
|
&se_info_control,
|
|
NULL
|
|
};
|
|
|
|
pwszAttrArray[0] = pwszSDAttr;
|
|
pwszAttrArray[1] = L"name";
|
|
pwszAttrArray[2] = NULL;
|
|
|
|
ZeroMemory(&mapping, sizeof(mapping));
|
|
|
|
fCanInstall = false;
|
|
|
|
// Get the access token for current thread
|
|
hThread = GetCurrentThread();
|
|
if (NULL == hThread)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "GetCurrentThread");
|
|
}
|
|
|
|
if (!OpenThreadToken(
|
|
hThread,
|
|
TOKEN_QUERY | TOKEN_DUPLICATE,
|
|
FALSE,
|
|
&hAccessToken))
|
|
{
|
|
hr = myHLastError();
|
|
|
|
if(hr==HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
|
|
{
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
if (NULL == hProcess)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "GetCurrentProcess");
|
|
}
|
|
|
|
if (!OpenProcessToken(hProcess,
|
|
TOKEN_DUPLICATE,
|
|
&hAccessToken))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "OpenProcessToken");
|
|
}
|
|
|
|
if (!DuplicateToken(hAccessToken, SecurityIdentification, &hDupToken))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "DuplicateToken");
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
_JumpError(hr, error, "OpenThreadToken");
|
|
}
|
|
}
|
|
|
|
hr = myLdapOpen(
|
|
NULL, // pwszDomainName
|
|
RLBF_REQUIRE_GC | RLBF_REQUIRE_SECURE_LDAP, // dwFlags
|
|
&pld,
|
|
NULL, // pstrDomainDN
|
|
&bstrConfigDN);
|
|
_JumpIfError(hr, error, "myLdapOpen");
|
|
|
|
timeout.tv_sec = csecLDAPTIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
|
|
hr = ldap_search_ext_s(
|
|
pld,
|
|
bstrConfigDN,
|
|
LDAP_SCOPE_SUBTREE,
|
|
pwszPKIContainerFilter,
|
|
pwszAttrArray,
|
|
0,
|
|
(PLDAPControl *) server_controls,
|
|
NULL,
|
|
&timeout,
|
|
0,
|
|
&pResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_ext_s");
|
|
|
|
pEntry = ldap_first_entry(pld, pResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = myHLdapLastError(pld, NULL);
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
bervalSD = ldap_get_values_len(pld, pEntry, pwszSDAttr);
|
|
|
|
if(bervalSD && (*bervalSD)->bv_val)
|
|
{
|
|
pSD = (*bervalSD)->bv_val;
|
|
|
|
if(IsValidSecurityDescriptor(pSD))
|
|
{
|
|
if(!AccessCheck(
|
|
pSD,
|
|
hDupToken,
|
|
ACTRL_DS_WRITE_PROP |
|
|
WRITE_DAC |
|
|
ACTRL_DS_CREATE_CHILD,
|
|
&mapping,
|
|
&PrivilegeSet,
|
|
&cPrivilegeSet,
|
|
&dwGrantedAccess,
|
|
&fAccess))
|
|
{
|
|
hr = myHLastError();
|
|
|
|
if(E_ACCESSDENIED==hr)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
_JumpError(hr, error, "AccessCheck");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((DBG_SS_CERTOCM, "Invalid security descriptor for PKI container" ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((DBG_SS_CERTOCM, "No security descriptor for PKI container" ));
|
|
}
|
|
|
|
if(fAccess)
|
|
{
|
|
fCanInstall = true;
|
|
}
|
|
|
|
error:
|
|
if(bervalSD)
|
|
{
|
|
ldap_value_free_len(bervalSD);
|
|
}
|
|
if (NULL != pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
myLdapClose(pld, NULL, bstrConfigDN);
|
|
if (hAccessToken)
|
|
{
|
|
CloseHandle(hAccessToken);
|
|
}
|
|
if (hDupToken)
|
|
{
|
|
CloseHandle(hDupToken);
|
|
}
|
|
|
|
//we should always return S_OK; since we do not want to abort
|
|
//ocmsetup just because we failed to contact the directory
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT myLdapFindObjectInForest(
|
|
IN LDAP *pld,
|
|
IN LPCWSTR pwszFilter,
|
|
OUT LPWSTR *ppwszURL)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR pwszAttrArray[2] = {wszDSDNATTRIBUTE, NULL};
|
|
LDAPMessage* pResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
LPWSTR *pwszValue = NULL;
|
|
hr = ldap_search_s(
|
|
pld,
|
|
NULL,
|
|
LDAP_SCOPE_SUBTREE,
|
|
const_cast<WCHAR*>(pwszFilter),
|
|
pwszAttrArray,
|
|
0,
|
|
&pResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_s");
|
|
|
|
pEntry = ldap_first_entry(pld, pResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = myHLdapLastError(pld, NULL);
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
pwszValue = ldap_get_values(pld, pEntry, wszDSDNATTRIBUTE);
|
|
|
|
if(pwszValue && pwszValue[0])
|
|
{
|
|
hr = myDupString(pwszValue[0], ppwszURL);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
}
|
|
|
|
error:
|
|
if(pwszValue)
|
|
{
|
|
ldap_value_free(pwszValue);
|
|
}
|
|
if (NULL != pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ExtractMachineNameFromDNSName(
|
|
LPCWSTR pcwszDNS,
|
|
LPWSTR *ppcwszMachineName)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszDot = wcschr(pcwszDNS, L'.');
|
|
DWORD nLen;
|
|
|
|
nLen = (pwszDot?
|
|
SAFE_SUBTRACT_POINTERS(pwszDot, pcwszDNS):
|
|
wcslen(pcwszDNS))+1;
|
|
|
|
*ppcwszMachineName = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*nLen);
|
|
_JumpIfAllocFailed(*ppcwszMachineName, error);
|
|
|
|
wcsncpy(*ppcwszMachineName, pcwszDNS, nLen);
|
|
|
|
(*ppcwszMachineName)[nLen-1] = L'\0';
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT myLdapFindComputerInForest(
|
|
IN LDAP *pld,
|
|
IN LPCWSTR pwszMachineDNS,
|
|
OUT LPWSTR *ppwszURL)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR pwszAttrArray[] = {
|
|
wszDSDNATTRIBUTE,
|
|
wszDSNAMEATTRIBUTE,
|
|
wszDSDNSHOSTNAMEATTRIBUTE,
|
|
NULL};
|
|
LDAPMessage* pResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
LPWSTR *pwszValue = NULL;
|
|
LPWSTR *pwszName = NULL;
|
|
LPWSTR *pwszDNSHostName = NULL;
|
|
LPWSTR pwszFilterFormat1 = L"(&(objectCategory=computer)(name=%s))";
|
|
LPWSTR pwszFilterFormat2 = L"(&(objectCategory=computer)(dNSHostName=%s))";
|
|
LPWSTR pwszFilter = NULL;
|
|
LPWSTR pwszMachineName = NULL;
|
|
bool fMachineNameIsInDNSFormat;
|
|
|
|
// First, try to find the machine based on the DNS name prefix, which usually
|
|
// matches the computer object name
|
|
|
|
hr = ExtractMachineNameFromDNSName(
|
|
pwszMachineDNS,
|
|
&pwszMachineName);
|
|
_JumpIfError(hr, error, "ExtractMachineNameFromDNSName");
|
|
|
|
// if extracted name and dns name don't match, then we were called
|
|
// with a DNS name
|
|
fMachineNameIsInDNSFormat = (0!=wcscmp(pwszMachineDNS, pwszMachineName));
|
|
|
|
pwszFilter = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*
|
|
(wcslen(pwszFilterFormat1)+wcslen(pwszMachineName)+1));
|
|
_JumpIfAllocFailed(pwszFilter, error);
|
|
|
|
wsprintf(pwszFilter, pwszFilterFormat1, pwszMachineName);
|
|
|
|
hr = ldap_search_s(
|
|
pld,
|
|
NULL,
|
|
LDAP_SCOPE_SUBTREE,
|
|
pwszFilter,
|
|
pwszAttrArray,
|
|
0,
|
|
&pResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_s");
|
|
|
|
pEntry = ldap_first_entry(pld, pResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
pwszName = ldap_get_values(pld, pEntry, wszDSNAMEATTRIBUTE);
|
|
if(pwszName && pwszName[0])
|
|
{
|
|
// found a matching object, but do DNS name match?
|
|
pwszDNSHostName = ldap_get_values(pld, pEntry, wszDSDNSHOSTNAMEATTRIBUTE);
|
|
|
|
if(fMachineNameIsInDNSFormat &&
|
|
pwszDNSHostName &&
|
|
pwszDNSHostName[0] &&
|
|
0 != _wcsicmp(pwszDNSHostName[0], pwszMachineDNS))
|
|
{
|
|
// Couldn't find a computer object matching the DNS prefix, try searching
|
|
// on dNSHostName. This attribute is not indexed so the searching will
|
|
// be very slow
|
|
|
|
LocalFree(pwszFilter);
|
|
pwszFilter = NULL;
|
|
|
|
ldap_msgfree(pResult);
|
|
pResult = NULL;
|
|
|
|
pEntry = NULL;
|
|
|
|
pwszFilter = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*
|
|
(wcslen(pwszFilterFormat2)+wcslen(pwszMachineDNS)+1));
|
|
_JumpIfAllocFailed(pwszFilter, error);
|
|
|
|
wsprintf(pwszFilter, pwszFilterFormat2, pwszMachineDNS);
|
|
|
|
hr = ldap_search_s(
|
|
pld,
|
|
NULL,
|
|
LDAP_SCOPE_SUBTREE,
|
|
pwszFilter,
|
|
pwszAttrArray,
|
|
0,
|
|
&pResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_s");
|
|
|
|
pEntry = ldap_first_entry(pld, pResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
}
|
|
}
|
|
|
|
pwszValue = ldap_get_values(pld, pEntry, wszDSDNATTRIBUTE);
|
|
|
|
if(pwszValue)
|
|
{
|
|
hr = myDupString(pwszValue[0], ppwszURL);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
}
|
|
|
|
error:
|
|
|
|
if(pwszValue)
|
|
{
|
|
ldap_value_free(pwszValue);
|
|
}
|
|
if(pwszName)
|
|
{
|
|
ldap_value_free(pwszName);
|
|
}
|
|
if(pwszMachineName)
|
|
{
|
|
LocalFree(pwszMachineName);
|
|
}
|
|
if(pwszFilter)
|
|
{
|
|
LocalFree(pwszFilter);
|
|
}
|
|
if (NULL != pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// The following code loads a list of certificates from a DS object (eg
|
|
// AIA CACertificate property), filters out unwanted certs and writes it
|
|
// back to DS.
|
|
//
|
|
// Certs are loaded into a data structure that looks like this:
|
|
//
|
|
// CFilteredCertList
|
|
// |
|
|
// CCertBucket1->CCertBucket2->...->CertBucketn
|
|
// | |
|
|
// CCertItem1->CertItem2->... CCertItem1->CCertItem2->...
|
|
//
|
|
// Each cert bucket has a list of certs that match some criteria, in our
|
|
// case they share the same subject and public key.
|
|
//
|
|
// After filtering, the buckets in the list must contain:
|
|
//
|
|
// if only expired certs were found with this subject&key
|
|
// keep the most recent expired cert
|
|
// else
|
|
// keep all valid certs only
|
|
//
|
|
// For that, we process one cert context at a time. The filtering algorithm is:
|
|
//
|
|
// if no matching bucket (same subj & key) found
|
|
// create a new bucket it
|
|
// else
|
|
// if cert is expired
|
|
// if bucket contains only expired certs and
|
|
// this cert is newer
|
|
// replace cert in bucket
|
|
// else
|
|
// if bucket contains expired certs
|
|
// replace cert in bucket
|
|
// else
|
|
// add cert to bucket
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CCertItem: wrapper for one certificate context
|
|
class CCertItem
|
|
{
|
|
public:
|
|
CCertItem(PCCERT_CONTEXT pcc) :
|
|
m_pcc(pcc), m_fExpired(false) {}
|
|
~CCertItem()
|
|
{
|
|
CleanupCertContext();
|
|
}
|
|
|
|
void SetExpired(bool fExpired) { m_fExpired = fExpired; }
|
|
void SetCertContext(PCCERT_CONTEXT pccNew)
|
|
{
|
|
CleanupCertContext();
|
|
m_pcc = pccNew;
|
|
}
|
|
PCCERT_CONTEXT GetCertContext() { return m_pcc; }
|
|
bool IsExpired() { return m_fExpired; }
|
|
|
|
private:
|
|
void CleanupCertContext()
|
|
{
|
|
if(m_pcc)
|
|
CertFreeCertificateContext(m_pcc);
|
|
}
|
|
|
|
PCCERT_CONTEXT m_pcc;
|
|
bool m_fExpired;
|
|
};
|
|
typedef TPtrList<CCertItem> CERTITEMLIST;
|
|
typedef TPtrListEnum<CCertItem> CERTITEMLISTENUM;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CCertBucket: bucket of certificates with same subject and publick key
|
|
class CCertBucket
|
|
{
|
|
public:
|
|
|
|
CCertItem *GetFirstCert()
|
|
{
|
|
return m_CertList.GetAt(0);
|
|
}
|
|
bool AddToBucket(CCertItem *pCertItem)
|
|
{
|
|
return m_CertList.AddHead(pCertItem);
|
|
}
|
|
bool CCertBucket::ReplaceBucket(CCertItem *pCertItem)
|
|
{
|
|
m_CertList.Cleanup();
|
|
return m_CertList.AddHead(pCertItem);
|
|
}
|
|
bool InitBucket(CCertItem *pCertItem)
|
|
{
|
|
return m_CertList.AddHead(pCertItem);
|
|
}
|
|
|
|
friend class CFilteredCertList;
|
|
|
|
private:
|
|
CERTITEMLIST m_CertList;
|
|
};
|
|
typedef TPtrList<CCertBucket> CERTBUCKETLIST;
|
|
typedef TPtrListEnum<CCertBucket> CERTBUCKETLISTENUM;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CFilteredCertList: list of certificate buckets (one bucket contains certs
|
|
// with same subject and public key). Upon insertion we follow the algorithm
|
|
// described above.
|
|
//
|
|
// To change the filtering behavior, derive from this class and override
|
|
// InsertCert method.
|
|
class CFilteredCertList
|
|
{
|
|
public:
|
|
CFilteredCertList() {};
|
|
~CFilteredCertList() {};
|
|
bool InsertCert(CCertItem *pCertItem);
|
|
int GetCount();
|
|
HRESULT ImportFromBervals(struct berval **pBervals);
|
|
HRESULT ExportToBervals(struct berval **&pBervals);
|
|
|
|
protected:
|
|
CCertBucket * FindBucket(CCertItem *pCertItem);
|
|
bool AddNewBucket(CCertItem *pCertItem);
|
|
bool BelongsToBucket(CCertBucket *pCertBucket, CCertItem *pCertItem);
|
|
bool ReplaceBucket(CCertItem *pCertItem);
|
|
bool InsertCertInBucket(CCertBucket *pCertBucket, CCertItem *pCertItem);
|
|
|
|
private:
|
|
CERTBUCKETLIST m_BucketList;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CFilteredCertList methods
|
|
|
|
int CFilteredCertList::GetCount()
|
|
{
|
|
int nCount = 0;
|
|
|
|
CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
|
|
CCertBucket * pBucket;
|
|
for(pBucket = BucketListEnum.Next();
|
|
pBucket;
|
|
pBucket = BucketListEnum.Next())
|
|
{
|
|
nCount += pBucket->m_CertList.GetCount();
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
bool CFilteredCertList::BelongsToBucket(
|
|
CCertBucket *pCertBucket,
|
|
CCertItem *pCertItem)
|
|
{
|
|
PCCERT_CONTEXT pCertContext1 =
|
|
pCertBucket->GetFirstCert()->GetCertContext();
|
|
PCCERT_CONTEXT pCertContext2 =
|
|
pCertItem->GetCertContext();
|
|
|
|
// belongs to this bucket if subject and public key match
|
|
return
|
|
(0 == memcmp(
|
|
pCertContext1->pCertInfo->Subject.pbData,
|
|
pCertContext2->pCertInfo->Subject.pbData,
|
|
pCertContext1->pCertInfo->Subject.cbData)) &&
|
|
(0 == memcmp(
|
|
pCertContext1->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
|
|
pCertContext2->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
|
|
pCertContext1->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
CCertBucket *CFilteredCertList::FindBucket(CCertItem *pCertItem)
|
|
{
|
|
CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
|
|
CCertBucket * pBucket;
|
|
for(pBucket = BucketListEnum.Next();
|
|
pBucket;
|
|
pBucket = BucketListEnum.Next())
|
|
{
|
|
if(BelongsToBucket(pBucket, pCertItem))
|
|
return pBucket;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool CFilteredCertList::AddNewBucket(CCertItem *pCertItem)
|
|
{
|
|
CCertBucket *pBucket = new CCertBucket();
|
|
if(!pBucket->InitBucket(pCertItem))
|
|
return false;
|
|
|
|
if(m_BucketList.AddHead(pBucket))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool CFilteredCertList::InsertCertInBucket(
|
|
CCertBucket *pCertBucket,
|
|
CCertItem *pCertItem)
|
|
{
|
|
bool fRet = false;
|
|
CCertItem * pFirstCert = pCertBucket->GetFirstCert();
|
|
// if cert is expired
|
|
if(pCertItem->IsExpired())
|
|
{
|
|
// if bucket contains only expired certs and
|
|
// this cert is newer
|
|
if(pFirstCert->IsExpired() &&
|
|
0 < CompareFileTime(
|
|
&(pCertItem->GetCertContext()->pCertInfo->NotAfter),
|
|
&(pFirstCert->GetCertContext()->pCertInfo->NotAfter)))
|
|
{
|
|
// replace cert in bucket
|
|
fRet = pCertBucket->ReplaceBucket(pCertItem);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if bucket contains expired certs
|
|
if(pFirstCert->IsExpired())
|
|
{
|
|
// replace cert in bucket
|
|
fRet = pCertBucket->ReplaceBucket(pCertItem);
|
|
}
|
|
else
|
|
{
|
|
// add cert to bucket
|
|
fRet = pCertBucket->AddToBucket(pCertItem);
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
|
|
}
|
|
|
|
bool CFilteredCertList::InsertCert(CCertItem *pCertItem)
|
|
{
|
|
CCertBucket *pBucket;
|
|
|
|
pBucket = FindBucket(pCertItem);
|
|
|
|
// if no matching bucket (same subj & key) found
|
|
// create a new bucket it
|
|
if(!pBucket)
|
|
{
|
|
return AddNewBucket(pCertItem);
|
|
}
|
|
else
|
|
{
|
|
return InsertCertInBucket(pBucket, pCertItem);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Loads cert contexts from LDAP structure, an array of pointers to
|
|
// berval structs which hold cert blobs.
|
|
|
|
HRESULT
|
|
CFilteredCertList::ImportFromBervals(
|
|
struct berval **pBervals)
|
|
{
|
|
HRESULT hr;
|
|
PCCERT_CONTEXT pcc;
|
|
int i;
|
|
FILETIME ft;
|
|
|
|
// Consider old certs that are one minute old
|
|
GetSystemTimeAsFileTime(&ft);
|
|
myMakeExprDateTime(&ft, -1, ENUM_PERIOD_MINUTES);
|
|
|
|
for (i = 0; NULL != pBervals[i]; i++)
|
|
{
|
|
struct berval *pberval = pBervals[i];
|
|
pcc = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
(BYTE *) pberval->bv_val,
|
|
pberval->bv_len);
|
|
if (NULL == pcc)
|
|
{
|
|
// not a valid cert, ignore
|
|
_PrintError(myHLastError(), "CreateCertificateContext");
|
|
continue;
|
|
}
|
|
|
|
CCertItem * pci = new CCertItem(pcc); // CCertItem takes ownership
|
|
_JumpIfAllocFailed(pci, error); // of this cert context and will
|
|
// CertFreeCertificateContext in
|
|
// destructor
|
|
pci->SetExpired(
|
|
0 > CompareFileTime(&pcc->pCertInfo->NotAfter, &ft));
|
|
|
|
if(!InsertCert(pci)) // InsertCert returns true if cert
|
|
{ // was added to the list, in which case
|
|
delete pcc; // the list destructor will cleanup.
|
|
} // If not, we need to delete explicitely
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Builds an LDAP structure from the list of certs, to be written back to DS.
|
|
// LDAP struct is an array of pointers to struct bervals structures, terminated
|
|
// with a NULL pointer. We allocate the pointer array and the space for berval
|
|
// structs in one call.
|
|
// Caller is responsible for LocalFree'ing pBervals.
|
|
HRESULT CFilteredCertList::ExportToBervals(struct berval **&pBervals)
|
|
{
|
|
HRESULT hr;
|
|
CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
|
|
CCertBucket *pBucket;
|
|
int i;
|
|
DWORD dwSize;
|
|
struct berval *pBervalData;
|
|
|
|
// total size of pointers array plus size of array of berval structs
|
|
dwSize = (GetCount()+1) * sizeof(pBervals[0]) +
|
|
GetCount() * sizeof(struct berval);
|
|
|
|
pBervals = (struct berval **) LocalAlloc(LMEM_FIXED, dwSize);
|
|
_JumpIfAllocFailed(pBervals, error);
|
|
|
|
// starting address for the berval arrays
|
|
pBervalData = (struct berval *)(pBervals+GetCount()+1);
|
|
|
|
for(i=0, pBucket = BucketListEnum.Next();
|
|
pBucket;
|
|
pBucket = BucketListEnum.Next())
|
|
{
|
|
CERTITEMLISTENUM CertListEnum(pBucket->m_CertList);
|
|
CCertItem *pCertItem;
|
|
|
|
for(pCertItem = CertListEnum.Next();
|
|
pCertItem;
|
|
i++, pCertItem = CertListEnum.Next())
|
|
{
|
|
// set the pointer to the associated berval struct
|
|
pBervals[i] = pBervalData+i;
|
|
// init the berval struct
|
|
pBervalData[i].bv_val = (char *)
|
|
pCertItem->GetCertContext()->pbCertEncoded;
|
|
pBervalData[i].bv_len = pCertItem->GetCertContext()->cbCertEncoded;
|
|
}
|
|
}
|
|
|
|
pBervals[i] = NULL;
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Loads the certificate blobs stored in the specified object&property, filters
|
|
// them and writes them back to DS.
|
|
// Filtering keeps all valid certificates and no expired certs. If only expired
|
|
// have been found, it keeps the most recent one.
|
|
|
|
HRESULT
|
|
myLdapFilterCertificates(
|
|
IN LDAP *pld,
|
|
IN LPCWSTR pcwszDN,
|
|
IN LPCWSTR pcwszAttribute,
|
|
OUT DWORD *pdwDisposition,
|
|
OPTIONAL OUT WCHAR **ppwszError)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cres;
|
|
LONG cber;
|
|
DWORD i;
|
|
LDAP_TIMEVAL timeval;
|
|
LDAPMessage *pmsg = NULL;
|
|
LDAPMessage *pres;
|
|
WCHAR *apwszAttrs[2];
|
|
struct berval **ppberval = NULL;
|
|
struct berval **prgpberVals = NULL;
|
|
CFilteredCertList NewCertList;
|
|
CAutoLPWSTR strDN;
|
|
LPWSTR pcwszSuffix; // no free
|
|
|
|
*pdwDisposition = LDAP_OTHER;
|
|
if (NULL != ppwszError)
|
|
{
|
|
*ppwszError = NULL;
|
|
}
|
|
|
|
hr = TrimURLDN(pcwszDN, &pcwszSuffix, &strDN);
|
|
_JumpIfError(hr, error, "TrimURLDN");
|
|
|
|
|
|
apwszAttrs[0] = const_cast<WCHAR *>(pcwszAttribute);
|
|
apwszAttrs[1] = NULL;
|
|
|
|
timeval.tv_sec = csecLDAPTIMEOUT;
|
|
timeval.tv_usec = 0;
|
|
|
|
hr = ldap_search_st(
|
|
pld, // ld
|
|
strDN, // base
|
|
LDAP_SCOPE_BASE, // scope
|
|
NULL, // filter
|
|
apwszAttrs, // attrs
|
|
FALSE, // attrsonly
|
|
&timeval, // timeout
|
|
&pmsg); // res
|
|
if (S_OK != hr)
|
|
{
|
|
*pdwDisposition = hr;
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpErrorStr(hr, error, "ldap_search_st", pcwszDN);
|
|
}
|
|
|
|
cres = ldap_count_entries(pld, pmsg);
|
|
if (0 == cres)
|
|
{
|
|
// No entries were found.
|
|
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_count_entries");
|
|
}
|
|
|
|
pres = ldap_first_entry(pld, pmsg);
|
|
if (NULL == pres)
|
|
{
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
ppberval = ldap_get_values_len(
|
|
pld,
|
|
pres,
|
|
const_cast<WCHAR *>(pcwszAttribute));
|
|
|
|
|
|
if (NULL != ppberval)
|
|
{
|
|
// count entries
|
|
cber = 0;
|
|
for (i = 0; NULL != ppberval[i]; i++, cber++)
|
|
NULL;
|
|
|
|
// load and filter certs
|
|
hr = NewCertList.ImportFromBervals(ppberval);
|
|
_JumpIfError(hr, error, "ImportFromBervals");
|
|
|
|
// if number of certs is the same, no need to write it back
|
|
// (order doesn't matter)
|
|
if (cber != NewCertList.GetCount())
|
|
{
|
|
// walk the list and copy the cert blobs
|
|
hr = NewCertList.ExportToBervals(prgpberVals);
|
|
_JumpIfError(hr, error, "ExportToBervals");
|
|
|
|
// set disposition assuming there's nothing to do:
|
|
|
|
*pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
|
|
|
|
LDAPMod *mods[2];
|
|
LDAPMod certmod;
|
|
|
|
mods[0] = &certmod;
|
|
mods[1] = NULL;
|
|
|
|
certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
certmod.mod_type = const_cast<WCHAR *>(pcwszAttribute);
|
|
certmod.mod_bvalues = prgpberVals;
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
strDN,
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
*pdwDisposition = hr;
|
|
if (hr != S_OK)
|
|
{
|
|
hr = myHLdapError(pld, hr, ppwszError);
|
|
_JumpError(hr, error, "ldap_modify_ext_s");
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != prgpberVals)
|
|
{
|
|
LocalFree(prgpberVals);
|
|
}
|
|
if (NULL != ppberval)
|
|
{
|
|
ldap_value_free_len(ppberval);
|
|
}
|
|
if (NULL != pmsg)
|
|
{
|
|
ldap_msgfree(pmsg);
|
|
}
|
|
return(hr);
|
|
}
|