Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2511 lines
69 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1998 - 1999
//
// File: vroot.cpp
//
//--------------------------------------------------------------------------
//+------------------------------------------------------------------------
//
// File: vroot.cpp
//
// Contents: Code for creating IIS web server virtual roots under K2.
//
// Functions: AddNewVDir()
//
// History: 5/16/97 JerryK Created
//
//-------------------------------------------------------------------------
// Include File Voodoo
#include "pch.cpp"
#pragma hdrstop
#include <lm.h>
#include <sddl.h>
#include "resource.h"
#include "certacl.h"
#include "multisz.h"
#define __dwFILE__ __dwFILE_CERTCLI_VROOT_CPP__
#undef DEFINE_GUID
#define INITGUID
#ifndef INITGUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID FAR name
#else
#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 } }
#endif // INITGUID
#include <iwamreg.h>
#include <iadmw.h>
#include <iiscnfg.h>
extern HINSTANCE g_hInstance;
#define MAX_METABASE_ATTEMPTS 10 // Times to bang head on wall
#define METABASE_PAUSE 500 // Time to pause in msec
#define VRE_DELETEONLY 0x00000001 // Obsolete VRoot -- delete
#define VRE_SCRIPTMAP 0x00000002 // Add additional associations to the script map
#define VRE_ALLOWNTLM 0x00000004 // Alloc NTLM authentication
#define VRE_CREATEAPP 0x00000008 // Create an in-process Web application
typedef struct _VROOTENTRY
{
WCHAR *pwszVRootName;
WCHAR *pwszDirectory; // relative to System32 directory
DWORD Flags;
} VROOTENTRY;
VROOTENTRY g_avr[] = {
// pwszVRootName pwszDirectory Flags
{ L"CertSrv", L"\\CertSrv", VRE_ALLOWNTLM | VRE_SCRIPTMAP | VRE_CREATEAPP},
{ L"CertControl", L"\\CertSrv\\CertControl", VRE_ALLOWNTLM },
{ L"CertEnroll", L"\\" wszCERTENROLLSHAREPATH, 0 },
{ L"CertQue", L"\\CertSrv\\CertQue", VRE_DELETEONLY },
{ L"CertAdm", L"\\CertSrv\\CertAdm", VRE_DELETEONLY },
{ NULL }
};
typedef struct _VRFSPARMS
{
IN DWORD Flags; // VFF_*
IN ENUM_CATYPES CAType; // CAType
IN BOOL fAsynchronous;
IN DWORD csecTimeOut;
OUT DWORD *pVRootDisposition; // VFD_*
OUT DWORD *pShareDisposition; // VFD_*
} VRFSPARMS;
// Globals
WCHAR const g_wszBaseRoot[] = L"/LM/W3svc/1/ROOT";
WCHAR const g_wszCertCliDotDll[] = L"certcli.dll";
WCHAR const g_wszDotAsp[] = L".asp";
WCHAR const g_wszDotCer[] = L".cer";
WCHAR const g_wszDotP7b[] = L".p7b";
WCHAR const g_wszDotCrl[] = L".crl";
WCHAR const g_wszW3SVC[] = L"/LM/W3SVC";
WCHAR const g_wszMSCEP[] = L"mscep.dll";
WCHAR const g_wszMSCEPID[] = L"MSCEPGroup";
// caller must have CoInitialize()'d
BOOL
IsIISInstalled(
OUT HRESULT *phr)
{
IMSAdminBase *pIMeta = NULL;
*phr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(VOID **) &pIMeta);
_JumpIfError2(*phr, error, "CoCreateInstance(CLSID_MSAdminBase)", *phr);
error:
if (NULL != pIMeta)
{
pIMeta->Release();
}
return(S_OK == *phr);
}
HRESULT
vrOpenRoot(
IN IMSAdminBase *pIMeta,
IN BOOL fReadOnly,
OUT METADATA_HANDLE *phMetaRoot)
{
HRESULT hr;
DWORD i;
hr = S_OK;
__try
{
// Re-try a few times to see if we can get past the block
for (i = 0; i < MAX_METABASE_ATTEMPTS; i++)
{
if (0 != i)
{
Sleep(METABASE_PAUSE); // Pause and try again
}
// Make an attempt to open the root
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
g_wszBaseRoot,
fReadOnly?
METADATA_PERMISSION_READ :
(METADATA_PERMISSION_READ |
METADATA_PERMISSION_WRITE),
1000,
phMetaRoot);
if (S_OK == hr)
{
break; // Success -- we're done!
}
// See if a previous call has things tied up
if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) != hr)
{
_LeaveIfError(hr, "OpenKey"); // Detected some other error
}
}
_LeaveIfError(hr, "OpenKey(timeout)"); // Detected some other error
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
//error:
return(hr);
}
HRESULT
vrCloseKey(
IN IMSAdminBase *pIMeta,
IN METADATA_HANDLE hMeta,
IN HRESULT hr)
{
HRESULT hr2;
hr2 = S_OK;
__try
{
hr2 = pIMeta->CloseKey(hMeta);
_LeaveIfError(hr2, "CloseKey");
}
__except(hr2 = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
if (S_OK != hr2)
{
if (S_OK == hr)
{
hr = hr2;
}
_PrintError(hr2, "CloseKey");
}
return(hr);
}
//+----------------------------------------------------------------------------
//
// Function: AddNewVDir(. . . .)
//
// Synopsis: Creates a new virtual root using the K2 metabase.
//
// Arguments: [pwszVRootName] Name to give to the virtual root
// [pwszDirectory] Path for the directory to use as the root.
//
// Returns: HRESULT status code regurgitated from metabase COM interfaces
//
//
// History: 05/16/97 JerryK Put in this file
// 05/22/97 JerryK Made OCM setup build with this stuff
// in place.
//
// Notes: Originally derived from sample code provided by MikeHow;
// hacked up a lot in between.
//
// We do a try, fail, pause, retry loop on our attempts to open
// the metabase master key to get around a K2 bug that can result
// in it being left busy if this function is called too many
// times successively.
//
// TO DO: COME BACK AND PUT SEMIREADABLE GUI LEVEL MESSAGEBOX REPORTING
// THAT THE VROOTS IN QUESTION DIDN'T SET UP CORRECTLY.
//
//-----------------------------------------------------------------------------
HRESULT
AddNewVDir(
IN LPWSTR pwszVRootName,
IN LPWSTR pwszDirectory,
IN BOOL fScriptMap,
IN BOOL fNTLM,
IN BOOL fCreateApp,
OUT BOOL *pfExists)
{
HRESULT hr;
METADATA_RECORD mr;
IMSAdminBase *pIMeta = NULL;
IWamAdmin *pIWam = NULL;
WCHAR *pwszNewPath = NULL;
WCHAR *pwszCurrentScriptMap=NULL;
WCHAR *pwszNewScriptMap=NULL;
WCHAR wszKeyType[] = TEXT(IIS_CLASS_WEB_VDIR);
METADATA_HANDLE hMetaRoot = NULL; // Open key to ROOT (where VDirs live)
METADATA_HANDLE hMetaKey = NULL;
DWORD dwMDData = MD_LOGON_NETWORK; // Create network token when logging on anonymous account
METADATA_RECORD MDData =
{
MD_LOGON_METHOD,
METADATA_INHERIT,
IIS_MD_UT_FILE,
DWORD_METADATA,
sizeof(dwMDData),
(unsigned char*)&dwMDData,
0
};
*pfExists = FALSE;
DBGPRINT((
DBG_SS_CERTLIBI,
"AddNewVDir(%ws, %ws, fScriptMap=%d, fNTLM=%d, fCreateApp=%d)\n",
pwszVRootName,
pwszDirectory,
fScriptMap,
fNTLM,
fCreateApp));
// Create an instance of the metabase object
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
__try
{
hr = vrOpenRoot(pIMeta, FALSE, &hMetaRoot);
_LeaveIfError(hr, "vrOpenRoot");
// Add new VDir called pwszVRootName
hr = pIMeta->AddKey(hMetaRoot, pwszVRootName);
DBGPRINT((
DBG_SS_CERTLIBI,
"AddNewVDir: AddKey(%ws) --> %x\n",
pwszVRootName,
hr));
if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr)
{
*pfExists = TRUE;
}
else
{
_LeaveIfError(hr, "AddKey");
}
if (fScriptMap) {
// get the current script map
DWORD dwDataSize;
mr.dwMDIdentifier=MD_SCRIPT_MAPS;
mr.dwMDAttributes=METADATA_INHERIT;
mr.dwMDUserType=IIS_MD_UT_FILE;
mr.dwMDDataType=MULTISZ_METADATA;
mr.dwMDDataLen=0;
mr.pbMDData=NULL;
hr=pIMeta->GetData(hMetaRoot, L"", &mr, &dwDataSize);
if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)!=hr) {
_LeaveError(hr, "GetData");
}
pwszCurrentScriptMap=reinterpret_cast<WCHAR *>(new unsigned char[dwDataSize]);
if (NULL==pwszCurrentScriptMap) {
hr=E_OUTOFMEMORY;
_LeaveError(hr, "new");
}
mr.pbMDData=reinterpret_cast<unsigned char *>(pwszCurrentScriptMap);
mr.dwMDDataLen=dwDataSize;
hr=pIMeta->GetData(hMetaRoot, L"", &mr, &dwDataSize);
_LeaveIfError(hr, "GetData");
}
hr = pIMeta->SetData(hMetaRoot, pwszVRootName, &MDData);
_LeaveIfError(hr, "CloseKey");
hr = pIMeta->CloseKey(hMetaRoot);
_LeaveIfError(hr, "CloseKey");
hMetaRoot = NULL;
// Build the name of the new VDir
pwszNewPath = new WCHAR[wcslen(g_wszBaseRoot) + 1 + wcslen(pwszVRootName) + 1];
if (NULL == pwszNewPath)
{
hr = E_OUTOFMEMORY;
_LeaveError(hr, "new");
}
wcscpy(pwszNewPath, g_wszBaseRoot);
wcscat(pwszNewPath, L"/");
wcscat(pwszNewPath, pwszVRootName);
// Open the new VDir
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
pwszNewPath,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
1000,
&hMetaKey);
_LeaveIfErrorStr(hr, "OpenKey", pwszNewPath);
// 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(pwszDirectory) + 1) * sizeof(WCHAR);
mr.pbMDData = reinterpret_cast<unsigned char *>(pwszDirectory);
hr = pIMeta->SetData(hMetaKey, L"", &mr);
_LeaveIfError(hr, "SetData");
// access permissions on VRoots: read & execute scripts only
DWORD dwAccessPerms = MD_ACCESS_SCRIPT | 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 = reinterpret_cast<unsigned char *>(&dwAccessPerms);
hr = pIMeta->SetData(hMetaKey, L"", &mr);
_LeaveIfError(hr, "SetData");
// 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 = reinterpret_cast<unsigned char *>(wszKeyType);
hr = pIMeta->SetData(hMetaKey, L"", &mr);
_LeaveIfError(hr, "SetData");
// set authentication to be anonymous
DWORD dwAuthenticationType = MD_AUTH_ANONYMOUS;
// chg to Basic/NTLM if we're told to
if (fNTLM)
dwAuthenticationType = 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<unsigned char *>(&dwAuthenticationType);
hr = pIMeta->SetData(hMetaKey, L"", &mr);
_LeaveIfError(hr, "SetData");
if (fScriptMap) {
// already have current script map.
// walk through the script map and find .asp
WCHAR * pwszCurAssoc=pwszCurrentScriptMap;
do {
if (L'\0'==pwszCurAssoc[0]) {
hr=HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_LeaveError(hr, ".asp association not found");
} else if (0==_wcsnicmp(pwszCurAssoc, g_wszDotAsp, 4)) {
break;
} else {
pwszCurAssoc+=wcslen(pwszCurAssoc)+1;
}
} while (TRUE);
// Walk through the script map and find the last association.
// We can't just subtract one from the total length
// because there is a bug in IIS where sometimes it has a
// triple terminator instead of a double terminator. <Sigh>
unsigned int cchCurScriptMap=0;
while(L'\0'!=pwszCurrentScriptMap[cchCurScriptMap]) {
cchCurScriptMap+=wcslen(pwszCurrentScriptMap+cchCurScriptMap)+1;
}
// create a new script map that has .crl, .cer, and .p7b in it.
// allocate enough space for the existing map, the three new associations, and the terminating \0.
unsigned int cchAssocLen=wcslen(pwszCurAssoc)+1;
pwszNewScriptMap=new WCHAR[cchCurScriptMap+cchAssocLen*3+1];
if (NULL==pwszNewScriptMap) {
hr=E_OUTOFMEMORY;
_LeaveError(hr, "new");
}
// build the map
WCHAR * pwszTravel=pwszNewScriptMap;
// copy the existing map
CopyMemory(pwszTravel, pwszCurrentScriptMap, cchCurScriptMap*sizeof(WCHAR));
pwszTravel+=cchCurScriptMap;
// add the .cer association
wcscpy(pwszTravel, pwszCurAssoc);
wcsncpy(pwszTravel, g_wszDotCer, 4);
pwszTravel+=cchAssocLen;
// add the .p7b association
wcscpy(pwszTravel, pwszCurAssoc);
wcsncpy(pwszTravel, g_wszDotP7b, 4);
pwszTravel+=cchAssocLen;
// add the .crl association
wcscpy(pwszTravel, pwszCurAssoc);
wcsncpy(pwszTravel, g_wszDotCrl, 4);
pwszTravel+=cchAssocLen;
// add the terminator
pwszTravel[0]=L'\0';
// set the new script map
mr.dwMDIdentifier=MD_SCRIPT_MAPS;
mr.dwMDAttributes=METADATA_INHERIT;
mr.dwMDUserType=IIS_MD_UT_FILE;
mr.dwMDDataType=MULTISZ_METADATA;
mr.dwMDDataLen=(cchCurScriptMap+cchAssocLen*3+1) * sizeof(WCHAR);
mr.pbMDData=reinterpret_cast<unsigned char *>(pwszNewScriptMap);
hr=pIMeta->SetData(hMetaKey, L"", &mr);
_LeaveIfError(hr, "SetData");
}
hr = pIMeta->CloseKey(hMetaKey);
_LeaveIfError(hr, "CloseKey");
hMetaKey = NULL;
// Flush out the changes and close
hr = pIMeta->SaveData();
// Note: W2k used to swallow this error
_LeaveIfError(hr, "SaveData");
// _PrintIfError(hr, "SaveData");
// hr = S_OK;
// Create a 'web application' so that scrdenrl.dll runs in-process
if (fCreateApp)
{
// Create an instance of the metabase object
hr = CoCreateInstance(
CLSID_WamAdmin,
NULL,
CLSCTX_ALL,
IID_IWamAdmin,
(void **) &pIWam);
_LeaveIfError(hr, "CoCreateInstance");
// Create the application running in-process
hr = pIWam->AppCreate(pwszNewPath, TRUE);
_LeaveIfError(hr, "AppCreate");
}
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
error:
if (NULL != pwszCurrentScriptMap)
{
delete [] pwszCurrentScriptMap;
}
if (NULL != pwszNewScriptMap)
{
delete [] pwszNewScriptMap;
}
if (NULL != pwszNewPath)
{
delete [] pwszNewPath;
}
if (pIMeta && NULL != hMetaKey)
{
pIMeta->CloseKey(hMetaKey);
}
if (NULL != hMetaRoot)
{
hr = vrCloseKey(pIMeta, hMetaRoot, hr);
}
if (NULL != pIWam)
{
pIWam->Release();
}
if (NULL != pIMeta)
{
pIMeta->Release();
}
return(hr);
}
BOOL
TestForVDir(
IN WCHAR *pwszVRootName)
{
HRESULT hr;
IMSAdminBase *pIMeta = NULL;
BOOL fExists = FALSE;
BOOL fCoInit = FALSE;
METADATA_HANDLE hMetaRoot = NULL; // Open key to ROOT (where VDirs live)
METADATA_HANDLE hTestHandle = NULL;
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = TRUE;
if (!IsIISInstalled(&hr))
{
goto error; // Ignore if IIS is not functioning or not installed
}
// Create an instance of the metabase object
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
__try
{
hr = vrOpenRoot(pIMeta, TRUE, &hMetaRoot);
_LeaveIfError(hr, "vrOpenRoot");
// If we got here, we must have the master root handle
// look for VDir
hr = pIMeta->OpenKey(
hMetaRoot,
pwszVRootName,
METADATA_PERMISSION_READ,
1000,
&hTestHandle);
DBGPRINT((
DBG_SS_CERTLIBI,
"TestForVDir: OpenKey(%ws) --> %x\n",
pwszVRootName,
hr));
if (S_OK != hr)
{
hr = S_OK;
__leave;
}
fExists = TRUE;
hr = pIMeta->CloseKey(hTestHandle);
_LeaveIfError(hr, "CloseKey");
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
error:
if (NULL != hMetaRoot)
{
hr = vrCloseKey(pIMeta, hMetaRoot, hr);
}
if (NULL != pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return(fExists);
}
#define SZ_HKEY_IIS_REGVROOT L"SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters\\Virtual Roots"
HRESULT
RemoveVDir(
IN WCHAR *pwszVRootName,
OUT BOOL *pfExisted)
{
HRESULT hr;
HRESULT hr2;
BOOL fCoInit = FALSE;
IMSAdminBase *pIMeta = NULL;
METADATA_HANDLE hMetaRoot = NULL; // Open key to ROOT (where VDirs live)
*pfExisted = FALSE;
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = TRUE;
// Create an instance of the metabase object
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
__try
{
hr = vrOpenRoot(pIMeta, FALSE, &hMetaRoot);
_LeaveIfError(hr, "vrOpenRoot");
// If we got to here, we must have the master root handle
// remove VDir
hr2 = pIMeta->DeleteAllData(
hMetaRoot,
pwszVRootName,
ALL_METADATA,
ALL_METADATA);
if (S_OK != hr2 && HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) != hr2)
{
hr = hr2;
_PrintError(hr2, "DeleteAllData");
}
if (S_OK == hr2)
{
*pfExisted = TRUE;
}
hr2 = pIMeta->DeleteKey(hMetaRoot, pwszVRootName);
if (S_OK != hr2 && HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) != hr2)
{
if (S_OK == hr)
{
hr = hr2;
}
_PrintError(hr2, "DeleteKey");
}
// HACKHACK: IIS reports S_OK in all cases above. However, if IIS is
// stopped, it will recreate vroots when restarted. We have to delete
// them from the registry manually (bleah!).
{
HKEY hKey;
hr2 = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
SZ_HKEY_IIS_REGVROOT,
0,
KEY_SET_VALUE,
&hKey);
_PrintIfError2(hr2, "RegDeleteValue", ERROR_FILE_NOT_FOUND);
if (hr2 == S_OK)
{
WCHAR wsz[MAX_PATH + 1];
if (wcslen(pwszVRootName) + 2 > ARRAYSIZE(wsz))
{
CSASSERT(!"pwszVRootName too long!");
}
else
{
wsz[0] = L'/';
wcscpy(&wsz[1], pwszVRootName);
hr2 = RegDeleteValue(hKey, wsz);
_PrintIfError2(
hr2,
"RegDeleteValue (manual deletion of IIS VRoot)",
ERROR_FILE_NOT_FOUND);
}
RegCloseKey(hKey);
}
// ignore missing vroot entries
if (S_OK == hr && (HRESULT) ERROR_FILE_NOT_FOUND != hr2)
{
hr = hr2;
}
}
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
error:
if (NULL != hMetaRoot)
{
hr = vrCloseKey(pIMeta, hMetaRoot, hr);
}
if (NULL != pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return(hr);
}
//+------------------------------------------------------------------------
// Function: vrModifyVirtualRoots()
//
// Synopsis: Creates the virtual roots needed for cert server web pages.
//
// Effects: Creates IIS Virtual Roots
//
// Arguments: None.
//-------------------------------------------------------------------------
HRESULT
vrModifyVirtualRoots(
IN BOOL fCreate, // else Delete
IN BOOL fNTLM,
OPTIONAL OUT DWORD *pDisposition)
{
HRESULT hr;
HRESULT hr2;
WCHAR wszSystem32Path[MAX_PATH];
WCHAR wszVRootPathTemp[MAX_PATH];
BOOL fCoInit = FALSE;
VROOTENTRY *pavr;
BOOL fExist;
DWORD Disposition = 0;
if (NULL != pDisposition)
{
*pDisposition = 0;
}
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = TRUE;
DBGPRINT((
DBG_SS_CERTLIBI,
"vrModifyVirtualRoots(tid=%x, fCreate=%d, fNTLM=%d)\n",
GetCurrentThreadId(),
fCreate,
fNTLM));
if (!IsIISInstalled(&hr))
{
// IIS is not functioning or not installed
_PrintError2(hr, "IsIISInstalled", hr);
hr = S_OK;
Disposition = VFD_NOTSUPPORTED;
goto error;
}
// Create path for SYSTEM32 directory
if (0 == GetSystemDirectory(wszSystem32Path, ARRAYSIZE(wszSystem32Path)))
{
hr = myHLastError();
_JumpError(hr, error, "GetSystemDirectory");
}
// Create virtual roots
for (pavr = g_avr; NULL != pavr->pwszVRootName; pavr++)
{
CSASSERT(ARRAYSIZE(wszVRootPathTemp) >
wcslen(wszSystem32Path) + wcslen(pavr->pwszDirectory));
wcscpy(wszVRootPathTemp, wszSystem32Path);
wcscat(wszVRootPathTemp, pavr->pwszDirectory);
if (fCreate)
{
if (0 == (VRE_DELETEONLY & pavr->Flags)) // if not obsolete
{
hr = AddNewVDir(
pavr->pwszVRootName,
wszVRootPathTemp,
(VRE_SCRIPTMAP & pavr->Flags)? TRUE : FALSE,
(fNTLM && (VRE_ALLOWNTLM & pavr->Flags))? TRUE : FALSE,
(VRE_CREATEAPP & pavr->Flags)? TRUE : FALSE,
&fExist);
if (S_OK != hr)
{
Disposition = VFD_CREATEERROR;
_JumpErrorStr(hr, error, "AddNewVDir", pavr->pwszVRootName);
}
Disposition = fExist? VFD_EXISTS : VFD_CREATED;
}
}
else // else Delete
{
hr2 = RemoveVDir(pavr->pwszVRootName, &fExist);
if (0 == (VRE_DELETEONLY & pavr->Flags)) // if not obsolete
{
if (S_OK != hr2)
{
if (S_OK == hr)
{
hr = hr2;
}
Disposition = VFD_DELETEERROR;
_PrintError(hr2, "RemoveVDir");
}
else
{
Disposition = fExist? VFD_DELETED : VFD_NOTFOUND;
}
}
}
}
error:
if (NULL != pDisposition)
{
*pDisposition = Disposition;
}
if (fCoInit)
{
CoUninitialize();
}
DBGPRINT((
DBG_SS_CERTLIBI,
"vrModifyVirtualRoots(tid=%x, hr=%x, disp=%d)\n",
GetCurrentThreadId(),
hr,
Disposition));
return(hr);
}
// myAddShare: create and test new net share
HRESULT
myAddShare(
LPCWSTR szShareName,
LPCWSTR szShareDescr,
LPCWSTR szSharePath,
BOOL fOverwrite,
OPTIONAL BOOL *pfCreated)
{
HRESULT hr;
BOOL fCreated = FALSE;
HANDLE hTestFile = INVALID_HANDLE_VALUE;
LPWSTR pwszTestComputerName = NULL;
LPWSTR pwszTestUNCPath = NULL;
// Share local path
SHARE_INFO_502 shareStruct;
ZeroMemory(&shareStruct, sizeof(shareStruct));
shareStruct.shi502_netname = const_cast<WCHAR *>(szShareName);
shareStruct.shi502_type = STYPE_DISKTREE;
shareStruct.shi502_remark = const_cast<WCHAR *>(szShareDescr);
shareStruct.shi502_max_uses = MAXDWORD;
shareStruct.shi502_path = const_cast<WCHAR *>(szSharePath);
hr = myGetSDFromTemplate(WSZ_DEFAULT_SHARE_SECURITY,
NULL,
&shareStruct.shi502_security_descriptor);
_JumpIfError(hr, error, "myGetSDFromTemplate");
hr = NetShareAdd(
NULL, // this computer
502, // SHARE_LEVEL_502 struct
(BYTE *) &shareStruct,
NULL);
fCreated = (S_OK == hr);
if (hr == (HRESULT) NERR_DuplicateShare)
{
SHARE_INFO_2* pstructDupShare = NULL;
hr = NetShareGetInfo(
NULL,
const_cast<WCHAR *>(szShareName),
2,
(BYTE **) &pstructDupShare);
_JumpIfError(hr, error, "NetShareGetInfo");
if (0 == wcscmp(pstructDupShare->shi2_path, szSharePath))
{
// they're the same path, so we're okay!
hr = S_OK;
}
else if (fOverwrite)
{
// not the same path, but we've been instructed to bash existing
// remove offending share
hr = NetShareDel(
NULL,
const_cast<WCHAR *>(szShareName),
0);
if (S_OK == hr)
{
// try again
hr = NetShareAdd(
NULL, // this computer
502, // SHARE_LEVEL_502 struct
(BYTE *) &shareStruct,
NULL);
fCreated = (S_OK == hr);
}
}
if (NULL != pstructDupShare)
{
NetApiBufferFree(pstructDupShare);
}
}
// if share does not exist by this time, we bail
_JumpIfError(hr, error, "NetShareAdd");
// TEST: is writable?
#define UNCPATH_TEMPLATE L"\\\\%ws\\%ws\\write.tmp"
hr = myGetMachineDnsName(&pwszTestComputerName);
_JumpIfError(hr, error, "myGetMachineDnsName");
// get the local machine name
pwszTestUNCPath = (LPWSTR)LocalAlloc(LMEM_FIXED,
(UINT)(( ARRAYSIZE(UNCPATH_TEMPLATE) +
wcslen(pwszTestComputerName) +
wcslen(szShareName) )
*sizeof(WCHAR)));
if (NULL == pwszTestUNCPath)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
// create UNC path
swprintf(pwszTestUNCPath, UNCPATH_TEMPLATE, pwszTestComputerName, szShareName);
hTestFile = CreateFile(
pwszTestUNCPath,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (hTestFile == INVALID_HANDLE_VALUE)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CreateFile (test for UNC translation)", pwszTestUNCPath);
}
// if we got this far, our test went well
hr = S_OK;
error:
// if created and then something went wrong, clean up
if (fCreated && (hr != S_OK))
{
// don't mash hr
HRESULT hr2;
hr2 = NetShareDel(
NULL,
const_cast<WCHAR *>(szShareName),
0);
// ignore NetShareDel hr
_PrintIfError(hr2, "NetShareDel"); // not fatal, might already be shared
}
if (INVALID_HANDLE_VALUE != hTestFile)
CloseHandle(hTestFile);
if (NULL != pwszTestComputerName)
LocalFree(pwszTestComputerName);
if (NULL != pwszTestUNCPath)
LocalFree(pwszTestUNCPath);
if(shareStruct.shi502_security_descriptor)
{
LocalFree(shareStruct.shi502_security_descriptor);
}
if(pfCreated)
*pfCreated = fCreated;
return hr;
}
HRESULT
vrModifyFileShares(
IN BOOL fCreate, // else Delete
OPTIONAL OUT DWORD *pDisposition)
{
HRESULT hr;
WCHAR wszSystem32Dir[MAX_PATH];
WCHAR wszRemark[512];
WCHAR *pwszDirectory = NULL;
DWORD Disposition = 0;
BOOL fCreated = FALSE;
if (NULL != pDisposition)
{
*pDisposition = 0;
}
if (fCreate)
{
if (0 == GetSystemDirectory(wszSystem32Dir, ARRAYSIZE(wszSystem32Dir)))
{
hr = myHLastError();
_JumpError(hr, error, "GetSystemDirectory");
}
hr = myBuildPathAndExt(
wszSystem32Dir,
wszCERTENROLLSHAREPATH,
NULL,
&pwszDirectory);
_JumpIfError(hr, error, "myBuildPathAndExt");
if (!LoadString(
g_hInstance,
IDS_FILESHARE_REMARK,
wszRemark,
ARRAYSIZE(wszRemark)))
{
hr = myHLastError();
CSASSERT(S_OK != hr);
_JumpError(hr, error, "LoadString");
}
hr = myAddShare(wszCERTENROLLSHARENAME,
wszRemark,
pwszDirectory,
TRUE,
&fCreated);
if (S_OK == hr)
{
Disposition = fCreated? VFD_CREATED : VFD_EXISTS;
}
else if(HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE) == hr)
{
// Could not validate the share. Can happen if net cable is disconnected.
// Put a warning message and ignore the error.
Disposition = VFD_VERIFYERROR;
hr = S_OK;
}
else
{
Disposition = VFD_CREATEERROR;
_JumpErrorStr(hr, error, "NetShareAdd", wszCERTENROLLSHARENAME);
}
}
else
{
hr = NetShareDel(NULL, wszCERTENROLLSHARENAME, NULL);
CSASSERT(NERR_Success == S_OK);
if (S_OK == hr)
{
Disposition = VFD_DELETED;
}
else if ((HRESULT) NERR_NetNameNotFound == hr)
{
Disposition = VFD_NOTFOUND;
hr = S_OK;
}
else
{
Disposition = VFD_DELETEERROR;
_JumpErrorStr(hr, error, "NetShareDel", wszCERTENROLLSHARENAME);
}
}
NetShareDel(NULL, L"CertSrv", NULL); // delete old share name
error:
if (NULL != pDisposition)
{
*pDisposition = Disposition;
}
if (NULL != pwszDirectory)
{
LocalFree(pwszDirectory);
}
return(myHError(hr));
}
// For now, this writes the entry "CertUtil -vroot", and is not generalized
HRESULT
myWriteRunOnceEntry(
IN BOOL fAdd // Add or Remove entry?
)
{
DWORD err;
// Add certutil -vroot to runonce commands
WCHAR szRunOnceCommand[] = L"certutil -vroot";
HKEY hkeyRunOnce = NULL;
DWORD dwDisposition;
err = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", // address of subkey name
0,
NULL,
0,
KEY_SET_VALUE,
NULL,
&hkeyRunOnce,
&dwDisposition);
_JumpIfError(err, error, "RegCreateKeyEx");
// add or remove entry?
if (fAdd)
{
err = RegSetValueEx(
hkeyRunOnce,
L"Certificate Services",
0,
REG_SZ,
(BYTE *) szRunOnceCommand,
sizeof(szRunOnceCommand));
_JumpIfError(err, error, "RegSetValueEx");
}
else
{
err = RegDeleteValue(hkeyRunOnce, L"Certificate Services");
_PrintIfError2(err, "RegDeleteValue", ERROR_FILE_NOT_FOUND);
if (ERROR_FILE_NOT_FOUND == err)
{
err = ERROR_SUCCESS;
}
_JumpIfError(err, error, "RegDeleteValue");
}
error:
if (hkeyRunOnce)
RegCloseKey(hkeyRunOnce);
return (myHError(err));
}
DWORD
vrWorkerThread(
OPTIONAL IN OUT VOID *pvparms)
{
HRESULT hr = S_OK;
HRESULT hr2;
VRFSPARMS *pparms = (VRFSPARMS *) pvparms;
DWORD Disposition;
BOOL fFailed = FALSE;
CSASSERT(NULL != pparms);
if ((VFF_CREATEFILESHARES | VFF_DELETEFILESHARES) & pparms->Flags)
{
hr = vrModifyFileShares(
(VFF_CREATEFILESHARES & pparms->Flags)? TRUE : FALSE,
&Disposition);
_PrintIfError(hr, "vrModifyFileShares");
if (NULL != pparms->pShareDisposition)
{
*pparms->pShareDisposition = Disposition;
}
if (VFD_CREATEERROR == Disposition || VFD_DELETEERROR == Disposition)
{
fFailed = TRUE;
}
}
if ((VFF_CREATEVROOTS | VFF_DELETEVROOTS) & pparms->Flags)
{
BOOL fNTLM = FALSE; // set fNTLM iff Enterprise CA
if (IsEnterpriseCA(pparms->CAType))
{
fNTLM = TRUE;
}
hr2 = vrModifyVirtualRoots(
(VFF_CREATEVROOTS & pparms->Flags)? TRUE : FALSE,
fNTLM,
&Disposition);
_PrintIfError2(hr2, "vrModifyVirtualRoots", S_FALSE);
if (S_OK == hr)
{
hr = hr2;
}
if (NULL != pparms->pVRootDisposition)
{
*pparms->pVRootDisposition = Disposition;
}
if (VFD_CREATEERROR == Disposition || VFD_DELETEERROR == Disposition)
{
fFailed = TRUE;
}
if (S_OK == hr)
{
if (VFF_CREATEVROOTS & pparms->Flags)
{
DWORD ASPDisposition;
BOOL fEnabledASP;
hr2 = EnableASPInIIS(&fEnabledASP);
_PrintIfError(hr2, "EnableASPInIIS");
ASPDisposition = VFD_NOACTION;
hr2 = SetCertSrvASPDependency();
_PrintIfError(hr2, "SetCertSrvASPDependency");
if (S_OK != hr2)
{
ASPDisposition = VFD_CREATEERROR;
}
// enable ASP processing in IIS
if (VFF_ENABLEASP & pparms->Flags)
{
hr2 = EnableASPInIIS_New(&fEnabledASP);
_PrintIfError(hr2, "EnableASPInIIS_New");
if (S_OK == hr2)
{
ASPDisposition = fEnabledASP? VFD_CREATED : VFD_EXISTS;
}
}
if (NULL != pparms->pVRootDisposition)
{
*pparms->pVRootDisposition |= (ASPDisposition << 16);
}
if (VFD_CREATEERROR == ASPDisposition)
{
fFailed = TRUE;
}
}
}
}
if ((S_OK == hr && !fFailed) || ((VFF_DELETEVROOTS) & pparms->Flags)) // on success or removal
{
// remove "attempt vroot" flag so we don't try again
if (VFF_CLEARREGFLAGIFOK & pparms->Flags)
{
DBGPRINT((DBG_SS_CERTLIBI, "clearing registry\n"));
hr = SetSetupStatus(NULL, SETUP_ATTEMPT_VROOT_CREATE, FALSE);
_JumpIfError(hr, error, "SetSetupStatus");
}
hr = myWriteRunOnceEntry(FALSE); // worker thread deletes on success
_JumpIfError(hr, error, "myWriteRunOnceEntry");
}
error:
LocalFree(pparms);
DBGPRINT((DBG_SS_CERTLIBI, "vrWorkerThread returns %x\n", hr));
return(myHError(hr));
}
//+------------------------------------------------------------------------
// Function: myModifyVirtualRootsAndFileShares
//
// Synopsis: Creates the virtual roots needed for cert server web pages.
//
// Effects: Creates IIS Virtual Roots
//
// Arguments: None.
//-------------------------------------------------------------------------
HRESULT
myModifyVirtualRootsAndFileShares(
IN DWORD Flags, // VFF_*: Create/Delete VRoots and/or Shares
IN ENUM_CATYPES CAType,
IN BOOL fAsynchronous,
IN DWORD csecTimeOut,
OPTIONAL OUT DWORD *pVRootDisposition, // VFD_*
OPTIONAL OUT DWORD *pShareDisposition) // VFD_*
{
HRESULT hr;
HANDLE hThread = NULL;
HMODULE hMod = NULL;
DWORD ThreadId;
DWORD dw;
BOOL fEnable = TRUE;
DWORD SetupStatus;
VRFSPARMS *pparms = NULL;
if (NULL != pVRootDisposition)
{
*pVRootDisposition = 0;
}
if (NULL != pShareDisposition)
{
*pShareDisposition = 0;
}
dw = (VFF_DELETEVROOTS | VFF_DELETEFILESHARES) & Flags;
if (0 != dw && dw != Flags)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Mixed VFF_DELETE* and create flags");
}
if (((VFF_CHECKREGFLAGFIRST | VFF_CLEARREGFLAGFIRST) & Flags) &&
(VFF_SETREGFLAGFIRST & Flags))
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Mixed VFF_SETREGFLAGFIRST & VFF_*REGFLAGFIRST");
}
hr = GetSetupStatus(NULL, &SetupStatus);
if (S_OK != hr)
{
_PrintError(hr, "GetSetupStatus(ignored)");
hr = S_OK;
SetupStatus = 0;
}
if (VFF_CHECKREGFLAGFIRST & Flags)
{
if (0 == (SETUP_ATTEMPT_VROOT_CREATE & SetupStatus))
{
fEnable = FALSE;
}
}
if (VFF_CLEARREGFLAGFIRST & Flags)
{
// remove "attempt vroot" flag so we don't try again
if (SETUP_ATTEMPT_VROOT_CREATE & SetupStatus)
{
hr = SetSetupStatus(NULL, SETUP_ATTEMPT_VROOT_CREATE, FALSE);
_JumpIfError(hr, error, "SetSetupStatus");
}
}
if (VFF_SETREGFLAGFIRST & Flags)
{
// set "attempt vroot" flag so we'll try again if necessary
if (0 == (SETUP_ATTEMPT_VROOT_CREATE & SetupStatus))
{
hr = SetSetupStatus(NULL, SETUP_ATTEMPT_VROOT_CREATE, TRUE);
_JumpIfError(hr, error, "SetSetupStatus");
}
}
hr = S_OK;
if (fEnable)
{
// only set RunOnce on a real attempt (worker thread clears this)
if (VFF_SETRUNONCEIFERROR & Flags)
{
hr = myWriteRunOnceEntry(TRUE);
_JumpIfError(hr, error, "myWriteRunOnceEntry");
}
pparms = (VRFSPARMS *) LocalAlloc(
LMEM_FIXED | LMEM_ZEROINIT,
sizeof(*pparms));
if (NULL == pparms)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
pparms->Flags = Flags;
pparms->CAType = CAType;
pparms->csecTimeOut = csecTimeOut;
pparms->fAsynchronous = fAsynchronous;
if (!fAsynchronous)
{
pparms->pVRootDisposition = pVRootDisposition;
pparms->pShareDisposition = pShareDisposition;
}
else
{
hMod = LoadLibrary(g_wszCertCliDotDll);
if (NULL == hMod)
{
hr = myHLastError();
_JumpError(hr, error, "LoadLibrary");
}
}
hThread = CreateThread(
NULL, // lpThreadAttributes (Security Attr)
0, // dwStackSize
vrWorkerThread,
pparms, // lpParameter
0, // dwCreationFlags
&ThreadId);
if (NULL == hThread)
{
hr = myHLastError();
_JumpError(hr, error, "CreateThread");
}
pparms = NULL; // freed by the new thread
DBGPRINT((DBG_SS_CERTLIBI, "VRoot Worker Thread = %x\n", ThreadId));
// asynch? proper thread creation is all we do
if (fAsynchronous)
{
hr = S_OK;
goto error;
}
// Wait for the worker thread to exit
hr = WaitForSingleObject(
hThread,
(INFINITE == csecTimeOut) ? INFINITE : csecTimeOut * 1000 );
DBGPRINT((DBG_SS_CERTLIBI, "Wait for worker thread returns %x\n", hr));
if ((HRESULT) WAIT_OBJECT_0 == hr)
{
// worker thread returned.
if (!GetExitCodeThread(hThread, (DWORD *) &hr))
{
hr = myHLastError();
_JumpError(hr, error, "GetExitCodeThread");
}
DBGPRINT((DBG_SS_CERTLIBI, "worker thread exit: %x\n", hr));
if (S_OK != hr)
{
// If not synchronous, leave DLL loaded...
hMod = NULL;
_JumpError(hr, error, "vrWorkerThread");
}
}
else
{
// timeout: abandoning thread, leave the dll loaded
hMod = NULL;
_PrintError(hr, "WaitForSingleObject (ignored)");
// whack error
hr = S_OK;
}
}
error:
if (NULL != pparms)
{
LocalFree(pparms);
}
if (NULL != hThread)
{
CloseHandle(hThread);
}
if (NULL != hMod)
{
FreeLibrary(hMod);
}
DBGPRINT((DBG_SS_CERTLIBI, "myModifyVirtualRootsAndFileShares returns %x\n", hr));
return(myHError(hr));
}
//-------------------------------------------------------------------------
//
// ASP/IIS related functions
//
//-------------------------------------------------------------------------
#define MD_ISAPI_RESTRICTION_LIST_OBSOLETE (IIS_MD_HTTP_BASE+163)
METADATA_RECORD ASPRestrictionsMDData =
{
MD_ISAPI_RESTRICTION_LIST_OBSOLETE,
0,
IIS_MD_UT_SERVER,
MULTISZ_METADATA,
0,
NULL,
0
};
METADATA_RECORD ApplicationDependenciesMDData =
{
MD_APP_DEPENDENCIES,
0,
IIS_MD_UT_SERVER,
MULTISZ_METADATA,
0,
NULL,
0
};
METADATA_RECORD WebSvcExtRestrictionsMDData =
{
MD_WEB_SVC_EXT_RESTRICTION_LIST,
0,
IIS_MD_UT_SERVER,
MULTISZ_METADATA,
0,
NULL,
0
};
const WCHAR g_wchExtensionOff = L'0';
const WCHAR g_wchExtensionOn = L'1';
LPCWSTR g_pcwszAspDll = L"asp.dll";
//+------------------------------------------------------------------------
// Builds the full path to asp.dll
//-------------------------------------------------------------------------
HRESULT BuildASPDllFullPath(LPWSTR &rpwszAspPath)
{
HRESULT hr;
WCHAR wszAsp[MAX_PATH];
LPCWSTR pcwszAsp = L"\\inetsrv\\asp.dll";
if (0 == GetSystemDirectory(wszAsp, MAX_PATH))
{
hr = myHLastError();
_JumpError(hr, error, "GetSystemDirectory");
}
rpwszAspPath = (LPWSTR)LocalAlloc(LMEM_FIXED,
sizeof(WCHAR)*(wcslen(wszAsp)+wcslen(pcwszAsp)+1));
_JumpIfAllocFailed(rpwszAspPath, error);
wcscpy(rpwszAspPath, wszAsp);
wcscat(rpwszAspPath, pcwszAsp);
hr = S_OK;
error:
return hr;
}
//+------------------------------------------------------------------------
// Saves the list of ASP restrictions to IIS metabase. IIS restriction
// list it's a multisz that looks like this:
//
// "1","DLL1","DLL2"...
// or
// "0","DLL1","DLL2"...
//
// When list starts with "1", the meaning is "enable all ASP DLLs
// excluding the list that follows". When it begins with "0", it means
// "disable all except the list".
//-------------------------------------------------------------------------
HRESULT SetASPRestrictions(CMultiSz& ASPRestrictionList)
{
HRESULT hr;
IMSAdminBase *pIMeta = NULL;
METADATA_HANDLE hMetaRoot = NULL;
void * pBuffer = NULL;
DWORD cBuffer;
bool fCoInit = false;
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = true;
hr = ASPRestrictionList.Marshal(pBuffer, cBuffer);
_JumpIfError(hr, error, "CMultiSz::Marshal");
ASPRestrictionsMDData.pbMDData = (unsigned char *)pBuffer;
ASPRestrictionsMDData.dwMDDataLen = cBuffer;
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
g_wszW3SVC,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
1000,
&hMetaRoot);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
hr = pIMeta->SetData(hMetaRoot, L"", &ASPRestrictionsMDData);
_JumpIfError(hr, error, "SetData");
hr = pIMeta->CloseKey(hMetaRoot);
_JumpIfError(hr, error, "CloseKey");
hMetaRoot = NULL;
hr = pIMeta->SaveData();
_JumpIfError(hr, error, "SaveData");
error:
LOCAL_FREE(pBuffer);
ASPRestrictionsMDData.pbMDData = NULL;
ASPRestrictionsMDData.dwMDDataLen = 0;
if (NULL != hMetaRoot)
{
pIMeta->CloseKey(hMetaRoot);
}
if(pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return hr;
}
//+------------------------------------------------------------------------
// Loads the list of ASP restrictions from IIS metabase.
//-------------------------------------------------------------------------
HRESULT GetASPRestrictions(CMultiSz& ASPRestrictionList)
{
HRESULT hr;
IMSAdminBase *pIMeta = NULL;
METADATA_HANDLE hMetaRoot = NULL;
DWORD dwSize;
bool fCoInit = false;
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = true;
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
g_wszW3SVC,
METADATA_PERMISSION_READ,
1000,
&hMetaRoot);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
hr = pIMeta->GetData(hMetaRoot, L"", &ASPRestrictionsMDData, &dwSize);
if(MD_ERROR_DATA_NOT_FOUND==hr)
{
// value not set means ASP not enabled, return empty list
hr = S_OK;
}
else
{
if(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
{
_JumpErrorStr(hr, error, "GetData", g_wszW3SVC);
}
hr = S_OK;
ASPRestrictionsMDData.pbMDData = (unsigned char*) LocalAlloc(LMEM_FIXED, dwSize);
_JumpIfAllocFailed(ASPRestrictionsMDData.pbMDData, error);
ASPRestrictionsMDData.dwMDDataLen = dwSize;
hr = pIMeta->GetData(hMetaRoot, L"", &ASPRestrictionsMDData, &dwSize);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
hr = ASPRestrictionList.Unmarshal(ASPRestrictionsMDData.pbMDData);
_JumpIfError(hr, error, "Unmarshal");
}
error:
LOCAL_FREE(ASPRestrictionsMDData.pbMDData);
ASPRestrictionsMDData.pbMDData = NULL;
ASPRestrictionsMDData.dwMDDataLen = 0;
if (NULL != hMetaRoot)
{
pIMeta->CloseKey(hMetaRoot);
}
if(pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return hr;
}
//+------------------------------------------------------------------------
//
// Verify if this is MSCEP setup specific
//
//
//+------------------------------------------------------------------------
HRESULT SetupMSCEPForIIS(LPCWSTR pcwszExtension);
BOOL IsMSCEPSetup(LPCWSTR pwsz, LPCWSTR pwszDLL)
{
BOOL fResult=FALSE;
LPWSTR pwchar=NULL;
if((NULL == pwsz) || (NULL == pwszDLL))
goto error;
pwchar=wcsrchr(pwsz, L'\\');
if(NULL == pwchar)
goto error;
pwchar++;
if(0 != _wcsicmp(pwchar, pwszDLL))
goto error;
fResult=TRUE;
error:
return fResult;
}
//+------------------------------------------------------------------------
// Tests if this ISAPI extension is enabled in IIS.
// If IIS restriction starts with a "1":
// "1", "DLL1", "DLL2" ...
// it means run all but specified DLLs; if we find the extension
// in the list then it's disabled
//
// If IIS restriction starts with a "0":
// "0", "DLL1", "DLL2" ...
// the meaning is disable all but specified DLLs; if we find
// the extension then it's enabled
//-------------------------------------------------------------------------
HRESULT IsISAPIExtensionEnabled(
LPCWSTR pcwszExtension,
bool& rfEnabled)
{
HRESULT hr;
CMultiSz ASPRestrictionList;
CMultiSzEnum ASPRestrictionListEnum;
rfEnabled = false;
//special case for mscep.dll. Always return FALSE to
//proceed with the installation
if(IsMSCEPSetup(pcwszExtension, g_wszMSCEP))
{
rfEnabled=FALSE;
return S_OK;
}
hr = GetASPRestrictions(ASPRestrictionList);
_JumpIfError(hr, error, "GetASPRestrictionList");
if(!ASPRestrictionList.IsEmpty())
{
ASPRestrictionListEnum.Set(ASPRestrictionList);
const CString *pStr = ASPRestrictionListEnum.Next();
bool fRunAllExceptTheseDlls = false;
if(0 == wcscmp(*pStr, L"1"))
{
fRunAllExceptTheseDlls = true;
}
for(pStr = ASPRestrictionListEnum.Next();
pStr;
pStr = ASPRestrictionListEnum.Next())
{
if(0 == _wcsicmp(pcwszExtension, *pStr))
break;
}
// XOR: Enable if "1" is found but asp.dll not present or "0" (!"1")
// is found but asp.dll present
rfEnabled = fRunAllExceptTheseDlls ^ (NULL != pStr);
}
hr = S_OK;
error:
return hr;
}
//+------------------------------------------------------------------------
// Tests if ASP processing is enabled in IIS.
//-------------------------------------------------------------------------
HRESULT IsASPEnabledInIIS(
bool& rfEnabled)
{
HRESULT hr;
LPWSTR pwszAsp = NULL;
hr = BuildASPDllFullPath(pwszAsp);
_JumpIfError(hr, error, "GetASPDllFullPath");
hr = IsISAPIExtensionEnabled(
pwszAsp,
rfEnabled);
_JumpIfErrorStr(hr, error, "GetASPDllFullPath", pwszAsp);
error:
LOCAL_FREE(pwszAsp);
return hr;
}
//+------------------------------------------------------------------------
// Enables ISAPI extension in IIS.
//
// - if list is empty/not found, set it to "0","extension", ie only enable
// this extension
// - if list starts with "1", remove extension if found
// - if list starts with "0", add extension if not already present
//-------------------------------------------------------------------------
#pragma warning(push) // BUGBUG: nonstandard extension used : 'argument' : conversion from 'CString' to 'C &'
#pragma warning(disable: 4239) // BUGBUG: nonstandard extension used : 'argument' : conversion from 'CString' to 'C &'
HRESULT
EnableISAPIExtension(
IN LPCWSTR pcwszExtension,
OUT BOOL *pfEnabledASP)
{
HRESULT hr;
CMultiSz ASPRestrictionList;
CMultiSzEnum ASPRestrictionListEnum;
CString *pStr;
DWORD dwIndex;
bool fUpdateIt = false;
*pfEnabledASP = FALSE;
//special case for mscep.dll.
if(IsMSCEPSetup(pcwszExtension, g_wszMSCEP))
{
hr=SetupMSCEPForIIS(pcwszExtension);
_JumpIfError(hr, error, "SetupMSCEPForIIS");
}
hr = GetASPRestrictions(ASPRestrictionList);
_JumpIfError(hr, error, "GetASPRestrictions");
ASPRestrictionListEnum.Set(ASPRestrictionList);
pStr = ASPRestrictionListEnum.Next();
if(!pStr)
{
// list is empty, add "0"
pStr = new CString(L"0");
if(!pStr || pStr->IsEmpty())
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "new");
}
ASPRestrictionList.AddTail(pStr);
}
dwIndex = ASPRestrictionList.FindIndex(CString(pcwszExtension));
if(0 == wcscmp(*pStr, L"0"))
{
// List means "disable all but the following DLLs".
// To enable it add ASP dll if not already there.
if(DWORD_MAX == dwIndex)
{
CString * pAsp = new CString(pcwszExtension);
if(!pAsp || pAsp->IsEmpty())
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "new");
}
ASPRestrictionList.AddTail(pAsp);
fUpdateIt = true;
}
}
else
{
// List means "enable all but the following DLLs", to enable it
// remove extension if found
dwIndex = ASPRestrictionList.FindIndex(CString(pcwszExtension));
if(DWORD_MAX != dwIndex)
{
ASPRestrictionList.RemoveAt(dwIndex);
fUpdateIt = true;
}
}
if (fUpdateIt)
{
hr = SetASPRestrictions(ASPRestrictionList);
_JumpIfError(hr, error, "SetASPRestrictions");
*pfEnabledASP = TRUE;
}
error:
return S_OK;
}
#pragma warning(pop) // BUGBUG: nonstandard extension used : 'argument' : conversion from 'CString' to 'C &'
//+------------------------------------------------------------------------
// Enables ASP processing in IIS.
//-------------------------------------------------------------------------
HRESULT
EnableASPInIIS(
OUT BOOL *pfEnabledASP)
{
HRESULT hr;
LPWSTR pwszAsp = NULL;
*pfEnabledASP = FALSE;
hr = BuildASPDllFullPath(pwszAsp);
_JumpIfError(hr, error, "GetASPDllFullPath");
hr = EnableISAPIExtension(pwszAsp, pfEnabledASP);
_JumpIfErrorStr(hr, error, "GetASPDllFullPath", pwszAsp);
error:
LOCAL_FREE(pwszAsp);
return S_OK;
}
//+------------------------------------------------------------------------
// New APIs after IIS redesigning of enabling/disabling extensions
//-------------------------------------------------------------------------
HRESULT
SetMultiSzIISMetadata(
METADATA_RECORD& MDRecord,
CMultiSz& MultiSz);
HRESULT
GetMultiSzIISMetadata(
METADATA_RECORD& MDRecord,
CMultiSz& MultiSz);
//+------------------------------------------------------------------------
// Searches for the websvc specified extension and turns it on if needed
//-------------------------------------------------------------------------
HRESULT
EnableWebSvcExtension(
IN LPCWSTR pcwszExtDll, // e.g. "asp.dll"
OUT BOOL *pfEnabled)
{
HRESULT hr = S_OK;
CMultiSz WebSvcExtRestrictions;
CMultiSzEnum WebSvcExtRestrictionsEnum;
CString *pstr;
CString strTmp;
CString strExtDll = pcwszExtDll;
*pfEnabled = FALSE;
_wcslwr(strExtDll.GetBuffer()); // we need to find case insensitive, we'll
// also wcslwr the strings from list
hr = GetMultiSzIISMetadata(WebSvcExtRestrictionsMDData, WebSvcExtRestrictions);
_JumpIfError(hr, error, "GetMultiSzIISMetadata");
WebSvcExtRestrictionsEnum.Set(WebSvcExtRestrictions);
for(pstr = WebSvcExtRestrictionsEnum.Next();
pstr;
pstr = WebSvcExtRestrictionsEnum.Next())
{
// create a copy so we don't modify the original string
strTmp = *pstr;
_wcslwr(strTmp.GetBuffer());
if(NULL != wcsstr(strTmp, strExtDll))
{
if(g_wchExtensionOff == *(pstr->GetBuffer()))
{
*(pstr->GetBuffer()) = g_wchExtensionOn;
*pfEnabled = TRUE;
}
break;
}
}
if(!pstr)
{
hr = ERROR_NOT_FOUND;
_JumpErrorStr(hr, error, "extension was not found in WebSvcExtRestrictions", pcwszExtDll);
}
if(TRUE == *pfEnabled)
{
hr = SetMultiSzIISMetadata(WebSvcExtRestrictionsMDData, WebSvcExtRestrictions);
_JumpIfError(hr, error, "GetISAPIRestrictions");
}
error:
return hr;
}
//+------------------------------------------------------------------------
// Enable ASP processing in IIS
//-------------------------------------------------------------------------
HRESULT
EnableASPInIIS_New(
OUT BOOL *pfEnabledASP)
{
HRESULT hr;
LPWSTR pwszAsp = NULL;
*pfEnabledASP = FALSE;
hr = EnableWebSvcExtension(g_pcwszAspDll, pfEnabledASP);
_JumpIfErrorStr(hr, error, "GetASPDllFullPath", pwszAsp);
error:
LOCAL_FREE(pwszAsp);
return S_OK;
}
//+------------------------------------------------------------------------
// Stores a multisz property to IIS metabase root
//-------------------------------------------------------------------------
HRESULT
SetMultiSzIISMetadata(
METADATA_RECORD& MDRecord,
CMultiSz& MultiSz)
{
HRESULT hr;
IMSAdminBase *pIMeta = NULL;
METADATA_HANDLE hMetaRoot = NULL;
void * pBuffer = NULL;
DWORD cBuffer;
bool fCoInit = false;
CSASSERT(MULTISZ_METADATA == MDRecord.dwMDDataType);
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = true;
hr = MultiSz.Marshal(pBuffer, cBuffer);
_JumpIfError(hr, error, "CMultiSz::Marshal");
CSASSERT(NULL == MDRecord.pbMDData);
MDRecord.pbMDData = (unsigned char *)pBuffer;
MDRecord.dwMDDataLen = cBuffer;
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
g_wszW3SVC,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
1000,
&hMetaRoot);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
hr = pIMeta->SetData(hMetaRoot, L"", &MDRecord);
_JumpIfError(hr, error, "SetData");
hr = pIMeta->CloseKey(hMetaRoot);
_JumpIfError(hr, error, "CloseKey");
hMetaRoot = NULL;
hr = pIMeta->SaveData();
_JumpIfError(hr, error, "SaveData");
error:
LOCAL_FREE(pBuffer);
MDRecord.pbMDData = NULL;
MDRecord.dwMDDataLen = 0;
if (NULL != hMetaRoot)
{
pIMeta->CloseKey(hMetaRoot);
}
if(pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return hr;
}
//+------------------------------------------------------------------------
// Retrieves a multisz property IIS metabase root
//-------------------------------------------------------------------------
HRESULT
GetMultiSzIISMetadata(
METADATA_RECORD& MDRecord,
CMultiSz& MultiSz)
{
HRESULT hr;
IMSAdminBase *pIMeta = NULL;
METADATA_HANDLE hMetaRoot = NULL;
DWORD dwSize = 0;
bool fCoInit = false;
CSASSERT(MULTISZ_METADATA == MDRecord.dwMDDataType);
hr = CoInitialize(NULL);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitialize");
}
fCoInit = true;
hr = CoCreateInstance(
CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **) &pIMeta);
_JumpIfError(hr, error, "CoCreateInstance");
hr = pIMeta->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
g_wszW3SVC,
METADATA_PERMISSION_READ,
1000,
&hMetaRoot);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
CSASSERT(NULL == MDRecord.pbMDData);
CSASSERT(0 == MDRecord.dwMDDataLen);
hr = pIMeta->GetData(hMetaRoot, L"", &MDRecord, &dwSize);
if(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
{
_JumpErrorStr(hr, error, "GetData", g_wszW3SVC);
}
hr = S_OK;
MDRecord.pbMDData = (unsigned char*) LocalAlloc(LMEM_FIXED, dwSize);
_JumpIfAllocFailed(MDRecord.pbMDData, error);
MDRecord.dwMDDataLen = dwSize;
hr = pIMeta->GetData(hMetaRoot, L"", &MDRecord, &dwSize);
_JumpIfErrorStr(hr, error, "OpenKey", g_wszW3SVC);
hr = MultiSz.Unmarshal(MDRecord.pbMDData);
_JumpIfError(hr, error, "Unmarshal");
error:
LOCAL_FREE(MDRecord.pbMDData);
MDRecord.pbMDData = NULL;
MDRecord.dwMDDataLen = 0;
if (NULL != hMetaRoot)
{
pIMeta->CloseKey(hMetaRoot);
}
if(pIMeta)
{
pIMeta->Release();
}
if (fCoInit)
{
CoUninitialize();
}
return hr;
}
//+------------------------------------------------------------------------
// Check if an ISAPI dependency is present in the list
//-------------------------------------------------------------------------
bool IsISAPIDependencySet(CMultiSz& ISAPIDependList, LPCWSTR pcwszDependency)
{
return ISAPIDependList.Find(pcwszDependency, false); // false == case insensitive
}
//+------------------------------------------------------------------------
// Add an ISAPI dependency to the list
//-------------------------------------------------------------------------
HRESULT AddISAPIDependency(CMultiSz& ISAPIDependList, LPCWSTR pcwszDependency)
{
HRESULT hr = S_OK;
CString *pStr;
pStr = new CString(pcwszDependency);
if(!pStr || pStr->IsEmpty())
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "new");
}
if(!ISAPIDependList.AddTail(pStr))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "CMultiSz::AddTail");
}
error:
if(S_OK != hr)
{
delete pStr;
}
return hr;
}
//+------------------------------------------------------------------------
// Set an application dependency in IIS metabase
//-------------------------------------------------------------------------
HRESULT
SetApplicationDependency(LPCWSTR pcwszDependencyString)
{
HRESULT hr;
CMultiSz AppDependList;
hr = GetMultiSzIISMetadata(ApplicationDependenciesMDData, AppDependList);
_JumpIfError(hr, error, "GetISAPIRestrictions");
if(!IsISAPIDependencySet(AppDependList, pcwszDependencyString))
{
hr = AddISAPIDependency(AppDependList, pcwszDependencyString);
_JumpIfError(hr, error, "AddISAPIDependency");
}
hr = SetMultiSzIISMetadata(ApplicationDependenciesMDData, AppDependList);
_JumpIfError(hr, error, "SetISAPIRestrictions");
error:
return S_OK;
}
//+------------------------------------------------------------------------
// Set CertSrv dependency on ASP in IIS metabase
//-------------------------------------------------------------------------
HRESULT
SetCertSrvASPDependency()
{
HRESULT hr;
CString strCertSrvASPDepend;
LPCSTR pcwszASP = ";ASP";
if(!strCertSrvASPDepend.LoadString(IDS_CERTIFICATE_SERVICES))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LoadString(IDS_CERTIFICATE_SERVICES)");
}
// build the dependency string "Certificate Services;ASP"
strCertSrvASPDepend += pcwszASP;
hr = SetApplicationDependency(strCertSrvASPDepend);
_JumpIfError(hr, error, "SetApplicationDependency");
error:
return S_OK;
}
//+------------------------------------------------------------------------
// Tests if ASP processing is enabled in IIS.
//-------------------------------------------------------------------------
HRESULT IsASPEnabledInIIS_New(
bool& rfEnabled)
{
HRESULT hr;
CMultiSz WebSvcExtRestrictions;
CMultiSzEnum WebSvcExtRestrictionsEnum;
CString *pstr;
CString strTmp;
rfEnabled = false;
hr = GetMultiSzIISMetadata(WebSvcExtRestrictionsMDData, WebSvcExtRestrictions);
_JumpIfError(hr, error, "GetMultiSzIISMetadata");
WebSvcExtRestrictionsEnum.Set(WebSvcExtRestrictions);
for(pstr = WebSvcExtRestrictionsEnum.Next();
pstr;
pstr = WebSvcExtRestrictionsEnum.Next())
{
// create a copy so we don't modify the original string
strTmp = *pstr;
_wcslwr(strTmp.GetBuffer());
if(NULL != wcsstr(strTmp, g_pcwszAspDll))
{
if(g_wchExtensionOn == *(pstr->GetBuffer())) // string format is "1,"path\asp.dll,..." if
// asp is enabled
{
rfEnabled = true;
}
break;
}
}
error:
return hr;
}
//+------------------------------------------------------------------------
//
//
// SetupMSCEPForIIS
//
//-------------------------------------------------------------------------
HRESULT SetupMSCEPForIIS(LPCWSTR pcwszExtension)
{
HRESULT hr=E_FAIL;
CString strMSCEPAppDepend;
BOOL fEnabled=FALSE;
CMultiSz WebSvcExtList;
WCHAR wszDescription[255];
LPWSTR pwszWebSvcExt=NULL;
CString *pstrWebSvcExt=NULL;
if(NULL == pcwszExtension)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "CheckForInput");
}
//*******************************************************
// set up the ApplicationDepedencies in the format of
// "ApplicationName";"GroupID"
if(!strMSCEPAppDepend.LoadString(IDS_MSCEP))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LoadString(IDS_MSCEP)");
}
// build the dependency string "Certificate Services;ASP"
strMSCEPAppDepend += L";";
strMSCEPAppDepend += g_wszMSCEPID;
// add to the metadata if it does not exist
hr = SetApplicationDependency(strMSCEPAppDepend);
_JumpIfError(hr, error, "SetApplicationDependency");
//*******************************************************
// set up the WebSvcExtRestrictionList in the format of
// 1,d:\windows\system32\certsrv\mscep\mscep.dll,0,GroupID,Description
// turn on the enable bit if there exists an entry
if(S_OK == (hr=EnableWebSvcExtension(g_wszMSCEP, &fEnabled)))
goto error;
// we have to add a new entry
if(!LoadString(g_hInstance,
IDS_MSCEP_DES,
wszDescription,
ARRAYSIZE(wszDescription)))
{
hr = myHLastError();
_JumpError(hr, error, "LoadString");
}
pwszWebSvcExt=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*
(wcslen(pcwszExtension) + wcslen(g_wszMSCEPID) + wcslen(wszDescription) + 7));
if(NULL == pwszWebSvcExt)
{
hr=E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(pwszWebSvcExt, L"1,");
wcscat(pwszWebSvcExt, pcwszExtension);
wcscat(pwszWebSvcExt, L",0,");
wcscat(pwszWebSvcExt, g_wszMSCEPID);
wcscat(pwszWebSvcExt, L",");
wcscat(pwszWebSvcExt, wszDescription);
hr = GetMultiSzIISMetadata(WebSvcExtRestrictionsMDData, WebSvcExtList);
_JumpIfError(hr, error, "GetMultiSzIISMetadata");
pstrWebSvcExt=new CString(pwszWebSvcExt);
if((NULL == pstrWebSvcExt) || (pstrWebSvcExt->IsEmpty()))
{
hr=E_OUTOFMEMORY;
_JumpError(hr, error, "new");
}
if(!WebSvcExtList.AddTail(pstrWebSvcExt))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "CMultiSz::AddTail");
}
hr = SetMultiSzIISMetadata(WebSvcExtRestrictionsMDData, WebSvcExtList);
_JumpIfError(hr, error, "SetISAPIRestrictions");
hr=S_OK;
error:
if(pwszWebSvcExt)
LocalFree(pwszWebSvcExt);
if(S_OK != hr)
{
if(pstrWebSvcExt)
delete pstrWebSvcExt;
}
return hr;
}