|
|
//
// global.cpp
//
#include "StdAfx.h"
#include <sddl.h>
#include "global.h"
#include "util.h"
#include "SSRLog.h"
CFBLogMgr g_fblog;
WCHAR g_wszSsrRoot[MAX_PATH + 1]; DWORD g_dwSsrRootLen = 0;
LPCWSTR g_pwszSSRRootToExpand = L"%windir%\\Security\\SSR";
//
// this is our ACL list
//
//LPCWSTR g_pwszSsrDCOMSecDescripACL = L"D:(A;;GA;;;SY)(A;;GA;;;BA)";
LPCWSTR g_pwszAppID = L"SOFTWARE\\Classes\\APPID\\{3f2db10f-6368-4702-a4b1-e5149d931370}"; LPCWSTR g_pwszAccessPermission = L"AccessPermission"; LPCWSTR g_pwszLaunchPermission = L"LaunchPermission";
//
// registry's SSR root
//
LPCWSTR g_pwszSSRRegRoot = L"Software\\Microsoft\\Security\\SSR";
LPCWSTR g_pwszSSR = L"SSR"; LPCWSTR g_pwszLogs = L"Logs";
//
// The following are reserved action verbs
//
CComBSTR g_bstrConfigure(L"Configure"); CComBSTR g_bstrRollback(L"Rollback"); CComBSTR g_bstrReport(L"Report");
//
// the following are reserved file-usage values
//
CComBSTR g_bstrLaunch(L"Launch"); CComBSTR g_bstrResult(L"Result");
//
// the following is the reserved action data's names
//
LPCWSTR g_pwszCurrSecurityPolicy = L"CurrSecurityPolicy"; LPCWSTR g_pwszTransformFiles = L"TransformFiles"; LPCWSTR g_pwszScriptFiles = L"ScriptFiles";
//
// the following are element tag names
//
CComBSTR g_bstrSsrMemberInfo(L"SsrMemberInfo"); CComBSTR g_bstrDescription(L"Description"); CComBSTR g_bstrSupportedAction(L"SupportedAction"); CComBSTR g_bstrProcedures(L"Procedures"); CComBSTR g_bstrDefaultProc(L"DefaultProc"); CComBSTR g_bstrCustomProc(L"CustomProc"); CComBSTR g_bstrTransformInfo(L"TransformInfo"); CComBSTR g_bstrScriptInfo(L"ScriptInfo");
//
// the following are attribute names
//
CComBSTR g_bstrAttrUniqueName(L"UniqueName"); CComBSTR g_bstrAttrMajorVersion(L"MajorVersion"); CComBSTR g_bstrAttrMinorVersion(L"MinorVersion"); CComBSTR g_bstrAttrProgID(L"ProgID"); CComBSTR g_bstrAttrActionName(L"ActionName"); CComBSTR g_bstrAttrActionType(L"ActionType"); CComBSTR g_bstrAttrTemplateFile(L"TemplateFile"); CComBSTR g_bstrAttrResultFile(L"ResultFile"); CComBSTR g_bstrAttrScriptFile(L"ScriptFile"); CComBSTR g_bstrAttrIsStatic(L"IsStatic"); CComBSTR g_bstrAttrIsExecutable(L"IsExecutable");
//
// these are the known action types
//
LPCWSTR g_pwszApply = L"Prepare"; LPCWSTR g_pwszPrepare = L"Prepare";
CComBSTR g_bstrReportFilesDir; CComBSTR g_bstrConfigureFilesDir; CComBSTR g_bstrRollbackFilesDir; CComBSTR g_bstrTransformFilesDir; CComBSTR g_bstrMemberFilesDir;
CComBSTR g_bstrTrue(L"True"); CComBSTR g_bstrFalse(L"False");
//
// A guid can be represented in string form such as
// {aabbccdd-1234-4321-abcd-1234567890ab}.
// The length of such string format guid returned from StringFromGUID2 etc.
// is 38
//
const long g_lGuidStringLen = 38;
//
// global helper function implementations
//
const BSTR SsrPGetActionVerbString ( IN SsrActionVerb action ) /*++
Routine Description:
Functionality: This will translate an SsrActionVerb value into the corresponding BSTR
Virtual: N/A. Arguments:
action - The SsrActionVerb value
Return Value:
Success: const BSTR of that verb's string;
Failure: NULL.
Notes:
Callers must not release in any form of the returned BSTR. It is a const BSTR, and you must honor that. --*/ { switch (action) { case ActionConfigure: return g_bstrConfigure; case ActionRollback: return g_bstrRollback; case ActionReport: return g_bstrReport; } return NULL; }
SsrActionVerb SsrPGetActionVerbFromString ( IN LPCWSTR pwszVerb ) /*++
Routine Description:
Functionality: This will translate an string action verb value into the corresponding SsrActionVerb value.
Virtual: N/A. Arguments:
pwszVerb - The action verb string
Return Value:
Success: the appropriate SsrActionVerb value if the verb is recognized
Failure: ActionInvalid.
Notes:
--*/ { SsrActionVerb ActVerb = ActionInvalid;
if (pwszVerb != NULL) { if (_wcsicmp(pwszVerb, g_bstrConfigure) == 0) { ActVerb = ActionConfigure; } else if (_wcsicmp(pwszVerb, g_bstrRollback) == 0) { ActVerb = ActionRollback; } else if (_wcsicmp(pwszVerb, g_bstrReport) == 0) { ActVerb = ActionReport; } }
return ActVerb; }
HRESULT SsrPDeleteEntireDirectory ( IN LPCWSTR pwszDirPath ) /*++
Routine Description:
Functionality: This will do a recursive delete of all files and subdirectories of the given directory. RemoveDirectory API only deletes empty directories.
Virtual: N/A. Arguments:
pwszDirPath - The path of the directory
Return Value:
Success: S_OK;
Failure: various error codes.
Notes:
--*/ { HRESULT hr = S_OK;
WIN32_FIND_DATA FindFileData;
//
// prepare the find file filter
//
DWORD dwDirLen = wcslen(pwszDirPath);
if (dwDirLen > MAX_PATH) { return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); }
CComBSTR bstrPath(pwszDirPath); bstrPath += CComBSTR(L"\\*");
if (bstrPath.m_str == NULL) { return E_OUTOFMEMORY; }
HANDLE hFind = ::FindFirstFile(bstrPath, &FindFileData);
//
// IF we have find something, then we have to do a recursive delete,
// Copy the given directory path to our local memory so that we can
// create a full path for the found file/directory
//
if (hFind != INVALID_HANDLE_VALUE) { WCHAR wszFullName[MAX_PATH + 2]; wszFullName[MAX_PATH + 1] = L'\0';
::memcpy(wszFullName, pwszDirPath, sizeof(WCHAR) * (dwDirLen + 1));
DWORD dwFileNameLength;
while (hFind != INVALID_HANDLE_VALUE) { //
// don't do anything to the parent directories
//
bool bDots = wcscmp(FindFileData.cFileName, L".") == 0 || wcscmp(FindFileData.cFileName, L"..") == 0;
if (!bDots) { //
// create the full name of the file/directory
//
dwFileNameLength = wcslen(FindFileData.cFileName);
if (dwDirLen + 1 + dwFileNameLength > MAX_PATH) { //
// we don't want names longer than MAX_PATH
//
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); break; }
//
// pwszFullName + dwDirLen is where the directory path ends
//
_snwprintf(wszFullName + dwDirLen, 1 + dwFileNameLength + 1, // backslah plus 0 term.
L"\\%s", FindFileData.cFileName );
if (FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) { //
// a directory. recursively delete the entire directory
//
hr = ::SsrPDeleteEntireDirectory(wszFullName); } else { //
// a file
//
if (!::DeleteFile(wszFullName)) { hr = HRESULT_FROM_WIN32(GetLastError()); break; } } } if (!::FindNextFile(hFind, &FindFileData)) { break; } } ::FindClose(hFind); }
if (SUCCEEDED(hr) && !::RemoveDirectory(pwszDirPath)) { hr = HRESULT_FROM_WIN32(GetLastError()); }
return hr; }
HRESULT SsrPCreateSubDirectories ( IN OUT LPWSTR pwszPath, IN LPCWSTR pwszSubRoot ) /*++
Routine Description:
Functionality: This will create all subdirectory leading to the path. The assumption is that the path contains a subdirectory identified by pwszSubRoot, the creation will start from there.
Virtual: N/A. Arguments:
pwszPath - The path of the directory
pwszSubRoot - The subdirectory where the creation sequence will start.
Return Value:
Success: S_OK;
Failure: various error codes.
Notes:
We actually don't change pwszPath. But we will temporarily modify the buffer during our operation, but upon returning, the buffer is fully restored to the original value.
--*/ { if (pwszPath == NULL || *pwszPath == L'\0') { return E_INVALIDARG; }
HRESULT hr = S_OK; LPCWSTR pwszCurrReadHead = pwszPath;
if (pwszSubRoot != NULL && *pwszSubRoot != L'\0') { pwszCurrReadHead = ::wcsstr(pwszPath, pwszSubRoot);
//
// if you specify a subroot, then it must exists in the path
//
if (pwszCurrReadHead == NULL) { return E_INVALIDARG; } }
LPWSTR pwszNextReadHead = ::wcsstr(pwszCurrReadHead, L"\\");
//
// will try to create all subdirectories under SSR
//
while (true) { //
// Temporarily zero terminate it so that we can try
// to create the directory
//
if (pwszNextReadHead != NULL) { *pwszNextReadHead = L'\0'; } if (!::CreateDirectory(pwszPath, NULL)) { DWORD dwError = GetLastError();
if (dwError == ERROR_ALREADY_EXISTS) { hr = S_OK; } else { hr = HRESULT_FROM_WIN32(dwError);
//
// log the directory that failed to create
//
g_fblog.LogFeedback(SSR_FB_ERROR_GENERIC | FBLog_Log, hr, pwszPath, IDS_FAIL_CREATE_DIRECTORY ); break; } }
//
// if we don't have any more backslash, then it's over
//
if (pwszNextReadHead == NULL) { break; }
//
// Restore the backslash
//
*pwszNextReadHead = L'\\'; pwszNextReadHead = ::wcsstr(pwszNextReadHead + 1, L"\\"); }
return hr; }
HRESULT SsrPLoadDOM ( BSTR bstrFile, // [in],
LONG lFlag, // [in],
IXMLDOMDocument2 * pDOM // [in]
) /*++
Routine Description:
Functionality: Do the XML DOM loading
Virtual: n/a. Arguments:
bstrFile- the XML/XSL file path
uFlag - The flag that determines the transformation characteristics. The one we use is SSR_LOADDOM_VALIDATE_ON_PARSE.
pDOM - The IXMLDOMDocument2 object interface.
Return Value:
Success: various success codes returned from DOM or ourselves. Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves. Use FAILED(hr) to test.
Notes:
--*/ { USES_CONVERSION;
HRESULT hr = S_OK;
if ( NULL == pDOM) { return E_INVALIDARG; }
//
// set the validateOnParse property
//
if (lFlag & SSR_LOADDOM_VALIDATE_ON_PARSE) { hr = pDOM->put_validateOnParse(VARIANT_TRUE); } else { hr = pDOM->put_validateOnParse(VARIANT_FALSE); }
if (FAILED(hr)) { g_fblog.LogFeedback(SSR_FB_ERROR_LOAD_MEMBER | FBLog_Log, hr, L"put_validateOnParse", IDS_DOM_PROPERTY_PUT_FAILED ); }
CComVariant varInput(bstrFile);
//
// we should try to see what happens if we set it to VARIANT_TRUE
//
VARIANT_BOOL fSuccess; hr = pDOM->load(varInput, &fSuccess);
if (fSuccess == VARIANT_FALSE) { //
// somehow it fails, we want to figure out what is going on,
// potential parsing errors
//
CComPtr<IXMLDOMParseError> srpParseError;
//
// in case of any failure, we will use the bstrReason
// to log and feedback.
//
CComBSTR bstrReason;
long ulLine = 0, ulColumn = 0, ulCode = 0;
//
// if any of the following fails, there is nothing we can do
// other than logging the error.
//
hr = pDOM->get_parseError(&srpParseError); if (FAILED(hr)) { bstrReason = L"SsrPLoadDOM failed on pDOM->get_parseError."; } else { hr = srpParseError->get_reason(&bstrReason); if (FAILED(hr)) { bstrReason = L"SsrPLoadDOM failed on srpParseError->get_reason."; } else { hr = srpParseError->get_errorCode(&ulCode); if (FAILED(hr)) { bstrReason = L"SsrPLoadDOM failed on srpParseError->get_errorCode."; }
hr = srpParseError->get_line(&ulLine);
if (FAILED(hr)) { bstrReason = L"SsrPLoadDOM failed on srpParseError->get_line."; } } }
const ULONG uHexMaxLen = 8; const ULONG uDecMaxLen = 16;
LPWSTR pwszError = NULL;
//
// we can't continue creating more specific error info
// if we fail to get the reason - which may include out-of-memory
// system errors. We don't bother to modify our error to
// out-of-memory because we are guessing in that case and also
// others will catch that error fairly quickly.
//
if (SUCCEEDED(hr) && bstrReason != NULL && ulLine != 0) { //
// It's a parsing error
//
srpParseError->get_linepos(&ulColumn);
CComBSTR bstrFmt; if (SUCCEEDED(bstrFmt.LoadString(IDS_XML_PARSING_ERROR))) { ULONG uTotLen = bstrFmt.Length() + uHexMaxLen + uDecMaxLen + ::wcslen(bstrReason) + ::wcslen(bstrFile) + uDecMaxLen + 1;
pwszError = new WCHAR[uTotLen];
if (pwszError == NULL) { hr = E_OUTOFMEMORY; } else { _snwprintf( pwszError, uTotLen, bstrFmt, ulCode, ulLine, bstrReason, bstrFile, ulColumn ); } } } else { if ((HRESULT) ulCode == INET_E_OBJECT_NOT_FOUND) { g_fblog.LogString(IDS_OBJECT_NOT_FOUND, bstrFile); } }
//
// loading DOM failure is critical, do both logging and feedback
//
g_fblog.LogFeedback(SSR_FB_ERROR_MEMBER_XML | FBLog_Log, bstrFile, ((pwszError != NULL) ? pwszError : bstrReason), IDS_DOM_LOAD_FAILED );
delete [] pwszError;
//
// I have seen the HRESULT code being a success code while
// it can't transform fSuccess == VARIANT_FALSE
//
if (SUCCEEDED(hr)) { hr = E_SSR_INVALID_XML_FILE; } }
return hr; }
HRESULT SsrPGetBSTRAttrValue ( IN IXMLDOMNamedNodeMap * pNodeMap, IN BSTR bstrName, OUT BSTR * pbstrValue ) /*++
Routine Description:
Functionality: A helper function to get string attribute values
Virtual: N/A. Arguments:
pNodeMap - The map of attributes
bstrName - The name of the attribute
pbstrValue - Receives the string value.
Return Value:
S_OK if succeeded.
Various error codes. Watch out for E_SSR_MISSING_STRING_ATTRIBUTE because if it is an optional attribute, then we should allow this failure.
Notes:
--*/ { if (pNodeMap == NULL || pbstrValue == NULL) { return E_INVALIDARG; }
*pbstrValue = NULL;
CComPtr<IXMLDOMNode> srpAttr; CComVariant varValue;
HRESULT hr = pNodeMap->getNamedItem(bstrName, &srpAttr); if (SUCCEEDED(hr) && srpAttr != NULL) { hr = srpAttr->get_nodeValue(&varValue); } if (SUCCEEDED(hr) && varValue.vt == VT_BSTR) { *pbstrValue = varValue.bstrVal;
//
// detach the bstr value so that we can reuse the variant
//
varValue.vt = VT_EMPTY; varValue.bstrVal = NULL; } else if (varValue.vt != VT_BSTR) { hr = E_SSR_MISSING_STRING_ATTRIBUTE; }
return hr; }
HRESULT SsrPCreateUniqueTempDirectory ( OUT LPWSTR pwszTempDirPath, IN DWORD dwBufLen ) /*++
Routine Description:
Functionality: Will create a unique (guid) temporary directory under ssr root
Virtual: No. Arguments:
pwszTempDirPath - The path of the temporary directory
dwBufLen - The buffer length in WCHAR counts
Return Value:
succeess: S_OK failure: various error codes
Notes:
--*/ { //
// we need the ssr root, the backslash and the guid (36 wchars) and 0 terminator
//
if (dwBufLen < g_dwSsrRootLen + 1 + g_lGuidStringLen + 1) { return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); }
WCHAR wszGuid[g_lGuidStringLen + 1]; GUID guid; memset(&guid, 0, sizeof(GUID)); memset(wszGuid, 0, sizeof(WCHAR) * (g_lGuidStringLen + 1) );
HRESULT hr = ::CoCreateGuid(&guid); if (S_OK == hr) { ::StringFromGUID2(guid, wszGuid, g_lGuidStringLen + 1);
memcpy(pwszTempDirPath, g_wszSsrRoot, g_dwSsrRootLen * sizeof(WCHAR)); pwszTempDirPath[g_dwSsrRootLen] = L'\\';
//
// skip the starting '{', and do not copy '}' either.
//
memcpy(pwszTempDirPath + g_dwSsrRootLen + 1, wszGuid + 1, (g_lGuidStringLen - 2) * sizeof(WCHAR) );
//
// NULL terminate it
//
pwszTempDirPath[g_dwSsrRootLen + g_lGuidStringLen - 1] = L'\0';
if (!CreateDirectory(pwszTempDirPath, NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); } }
return hr; }
HRESULT SsrPMoveFiles ( IN LPCWSTR pwszSrcDirRoot, IN LPCWSTR pwszDesDirRoot, IN LPCWSTR pwszRelPath ) /*++
Routine Description:
Functionality: Will move a file given by the relative path of the src directory to the destination directory of the same relative path
Virtual: No. Arguments:
pwszSrcDirRoot - The src directory root path. Combined with the pwszRelPath, it becomes the full path of the src file
pwszDesDirRoot - The dest directory root path. Combined with the pwszRelPath, it becomes the full path of the destination file.
pwszRelPath - the file path relative to the src directory root.
Return Value:
Success: S_OK.
Failure: various error codes.
Notes:
If the destination directory doesn't exist, then we will create it --*/
{ HRESULT hr = S_OK;
//
// first, we must the source and destination files paths
//
DWORD dwSrcRootLen = wcslen(pwszSrcDirRoot); DWORD dwDesRootLen = wcslen(pwszDesDirRoot);
DWORD dwRelPathLen = wcslen(pwszRelPath);
LPWSTR pwszSrcPath = new WCHAR[dwSrcRootLen + 1 + dwRelPathLen + 1]; LPWSTR pwszDesPath = new WCHAR[dwDesRootLen + 1 + dwRelPathLen + 1];
if (pwszSrcPath != NULL && pwszDesPath != NULL) { ::memcpy(pwszSrcPath, pwszSrcDirRoot, sizeof(WCHAR) * dwSrcRootLen); pwszSrcPath[dwSrcRootLen] = L'\\';
//
// copy one WCHAR more then length so that the 0 terminator is set
//
::memcpy(pwszSrcPath + dwSrcRootLen + 1, pwszRelPath, sizeof(WCHAR) * (dwRelPathLen + 1));
::memcpy(pwszDesPath, pwszDesDirRoot, sizeof(WCHAR) * dwDesRootLen);
pwszDesPath[dwDesRootLen] = L'\\'; ::memcpy(pwszDesPath + dwDesRootLen + 1, pwszRelPath, sizeof(WCHAR) * (dwRelPathLen + 1));
if (!::MoveFile(pwszSrcPath, pwszDesPath)) { DWORD dwError = GetLastError();
if (ERROR_FILE_NOT_FOUND != dwError) { hr = HRESULT_FROM_WIN32(dwError); }
//
// log the file name that fails to move
//
g_fblog.LogFeedback(SSR_FB_ERROR_GENERIC | FBLog_Log, dwError, pwszSrcPath, IDS_FAIL_MOVE_FILE ); } } else { hr = E_OUTOFMEMORY; }
delete [] pwszSrcPath; delete [] pwszDesPath;
return hr; }
bool SsrPPressOn ( IN SsrActionVerb lActionVerb, IN LONG lActionType, IN HRESULT hr ) /*++
Routine Description:
Functionality: Determines if we should continue based on the error
Virtual: No. Arguments:
lActionVerb - The action
lActionType - The type of the action
hr - The HRESULT to test
Return Value:
true or false
Notes:
This function is just a placeholder for the need of the test. The implementation is far from complete. --*/ { UNREFERENCED_PARAMETER(lActionVerb); UNREFERENCED_PARAMETER(lActionType);
if (hr == E_OUTOFMEMORY || hr == E_SSR_MEMBER_XSD_INVALID) { return false; }
return true; }
const BSTR SsrPGetDirectory ( IN SsrActionVerb lActionVerb, IN BOOL bScriptFile ) /*++
Routine Description:
Functionality: SSR controls the physical location for its members to place their files. This function returns to the caller the physical directory path for a given member. Since SSR is an action oriented architecture, such locations are also relative to action. If bstrActionVerb is a non-empty string, then the function retrieves the loction for that action. Otherwise, the function retrieves the location for the member's root (actions are sub-directories of this root)
Virtual: No. Arguments:
lActionVerb - The action verb in long format
bScriptFile - Whether or not it is asking for a script file. If false, it is asking for transformation file
Return Value:
the file path if we recognize the call.
Notes:
!!!Warning!!!
Caller should never release the BSTR --*/ { if (!bScriptFile) { //
// asking for transformation files. All of them goes to
// the TransformFiles directory
//
return g_bstrTransformFilesDir; }
if (lActionVerb == ActionConfigure) { return g_bstrConfigureFilesDir; } else if (lActionVerb == ActionRollback) { return g_bstrRollbackFilesDir; } else if (lActionVerb == ActionReport) { return g_bstrReportFilesDir; } else { return NULL; }
}
HRESULT SsrPDoDCOMSettings ( bool bReg ) /*++
Routine Description:
Functionality: This function will set the security related registry settings for our SSR engine com objects
Virtual: No. Arguments:
bReg - whether this is to register or to un-register
Return Value:
S_OK if succeeded. Otherwise, various error codes
Notes:
--*/ { HRESULT hr = S_OK;
PSECURITY_DESCRIPTOR pSD = NULL;
BOOL bDaclPresent = FALSE, bDaclDefault = FALSE;
ULONG ulSDSize = 0;
return 0; /*
if (bReg) { if (!ConvertStringSecurityDescriptorToSecurityDescriptor( g_pwszSsrDCOMSecDescripACL, SDDL_REVISION_1, &pSD, &ulSDSize ) ) { return HRESULT_FROM_WIN32(GetLastError()); } } else { //
// unregister, easy, just delete the AppID key
//
LONG lStatus = RegDeleteKey(HKEY_LOCAL_MACHINE, g_pwszAppID);
if ( lStatus != NO_ERROR ) { hr = HRESULT_FROM_WIN32(GetLastError()); } return hr; }
if (SUCCEEDED(hr)) { //
// now let's set the ACLs on these keys
//
HKEY hKey = NULL;
BYTE * lpData = (BYTE*)pSD; DWORD dwDataSize = ulSDSize;
if (FAILED(hr)) { return hr; }
LONG lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, g_pwszAppID, 0, KEY_WRITE, &hKey );
if ( lStatus != NO_ERROR ) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { //
// set the access
//
lStatus = RegSetValueEx( hKey, g_pwszAccessPermission, 0, REG_BINARY, lpData, dwDataSize );
if ( lStatus == NO_ERROR ) { //
// set the launch permission
//
lStatus = RegSetValueEx( hKey, g_pwszLaunchPermission, 0, REG_BINARY, lpData, dwDataSize ); }
if ( lStatus != NO_ERROR ) { hr = HRESULT_FROM_WIN32(GetLastError()); }
RegCloseKey(hKey); } }
if (pSD != NULL) { LocalFree(pSD); }
return hr; */}
|