windows-nt/Source/XPSP1/NT/ds/security/services/ca/ocmsetup/dssetup.cpp
2020-09-26 16:20:57 +08:00

721 lines
16 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: setup.cpp
//
// Contents:
//
//---------------------------------------------------------------------------
#include "pch.cpp"
#pragma hdrstop
#include <assert.h>
#include <accctrl.h>
#include <ntldap.h>
#include "certca.h"
#include "cainfop.h"
#include "csldap.h"
#include "dssetup.h"
#define __dwFILE__ __dwFILE_OCMSETUP_DSSETUP_CPP__
typedef HRESULT (* LPFNDLL_INSTALL)(BOOL bInstall, LPCWSTR pszCmdLine);
BOOL
IsCAExistInDS(
IN WCHAR const *pwszSanitizedName)
{
BOOL exist = FALSE;
HRESULT hr;
HCAINFO hCAInfo = NULL;
WCHAR *pwszDSName = NULL;
hr = mySanitizedNameToDSName(pwszSanitizedName, &pwszDSName);
_JumpIfError(hr, error, "mySanitizedNameToDSName");
hr = CAFindByName(
pwszDSName,
NULL,
CA_FIND_INCLUDE_UNTRUSTED | CA_FIND_INCLUDE_NON_TEMPLATE_CA,
&hCAInfo);
_JumpIfErrorStr(hr, error, "IsCAExistInDS:CAFindByName", pwszDSName);
if (NULL == hCAInfo)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "invalid CA in DS");
}
exist = TRUE;
error:
if (NULL != pwszDSName)
{
LocalFree(pwszDSName);
}
if (NULL != hCAInfo)
{
CACloseCA(hCAInfo);
}
return exist;
}
BOOL
IsDSAvailable(VOID)
{
// fail out quickly if DS not present on domain
if (S_OK != myDoesDSExist(FALSE))
return FALSE;
BOOL available = FALSE;
LDAP *pldap = NULL;
ULONG ldaperr;
HRESULT hr;
HMODULE hMod = NULL;
hMod = LoadLibrary(L"wldap32.dll");
if (NULL == hMod)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "LoadLibrary", L"wldap32.dll");
}
// bind to ds
hr = myRobustLdapBind(&pldap, FALSE);
_JumpIfError(hr, error, "myRobustLdapBind");
available = TRUE;
error:
if (NULL != pldap)
{
ldap_unbind(pldap);
}
if (NULL != hMod)
{
FreeLibrary(hMod);
}
return available;
}
HRESULT
RemoveCAInDS(
IN WCHAR const *pwszSanitizedName)
{
HRESULT hr;
HCAINFO hCAInfo = NULL;
WCHAR *pwszDSName = NULL;
hr = mySanitizedNameToDSName(pwszSanitizedName, &pwszDSName);
_JumpIfError(hr, error, "mySanitizedNameToDSName");
hr = CAFindByName(
pwszDSName,
NULL,
CA_FIND_INCLUDE_UNTRUSTED | CA_FIND_INCLUDE_NON_TEMPLATE_CA,
&hCAInfo);
_JumpIfErrorStr(hr, error, "RemoveCAInDS:CAFindByName", pwszDSName);
if (NULL == hCAInfo)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "invalid CA in DS");
}
hr = CADeleteCA(hCAInfo);
_JumpIfError(hr, error, "CADeleteCA");
hr = RemoveCAMachineFromCertPublishers();
_JumpIfError(hr, error, "RemoveCAInDS");
error:
if (NULL != pwszDSName)
{
LocalFree(pwszDSName);
}
if (NULL != hCAInfo)
{
CACloseCA(hCAInfo);
}
return hr;
}
DWORD
GetAuthoritativeDomainDn(
IN LDAP* LdapHandle,
OUT BSTR *DomainDn,
OUT BSTR *ConfigDn
)
/*++
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 RtlFreeHeap() using the process heap.
Parameters:
LdapHandle : a valid handle to an ldap session
DomainDn : a pointer to a string to be allocated in this routine
ConfigDn : a pointer to a string to be allocated in this routine
Return Values:
An error from the win32 error space.
ERROR_SUCCESS and
Other operation errors.
--*/
{
DWORD WinError = ERROR_SUCCESS;
ULONG LdapError;
LDAPMessage *SearchResult = NULL;
LDAPMessage *Entry = NULL;
WCHAR *Attr = NULL;
BerElement *BerElement;
WCHAR **Values = NULL;
WCHAR *AttrArray[3];
WCHAR *DefaultNamingContext = L"defaultNamingContext";
WCHAR *ConfigurationNamingContext = L"configurationNamingContext";
WCHAR *ObjectClassFilter = L"objectClass=*";
//
// These must be present
//
//
// Set the out parameters to null
//
if(ConfigDn)
{
*ConfigDn = NULL;
}
if(DomainDn)
{
*DomainDn = NULL;
}
//
// Query for the ldap server oerational attributes to obtain the default
// naming context.
//
AttrArray[0] = DefaultNamingContext;
AttrArray[1] = ConfigurationNamingContext;
AttrArray[2] = NULL; // this is the sentinel
LdapError = ldap_search_sW(LdapHandle,
NULL,
LDAP_SCOPE_BASE,
ObjectClassFilter,
AttrArray,
FALSE,
&SearchResult);
WinError = myHLdapError(LdapHandle, LdapError, NULL);
if (ERROR_SUCCESS == WinError) {
Entry = ldap_first_entry(LdapHandle, SearchResult);
if (Entry) {
Attr = ldap_first_attributeW(LdapHandle, Entry, &BerElement);
while (Attr) {
if (!_wcsicmp(Attr, DefaultNamingContext)) {
if(DomainDn)
{
Values = ldap_get_values(LdapHandle, Entry, Attr);
if (Values && Values[0]) {
(*DomainDn) = SysAllocString(Values[0]);
_JumpIfOutOfMemory(WinError, error, *DomainDn);
}
ldap_value_free(Values);
}
} else if (!_wcsicmp(Attr, ConfigurationNamingContext)) {
if(ConfigDn)
{
Values = ldap_get_valuesW(LdapHandle, Entry, Attr);
if (Values && Values[0]) {
(*ConfigDn) = SysAllocString(Values[0]);
_JumpIfOutOfMemory(WinError, error, *ConfigDn);
}
ldap_value_free(Values);
}
}
Attr = ldap_next_attribute(LdapHandle, Entry, BerElement);
}
}
if ( (DomainDn &&(!(*DomainDn))) || (ConfigDn && (!(*ConfigDn)))) {
//
// We could get the default domain - bail out
//
WinError = ERROR_CANT_ACCESS_DOMAIN_INFO;
}
}
error:
return WinError;
}
HRESULT CreateCertDSHierarchy(VOID)
{
HRESULT hr = S_OK;
ULONG ldaperr;
LDAP *pld = NULL;
LDAPMod objectClass;
DWORD err;
WCHAR *objectClassVals[3];
LDAPMod *mods[2];
BSTR bstrConfigDN = NULL;
WCHAR * awszLocations[] = {
L"CN=Public Key Services,CN=Services,",
L"CN=Enrollment Services,CN=Public Key Services,CN=Services,",
NULL,
};
WCHAR ** pwszCurLocation;
DWORD cbBuffer;
BSTR bstrBuffer = NULL;
// bind to ds
hr = myRobustLdapBind(&pld, FALSE);
if(hr != S_OK)
{
goto error;
}
err = GetAuthoritativeDomainDn(pld, NULL, &bstrConfigDN);
if(err)
{
hr = HRESULT_FROM_WIN32(err);
goto error;
}
pwszCurLocation = awszLocations;
// Build the Public Key Services container
mods[0] = &objectClass;
mods[1] = NULL;
objectClass.mod_op = 0;
objectClass.mod_type = TEXT("objectclass");
objectClass.mod_values = objectClassVals;
objectClassVals[0] = TEXT("top");
objectClassVals[1] = TEXT("container");
objectClassVals[2] = NULL;
while(*pwszCurLocation)
{
cbBuffer = wcslen(*pwszCurLocation) + wcslen(bstrConfigDN) + 1;
// Build a string containing the CA Location
bstrBuffer = SysAllocStringLen(NULL, cbBuffer);
if(bstrBuffer == NULL)
{
hr = E_OUTOFMEMORY;
goto error;
}
wcscpy(bstrBuffer, *pwszCurLocation);
wcscat(bstrBuffer, bstrConfigDN);
ldaperr = ldap_add_s(pld, bstrBuffer, mods);
SysFreeString(bstrBuffer);
if(ldaperr != LDAP_SUCCESS && ldaperr != LDAP_ALREADY_EXISTS)
{
hr = myHLdapError(pld, ldaperr, NULL);
goto error;
}
pwszCurLocation++;
}
error:
if(bstrConfigDN)
{
SysFreeString(bstrConfigDN);
}
if(pld != NULL)
{
ldap_unbind(pld);
pld = NULL;
}
return hr;
}
//
HRESULT InitializeCertificateTemplates(VOID)
{
HRESULT hr = S_OK;
DWORD err = ERROR_SUCCESS;
HINSTANCE hCertCli = NULL;
LPFNDLL_INSTALL lpfnDllInstall = NULL;
hCertCli = LoadLibrary(L"certcli.dll");
if(hCertCli == NULL)
{
hr = myHLastError();
goto error;
}
lpfnDllInstall = (LPFNDLL_INSTALL)GetProcAddress(hCertCli, "DllInstall");
if(lpfnDllInstall == NULL)
{
hr = myHLastError();
goto error;
}
err = lpfnDllInstall(TRUE, L"i");
hr = HRESULT_FROM_WIN32(err);
error:
return hr;
}
HRESULT
DNStoDirectoryName(IN LPWSTR wszDNSDomain,
OUT LPWSTR *pwszDN)
{
HRESULT hr = S_OK;
DWORD cDN;
LPWSTR wszResult = NULL;
LPWSTR wszCurrent, wszNext;
if (wszDNSDomain == NULL)
{
hr = E_POINTER;
_JumpError(hr, error, "DNStoDirectoryName");
}
if(0==wcsncmp(wszDNSDomain, L"\\\\", 2))
{
// this is a DC DNS name, skip the machine name
wszDNSDomain = wcschr(wszDNSDomain, L'.');
// no dot found?
if(!wszDNSDomain)
{
hr =E_UNEXPECTED;
_JumpError(hr, error, "DC DNS doesn't contain at least one dot");
}
// jump over the dot
wszDNSDomain++;
// no domain name following the DC name?
if(L'\0'==*wszDNSDomain)
{
hr = E_UNEXPECTED;
_JumpError(hr, error, "DC DNS name doesn't contain a domain name");
}
}
// Estimate the size of the output string
cDN = wcslen(wszDNSDomain) + 3;
wszCurrent=wszDNSDomain;
while(wszCurrent = wcschr(wszCurrent, L'.'))
{
cDN += 4; // sizeof ,DC=
wszCurrent++;
}
cDN += 1; // NULL terminate
wszResult = (LPWSTR)LocalAlloc(LMEM_FIXED, cDN * sizeof(WCHAR));
if(wszResult == NULL)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wszCurrent=wszDNSDomain;
// prepend the first DC=
wszNext = wszResult + 3;
wcscpy(wszResult, L"DC=");
while(*wszCurrent)
{
if(*wszCurrent != '.')
{
*wszNext++ = *wszCurrent++;
}
else
{
wszCurrent++;
if(*wszCurrent)
{
wcscpy(wszNext, L",DC=");
wszNext += 4;
}
}
}
*wszNext = 0;
if(pwszDN)
{
*pwszDN = wszResult;
wszResult = NULL;
}
error:
if(wszResult)
{
LocalFree(wszResult);
}
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 = myRobustLdapBind(
&pld,
TRUE);
_JumpIfError(hr, error, "myRobustLdapBind");
hr = myGetAuthoritativeDomainDn(
pld,
NULL,
&bstrConfigDN);
_JumpIfError(hr, error, "myGetAuthoritativeDomainDn");
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_sW");
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(bstrConfigDN)
{
SysFreeString(bstrConfigDN);
}
if (NULL != pResult)
{
ldap_msgfree(pResult);
}
if (pld)
{
ldap_unbind(pld);
}
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;
}