// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" // sql queries enum eWebSvcExtQuery { ldqComponent=1 , ldqFile, ldqDescription, ldqGroup, ldqAttributes, ldqInstalled, ldqAction }; LPCWSTR vcsWebSvcExtRoot = L"/LM/W3SVC"; // prototypes for private helper functions static HRESULT AddWebSvcExtToList( __in SCA_WEBSVCEXT** ppsWseList ); //static HRESULT ScaCheckWebSvcExtValue( // __in IMSAdminBase* piMetabase, // __in DWORD dwMDIdentifier // ); static HRESULT ScaWebSvcExtInstall( __in LPWSTR *pwzWebSvcExtList, __in DWORD_PTR *pcchWebSvcExtList, __in SCA_WEBSVCEXT* psWseList ); static HRESULT ScaWebSvcExtUninstall( __in LPWSTR *pwzWebSvcExtList, __in const DWORD *pcchWebSvcExtList, __in SCA_WEBSVCEXT* psWseList ); // functions HRESULT __stdcall ScaWebSvcExtRead( __in SCA_WEBSVCEXT** ppsWseList, __inout LPWSTR *ppwzCustomActionData ) { Assert(ppsWseList); HRESULT hr = S_OK; MSIHANDLE hRec; LPWSTR pwzData = NULL; INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; SCA_WEBSVCEXT* psWebSvcExt = NULL; WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); ExitOnFailure(hr, "Failed to unwrap query for ScaWebSvcExtRead"); if (0 == WcaGetQueryRecords(hWrapQuery)) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtRead() because IIsWebServiceExtension data not present"); ExitFunction1(hr = S_FALSE); } // loop through all the web service extensions while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) { // Get the Component first. If the Component is not being modified during // this transaction, skip processing this whole record. hr = WcaGetRecordString(hRec, ldqComponent, &pwzData); ExitOnFailure(hr, "Failed to get Component for WebSvcExt"); hr = WcaGetRecordInteger(hRec, ldqInstalled, (int *)&isInstalled); ExitOnFailure(hr, "Failed to get Component installed state for WebSvcExt"); hr = WcaGetRecordInteger(hRec, ldqAction, (int *)&isAction); ExitOnFailure(hr, "Failed to get Component action state for WebSvcExt"); if (!WcaIsInstalling(isInstalled, isAction) && !WcaIsReInstalling(isInstalled, isAction) && !WcaIsUninstalling(isInstalled, isAction)) { continue; // skip this record. } hr = AddWebSvcExtToList(ppsWseList); ExitOnFailure(hr, "failed to add element to web svc ext list"); psWebSvcExt = *ppsWseList; Assert(psWebSvcExt); psWebSvcExt->isInstalled = isInstalled; psWebSvcExt->isAction = isAction; hr = WcaGetRecordString(hRec, ldqFile, &pwzData); ExitOnFailure(hr, "Failed to get File for WebSvcExt"); hr = ::StringCchCopyW(psWebSvcExt->wzFile, countof(psWebSvcExt->wzFile), pwzData); ExitOnFailure(hr, "Failed to copy File for WebSvcExt"); hr = WcaGetRecordString(hRec, ldqDescription, &pwzData); ExitOnFailure(hr, "Failed to get Description for WebSvcExt"); hr = ::StringCchCopyW(psWebSvcExt->wzDescription, countof(psWebSvcExt->wzDescription), pwzData); ExitOnFailure(hr, "Failed to copy Description for WebSvcExt"); hr = WcaGetRecordString(hRec, ldqGroup, &pwzData); ExitOnFailure(hr, "Failed to get Group for WebSvcExt"); hr = ::StringCchCopyW(psWebSvcExt->wzGroup, countof(psWebSvcExt->wzGroup), pwzData); ExitOnFailure(hr, "Failed to copy Group for WebSvcExt"); hr = WcaGetRecordInteger(hRec, ldqAttributes, &psWebSvcExt->iAttributes); ExitOnFailure(hr, "Failed to get Attributes for WebSvcExt"); } if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "Failure while processing WebSvcExt"); LExit: WcaFinishUnwrapQuery(hWrapQuery); ReleaseStr(pwzData); return hr; } // Commit does both install and uninstall HRESULT __stdcall ScaWebSvcExtCommit( __in IMSAdminBase* piMetabase, __in SCA_WEBSVCEXT* psWseList ) { Assert(piMetabase); HRESULT hr = S_OK; METADATA_RECORD mr; LPWSTR wzWebSvcExtList = NULL; DWORD cbWebSvcExtList = 0; DWORD_PTR cchWebSvcExtList = 0; if (!psWseList) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtCommit() because there are no web service extensions in the list"); ExitFunction(); } // Get current set of web service extensions. ::ZeroMemory(&mr, sizeof(mr)); mr.dwMDIdentifier = MD_WEB_SVC_EXT_RESTRICTION_LIST; mr.dwMDAttributes = 0; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.pbMDData = NULL; mr.dwMDDataLen = 0; hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cbWebSvcExtList); if (MD_ERROR_DATA_NOT_FOUND == hr) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtCommit() because WebSvcExtRestrictionList value is not present"); ExitFunction1(hr = S_FALSE); } else if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) { // cchWebSvcExtList is returned in bytes. Convert to WCHAR size to call StrAlloc cchWebSvcExtList = cbWebSvcExtList / sizeof(WCHAR); hr = StrAlloc(&wzWebSvcExtList, cchWebSvcExtList); ExitOnFailure(hr, "Failed allocating space for web service extensions"); } else { ExitOnFailure(hr, "Failed retrieving web service extensions"); } mr.pbMDData = (unsigned char*)wzWebSvcExtList; mr.dwMDDataLen = cbWebSvcExtList; hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cbWebSvcExtList); ExitOnFailure(hr, "Failed retrieving web service extensions"); // Make changes to local copy of metabase while (psWseList) { if (WcaIsInstalling(psWseList->isInstalled, psWseList->isAction)) { hr = ScaWebSvcExtInstall(&wzWebSvcExtList, &cchWebSvcExtList, psWseList); ExitOnFailure(hr, "Failed to install Web Service extension"); } else if (WcaIsUninstalling(psWseList->isInstalled, psWseList->isAction)) { hr = ScaWebSvcExtUninstall(&wzWebSvcExtList, (DWORD *)&cchWebSvcExtList, psWseList); ExitOnFailure(hr, "Failed to uninstall Web Service extension"); } psWseList = psWseList->psWseNext; } // Write Metabase hr = ScaWriteMetabaseValue(piMetabase, vcsWebSvcExtRoot, NULL, MD_WEB_SVC_EXT_RESTRICTION_LIST, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, wzWebSvcExtList); ExitOnFailure(hr, "Failed to write WebServiceExtensions: '%ls'", wzWebSvcExtList); LExit: ReleaseStr(wzWebSvcExtList); return hr; } static HRESULT ScaWebSvcExtInstall( __in LPWSTR *ppwzWebSvcExtList, __in DWORD_PTR *pcchWebSvcExtList, __in SCA_WEBSVCEXT* psWseList ) { Assert( ppwzWebSvcExtList && pcchWebSvcExtList && psWseList); Assert(*ppwzWebSvcExtList); HRESULT hr = S_OK; LPWSTR pwzWebSvcExt = NULL; int iAllow; int iUiDeletable; BOOL fAlreadyExists = FALSE; DWORD_PTR dwIndex = 0xFFFFFFFF; LPCWSTR wzFoundString = NULL; // Check if it's already in there hr = MultiSzFindSubstring(*ppwzWebSvcExtList, psWseList->wzFile, &dwIndex, &wzFoundString); ExitOnFailure(hr, "failed to search for string:%ls in web service extension MULTISZ", psWseList->wzFile); if (S_FALSE != hr && NULL != wcsstr(wzFoundString, psWseList->wzGroup) && NULL != wcsstr(wzFoundString, psWseList->wzDescription)) { fAlreadyExists = TRUE; } // Construct the single string in the format required for the WebSvc Ext list in metabase iAllow = (psWseList->iAttributes & 1); iUiDeletable = ((psWseList->iAttributes >> 1) & 1); hr = StrAllocFormatted(&pwzWebSvcExt, L"%d,%s,%d,%s,%s", iAllow, psWseList->wzFile, iUiDeletable, psWseList->wzGroup, psWseList->wzDescription); ExitOnFailure(hr, "Failure allocating space for web service extensions"); if (fAlreadyExists) { hr = MultiSzReplaceString(ppwzWebSvcExtList, dwIndex, pwzWebSvcExt); ExitOnFailure(hr, "failed to update web service extension string: %ls", pwzWebSvcExt); } else { hr = MultiSzPrepend(ppwzWebSvcExtList, pcchWebSvcExtList, pwzWebSvcExt); ExitOnFailure(hr, "failed to prepend web service extension string: %ls", pwzWebSvcExt); } LExit: ReleaseStr(pwzWebSvcExt); return hr; } static HRESULT ScaWebSvcExtUninstall( __in LPWSTR *ppwzWebSvcExtList, __in const DWORD* /*pcchWebSvcExtList*/, __in SCA_WEBSVCEXT* psWseList ) { Assert(ppwzWebSvcExtList && *ppwzWebSvcExtList && psWseList); Assert(*ppwzWebSvcExtList); HRESULT hr = S_OK; DWORD_PTR dwIndex = 0xFFFFFFFF; LPCWSTR wzFoundString = NULL; // Find the string to remove hr = MultiSzFindSubstring(*ppwzWebSvcExtList, psWseList->wzFile, &dwIndex, &wzFoundString); ExitOnFailure(hr, "failed to search for string:%ls in web service extension MULTISZ", psWseList->wzFile); // If we found a match (ignoring the Allow and Deletable flags) if (S_FALSE != hr && NULL != wcsstr(wzFoundString, psWseList->wzGroup) && NULL != wcsstr(wzFoundString, psWseList->wzDescription)) { hr = MultiSzRemoveString(ppwzWebSvcExtList, dwIndex); ExitOnFailure(hr, "failed to remove string: %d from web service extension MULTISZ", dwIndex); } LExit: return hr; } //static HRESULT ScaCheckWebSvcExtValue( // __in IMSAdminBase* piMetabase, // __in DWORD dwMDIdentifier // ) //{ // if (!piMetabase) // { // return E_INVALIDARG; // } // // HRESULT hr = S_OK; // METADATA_RECORD mr = { 0 }; // DWORD cch = 0; // // mr.dwMDIdentifier = dwMDIdentifier; // mr.dwMDUserType = IIS_MD_UT_SERVER; // // hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cch); // if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) // { // hr = S_OK; // } // else if (MD_ERROR_DATA_NOT_FOUND == hr) // { // hr = S_FALSE; // } // // return hr; //} void ScaWebSvcExtFreeList( __in SCA_WEBSVCEXT* psWseList ) { SCA_WEBSVCEXT* psWseDelete = psWseList; while (psWseList) { psWseDelete = psWseList; psWseList = psWseList->psWseNext; MemFree(psWseDelete); } } static HRESULT AddWebSvcExtToList( __in SCA_WEBSVCEXT** ppsWseList ) { HRESULT hr = S_OK; SCA_WEBSVCEXT* psWse = static_cast(MemAlloc(sizeof(SCA_WEBSVCEXT), TRUE)); ExitOnNull(psWse, hr, E_OUTOFMEMORY, "failed to allocate element for web svc ext list"); psWse->psWseNext = *ppsWseList; *ppsWseList = psWse; LExit: return hr; }