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.
2698 lines
82 KiB
2698 lines
82 KiB
//--------------------------------------------------------------------
|
|
// SetupUtil - implementation
|
|
// Copyright (C) Microsoft Corporation, 1999
|
|
//
|
|
// Created by: Louis Thomas (louisth), 8-10-99
|
|
//
|
|
// Functions needed to set up CEP
|
|
//
|
|
|
|
//--------------------------------------------------------------------
|
|
// includes
|
|
#include "global.hxx"
|
|
#include <xenroll.h>
|
|
#include <dbgdef.h>
|
|
#include "ErrorHandling.h"
|
|
#include "SetupUtil.h"
|
|
|
|
//--------------------------------------------------------------------
|
|
// Constants
|
|
static const WCHAR gc_wszRegKeyServices[]=L"System\\CurrentControlSet\\Services";
|
|
static const WCHAR gc_wszCertSrvDir[]=L"CertSrv";
|
|
|
|
// from <wincrypt.h>
|
|
static const WCHAR gc_wszEnrollmentAgentOid[]=L"1.3.6.1.4.1.311.20.2.1"; //szOID_ENROLLMENT_AGENT
|
|
|
|
// from ca\include\certlib.h; ca\certlib\acl.cpp
|
|
const GUID GUID_ENROLL={0x0e10c968, 0x78fb, 0x11d2, {0x90, 0xd4, 0x00, 0xc0, 0x4f, 0x79, 0xdc, 0x55}};
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// IIS magic
|
|
#undef DEFINE_GUID
|
|
#define INITGUID
|
|
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
EXTERN_C const GUID name \
|
|
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
|
#include <iwamreg.h>
|
|
#include <iadmw.h>
|
|
#include <iiscnfg.h>
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// constants
|
|
#include "..\common.h"
|
|
#define MAX_METABASE_ATTEMPTS 10 // Times to bang head on wall
|
|
#define METABASE_PAUSE 500 // Time to pause in msec
|
|
static const WCHAR gc_wszBaseRoot[]=L"/LM/W3svc/1/ROOT";
|
|
static const WCHAR gc_wszCepDllName[]=CEP_DLL_NAME;
|
|
static const WCHAR gc_wszCepStoreName[]=CEP_STORE_NAME;
|
|
static const WCHAR gc_wszAppPoolBase[]=L"/LM/W3svc/APPPOOLS";
|
|
|
|
//---------------------------------------------------------------------
|
|
// function prototypes
|
|
|
|
HRESULT
|
|
EnableISAPIExtension(
|
|
IN LPCWSTR pcwszExtension,
|
|
OUT BOOL *pfEnabledASP);
|
|
|
|
HRESULT IsISAPIExtensionEnabled(
|
|
LPCWSTR pcwszExtension,
|
|
bool& rfEnabled);
|
|
|
|
|
|
//####################################################################
|
|
// module local functions
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT myHExceptionCode(EXCEPTION_POINTERS * pep)
|
|
{
|
|
HRESULT hr=pep->ExceptionRecord->ExceptionCode;
|
|
if (!FAILED(hr)) {
|
|
hr=HRESULT_FROM_WIN32(hr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT
|
|
vrOpenRoot(
|
|
IN IMSAdminBase *pIMeta,
|
|
IN BOOL fReadOnly,
|
|
IN const WCHAR * wszBaseRoot,
|
|
OUT METADATA_HANDLE *phMetaRoot)
|
|
{
|
|
HRESULT hr;
|
|
unsigned int nAttempts;
|
|
|
|
// Re-try a few times to see if we can get past the block
|
|
nAttempts=0;
|
|
do {
|
|
|
|
// Pause on retry
|
|
if (0!=nAttempts) {
|
|
Sleep(METABASE_PAUSE);
|
|
}
|
|
|
|
// Make an attempt to open the root
|
|
__try {
|
|
hr=pIMeta->OpenKey(
|
|
METADATA_MASTER_ROOT_HANDLE,
|
|
wszBaseRoot,
|
|
fReadOnly?
|
|
METADATA_PERMISSION_READ :
|
|
(METADATA_PERMISSION_READ |
|
|
METADATA_PERMISSION_WRITE),
|
|
1000,
|
|
phMetaRoot);
|
|
} _TrapException(hr);
|
|
|
|
nAttempts++;
|
|
|
|
} while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY)==hr && nAttempts<MAX_METABASE_ATTEMPTS);
|
|
|
|
_JumpIfError(hr, error, "OpenKey");
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT GetRegString(IN HKEY hKey, IN const WCHAR * wszValue, OUT WCHAR ** pwszString)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwDataSize;
|
|
DWORD dwType;
|
|
DWORD dwError;
|
|
|
|
// must be cleaned up
|
|
WCHAR * wszString=NULL;
|
|
|
|
// init out params
|
|
*pwszString=NULL;
|
|
|
|
// get value
|
|
dwDataSize=0;
|
|
dwError=RegQueryValueExW(hKey, wszValue, NULL, &dwType, NULL, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszValue);
|
|
}
|
|
_Verify(REG_SZ==dwType, hr, error);
|
|
wszString=(WCHAR *)LocalAlloc(LPTR, dwDataSize);
|
|
_JumpIfOutOfMemory(hr, error, pwszString);
|
|
dwError=RegQueryValueExW(hKey, wszValue, NULL, &dwType, (BYTE *)wszString, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszValue);
|
|
}
|
|
_Verify(REG_SZ==dwType, hr, error);
|
|
|
|
// it worked
|
|
hr=S_OK;
|
|
*pwszString=wszString;
|
|
wszString=NULL;
|
|
|
|
error:
|
|
if (NULL!=wszString) {
|
|
LocalFree(wszString);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT OpenCertSrvConfig(HKEY * phkey)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwError;
|
|
DWORD dwType;
|
|
DWORD dwDataSize;
|
|
|
|
// must be cleaned up
|
|
HKEY hServices=NULL;
|
|
HKEY hCertSvc=NULL;
|
|
HKEY hConfig=NULL;
|
|
|
|
// initialize out params
|
|
*phkey=NULL;
|
|
|
|
// Open HKLM\System\CurrentControlSet\Services
|
|
dwError=RegOpenKeyExW(HKEY_LOCAL_MACHINE, gc_wszRegKeyServices, 0, KEY_READ, &hServices);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyEx", gc_wszRegKeyServices);
|
|
}
|
|
|
|
// open CertSvc\Configuration
|
|
dwError=RegOpenKeyExW(hServices, wszSERVICE_NAME, 0, KEY_READ, &hCertSvc);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyEx", wszSERVICE_NAME);
|
|
}
|
|
dwError=RegOpenKeyExW(hCertSvc, wszREGKEYCONFIG, 0, KEY_READ, &hConfig);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyEx", wszREGKEYCONFIG);
|
|
}
|
|
|
|
// we were successfull
|
|
hr=S_OK;
|
|
*phkey=hConfig;
|
|
hConfig=0;
|
|
|
|
error:
|
|
if (NULL!=hConfig) {
|
|
RegCloseKey(hConfig);
|
|
}
|
|
if (NULL!=hCertSvc) {
|
|
RegCloseKey(hCertSvc);
|
|
}
|
|
if (NULL!=hServices) {
|
|
RegCloseKey(hServices);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT OpenCurrentCAConfig(HKEY * phkey)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwError;
|
|
DWORD dwType;
|
|
DWORD dwDataSize;
|
|
|
|
// must be cleaned up
|
|
HKEY hConfig=NULL;
|
|
HKEY hCurConfig=NULL;
|
|
WCHAR * wszActiveConfig=NULL;
|
|
|
|
// initialize out params
|
|
*phkey=NULL;
|
|
|
|
// Open HKLM\System\CurrentControlSet\Services\CertSvc\Configuration
|
|
hr=OpenCertSrvConfig(&hConfig);
|
|
_JumpIfError(hr, error, "OpenCertSrvConfig");
|
|
|
|
// get value "active"
|
|
hr=GetRegString(hConfig, wszREGACTIVE, &wszActiveConfig);
|
|
_JumpIfErrorStr(hr, error, "GetRegString", wszREGACTIVE);
|
|
|
|
// and open <active>
|
|
dwError=RegOpenKeyExW(hConfig, wszActiveConfig, 0, KEY_ALL_ACCESS, &hCurConfig);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyEx", wszActiveConfig);
|
|
}
|
|
|
|
// we were successfull
|
|
hr=S_OK;
|
|
*phkey=hCurConfig;
|
|
hCurConfig=0;
|
|
|
|
error:
|
|
if (NULL!=hCurConfig) {
|
|
RegCloseKey(hCurConfig);
|
|
}
|
|
if (NULL!=wszActiveConfig) {
|
|
LocalFree(wszActiveConfig);
|
|
}
|
|
if (NULL!=hConfig) {
|
|
RegCloseKey(hConfig);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// stolen from certlib.cpp. use this until Pete fixes the api.
|
|
static HRESULT GetCADsName(OUT WCHAR **pwszName)
|
|
{
|
|
#define cwcCNMAX 64 // 64 chars max for CN
|
|
#define cwcCHOPHASHMAX (1+5) // "-%05hu" decimal USHORT hash digits
|
|
#define cwcSUFFIXMAX (1 + 5 + 1) // five decimal digits plus parentheses
|
|
#define cwcCHOPBASE (cwcCNMAX-(cwcCHOPHASHMAX+cwcSUFFIXMAX))
|
|
|
|
HRESULT hr;
|
|
DWORD cwc;
|
|
DWORD cwcCopy;
|
|
WCHAR wszDSName[cwcCHOPBASE+cwcCHOPHASHMAX+1];
|
|
|
|
// must be cleaned up
|
|
HKEY hConfig=NULL;
|
|
WCHAR * wszSanitizedName=NULL;
|
|
|
|
// initialize out params
|
|
*pwszName=NULL;
|
|
|
|
// Open HKLM\System\CurrentControlSet\Services\CertSvc\Configuration
|
|
hr=OpenCertSrvConfig(&hConfig);
|
|
_JumpIfError(hr, error, "OpenCertSrvConfig");
|
|
|
|
// get value "active" - this is the sanitized name
|
|
hr=GetRegString(hConfig, wszREGACTIVE, &wszSanitizedName);
|
|
_JumpIfErrorStr(hr, error, "GetRegString", wszREGACTIVE);
|
|
|
|
|
|
// ----- begin stolen code -----
|
|
cwc = wcslen(wszSanitizedName);
|
|
cwcCopy = cwc;
|
|
if (cwcCHOPBASE < cwcCopy)
|
|
{
|
|
cwcCopy = cwcCHOPBASE;
|
|
}
|
|
CopyMemory(wszDSName, wszSanitizedName, cwcCopy * sizeof(WCHAR));
|
|
wszDSName[cwcCopy] = L'\0';
|
|
|
|
if (cwcCHOPBASE < cwc)
|
|
{
|
|
// Hash the rest of the name into a USHORT
|
|
USHORT usHash = 0;
|
|
DWORD i;
|
|
WCHAR *pwsz;
|
|
|
|
// Truncate an incomplete sanitized Unicode character
|
|
|
|
pwsz = wcsrchr(wszDSName, L'!');
|
|
if (NULL != pwsz && wcslen(pwsz) < 5)
|
|
{
|
|
cwcCopy -= wcslen(pwsz);
|
|
*pwsz = L'\0';
|
|
}
|
|
|
|
for (i = cwcCopy; i < cwc; i++)
|
|
{
|
|
USHORT usLowBit = (0x8000 & usHash)? 1 : 0;
|
|
|
|
usHash = ((usHash << 1) | usLowBit) + wszSanitizedName[i];
|
|
}
|
|
wsprintf(&wszDSName[cwcCopy], L"-%05hu", usHash);
|
|
//CSASSERT(wcslen(wszDSName) < ARRAYSIZE(wszDSName));
|
|
}
|
|
// ----- end stolen code -----
|
|
|
|
*pwszName=(WCHAR *)LocalAlloc(LPTR, (wcslen(wszDSName)+1)*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, *pwszName);
|
|
wcscpy(*pwszName, wszDSName);
|
|
|
|
hr=S_OK;
|
|
error:
|
|
if (NULL!=wszSanitizedName) {
|
|
LocalFree(wszSanitizedName);
|
|
}
|
|
if (NULL!=hConfig) {
|
|
RegCloseKey(hConfig);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT SetSDOnCEPCertificate(BSTR bstrCertificate, SID * psidAccount)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
DWORD dwKeySpec=0;
|
|
BOOL fFreeProv=FALSE;
|
|
CERT_BLOB blobCert;
|
|
DWORD dwSD=0;
|
|
PACL pAcl=NULL;
|
|
BOOL fDacl = TRUE;
|
|
BOOL fDef = FALSE;
|
|
ACL_SIZE_INFORMATION AclInfo;
|
|
DWORD dwAccess=ACTRL_FILE_READ | ACTRL_FILE_READ_PROP | ACTRL_FILE_READ_ATTRIB;
|
|
ACE_HEADER *pFirstAce=NULL;
|
|
|
|
PSECURITY_DESCRIPTOR pNewSD = NULL;
|
|
PACL pNewAcl = NULL;
|
|
PSECURITY_DESCRIPTOR pSID=NULL;
|
|
HCERTSTORE hCEPStore=NULL;
|
|
PCERT_CONTEXT pCertContext=NULL;
|
|
PCCERT_CONTEXT pCEPCert=NULL;
|
|
HCRYPTPROV hProv=NULL;
|
|
|
|
//open the CEP store
|
|
if(NULL == (hCEPStore=CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
ENCODE_TYPE,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
|
|
CEP_STORE_NAME)))
|
|
{
|
|
hr=E_UNEXPECTED;
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
//get the certificate context
|
|
memset(&blobCert, 0, sizeof(blobCert));
|
|
blobCert.cbData = (DWORD)SysStringByteLen(bstrCertificate);
|
|
blobCert.pbData = (BYTE *)bstrCertificate;
|
|
|
|
if(!CryptQueryObject(
|
|
CERT_QUERY_OBJECT_BLOB,
|
|
&blobCert,
|
|
CERT_QUERY_CONTENT_FLAG_CERT,
|
|
CERT_QUERY_FORMAT_FLAG_ALL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
(const void **)&pCertContext))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CryptQueryObject");
|
|
}
|
|
|
|
if(NULL == pCertContext)
|
|
{
|
|
hr=E_UNEXPECTED;
|
|
_JumpError(hr, error, "CryptQueryObject");
|
|
}
|
|
|
|
//find the certificate in CEP store
|
|
if(NULL == (pCEPCert=CertFindCertificateInStore(
|
|
hCEPStore,
|
|
X509_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_EXISTING,
|
|
pCertContext,
|
|
NULL)))
|
|
{
|
|
hr=E_UNEXPECTED;
|
|
_JumpError(hr, error, "CertFindCertificateInStore");
|
|
}
|
|
|
|
//set the SD on the private key
|
|
if(!CryptAcquireCertificatePrivateKey(pCEPCert,
|
|
CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
|
|
NULL,
|
|
&hProv,
|
|
&dwKeySpec,
|
|
&fFreeProv))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CryptAcquireCertificatePrivateKey");
|
|
}
|
|
|
|
if(!CryptGetProvParam(
|
|
hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
NULL,
|
|
&dwSD,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CryptAcquireCertificatePrivateKey");
|
|
}
|
|
|
|
pSID = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSD);
|
|
if (NULL == pSID)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if(!CryptGetProvParam(
|
|
hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
(BYTE *)pSID,
|
|
&dwSD,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CryptGetProvParam");
|
|
}
|
|
|
|
//get acl from sd
|
|
if(!GetSecurityDescriptorDacl(
|
|
pSID,
|
|
&fDacl,
|
|
&pAcl,
|
|
&fDef))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetSecurityDescriptorDacl");
|
|
}
|
|
|
|
//if no dacl or everyone access, quit
|
|
if((NULL==pAcl) || (FALSE == fDacl))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "GetSecurityDescriptorDacl");
|
|
}
|
|
|
|
//get acl info
|
|
if(!GetAclInformation(
|
|
pAcl,
|
|
&AclInfo,
|
|
sizeof(AclInfo),
|
|
AclSizeInformation))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAclInformation");
|
|
}
|
|
|
|
//allocate enough for new dacl since we might add a new ACE
|
|
dwSD=AclInfo.AclBytesInUse
|
|
+sizeof(ACCESS_ALLOWED_ACE)
|
|
-sizeof(DWORD) //ACCESS_ALLOWED_ACE::SidStart
|
|
+GetLengthSid(psidAccount);
|
|
|
|
pNewAcl = (PACL)LocalAlloc(LPTR, dwSD);
|
|
if(NULL == pNewAcl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if(!InitializeAcl(pNewAcl, dwSD, ACL_REVISION_DS))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "InitializeAcl");
|
|
}
|
|
|
|
// find the first ace in the dacl
|
|
if (!GetAce(pAcl, 0, (void **)&pFirstAce))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAce");
|
|
}
|
|
|
|
// add all the old aces
|
|
if (!AddAce(pNewAcl, ACL_REVISION_DS, 0, pFirstAce, AclInfo.AclBytesInUse-sizeof(ACL)))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "AddAce");
|
|
}
|
|
|
|
//add the access allowed ACE
|
|
if(!AddAccessAllowedAce(pNewAcl, ACL_REVISION, dwAccess, psidAccount))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "AddAccessAllowedAce");
|
|
}
|
|
|
|
// initialize a security descriptor.
|
|
pNewSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
if (pNewSD == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if(!InitializeSecurityDescriptor(pNewSD, SECURITY_DESCRIPTOR_REVISION))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "InitializeSecurityDescriptor");
|
|
}
|
|
|
|
// add the ACL to the security descriptor.
|
|
if(!SetSecurityDescriptorDacl(
|
|
pNewSD,
|
|
TRUE, // fDaclPresent flag
|
|
pNewAcl,
|
|
FALSE)) // not a default DACL
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "SetSecurityDescriptorDacl");
|
|
}
|
|
|
|
//set sd to be protected
|
|
if(!SetSecurityDescriptorControl(
|
|
pNewSD,
|
|
SE_DACL_PROTECTED,
|
|
SE_DACL_PROTECTED))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "SetSecurityDescriptorControl");
|
|
}
|
|
|
|
if(!IsValidSecurityDescriptor(pNewSD))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "IsValidSecurityDescriptor");
|
|
}
|
|
|
|
//we just set it back to the privaet key
|
|
if(!CryptSetProvParam(
|
|
hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
(BYTE*)pNewSD,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CryptSetProvParam");
|
|
}
|
|
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
|
|
if(pNewSD)
|
|
{
|
|
LocalFree(pNewSD);
|
|
}
|
|
|
|
if(pSID)
|
|
{
|
|
LocalFree(pSID);
|
|
}
|
|
|
|
if(pNewAcl)
|
|
{
|
|
LocalFree(pNewAcl);
|
|
}
|
|
|
|
if(fFreeProv)
|
|
{
|
|
if(hProv)
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
|
|
if(pCEPCert)
|
|
{
|
|
CertFreeCertificateContext(pCEPCert);
|
|
}
|
|
|
|
if(pCertContext)
|
|
{
|
|
CertFreeCertificateContext(pCertContext);
|
|
}
|
|
|
|
if(hCEPStore)
|
|
{
|
|
CertCloseStore(hCEPStore, 0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT EnrollForRACert(
|
|
IN const WCHAR * wszDistinguishedName,
|
|
IN const WCHAR * wszCSPName,
|
|
IN DWORD dwCSPType,
|
|
IN DWORD dwKeySize,
|
|
IN DWORD dwKeySpec,
|
|
IN const WCHAR * wszTemplate,
|
|
IN SID *psidAccount
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LONG nDisposition;
|
|
LONG nRequestID;
|
|
|
|
// must be cleaned up
|
|
ICEnroll3 * pXEnroll=NULL;
|
|
BSTR bszConfigString=NULL;
|
|
BSTR bszRequest=NULL;
|
|
ICertConfig * pICertConfig=NULL;
|
|
ICertRequest * pICertRequest=NULL;
|
|
ICertAdmin * pICertAdmin=NULL;
|
|
BSTR bszCertificate=NULL;
|
|
|
|
// get the config string
|
|
hr=CoCreateInstance(
|
|
CLSID_CCertConfig,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICertConfig,
|
|
(void **)&pICertConfig);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_CCertConfig)");
|
|
|
|
hr = pICertConfig->GetConfig(CC_LOCALCONFIG, &bszConfigString);
|
|
_JumpIfError(hr, error, "GetConfig");
|
|
|
|
|
|
// create XEnroll
|
|
hr=CoCreateInstance(
|
|
CLSID_CEnroll,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICEnroll3,
|
|
(void **)&pXEnroll);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_CEnroll)");
|
|
|
|
// build the Offline enrollment agent cert.
|
|
|
|
hr=pXEnroll->put_ProviderName((WCHAR *)wszCSPName);
|
|
_JumpIfError(hr, error, "put_ProviderName");
|
|
hr=pXEnroll->put_ProviderType(dwCSPType);
|
|
_JumpIfError(hr, error, "put_ProviderType");
|
|
hr=pXEnroll->put_ProviderFlags(CRYPT_MACHINE_KEYSET); // used in CryptAcquireContext
|
|
_JumpIfError(hr, error, "put_ProviderFlags");
|
|
hr=pXEnroll->put_GenKeyFlags(dwKeySize<<16);
|
|
_JumpIfError(hr, error, "put_GenKeyFlags");
|
|
hr=pXEnroll->put_KeySpec(dwKeySpec);
|
|
_JumpIfError(hr, error, "put_KeySpec");
|
|
hr=pXEnroll->put_LimitExchangeKeyToEncipherment(AT_KEYEXCHANGE==dwKeySpec);
|
|
_JumpIfError(hr, error, "put_LimitExchangeKeyToEncipherment");
|
|
hr=pXEnroll->put_UseExistingKeySet(FALSE);
|
|
_JumpIfError(hr, error, "put_UseExistingKeySet");
|
|
hr=pXEnroll->put_RequestStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE); // the keys attached to the dummy request cert go in the local machine store
|
|
_JumpIfError(hr, error, "put_RequestStoreFlags");
|
|
hr=pXEnroll->addCertTypeToRequest((WCHAR *)wszTemplate);
|
|
_JumpIfErrorStr(hr, error, "addCertTypeToRequest", wszTemplate);
|
|
|
|
hr=pXEnroll->createPKCS10((WCHAR *)wszDistinguishedName, (WCHAR *)gc_wszEnrollmentAgentOid, &bszRequest);
|
|
_JumpIfError(hr, error, "CreatePKCS10");
|
|
|
|
// create ICertRequest
|
|
hr=CoCreateInstance(
|
|
CLSID_CCertRequest,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICertRequest,
|
|
(void **)&pICertRequest);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_CCertRequest)");
|
|
|
|
// request the cert
|
|
hr=pICertRequest->Submit(CR_IN_BASE64, bszRequest, NULL, bszConfigString, &nDisposition);
|
|
_JumpIfError(hr, error, "Submit");
|
|
|
|
// did we get it?
|
|
if (CR_DISP_UNDER_SUBMISSION==nDisposition) {
|
|
// we need to approve it. No problem!
|
|
hr=pICertRequest->GetRequestId(&nRequestID);
|
|
_JumpIfError(hr, error, "GetRequestId");
|
|
|
|
// create ICertAdmin
|
|
hr=CoCreateInstance(
|
|
CLSID_CCertAdmin,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICertAdmin,
|
|
(void **)&pICertAdmin);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_CCertAdmin)");
|
|
|
|
// resubmit it
|
|
hr=pICertAdmin->ResubmitRequest(bszConfigString, nRequestID, &nDisposition);
|
|
_JumpIfError(hr, error, "ResubmitRequest");
|
|
// This should have worked, but we're going to ignore the
|
|
// returned disposition and use the one from the next call.
|
|
|
|
// now, get the cert that we just approved
|
|
hr=pICertRequest->RetrievePending(nRequestID, bszConfigString, &nDisposition);
|
|
_JumpIfError(hr, error, "RetrievePending");
|
|
}
|
|
|
|
// We should have it by now.
|
|
_Verify(CR_DISP_ISSUED==nDisposition, hr, error);
|
|
|
|
// grab the cert from the CA
|
|
hr=pICertRequest->GetCertificate(CR_OUT_BASE64, &bszCertificate);
|
|
_JumpIfError(hr, error, "GetCertificate");
|
|
|
|
|
|
// install the cert
|
|
|
|
|
|
hr=pXEnroll->put_MyStoreName((WCHAR *)gc_wszCepStoreName); // We have to use our special store
|
|
_JumpIfError(hr, error, "put_MyStoreName");
|
|
hr=pXEnroll->put_MyStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE); // the keys attached to the final cert also go in the local machine store
|
|
_JumpIfError(hr, error, "put_MyStoreFlags");
|
|
hr=pXEnroll->put_RootStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE);
|
|
_JumpIfError(hr, error, "put_RootStoreFlags");
|
|
hr=pXEnroll->put_CAStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE);
|
|
_JumpIfError(hr, error, "put_CAStoreFlags");
|
|
hr=pXEnroll->put_SPCFileName(L"");
|
|
_JumpIfError(hr, error, "put_MyStoreName");
|
|
|
|
hr=pXEnroll->acceptPKCS7(bszCertificate);
|
|
_JumpIfError(hr, error, "acceptPKCS7");
|
|
|
|
//set the SD on the private key of the enrolled certificate
|
|
if(psidAccount)
|
|
{
|
|
hr=SetSDOnCEPCertificate(bszCertificate, psidAccount);
|
|
_JumpIfError(hr, error, "acceptPKCS7");
|
|
}
|
|
|
|
// all done
|
|
hr=S_OK;
|
|
error:
|
|
if(NULL!=pICertConfig){
|
|
pICertConfig->Release();
|
|
}
|
|
|
|
if (NULL!=bszCertificate) {
|
|
SysFreeString(bszCertificate);
|
|
}
|
|
if (NULL!=pICertAdmin) {
|
|
pICertAdmin->Release();
|
|
}
|
|
if (NULL!=pICertRequest) {
|
|
pICertRequest->Release();
|
|
}
|
|
if (NULL!=bszRequest) {
|
|
SysFreeString(bszRequest);
|
|
}
|
|
if (NULL!=bszConfigString) {
|
|
SysFreeString(bszConfigString);
|
|
}
|
|
if (NULL!=pXEnroll) {
|
|
pXEnroll->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// DEBUG, not used
|
|
static BOOL DumpTokenGroups(void)
|
|
{
|
|
#define MAX_NAME 256
|
|
DWORD i, dwSize = 0, dwResult = 0;
|
|
HANDLE hToken;
|
|
PTOKEN_GROUPS pGroupInfo;
|
|
SID_NAME_USE SidType;
|
|
char lpName[MAX_NAME];
|
|
char lpDomain[MAX_NAME];
|
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
|
|
// Open a handle to the access token for the calling process.
|
|
|
|
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken )) {
|
|
wprintf( L"OpenProcessToken Error %u\n", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
// Call GetTokenInformation to get the buffer size.
|
|
|
|
if(!GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize)) {
|
|
dwResult = GetLastError();
|
|
if( dwResult != ERROR_INSUFFICIENT_BUFFER ) {
|
|
wprintf( L"GetTokenInformation Error %u\n", dwResult );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Allocate the buffer.
|
|
|
|
pGroupInfo = (PTOKEN_GROUPS) GlobalAlloc( GPTR, dwSize );
|
|
|
|
// Call GetTokenInformation again to get the group information.
|
|
|
|
if(! GetTokenInformation(hToken, TokenGroups, pGroupInfo,
|
|
dwSize, &dwSize ) ) {
|
|
wprintf( L"GetTokenInformation Error %u\n", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Loop through the group SIDs looking for the administrator SID.
|
|
for(i=0; i<pGroupInfo->GroupCount; i++) {
|
|
|
|
// Lookup the account name and print it.
|
|
|
|
dwSize = MAX_NAME;
|
|
if( !LookupAccountSidA( NULL, pGroupInfo->Groups[i].Sid,
|
|
lpName, &dwSize, lpDomain,
|
|
&dwSize, &SidType ) ) {
|
|
dwResult = GetLastError();
|
|
if( dwResult == ERROR_NONE_MAPPED )
|
|
strcpy( lpName, "NONE_MAPPED" );
|
|
else {
|
|
wprintf(L"LookupAccountSid Error %u\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
char * szSid=NULL;
|
|
if (!ConvertSidToStringSidA(pGroupInfo->Groups[i].Sid, &szSid)) {
|
|
wprintf(L"ConvertSidToStringSid Error %u\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
// Find out if the SID is enabled in the token
|
|
char * szEnable;
|
|
if (pGroupInfo->Groups[i].Attributes & SE_GROUP_ENABLED) {
|
|
szEnable="enabled";
|
|
} else if (pGroupInfo->Groups[i].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) {
|
|
szEnable="deny-only";
|
|
} else {
|
|
szEnable="not enabled";
|
|
}
|
|
|
|
wprintf( L"Member of %hs\\%hs (%hs) (%hs)\n",
|
|
lpDomain, lpName, szSid, szEnable );
|
|
|
|
LocalFree(szSid);
|
|
}
|
|
|
|
if ( pGroupInfo )
|
|
GlobalFree( pGroupInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// DEBUG, not used
|
|
static void DumpAcl(PACL pAcl, ACL_SIZE_INFORMATION aclsizeinfo)
|
|
{
|
|
HRESULT hr;
|
|
unsigned int nIndex;
|
|
DWORD dwError;
|
|
|
|
wprintf(L"/-- begin ACL ---\n");
|
|
for (nIndex=0; nIndex<aclsizeinfo.AceCount; nIndex++) {
|
|
ACE_HEADER * pAceHeader;
|
|
PSID pSid=NULL;
|
|
wprintf(L"| ");
|
|
if (!GetAce(pAcl, nIndex, (void**)&pAceHeader)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
wprintf(L" (GetAce failed:0x%08X)\n", hr);
|
|
continue;
|
|
}
|
|
wprintf(L"[");
|
|
if (ACCESS_ALLOWED_ACE_TYPE==pAceHeader->AceType) {
|
|
wprintf(L"aA_");
|
|
pSid=&((ACCESS_ALLOWED_ACE *)pAceHeader)->SidStart;
|
|
} else if (ACCESS_DENIED_ACE_TYPE==pAceHeader->AceType) {
|
|
wprintf(L"aD_");
|
|
pSid=&((ACCESS_DENIED_ACE *)pAceHeader)->SidStart;
|
|
} else if (ACCESS_ALLOWED_OBJECT_ACE_TYPE==pAceHeader->AceType) {
|
|
wprintf(L"aAo");
|
|
pSid=&((ACCESS_ALLOWED_OBJECT_ACE *)pAceHeader)->SidStart;
|
|
if (((ACCESS_ALLOWED_OBJECT_ACE *)pAceHeader)->Flags!=(ACE_OBJECT_TYPE_PRESENT|ACE_INHERITED_OBJECT_TYPE_PRESENT)) {
|
|
pSid=((BYTE *)pSid)-sizeof(GUID);
|
|
}
|
|
} else if (ACCESS_DENIED_OBJECT_ACE_TYPE==pAceHeader->AceType) {
|
|
wprintf(L"aDo");
|
|
pSid=&((ACCESS_DENIED_OBJECT_ACE *)pAceHeader)->SidStart;
|
|
if (((ACCESS_DENIED_OBJECT_ACE *)pAceHeader)->Flags!=(ACE_OBJECT_TYPE_PRESENT|ACE_INHERITED_OBJECT_TYPE_PRESENT)) {
|
|
pSid=((BYTE *)pSid)-sizeof(GUID);
|
|
}
|
|
} else {
|
|
wprintf(L"sa?");
|
|
}
|
|
|
|
wprintf(L"] ");
|
|
if (NULL!=pSid) {
|
|
// print the sid
|
|
{
|
|
WCHAR wszName[MAX_NAME];
|
|
WCHAR wszDomain[MAX_NAME];
|
|
DWORD dwSize=MAX_NAME;
|
|
SID_NAME_USE SidType;
|
|
if(!LookupAccountSidW(
|
|
NULL, pSid,
|
|
wszName, &dwSize, wszDomain,
|
|
&dwSize, &SidType))
|
|
{
|
|
dwError=GetLastError();
|
|
if (dwError==ERROR_NONE_MAPPED) {
|
|
wprintf(L"(Unknown)");
|
|
} else {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
wprintf(L"(Error 0x%08X)", hr);
|
|
}
|
|
} else {
|
|
wprintf(L"%ws\\%ws", wszDomain, wszName);
|
|
}
|
|
}
|
|
{
|
|
WCHAR * wszSid=NULL;
|
|
if (!ConvertSidToStringSidW(pSid, &wszSid)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
wprintf(L"(Error 0x%08X)", hr);
|
|
} else {
|
|
wprintf(L" %ws", wszSid);
|
|
LocalFree(wszSid);
|
|
}
|
|
}
|
|
}
|
|
wprintf(L"\n");
|
|
|
|
}
|
|
wprintf(L"\\-- end ACL ---\n");
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT GetRootDomEntitySid(SID ** ppSid, DWORD dwEntityRid)
|
|
{
|
|
HRESULT hr;
|
|
NET_API_STATUS nasError;
|
|
unsigned int nSubAuthorities;
|
|
unsigned int nSubAuthIndex;
|
|
|
|
// must be cleaned up
|
|
SID * psidRootDomEntity=NULL;
|
|
USER_MODALS_INFO_2 * pumi2=NULL;
|
|
DOMAIN_CONTROLLER_INFOW * pdci=NULL;
|
|
DOMAIN_CONTROLLER_INFOW * pdciForest=NULL;
|
|
|
|
// initialize out params
|
|
*ppSid=NULL;
|
|
|
|
|
|
// get the forest name
|
|
nasError=DsGetDcNameW(NULL, NULL, NULL, NULL, 0, &pdciForest);
|
|
if (NERR_Success!=nasError) {
|
|
hr=HRESULT_FROM_WIN32(nasError);
|
|
_JumpError(hr, error, "DsGetDcNameW");
|
|
}
|
|
|
|
// get the top level DC name
|
|
nasError=DsGetDcNameW(NULL, pdciForest->DnsForestName, NULL, NULL, 0, &pdci);
|
|
if (NERR_Success!=nasError) {
|
|
hr=HRESULT_FROM_WIN32(nasError);
|
|
_JumpError(hr, error, "DsGetDcNameW");
|
|
}
|
|
|
|
// get the domain Sid on the top level DC.
|
|
nasError=NetUserModalsGet(pdci->DomainControllerName, 2, (LPBYTE *)&pumi2);
|
|
if(NERR_Success!=nasError) {
|
|
hr=HRESULT_FROM_WIN32(nasError);
|
|
_JumpError(hr, error, "NetUserModalsGet");
|
|
}
|
|
|
|
nSubAuthorities=*GetSidSubAuthorityCount(pumi2->usrmod2_domain_id);
|
|
|
|
// allocate storage for new Sid. account domain Sid + account Rid
|
|
psidRootDomEntity=(SID *)LocalAlloc(LPTR, GetSidLengthRequired((UCHAR)(nSubAuthorities+1)));
|
|
_JumpIfOutOfMemory(hr, error, psidRootDomEntity);
|
|
|
|
// copy the first few peices into the SID
|
|
if (!InitializeSid(psidRootDomEntity,
|
|
GetSidIdentifierAuthority(pumi2->usrmod2_domain_id),
|
|
(BYTE)(nSubAuthorities+1)))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "InitializeSid");
|
|
}
|
|
|
|
// copy existing subauthorities from account domain Sid into new Sid
|
|
for (nSubAuthIndex=0; nSubAuthIndex < nSubAuthorities ; nSubAuthIndex++) {
|
|
*GetSidSubAuthority(psidRootDomEntity, nSubAuthIndex)=
|
|
*GetSidSubAuthority(pumi2->usrmod2_domain_id, nSubAuthIndex);
|
|
}
|
|
|
|
// append Rid to new Sid
|
|
*GetSidSubAuthority(psidRootDomEntity, nSubAuthorities)=dwEntityRid;
|
|
|
|
*ppSid=psidRootDomEntity;
|
|
psidRootDomEntity=NULL;
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=psidRootDomEntity) {
|
|
LocalFree(psidRootDomEntity);
|
|
}
|
|
if (NULL!=pdci) {
|
|
NetApiBufferFree(pdci);
|
|
}
|
|
if (NULL!=pdci) {
|
|
NetApiBufferFree(pdciForest);
|
|
}
|
|
if (NULL!=pumi2) {
|
|
NetApiBufferFree(pumi2);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT GetEntAdminSid(SID ** ppSid)
|
|
{
|
|
return GetRootDomEntitySid(ppSid, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT GetRootDomAdminSid(SID ** ppSid)
|
|
{
|
|
return GetRootDomEntitySid(ppSid, DOMAIN_GROUP_RID_ADMINS);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT GetThisComputerSid(SID ** ppSid)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cchSize;
|
|
DWORD dwSidSize;
|
|
DWORD dwDomainSize;
|
|
SID_NAME_USE snu;
|
|
|
|
// must be cleaned up
|
|
SID * psidThisComputer=NULL;
|
|
WCHAR * wszName=NULL;
|
|
WCHAR * wszDomain=NULL;
|
|
|
|
// initialize out params
|
|
*ppSid=NULL;
|
|
|
|
// get the size of the computer's name
|
|
cchSize=0;
|
|
_Verify(!GetComputerObjectNameW(NameSamCompatible, NULL, &cchSize), hr, error);
|
|
if (ERROR_INSUFFICIENT_BUFFER!=GetLastError()) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetComputerObjectNameW");
|
|
}
|
|
|
|
// bug in GetComputerObjectNameW
|
|
cchSize++;
|
|
|
|
// allocate memory
|
|
wszName=(WCHAR *)LocalAlloc(LPTR, cchSize*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszName);
|
|
|
|
// get the computer's name
|
|
if (!GetComputerObjectNameW(NameSamCompatible, wszName, &cchSize)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetComputerObjectNameW");
|
|
}
|
|
|
|
// get the size of the sid
|
|
dwSidSize=0;
|
|
dwDomainSize=0;
|
|
_Verify(!LookupAccountNameW(NULL, wszName, NULL, &dwSidSize, NULL, &dwDomainSize, &snu), hr, error);
|
|
if (ERROR_INSUFFICIENT_BUFFER!=GetLastError()) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "LookupAccountNameW");
|
|
}
|
|
|
|
// allocate memory
|
|
wszDomain=(WCHAR *)LocalAlloc(LPTR, dwDomainSize*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszDomain);
|
|
psidThisComputer=(SID *)LocalAlloc(LPTR, dwSidSize);
|
|
_JumpIfOutOfMemory(hr, error, psidThisComputer);
|
|
|
|
// get the sid
|
|
if (!LookupAccountNameW(NULL, wszName, psidThisComputer, &dwSidSize, wszDomain, &dwDomainSize, &snu)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "LookupAccountNameW");
|
|
}
|
|
|
|
// success!
|
|
*ppSid=psidThisComputer;
|
|
psidThisComputer=NULL;
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=psidThisComputer) {
|
|
LocalFree(psidThisComputer);
|
|
}
|
|
if (NULL!=wszName) {
|
|
LocalFree(wszName);
|
|
}
|
|
if (NULL!=wszDomain) {
|
|
LocalFree(wszDomain);
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
static HRESULT ConfirmAccess(PSECURITY_DESCRIPTOR * ppSD, SID * pTrustworthySid, BOOL * pbSDChanged)
|
|
{
|
|
//define ENROLL_ACCESS_MASK (0x130)
|
|
HRESULT hr;
|
|
PACL pAcl;
|
|
BOOL bAclPresent;
|
|
BOOL bDefaultAcl;
|
|
unsigned int nIndex;
|
|
ACL_SIZE_INFORMATION aclsizeinfo;
|
|
bool bSidInAcl;
|
|
|
|
// must be cleaned up
|
|
PSECURITY_DESCRIPTOR pAbsSD=NULL;
|
|
ACL * pAbsDacl=NULL;
|
|
ACL * pAbsSacl=NULL;
|
|
SID * pAbsOwner=NULL;
|
|
SID * pAbsPriGrp=NULL;
|
|
ACL * pNewDacl=NULL;
|
|
PSECURITY_DESCRIPTOR pNewSD=NULL;
|
|
|
|
// initialize out params
|
|
*pbSDChanged=FALSE;
|
|
|
|
// get the (D)ACL from the security descriptor
|
|
if (!GetSecurityDescriptorDacl(*ppSD, &bAclPresent, &pAcl, &bDefaultAcl)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetSecurityDescriptorDacl");
|
|
}
|
|
_Verify(bAclPresent, hr, error);
|
|
if (NULL==pAcl) {
|
|
hr=E_FAIL;
|
|
_JumpError(hr, error, "GetSecurityDescriptorDacl");
|
|
}
|
|
|
|
// find out how many ACEs
|
|
if (!GetAclInformation(pAcl, &aclsizeinfo, sizeof(aclsizeinfo), AclSizeInformation)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAclInformation");
|
|
}
|
|
|
|
//DumpAcl(pAcl,aclsizeinfo);
|
|
|
|
// find our sid in the acl
|
|
bSidInAcl=false;
|
|
for (nIndex=0; nIndex<aclsizeinfo.AceCount; nIndex++) {
|
|
ACE_HEADER * pAceHeader;
|
|
ACCESS_ALLOWED_OBJECT_ACE * pAccessAce;
|
|
PSID pSid=NULL;
|
|
if (!GetAce(pAcl, nIndex, (void**)&pAceHeader)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAce");
|
|
}
|
|
|
|
// find the sid for this ACE
|
|
if (ACCESS_ALLOWED_OBJECT_ACE_TYPE!=pAceHeader->AceType && ACCESS_DENIED_OBJECT_ACE_TYPE!=pAceHeader->AceType) {
|
|
// we are only interested in OBJECT ace types
|
|
continue;
|
|
}
|
|
|
|
// note that ACCESS_ALLOWED_OBJECT_ACE and ACCESS_DENIED_OBJECT_ACE are the same structurally.
|
|
pAccessAce=(ACCESS_ALLOWED_OBJECT_ACE *)pAceHeader;
|
|
_Verify(ACE_OBJECT_TYPE_PRESENT==pAccessAce->Flags, hr, error);
|
|
pSid=((BYTE *)&pAccessAce->SidStart)-sizeof(GUID);
|
|
|
|
// confirm the GUID
|
|
if (!IsEqualGUID(pAccessAce->ObjectType, GUID_ENROLL)) {
|
|
continue;
|
|
}
|
|
|
|
// make sure this is the sid we are looking for
|
|
if (!EqualSid(pSid, pTrustworthySid)) {
|
|
continue;
|
|
}
|
|
|
|
// Was this a denial?
|
|
if (ACCESS_DENIED_OBJECT_ACE_TYPE==pAceHeader->AceType) {
|
|
// It's not anymore!
|
|
pAceHeader->AceType=ACCESS_ALLOWED_OBJECT_ACE_TYPE;
|
|
*pbSDChanged=TRUE;
|
|
}
|
|
|
|
// is the mask wrong?
|
|
if (0==(pAccessAce->Mask&ACTRL_DS_CONTROL_ACCESS)) {
|
|
// It's not anymore!
|
|
pAccessAce->Mask|=ACTRL_DS_CONTROL_ACCESS;
|
|
*pbSDChanged=TRUE;
|
|
}
|
|
|
|
// The sid is now in the acl and set to allow access.
|
|
bSidInAcl=true;
|
|
break;
|
|
}
|
|
|
|
// Was the sid in the acl?
|
|
if (false==bSidInAcl) {
|
|
SECURITY_DESCRIPTOR_CONTROL sdcon;
|
|
DWORD dwRevision;
|
|
DWORD dwNewAclSize;
|
|
DWORD dwAbsSDSize=0;
|
|
DWORD dwDaclSize=0;
|
|
DWORD dwSaclSize=0;
|
|
DWORD dwOwnerSize=0;
|
|
DWORD dwPriGrpSize=0;
|
|
ACE_HEADER * pFirstAce;
|
|
DWORD dwRelSDSize=0;
|
|
|
|
// we have to be self-relative
|
|
if (!GetSecurityDescriptorControl(*ppSD, &sdcon, &dwRevision)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetSecurityDescriptorControl");
|
|
}
|
|
_Verify(sdcon&SE_SELF_RELATIVE, hr, error);
|
|
|
|
// get the sizes
|
|
_Verify(!MakeAbsoluteSD(*ppSD, NULL, &dwAbsSDSize, NULL, &dwDaclSize, NULL, &dwSaclSize, NULL, &dwOwnerSize, NULL, &dwPriGrpSize), hr, error);
|
|
if (ERROR_INSUFFICIENT_BUFFER!=GetLastError()) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "MakeAbsoluteSD");
|
|
}
|
|
|
|
// allocate memory
|
|
pAbsSD=(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwAbsSDSize);
|
|
_JumpIfOutOfMemory(hr, error, pAbsSD);
|
|
pAbsDacl=(ACL * )LocalAlloc(LPTR, dwDaclSize);
|
|
_JumpIfOutOfMemory(hr, error, pAbsDacl);
|
|
pAbsSacl=(ACL * )LocalAlloc(LPTR, dwSaclSize);
|
|
_JumpIfOutOfMemory(hr, error, pAbsSacl);
|
|
pAbsOwner=(SID *)LocalAlloc(LPTR, dwOwnerSize);
|
|
_JumpIfOutOfMemory(hr, error, pAbsOwner);
|
|
pAbsPriGrp=(SID *)LocalAlloc(LPTR, dwPriGrpSize);
|
|
_JumpIfOutOfMemory(hr, error, pAbsPriGrp);
|
|
|
|
// copy the SD to the memory buffers
|
|
if (!MakeAbsoluteSD(*ppSD, pAbsSD, &dwAbsSDSize, pAbsDacl, &dwDaclSize, pAbsSacl, &dwSaclSize, pAbsOwner, &dwOwnerSize, pAbsPriGrp, &dwPriGrpSize)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "MakeAbsoluteSD");
|
|
}
|
|
|
|
// get the current size info for the dacl
|
|
if (!GetAclInformation(pAbsDacl, &aclsizeinfo, sizeof(aclsizeinfo), AclSizeInformation)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAclInformation");
|
|
}
|
|
|
|
// figure out the new size
|
|
dwNewAclSize=aclsizeinfo.AclBytesInUse+sizeof(_ACCESS_ALLOWED_OBJECT_ACE)
|
|
-sizeof(GUID) //ACCESS_ALLOWED_OBJECT_ACE::InheritedObjectType
|
|
-sizeof(DWORD) //ACCESS_ALLOWED_OBJECT_ACE::SidStart
|
|
+GetLengthSid(pTrustworthySid);
|
|
|
|
// allocate memory
|
|
pNewDacl=(ACL *)LocalAlloc(LPTR, dwNewAclSize);
|
|
_JumpIfOutOfMemory(hr, error, pNewDacl);
|
|
|
|
// init the header
|
|
if (!InitializeAcl(pNewDacl, dwNewAclSize, ACL_REVISION_DS)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "InitializeAcl");
|
|
}
|
|
|
|
// find the first ace in the dacl
|
|
if (!GetAce(pAbsDacl, 0, (void **)&pFirstAce)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetAce");
|
|
}
|
|
|
|
// add all the old aces
|
|
if (!AddAce(pNewDacl, ACL_REVISION_DS, 0, pFirstAce, aclsizeinfo.AclBytesInUse-sizeof(ACL))) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "AddAce");
|
|
}
|
|
|
|
// finally, add the new acl
|
|
if (!AddAccessAllowedObjectAce(pNewDacl, ACL_REVISION_DS, OBJECT_INHERIT_ACE, ACTRL_DS_CONTROL_ACCESS, (GUID *)&GUID_ENROLL, NULL, pTrustworthySid)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "AddAccessAllowedObjectAce");
|
|
}
|
|
|
|
// stick the new dacl in the sd
|
|
if (!SetSecurityDescriptorDacl(pAbsSD, TRUE, pNewDacl, FALSE)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "SetSecurityDescriptorDacl");
|
|
}
|
|
|
|
// compact everything back together
|
|
// get the size
|
|
_Verify(!MakeSelfRelativeSD(pAbsSD, NULL, &dwRelSDSize), hr, error);
|
|
if (ERROR_INSUFFICIENT_BUFFER!=GetLastError()) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "MakeSelfRelativeSD");
|
|
}
|
|
|
|
// allocate memory
|
|
pNewSD=(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwRelSDSize);
|
|
_JumpIfOutOfMemory(hr, error, pNewSD);
|
|
|
|
// copy the SD to the new memory buffer
|
|
if (!MakeSelfRelativeSD(pAbsSD, pNewSD, &dwRelSDSize)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "MakeSelfRelativeSD");
|
|
}
|
|
|
|
// Whew! We made it!
|
|
LocalFree(*ppSD);
|
|
*ppSD=pNewSD;
|
|
pNewSD=NULL;
|
|
*pbSDChanged=TRUE;
|
|
|
|
} // <- end if sid not in acl
|
|
|
|
_Verify(IsValidSecurityDescriptor(*ppSD), hr, error);
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=pNewSD) {
|
|
LocalFree(pNewSD);
|
|
}
|
|
if (NULL!=pNewDacl) {
|
|
LocalFree(pNewDacl);
|
|
}
|
|
if (NULL!=pAbsSD) {
|
|
LocalFree(pAbsSD);
|
|
}
|
|
if (NULL!=pAbsDacl) {
|
|
LocalFree(pAbsDacl);
|
|
}
|
|
if (NULL!=pAbsSacl) {
|
|
LocalFree(pAbsSacl);
|
|
}
|
|
if (NULL!=pAbsOwner) {
|
|
LocalFree(pAbsOwner);
|
|
}
|
|
if (NULL!=pAbsPriGrp) {
|
|
LocalFree(pAbsPriGrp);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//####################################################################
|
|
// public functions
|
|
|
|
//--------------------------------------------------------------------
|
|
BOOL IsNT5(void)
|
|
{
|
|
HRESULT hr;
|
|
OSVERSIONINFO ovi;
|
|
static BOOL s_fDone=FALSE;
|
|
static BOOL s_fNT5=FALSE;
|
|
|
|
if (!s_fDone) {
|
|
|
|
s_fDone=TRUE;
|
|
|
|
// get and confirm platform info
|
|
ovi.dwOSVersionInfoSize = sizeof(ovi);
|
|
if (!GetVersionEx(&ovi)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetVersionEx");
|
|
}
|
|
if (VER_PLATFORM_WIN32_NT!=ovi.dwPlatformId) {
|
|
hr=ERROR_CANCELLED;
|
|
_JumpError(hr, error, "Not a supported OS");
|
|
}
|
|
if ((5 <= ovi.dwMajorVersion) && (1 <= ovi.dwMinorVersion)){
|
|
s_fNT5=TRUE;
|
|
}
|
|
}
|
|
|
|
error:
|
|
return s_fNT5;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
BOOL IsIISInstalled(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// must be cleaned up
|
|
IMSAdminBase * pIMeta=NULL;
|
|
|
|
hr=CoCreateInstance(
|
|
CLSID_MSAdminBase,
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_IMSAdminBase,
|
|
(VOID **) &pIMeta);
|
|
if (FAILED(hr)) {
|
|
_IgnoreError(hr, "CoCreateInstance(CLSID_MSAdminBase)");
|
|
}
|
|
|
|
//error:
|
|
if (NULL!=pIMeta) {
|
|
pIMeta->Release();
|
|
}
|
|
|
|
return (S_OK==hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT CEPUpdateApplicationPool(BOOL fDC, const WCHAR * pwszApplicationPool, BOOL fLocalSystem, const WCHAR * pwszUserName, const WCHAR * pwszPassword)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
METADATA_RECORD mr;
|
|
DWORD dwLogonMethod=MD_LOGON_INTERACTIVE;
|
|
DWORD dwAppPoolID=MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER;
|
|
DWORD dwTimeout=0;
|
|
|
|
// must be cleaned up
|
|
WCHAR * wszFullAppPath=NULL; // "/lm/w3svc/apppools/SCEP"
|
|
IMSAdminBase * pIMeta=NULL;
|
|
METADATA_HANDLE hMetaRoot=NULL;
|
|
METADATA_HANDLE hMetaKey=NULL;
|
|
|
|
|
|
//check input parameter
|
|
if(NULL==pwszApplicationPool)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
_JumpIfError(hr, error, "paramCheck");
|
|
}
|
|
|
|
//change the logon type to network logon on the DC so that the domain account does not have
|
|
//to have the local logon provilege to the DC; NETWORK logon does not have the correct token
|
|
//to validate user on the network; since we are running the DC, we should be validate locally
|
|
//and everything should just work.
|
|
if(fDC)
|
|
dwLogonMethod=MD_LOGON_NETWORK;
|
|
|
|
if(fLocalSystem)
|
|
{
|
|
dwAppPoolID=MD_APPPOOL_IDENTITY_TYPE_LOCALSYSTEM;
|
|
}
|
|
else
|
|
{
|
|
dwAppPoolID=MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER;
|
|
|
|
if((NULL==pwszUserName) || (NULL==pwszPassword))
|
|
{
|
|
hr=E_INVALIDARG;
|
|
_JumpIfError(hr, error, "paramCheck");
|
|
}
|
|
|
|
}
|
|
|
|
wszFullAppPath=(WCHAR *)LocalAlloc(LPTR, (wcslen(gc_wszAppPoolBase)+1+wcslen(pwszApplicationPool)+1)*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszFullAppPath);
|
|
wcscpy(wszFullAppPath, gc_wszAppPoolBase);
|
|
wcscat(wszFullAppPath, L"/");
|
|
wcscat(wszFullAppPath, pwszApplicationPool);
|
|
|
|
// Create an instance of the metabase object
|
|
hr=CoCreateInstance(
|
|
CLSID_MSAdminBase,
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_IMSAdminBase,
|
|
(void **) &pIMeta);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_MSAdminBase)");
|
|
|
|
// open the top level
|
|
hr=vrOpenRoot(pIMeta, FALSE, gc_wszAppPoolBase, &hMetaRoot);
|
|
_JumpIfError(hr, error, "vrOpenRoot");
|
|
|
|
// Add new VDir called "SCEP"
|
|
hr=pIMeta->AddKey(hMetaRoot, pwszApplicationPool);
|
|
|
|
if(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)==hr)
|
|
{
|
|
// That's fine.
|
|
_IgnoreError(hr, "AddKey");
|
|
}
|
|
else
|
|
{
|
|
_JumpIfErrorStr(hr, error, "AddKey", pwszApplicationPool);
|
|
}
|
|
|
|
// close the root key
|
|
hr=pIMeta->CloseKey(hMetaRoot);
|
|
hMetaRoot=NULL;
|
|
_JumpIfError(hr, error, "CloseKey");
|
|
|
|
|
|
// Open the new VDir at /lm/w3svc/apppools/SCEP
|
|
hr=pIMeta->OpenKey(
|
|
METADATA_MASTER_ROOT_HANDLE,
|
|
wszFullAppPath,
|
|
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
|
|
1000,
|
|
&hMetaKey);
|
|
|
|
_JumpIfErrorStr(hr, error, "OpenKey", wszFullAppPath);
|
|
|
|
//set properties on this application
|
|
|
|
if(FALSE == fLocalSystem)
|
|
{
|
|
//set the UserName
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_WAM_USER_NAME;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(pwszUserName)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(pwszUserName);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
//set the password
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_WAM_PWD;
|
|
mr.dwMDAttributes=METADATA_INHERIT | METADATA_SECURE;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(pwszPassword)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(pwszPassword);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
//set the logon method
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_LOGON_METHOD;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwLogonMethod);
|
|
mr.pbMDData=(BYTE *)(&dwLogonMethod);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
}
|
|
|
|
//set the application identity
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_APPPOOL_IDENTITY_TYPE;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_SERVER;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwAppPoolID);
|
|
mr.pbMDData=(BYTE *)(&dwAppPoolID);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
#ifdef MD_APPPOOL_FRIENDLY_NAME
|
|
//set the application pool friendly name
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_APPPOOL_FRIENDLY_NAME;
|
|
mr.dwMDAttributes=METADATA_NO_ATTRIBUTES;
|
|
mr.dwMDUserType=IIS_MD_UT_SERVER;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(pwszApplicationPool)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(pwszApplicationPool);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
#endif
|
|
|
|
//set the PeriodicRestartTime to 0
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_APPPOOL_PERIODIC_RESTART_TIME;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_SERVER;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwTimeout);
|
|
mr.pbMDData=(BYTE *)(&dwTimeout);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
//set the IDleTimeOut to 0
|
|
memset(&mr, 0, sizeof(METADATA_RECORD));
|
|
mr.dwMDIdentifier=MD_APPPOOL_IDLE_TIMEOUT;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_SERVER;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwTimeout);
|
|
mr.pbMDData=(BYTE *)(&dwTimeout);
|
|
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// done with this key.
|
|
hr=pIMeta->CloseKey(hMetaKey);
|
|
hMetaKey=NULL;
|
|
_JumpIfError(hr, error, "CloseKey");
|
|
|
|
// Flush out the changes and close
|
|
pIMeta->SaveData();
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
|
|
if (NULL!=wszFullAppPath)
|
|
{
|
|
LocalFree(wszFullAppPath);
|
|
}
|
|
|
|
if (NULL!=hMetaKey)
|
|
{
|
|
pIMeta->CloseKey(hMetaKey);
|
|
}
|
|
|
|
if (NULL!=hMetaRoot)
|
|
{
|
|
pIMeta->CloseKey(hMetaRoot);
|
|
}
|
|
|
|
if (NULL!=pIMeta)
|
|
{
|
|
pIMeta->Release();
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT AddVDir(IN BOOL fDC,
|
|
IN const WCHAR * wszDirectory,
|
|
IN const WCHAR * wszApplicationPool,
|
|
IN BOOL fLocalSystem,
|
|
IN const WCHAR * wszUserName,
|
|
IN const WCHAR * wszPassword)
|
|
{
|
|
|
|
HRESULT hr;
|
|
METADATA_RECORD mr;
|
|
DWORD dwAccessPerms;
|
|
DWORD dwAuthenticationType;
|
|
const WCHAR * wszKeyType=IIS_CLASS_WEB_VDIR_W;
|
|
WCHAR wszSysDirBuf[MAX_PATH + 2];
|
|
bool fISAPIEnabled=false;
|
|
BOOL fEnabled=FALSE;
|
|
|
|
// must be cleaned up
|
|
IMSAdminBase * pIMeta=NULL;
|
|
IWamAdmin * pIWam=NULL;
|
|
IIISApplicationAdmin *pIIISAppAdmin=NULL;
|
|
METADATA_HANDLE hMetaRoot=NULL;
|
|
METADATA_HANDLE hMetaKey=NULL;
|
|
WCHAR * wszPhysicalPath=NULL; // "c:\winnt\system32\certsrv\mscep"
|
|
WCHAR * wszRelativeVirtualPath=NULL; // "certsrv/mscep"
|
|
WCHAR * wszFullVirtualPath=NULL; // "/LM/W3svc/1/ROOT/certsrv/mscep"
|
|
WCHAR * wszFullPhysicalPath=NULL; // "c:\winnt\system32\certsrv\mscep\mscep.dll"
|
|
|
|
// build the directories
|
|
if (FALSE==GetSystemDirectoryW(wszSysDirBuf, MAX_PATH + 2)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "GetSystemDirectory");
|
|
}
|
|
|
|
wszPhysicalPath=(WCHAR *)LocalAlloc(LPTR, (wcslen(wszSysDirBuf)+1+wcslen(gc_wszCertSrvDir)+1+wcslen(wszDirectory)+1)*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszPhysicalPath);
|
|
wcscpy(wszPhysicalPath, wszSysDirBuf);
|
|
wcscat(wszPhysicalPath, L"\\");
|
|
wcscat(wszPhysicalPath, gc_wszCertSrvDir);
|
|
wcscat(wszPhysicalPath, L"\\");
|
|
wcscat(wszPhysicalPath, wszDirectory);
|
|
|
|
wszRelativeVirtualPath=(WCHAR *)LocalAlloc(LPTR, (wcslen(gc_wszCertSrvDir)+1+wcslen(wszDirectory)+1)*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszRelativeVirtualPath);
|
|
wcscpy(wszRelativeVirtualPath, gc_wszCertSrvDir);
|
|
wcscat(wszRelativeVirtualPath, L"/");
|
|
wcscat(wszRelativeVirtualPath, wszDirectory);
|
|
|
|
wszFullVirtualPath=(WCHAR *)LocalAlloc(LPTR, (wcslen(gc_wszBaseRoot)+1+wcslen(wszRelativeVirtualPath)+1)*sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszFullVirtualPath);
|
|
wcscpy(wszFullVirtualPath, gc_wszBaseRoot);
|
|
wcscat(wszFullVirtualPath, L"/");
|
|
wcscat(wszFullVirtualPath, wszRelativeVirtualPath);
|
|
|
|
wszFullPhysicalPath=(WCHAR *)LocalAlloc(LPTR, (wcslen(wszPhysicalPath)+1+wcslen(L"\\")+wcslen(CEP_DLL_NAME)) * sizeof(WCHAR));
|
|
_JumpIfOutOfMemory(hr, error, wszFullPhysicalPath);
|
|
wcscpy(wszFullPhysicalPath, wszPhysicalPath);
|
|
wcscat(wszFullPhysicalPath, L"\\");
|
|
wcscat(wszFullPhysicalPath, CEP_DLL_NAME);
|
|
|
|
//enable the ISAPI Extension on IIS
|
|
hr=IsISAPIExtensionEnabled(wszFullPhysicalPath, fISAPIEnabled);
|
|
|
|
//do not respond to error for backward compatibility of previous IDS build
|
|
if((S_OK == hr) && (!fISAPIEnabled))
|
|
{
|
|
hr=EnableISAPIExtension(wszFullPhysicalPath, &fEnabled);
|
|
_JumpIfError(hr, error, "EnableISAPIExtension");
|
|
}
|
|
|
|
// Create an instance of the metabase object
|
|
hr=CoCreateInstance(
|
|
CLSID_MSAdminBase,
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_IMSAdminBase,
|
|
(void **) &pIMeta);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_MSAdminBase)");
|
|
|
|
// open the top level
|
|
hr=vrOpenRoot(pIMeta, FALSE/*not read-only*/, gc_wszBaseRoot, &hMetaRoot);
|
|
_JumpIfError(hr, error, "vrOpenRoot");
|
|
|
|
// Add new VDir called gc_wszVRootName
|
|
__try {
|
|
hr=pIMeta->AddKey(hMetaRoot, wszRelativeVirtualPath);
|
|
} _TrapException(hr);
|
|
if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)==hr) {
|
|
// That's fine.
|
|
_IgnoreError(hr, "AddKey");
|
|
} else {
|
|
_JumpIfErrorStr(hr, error, "AddKey", wszRelativeVirtualPath);
|
|
}
|
|
|
|
// close the root key
|
|
__try {
|
|
hr=pIMeta->CloseKey(hMetaRoot);
|
|
} _TrapException(hr);
|
|
hMetaRoot=NULL;
|
|
_JumpIfError(hr, error, "CloseKey");
|
|
|
|
|
|
// Open the new VDir
|
|
__try {
|
|
hr=pIMeta->OpenKey(
|
|
METADATA_MASTER_ROOT_HANDLE,
|
|
wszFullVirtualPath,
|
|
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
|
|
1000,
|
|
&hMetaKey);
|
|
} _TrapException(hr);
|
|
_JumpIfErrorStr(hr, error, "OpenKey", wszFullVirtualPath);
|
|
|
|
// Set the physical path for this VDir
|
|
|
|
// virtual root path
|
|
mr.dwMDIdentifier=MD_VR_PATH;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(wszPhysicalPath)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(wszPhysicalPath);
|
|
__try {
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// access permissions on VRoots: read & execute
|
|
dwAccessPerms=MD_ACCESS_EXECUTE | MD_ACCESS_READ;
|
|
mr.dwMDIdentifier=MD_ACCESS_PERM;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwAccessPerms);
|
|
mr.pbMDData=(BYTE *)(&dwAccessPerms);
|
|
__try {
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// indicate that what we created is a vroot - set the key type
|
|
mr.dwMDIdentifier=MD_KEY_TYPE;
|
|
mr.dwMDAttributes=METADATA_NO_ATTRIBUTES;
|
|
mr.dwMDUserType=IIS_MD_UT_SERVER;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(wszKeyType)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(wszKeyType);
|
|
__try {
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// set authentication to be anonymous or NTLM
|
|
dwAuthenticationType=MD_AUTH_ANONYMOUS|MD_AUTH_NT;
|
|
mr.dwMDIdentifier=MD_AUTHORIZATION;
|
|
mr.dwMDAttributes=METADATA_INHERIT;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=DWORD_METADATA;
|
|
mr.dwMDDataLen=sizeof(dwAuthenticationType);
|
|
mr.pbMDData=reinterpret_cast<BYTE *>(&dwAuthenticationType);
|
|
__try {
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// set the default document
|
|
mr.dwMDIdentifier=MD_DEFAULT_LOAD_FILE;
|
|
mr.dwMDAttributes=METADATA_NO_ATTRIBUTES;
|
|
mr.dwMDUserType=IIS_MD_UT_FILE;
|
|
mr.dwMDDataType=STRING_METADATA;
|
|
mr.dwMDDataLen=(wcslen(gc_wszCepDllName)+1)*sizeof(WCHAR);
|
|
mr.pbMDData=(BYTE *)(gc_wszCepDllName);
|
|
__try {
|
|
hr=pIMeta->SetData(hMetaKey, L"", &mr);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "SetData");
|
|
|
|
// done with this key.
|
|
__try {
|
|
hr=pIMeta->CloseKey(hMetaKey);
|
|
} _TrapException(hr);
|
|
hMetaKey=NULL;
|
|
_JumpIfError(hr, error, "CloseKey");
|
|
|
|
// Flush out the changes and close
|
|
__try {
|
|
hr=pIMeta->SaveData();
|
|
} _TrapException(hr);
|
|
// _JumpIfError(hr, "SaveData");
|
|
if (FAILED(hr)) {
|
|
_IgnoreError(hr, "SaveData");
|
|
}
|
|
hr=S_OK;
|
|
|
|
// Create a 'web application' so that scrdenrl.dll runs in-process
|
|
// Create an instance of the metabase object
|
|
hr=CoCreateInstance(
|
|
CLSID_WamAdmin,
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_IWamAdmin,
|
|
(void **) &pIWam);
|
|
_JumpIfError(hr, error, "CoCreateInstance(CLSID_WamAdmin)");
|
|
|
|
// Create the application running in-process
|
|
__try {
|
|
hr=pIWam->AppCreate(wszFullVirtualPath, TRUE);
|
|
} _TrapException(hr);
|
|
_JumpIfError(hr, error, "AppCreate");
|
|
|
|
//create an application pool
|
|
hr=CoCreateInstance(
|
|
CLSID_WamAdmin,
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_IIISApplicationAdmin,
|
|
(void **) &pIIISAppAdmin);
|
|
_JumpIfError(hr, error, "CoCreateInstance(IID_IIISApplicationAdmin)");
|
|
|
|
hr=pIIISAppAdmin->CreateApplication(wszFullVirtualPath, eAppRunInProc, wszApplicationPool, TRUE);
|
|
|
|
if(hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
|
|
{
|
|
// That's fine since installation can be run multiple times
|
|
_IgnoreError(hr, "CreateApplication");
|
|
}
|
|
else
|
|
{
|
|
_JumpIfErrorStr(hr, error, "CreateApplication", wszFullVirtualPath);
|
|
}
|
|
|
|
//update an application pool
|
|
hr=CEPUpdateApplicationPool(fDC, wszApplicationPool, fLocalSystem, wszUserName, wszPassword);
|
|
_JumpIfError(hr, error, "CEPCreateApplicationPool");
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=wszFullVirtualPath) {
|
|
LocalFree(wszFullVirtualPath);
|
|
}
|
|
if (NULL!=wszRelativeVirtualPath) {
|
|
LocalFree(wszRelativeVirtualPath);
|
|
}
|
|
if (NULL!=wszPhysicalPath) {
|
|
LocalFree(wszPhysicalPath);
|
|
}
|
|
|
|
if (NULL!=wszFullPhysicalPath)
|
|
{
|
|
LocalFree(wszFullPhysicalPath);
|
|
}
|
|
|
|
if (NULL!=hMetaKey) {
|
|
HRESULT hr2;
|
|
__try {
|
|
hr2=pIMeta->CloseKey(hMetaKey);
|
|
} _TrapException(hr2);
|
|
_TeardownError(hr, hr2, "CloseKey");
|
|
}
|
|
if (NULL!=hMetaRoot) {
|
|
HRESULT hr2;
|
|
__try {
|
|
hr2=pIMeta->CloseKey(hMetaRoot);
|
|
} _TrapException(hr2);
|
|
_TeardownError(hr, hr2, "CloseKey");
|
|
}
|
|
if (NULL!=pIIISAppAdmin) {
|
|
pIIISAppAdmin->Release();
|
|
}
|
|
if (NULL!=pIWam) {
|
|
pIWam->Release();
|
|
}
|
|
if (NULL!=pIMeta) {
|
|
pIMeta->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT CepStopService(IN DWORD dwServicePeriod, const WCHAR * wszServiceName, BOOL * pbWasRunning)
|
|
{
|
|
HRESULT hr;
|
|
SERVICE_STATUS ss;
|
|
unsigned int nAttempts;
|
|
|
|
// must be cleaned up
|
|
SC_HANDLE hSCManager=NULL;
|
|
SC_HANDLE hService=NULL;
|
|
|
|
// initialize out parameters
|
|
*pbWasRunning=FALSE;
|
|
|
|
// talk to the service manager
|
|
hSCManager=OpenSCManagerW(NULL/*machine*/, NULL/*db*/, SC_MANAGER_ALL_ACCESS);
|
|
if (NULL==hSCManager) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "OpenSCManager");
|
|
}
|
|
|
|
// get to the service
|
|
hService=OpenServiceW(hSCManager, wszServiceName, SERVICE_ALL_ACCESS);
|
|
if (NULL==hService) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "OpenService", wszServiceName);
|
|
}
|
|
|
|
// see if the service is running
|
|
if (FALSE==QueryServiceStatus(hService, &ss)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "QueryServiceStatus", wszServiceName);
|
|
}
|
|
if (SERVICE_STOPPED!=ss.dwCurrentState && SERVICE_STOP_PENDING!=ss.dwCurrentState) {
|
|
*pbWasRunning=TRUE;
|
|
}
|
|
|
|
// begin the service stopping loop
|
|
for (nAttempts=0; SERVICE_STOPPED!=ss.dwCurrentState && nAttempts<dwServicePeriod; nAttempts++) {
|
|
|
|
// service is running, must stop it.
|
|
if (SERVICE_STOP_PENDING!=ss.dwCurrentState) {
|
|
if (!ControlService(hService, SERVICE_CONTROL_STOP, &ss)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "ControlService(Stop)", wszServiceName);
|
|
}
|
|
}
|
|
|
|
// wait a little while
|
|
Sleep(1000);
|
|
|
|
// see if the service is running
|
|
if (FALSE==QueryServiceStatus(hService, &ss)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "QueryServiceStatus", wszServiceName);
|
|
}
|
|
}
|
|
|
|
if (nAttempts>=dwServicePeriod) {
|
|
// it never stopped
|
|
hr=HRESULT_FROM_WIN32(ERROR_SERVICE_REQUEST_TIMEOUT);
|
|
_JumpErrorStr(hr, error, "Stopping service", wszServiceName);
|
|
}
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
if (NULL!=hSCManager) {
|
|
CloseServiceHandle(hSCManager);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT CepStartService(const WCHAR * wszServiceName)
|
|
{
|
|
HRESULT hr;
|
|
SERVICE_STATUS ss;
|
|
|
|
// must be cleaned up
|
|
SC_HANDLE hSCManager=NULL;
|
|
SC_HANDLE hService=NULL;
|
|
|
|
// talk to the service manager
|
|
hSCManager=OpenSCManagerW(NULL/*machine*/, NULL/*db*/, SC_MANAGER_ALL_ACCESS);
|
|
if (NULL==hSCManager) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "OpenSCManager");
|
|
}
|
|
|
|
// get to the service
|
|
hService=OpenServiceW(hSCManager, wszServiceName, SERVICE_ALL_ACCESS);
|
|
if (NULL==hService) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "OpenService", wszServiceName);
|
|
}
|
|
|
|
// now, start the service.
|
|
if (FALSE==StartServiceW(hService, 0 /*num args*/, NULL /*args*/)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "OpenSCManager");
|
|
}
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
if (NULL!=hSCManager) {
|
|
CloseServiceHandle(hSCManager);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
BOOL IsGoodCaInstalled(void)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwError;
|
|
DWORD dwType;
|
|
DWORD dwDataSize;
|
|
DWORD dwSetupStatus;
|
|
BOOL bResult=FALSE;
|
|
DWORD dwCAType;
|
|
|
|
// must be cleaned up
|
|
HKEY hCurConfig=NULL;
|
|
|
|
// get the current configuration
|
|
hr=OpenCurrentCAConfig(&hCurConfig);
|
|
_JumpIfError(hr, error, "OpenCurrentCAConfig");
|
|
|
|
// get value SetupStatus
|
|
dwDataSize=sizeof(dwSetupStatus);
|
|
dwError=RegQueryValueExW(hCurConfig, wszREGSETUPSTATUS, NULL, &dwType, (BYTE *)&dwSetupStatus, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGSETUPSTATUS);
|
|
}
|
|
_Verify(REG_DWORD==dwType, hr, error);
|
|
_Verify(sizeof(dwSetupStatus)==dwDataSize, hr, error);
|
|
|
|
// make sure we have all the needed components set up
|
|
_Verify(0!=(dwSetupStatus&SETUP_SERVER_FLAG), hr, error);
|
|
_Verify(0!=(dwSetupStatus&SETUP_CLIENT_FLAG), hr, error);
|
|
_Verify(0==(dwSetupStatus&SETUP_SUSPEND_FLAG), hr, error);
|
|
|
|
// Check the CA Type too
|
|
dwDataSize=sizeof(dwCAType);
|
|
dwError=RegQueryValueExW(hCurConfig, wszREGCATYPE, NULL, &dwType, (BYTE *)&dwCAType, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGCATYPE);
|
|
}
|
|
_Verify(REG_DWORD==dwType, hr, error);
|
|
_Verify(sizeof(dwCAType)==dwDataSize, hr, error);
|
|
|
|
_Verify(dwCAType<=ENUM_UNKNOWN_CA, hr, error);
|
|
|
|
|
|
bResult=TRUE;
|
|
error:
|
|
if (NULL!=hCurConfig) {
|
|
RegCloseKey(hCurConfig);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
BOOL IsServiceRunning(IN const WCHAR * wszServiceName)
|
|
{
|
|
HRESULT hr;
|
|
SERVICE_STATUS ss;
|
|
BOOL bResult=FALSE;
|
|
|
|
// must be cleaned up
|
|
SC_HANDLE hSCManager=NULL;
|
|
SC_HANDLE hService=NULL;
|
|
|
|
// talk to the service manager
|
|
hSCManager=OpenSCManagerW(NULL/*machine*/, NULL/*db*/, SC_MANAGER_ALL_ACCESS);
|
|
if (NULL==hSCManager) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "OpenSCManager");
|
|
}
|
|
|
|
// get to the service
|
|
hService=OpenServiceW(hSCManager, wszServiceName, SERVICE_ALL_ACCESS);
|
|
if (NULL==hService) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "OpenService", wszSERVICE_NAME);
|
|
}
|
|
|
|
// see if the service is running
|
|
if (FALSE==QueryServiceStatus(hService, &ss)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpErrorStr(hr, error, "QueryServiceStatus", wszSERVICE_NAME);
|
|
}
|
|
_Verify(SERVICE_RUNNING==ss.dwCurrentState, hr, error)
|
|
_Verify(0!=(SERVICE_ACCEPT_PAUSE_CONTINUE&ss.dwControlsAccepted), hr, error);
|
|
|
|
// looks like it is
|
|
bResult=TRUE;
|
|
|
|
error:
|
|
if (NULL!=hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
if (NULL!=hSCManager) {
|
|
CloseServiceHandle(hSCManager);
|
|
}
|
|
|
|
return bResult;
|
|
|
|
}
|
|
//--------------------------------------------------------------------
|
|
BOOL IsCaRunning(void)
|
|
{
|
|
return IsServiceRunning(wszSERVICE_NAME);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT EnrollForRACertificates(
|
|
IN const WCHAR * wszDistinguishedName,
|
|
IN const WCHAR * wszSignCSPName,
|
|
IN DWORD dwSignCSPType,
|
|
IN DWORD dwSignKeySize,
|
|
IN const WCHAR * wszEncryptCSPName,
|
|
IN DWORD dwEncryptCSPType,
|
|
IN DWORD dwEncryptKeySize,
|
|
IN SID *psidAccount)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr=EnrollForRACert(
|
|
wszDistinguishedName,
|
|
wszSignCSPName,
|
|
dwSignCSPType,
|
|
dwSignKeySize,
|
|
AT_SIGNATURE,
|
|
wszCERTTYPE_ENROLLMENT_AGENT_OFFLINE,
|
|
psidAccount);
|
|
_JumpIfError(hr, error, "EnrollForRACert(OfflineEnrollmentAgent)");
|
|
|
|
hr=EnrollForRACert(
|
|
wszDistinguishedName,
|
|
wszEncryptCSPName,
|
|
dwEncryptCSPType,
|
|
dwEncryptKeySize,
|
|
AT_KEYEXCHANGE,
|
|
wszCERTTYPE_CEP_ENCRYPTION,
|
|
psidAccount);
|
|
_JumpIfError(hr, error, "EnrollForRACert(CepEncryption)");
|
|
|
|
// all done
|
|
hr=S_OK;
|
|
error:
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT DoCertSrvRegChanges(IN BOOL bDisablePendingFirst)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwDataSize;
|
|
DWORD dwType;
|
|
DWORD dwError;
|
|
WCHAR * wszTravel;
|
|
DWORD dwNewDataSize;
|
|
DWORD dwRequestDisposition;
|
|
|
|
bool bSubjectTemplateAlreadyModified=false;
|
|
|
|
// must be cleaned up
|
|
HKEY hCaConfig=NULL;
|
|
WCHAR * mwszSubjectTemplate=NULL;
|
|
HKEY hPolicyModules=NULL;
|
|
HKEY hCurPolicy=NULL;
|
|
WCHAR * wszCurPolicy=NULL;
|
|
|
|
// get the current CA config key
|
|
hr=OpenCurrentCAConfig(&hCaConfig);
|
|
_JumpIfError(hr, error, "OpenCurrentCAConfig");
|
|
|
|
//
|
|
// add strings to the SubjectTemplate value
|
|
//
|
|
|
|
// get the size of the Multi_SZ
|
|
dwDataSize=0;
|
|
dwError=RegQueryValueExW(hCaConfig, wszREGSUBJECTTEMPLATE, NULL, &dwType, NULL, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGSUBJECTTEMPLATE);
|
|
}
|
|
_Verify(REG_MULTI_SZ==dwType, hr, error);
|
|
|
|
// add exra space for the strings we want to add
|
|
dwDataSize+=(wcslen(wszPROPUNSTRUCTUREDNAME)+1)*sizeof(WCHAR);
|
|
dwDataSize+=(wcslen(wszPROPUNSTRUCTUREDADDRESS)+1)*sizeof(WCHAR);
|
|
dwDataSize+=(wcslen(wszPROPDEVICESERIALNUMBER)+1)*sizeof(WCHAR);
|
|
dwNewDataSize=dwDataSize;
|
|
mwszSubjectTemplate=(WCHAR *)LocalAlloc(LPTR, dwDataSize);
|
|
_JumpIfOutOfMemory(hr, error, mwszSubjectTemplate);
|
|
|
|
// get the Multi_SZ
|
|
dwError=RegQueryValueExW(hCaConfig, wszREGSUBJECTTEMPLATE, NULL, &dwType, (BYTE *)mwszSubjectTemplate, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGSUBJECTTEMPLATE);
|
|
}
|
|
_Verify(REG_MULTI_SZ==dwType, hr, error);
|
|
|
|
// walk to the end
|
|
for (wszTravel=mwszSubjectTemplate; 0!=wcslen(wszTravel); wszTravel+=wcslen(wszTravel)+1) {
|
|
// while walking, make sure we haven't added these strings already
|
|
if (0==wcscmp(wszTravel, wszPROPUNSTRUCTUREDNAME)) {
|
|
bSubjectTemplateAlreadyModified=true;
|
|
break;
|
|
}
|
|
}
|
|
// we are now pointing at the last '\0' in the string, which we will overwrite
|
|
|
|
// did we do this already? If so, don't do it again.
|
|
if (false==bSubjectTemplateAlreadyModified) {
|
|
|
|
// add the strings
|
|
wcscpy(wszTravel, wszPROPUNSTRUCTUREDNAME);
|
|
wszTravel+=wcslen(wszTravel)+1;
|
|
wcscpy(wszTravel, wszPROPUNSTRUCTUREDADDRESS);
|
|
wszTravel+=wcslen(wszTravel)+1;
|
|
wcscpy(wszTravel, wszPROPDEVICESERIALNUMBER);
|
|
wszTravel+=wcslen(wszTravel)+1;
|
|
// add extra terminator
|
|
wszTravel[0]='\0';
|
|
|
|
// save the Multi_SZ
|
|
dwError=RegSetValueExW(hCaConfig, wszREGSUBJECTTEMPLATE, NULL, dwType, (BYTE *)mwszSubjectTemplate, dwNewDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegSetValueExW", wszREGSUBJECTTEMPLATE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove the Pending First flag from the current policy settings
|
|
//
|
|
|
|
if (FALSE!=bDisablePendingFirst) {
|
|
|
|
// open the current policy
|
|
dwError=RegOpenKeyExW(hCaConfig, wszREGKEYPOLICYMODULES, NULL, KEY_READ, &hPolicyModules);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyExW", wszREGKEYPOLICYMODULES);
|
|
}
|
|
|
|
hr=GetRegString(hPolicyModules, wszREGACTIVE, &wszCurPolicy);
|
|
_JumpIfErrorStr(hr, error, "GetRegString", wszREGACTIVE);
|
|
|
|
dwError=RegOpenKeyExW(hPolicyModules, wszCurPolicy, NULL, KEY_ALL_ACCESS, &hCurPolicy);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegOpenKeyExW", wszCurPolicy);
|
|
}
|
|
|
|
// read the value
|
|
dwDataSize=sizeof(dwRequestDisposition);
|
|
dwError=RegQueryValueExW(hCurPolicy, wszREGREQUESTDISPOSITION, NULL, &dwType, (BYTE *)&dwRequestDisposition, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGREQUESTDISPOSITION);
|
|
}
|
|
_Verify(REG_DWORD==dwType, hr, error);
|
|
_Verify(sizeof(dwRequestDisposition)==dwDataSize, hr, error);
|
|
|
|
// clear the pending-first flag
|
|
dwRequestDisposition&=~REQDISP_PENDINGFIRST;
|
|
|
|
// save the vale
|
|
dwError=RegSetValueExW(hCurPolicy, wszREGREQUESTDISPOSITION, NULL, dwType, (BYTE *)&dwRequestDisposition, dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegSetValueExW", wszREGREQUESTDISPOSITION);
|
|
}
|
|
}
|
|
|
|
// all done
|
|
hr=S_OK;
|
|
error:
|
|
if (NULL!=wszCurPolicy) {
|
|
LocalFree(wszCurPolicy);
|
|
}
|
|
if (NULL!=hCurPolicy) {
|
|
RegCloseKey(hCurPolicy);
|
|
}
|
|
if (NULL!=hPolicyModules) {
|
|
RegCloseKey(hPolicyModules);
|
|
}
|
|
if (NULL!=mwszSubjectTemplate) {
|
|
LocalFree(mwszSubjectTemplate);
|
|
}
|
|
if (NULL!=hCaConfig) {
|
|
RegCloseKey(hCaConfig);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT GetCaType(OUT ENUM_CATYPES * pCAType)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwDataSize;
|
|
DWORD dwCAType;
|
|
DWORD dwType;
|
|
DWORD dwError;
|
|
|
|
// must be cleaned up
|
|
HKEY hCaConfig=NULL;
|
|
|
|
// init out params
|
|
*pCAType=ENUM_UNKNOWN_CA;
|
|
|
|
// get the current CA config key
|
|
hr=OpenCurrentCAConfig(&hCaConfig);
|
|
_JumpIfError(hr, error, "OpenCurrentCAConfig");
|
|
|
|
// read the CA Type
|
|
dwDataSize=sizeof(dwCAType);
|
|
dwError=RegQueryValueExW(hCaConfig, wszREGCATYPE, NULL, &dwType, (BYTE *)&dwCAType, &dwDataSize);
|
|
if (ERROR_SUCCESS!=dwError) {
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
_JumpErrorStr(hr, error, "RegQueryValueExW", wszREGCATYPE);
|
|
}
|
|
_Verify(REG_DWORD==dwType, hr, error);
|
|
_Verify(sizeof(dwCAType)==dwDataSize, hr, error);
|
|
|
|
_Verify(dwCAType<=ENUM_UNKNOWN_CA, hr, error);
|
|
|
|
// all done
|
|
hr=S_OK;
|
|
*pCAType=(ENUM_CATYPES)dwCAType;
|
|
|
|
error:
|
|
if (NULL!=hCaConfig) {
|
|
RegCloseKey(hCaConfig);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
BOOL IsUserInAdminGroup(IN BOOL bEnterprise)
|
|
{
|
|
BOOL bIsMember=FALSE;
|
|
HRESULT hr;
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority=SECURITY_NT_AUTHORITY;
|
|
|
|
// must be cleaned up
|
|
HANDLE hAccessToken=NULL;
|
|
HANDLE hDupToken=NULL;
|
|
SID * psidLocalAdmins=NULL;
|
|
SID * psidEntAdmins=NULL;
|
|
SID * psidRootDomAdmins=NULL;
|
|
|
|
// Get the access token for this process
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hAccessToken)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "OpenProcessToken");
|
|
}
|
|
|
|
// CheckTokenMembership must operate on impersonation token, so make one
|
|
if (!DuplicateToken(hAccessToken, SecurityIdentification, &hDupToken)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "DuplicateToken");
|
|
}
|
|
|
|
if (bEnterprise) {
|
|
// see if the user is a member of the [domain]\Enmterprised Administrators group
|
|
BOOL bIsEntAdmin=FALSE;
|
|
BOOL bIsRootDomAdmin=FALSE;
|
|
|
|
// get the Enterpise Admin SID
|
|
hr=GetEntAdminSid(&psidEntAdmins);
|
|
_JumpIfError(hr, error, "GetEntAdminSid");
|
|
|
|
// check for membership
|
|
if (!CheckTokenMembership(hDupToken, psidEntAdmins, &bIsEntAdmin)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CheckTokenMembership");
|
|
}
|
|
|
|
// get the root Domain Admin SID
|
|
hr=GetRootDomAdminSid(&psidRootDomAdmins);
|
|
_JumpIfError(hr, error, "GetRootDomAdminSid");
|
|
|
|
// check for membership
|
|
if (!CheckTokenMembership(hDupToken, psidRootDomAdmins, &bIsRootDomAdmin)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CheckTokenMembership");
|
|
}
|
|
|
|
// either one will do
|
|
bIsMember=(bIsEntAdmin || bIsRootDomAdmin);
|
|
|
|
} else {
|
|
// see if the user is a member of the BUILTIN\Administrators group
|
|
|
|
// get the well-known SID
|
|
if (!AllocateAndInitializeSid(&siaNtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0, (void **)&psidLocalAdmins))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "AllocateAndInitializeSid");
|
|
}
|
|
|
|
// check for membership
|
|
if (!CheckTokenMembership(hDupToken, psidLocalAdmins, &bIsMember)) {
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
_JumpError(hr, error, "CheckTokenMembership");
|
|
}
|
|
}
|
|
|
|
|
|
error:
|
|
if (NULL!=hAccessToken) {
|
|
CloseHandle(hAccessToken);
|
|
}
|
|
|
|
if (NULL!=hDupToken) {
|
|
CloseHandle(hDupToken);
|
|
}
|
|
|
|
if (NULL!=psidLocalAdmins) {
|
|
FreeSid(psidLocalAdmins);
|
|
}
|
|
|
|
if (NULL!=psidEntAdmins) {
|
|
LocalFree(psidEntAdmins);
|
|
}
|
|
|
|
if (NULL!=psidRootDomAdmins) {
|
|
LocalFree(psidRootDomAdmins);
|
|
}
|
|
|
|
return bIsMember;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
HRESULT DoCertSrvEnterpriseChanges(SID * psidAccount)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwFlags;
|
|
BOOL bSDChanged1;
|
|
BOOL bSDChanged2;
|
|
BOOL bSDChanged3;
|
|
BOOL bSDChanged4;
|
|
|
|
// must be cleaned up
|
|
HCERTTYPE hEAOTemplate=NULL;
|
|
HCERTTYPE hCETemplate=NULL;
|
|
HCERTTYPE hIIOTemplate=NULL;
|
|
PSECURITY_DESCRIPTOR pSD=NULL;
|
|
WCHAR * wszCAName=NULL;
|
|
HCAINFO hCA=NULL;
|
|
SID * psidEntAdmins=NULL;
|
|
SID * psidRootDomAdmins=NULL;
|
|
SID * psidThisComputer=NULL;
|
|
|
|
//
|
|
// first, make sure the CA will issue the cert templates we want
|
|
//
|
|
|
|
// get the sanitized CA name
|
|
hr=GetCADsName(&wszCAName);
|
|
_JumpIfError(hr, error, "GetCADsName");
|
|
|
|
// get the CA (in the DS)
|
|
hr=CAFindByName(wszCAName, NULL, 0, &hCA);
|
|
_JumpIfErrorStr(hr, error, "CAFindCaByName", wszCAName);
|
|
|
|
// check the flags to confirm it supports templates
|
|
hr=CAGetCAFlags(hCA, &dwFlags);
|
|
_JumpIfError(hr, error, "CAGetCAFlags");
|
|
_Verify(0==(dwFlags&CA_FLAG_NO_TEMPLATE_SUPPORT), hr, error);
|
|
|
|
// get the enrollment agent offline template
|
|
hr=CAFindCertTypeByName(wszCERTTYPE_ENROLLMENT_AGENT_OFFLINE, NULL, CT_ENUM_USER_TYPES, &hEAOTemplate);
|
|
_JumpIfErrorStr(hr, error, "CAFindCertTypeByName", wszCERTTYPE_ENROLLMENT_AGENT_OFFLINE);
|
|
// make sure that the CA will issue this template
|
|
hr=CAAddCACertificateType(hCA, hEAOTemplate);
|
|
_JumpIfErrorStr(hr, error, "CAAddCACertificateType", wszCERTTYPE_ENROLLMENT_AGENT_OFFLINE);
|
|
|
|
// get the CEP encryption template
|
|
hr=CAFindCertTypeByName(wszCERTTYPE_CEP_ENCRYPTION, NULL, CT_ENUM_MACHINE_TYPES, &hCETemplate);
|
|
_JumpIfErrorStr(hr, error, "CAFindCertTypeByName", wszCERTTYPE_CEP_ENCRYPTION);
|
|
// make sure that the CA will issue this template
|
|
hr=CAAddCACertificateType(hCA, hCETemplate);
|
|
_JumpIfErrorStr(hr, error, "CAAddCACertificateType", wszCERTTYPE_CEP_ENCRYPTION);
|
|
|
|
// get the IPSEC Intermediate offline template
|
|
hr=CAFindCertTypeByName(wszCERTTYPE_IPSEC_INTERMEDIATE_OFFLINE, NULL, CT_ENUM_MACHINE_TYPES, &hIIOTemplate);
|
|
_JumpIfErrorStr(hr, error, "CAFindCertTypeByName", wszCERTTYPE_IPSEC_INTERMEDIATE_OFFLINE);
|
|
// make sure that the CA will issue this template
|
|
hr=CAAddCACertificateType(hCA, hIIOTemplate);
|
|
_JumpIfErrorStr(hr, error, "CAAddCACertificateType", wszCERTTYPE_IPSEC_INTERMEDIATE_OFFLINE);
|
|
|
|
// make sure that all gets written.
|
|
hr=CAUpdateCA(hCA);
|
|
_JumpIfError(hr, error, "CAUpdateCA");
|
|
|
|
|
|
|
|
//
|
|
// now, check the ACLs
|
|
//
|
|
|
|
// get the Enterpise Admin SID
|
|
hr=GetEntAdminSid(&psidEntAdmins);
|
|
_JumpIfError(hr, error, "GetEntAdminSid");
|
|
|
|
// get the root Domain Admin SID
|
|
hr=GetRootDomAdminSid(&psidRootDomAdmins);
|
|
_JumpIfError(hr, error, "GetRootDomAdminSid");
|
|
|
|
// get this computer's SID
|
|
hr=GetThisComputerSid(&psidThisComputer);
|
|
_JumpIfError(hr, error, "GetThisComputerSid");
|
|
|
|
|
|
|
|
// enrollment agent offline template needs Enterprise Admins and root Domain Admins
|
|
hr=CACertTypeGetSecurity(hEAOTemplate, &pSD);
|
|
_JumpIfError(hr, error, "CACertTypeGetSecurity");
|
|
|
|
hr=ConfirmAccess(&pSD, psidEntAdmins, &bSDChanged1);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
hr=ConfirmAccess(&pSD, psidRootDomAdmins, &bSDChanged2);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
if (bSDChanged1 || bSDChanged2) {
|
|
hr=CACertTypeSetSecurity(hEAOTemplate, pSD);
|
|
_JumpIfError(hr, error, "CACertTypeSetSecurity");
|
|
|
|
hr=CAUpdateCertType(hEAOTemplate);
|
|
_JumpIfError(hr, error, "CAUpdateCertType");
|
|
}
|
|
|
|
LocalFree(pSD);
|
|
pSD=NULL;
|
|
|
|
|
|
// CEP encryption template needs Enterprise Admins and root Domain Admins
|
|
hr=CACertTypeGetSecurity(hCETemplate, &pSD);
|
|
_JumpIfError(hr, error, "CACertTypeGetSecurity");
|
|
|
|
hr=ConfirmAccess(&pSD, psidEntAdmins, &bSDChanged1);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
hr=ConfirmAccess(&pSD, psidRootDomAdmins, &bSDChanged2);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
if (bSDChanged1 || bSDChanged2) {
|
|
hr=CACertTypeSetSecurity(hCETemplate, pSD);
|
|
_JumpIfError(hr, error, "CACertTypeSetSecurity");
|
|
|
|
hr=CAUpdateCertType(hCETemplate);
|
|
_JumpIfError(hr, error, "CAUpdateCertType");
|
|
}
|
|
|
|
LocalFree(pSD);
|
|
pSD=NULL;
|
|
|
|
|
|
// IPSEC Intermediate offline template needs Enterprise Admins and root Domain Admins and the current machine
|
|
hr=CACertTypeGetSecurity(hIIOTemplate, &pSD);
|
|
_JumpIfError(hr, error, "CACertTypeGetSecurity");
|
|
|
|
hr=ConfirmAccess(&pSD, psidEntAdmins, &bSDChanged1);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
hr=ConfirmAccess(&pSD, psidRootDomAdmins, &bSDChanged2);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
hr=ConfirmAccess(&pSD, psidThisComputer, &bSDChanged3);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
|
|
//if a service account if used, add the account to the template
|
|
if(psidAccount)
|
|
{
|
|
hr=ConfirmAccess(&pSD, psidAccount, &bSDChanged4);
|
|
_JumpIfError(hr, error, "ConfirmAccess");
|
|
}
|
|
else
|
|
{
|
|
bSDChanged4=FALSE;
|
|
}
|
|
|
|
if (bSDChanged1 || bSDChanged2 || bSDChanged3 || bSDChanged4) {
|
|
hr=CACertTypeSetSecurity(hIIOTemplate, pSD);
|
|
_JumpIfError(hr, error, "CACertTypeSetSecurity");
|
|
|
|
hr=CAUpdateCertType(hIIOTemplate);
|
|
_JumpIfError(hr, error, "CAUpdateCertType");
|
|
}
|
|
|
|
hr=S_OK;
|
|
error:
|
|
if (NULL!=psidThisComputer) {
|
|
LocalFree(psidThisComputer);
|
|
}
|
|
if (NULL!=psidEntAdmins) {
|
|
LocalFree(psidEntAdmins);
|
|
}
|
|
if (NULL!=psidRootDomAdmins) {
|
|
LocalFree(psidRootDomAdmins);
|
|
}
|
|
if (NULL!=pSD) {
|
|
LocalFree(pSD);
|
|
}
|
|
if (NULL!=hEAOTemplate) {
|
|
CACloseCertType(hEAOTemplate);
|
|
}
|
|
if (NULL!=hCETemplate) {
|
|
CACloseCertType(hCETemplate);
|
|
}
|
|
if (NULL!=hIIOTemplate) {
|
|
CACloseCertType(hIIOTemplate);
|
|
}
|
|
if (NULL!=wszCAName) {
|
|
LocalFree(wszCAName);
|
|
}
|
|
if (NULL!=hCA) {
|
|
CACloseCA(hCA);
|
|
}
|
|
|
|
return hr;
|
|
}
|