//+--------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1993 - 1997. // // File: util.cpp // // Contents: Implements the utility class CUtility // // Classes: // // Methods: CUtility::CkForAccessDenied // CUtility::CkAccessRights // CUtility::PostErrorMessage (x2) // CUtility::WriteRegSzNamedValue // CUtility::WriteRegDwordNamedValue // CUtility::WriteRegSingleACL // CUtility::WriteRegKeyACL // CUtility::WriteRegKeyACL2 // CUtility::WriteLsaPassword // CUtility::DeleteRegKey // CUtility::DeleteRegValue // CUtility::WriteSrvIdentity // CUtility::ACLEditor // CUtility::ACLEditor2 // CUtility::InvokeUserBrowser // CUtility::InvokeMachineBrowser // CUtility::StringFromGUID // CUtility::IsEqualGuid // CUtility::AdjustPrivilege // CUtility::VerifyRemoteMachine // CUtility::RetrieveUserPassword // CUtility::StoreUserPassword // CUtility::LookupProcessInfo // CUtility::MakeSecDesc // CUtility::CheckForValidSD // CUtility::SDisIAC // CUtility::CheckSDForCOM_RIGHTS_EXECUTE // CUtility::ChangeService // CUtility::UpdateDCOMInfo(void) // CUtility::FixHelp // CUtility::CopySD // CUtility::SetInheritanceFlags // // Functons: callBackFunc // ControlFixProc // // History: 23-Apr-96 BruceMa Created. // //---------------------------------------------------------------------- #include "stdafx.h" #include "assert.h" #include "resource.h" #include "afxtempl.h" #include "types.h" #include "datapkt.h" #include "clspsht.h" #if !defined(STANDALONE_BUILD) extern "C" { #include } #endif #include "util.h" #include "virtreg.h" extern "C" { #if !defined(STANDALONE_BUILD) #include #include #include #endif #include #if !defined(STANDALONE_BUILD) #include #include #endif #include #include #include } #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif const IID IID_IAccessControl = {0xEEDD23E0,0x8410,0x11CE,{0xA1,0xC3,0x08,0x00,0x2B,0x2B,0x8D,0x8F}}; static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 }; static const WCHAR wszDigits[] = L"0123456789ABCDEF"; static const DWORD SIZEOF_SID = 44; // This leaves space for 2 access allowed ACEs in the ACL. const DWORD SIZEOF_ACL = sizeof(ACL) + 2 * sizeof(ACCESS_ALLOWED_ACE) + 2 * SIZEOF_SID; static const DWORD SIZEOF_TOKEN_USER = sizeof(TOKEN_USER) + SIZEOF_SID; static const SID LOCAL_SYSTEM_SID = {SID_REVISION, 1, {0,0,0,0,0,5}, SECURITY_LOCAL_SYSTEM_RID }; static const DWORD NUM_SEC_PKG = 8; // These are required for the method CUtility::UpdateDCOMInfo which invokes // an RPC proxy which expects the following extern "C" void * _stdcall MIDL_user_allocate(size_t size) { return new BYTE[size]; } extern "C" void _stdcall MIDL_user_free(void *p) { delete p; } CUtility::CUtility(void) { HRESULT hr = OleInitialize(NULL); m_bCheckedDC = NULL; // have we checked if we're on a BDC yet ? m_bIsBdc = FALSE; m_pszDomainController = NULL; } CUtility::~CUtility(void) { #if !defined(STANDALONE_BUILD) if (m_pszDomainController) NetApiBufferFree(m_pszDomainController); #endif OleUninitialize(); } void CUtility::CkForAccessDenied(int err) { if (err == ERROR_ACCESS_DENIED) { CString sMsg; CString sCaption; sMsg.LoadString(IDS_ACCESSDENIED); sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, sMsg, sCaption, MB_OK); } } BOOL CUtility::CkAccessRights(HKEY hRoot, LPCTSTR szKeyPath) { int err; HKEY hKey = NULL; BYTE aSid[256]; DWORD cbSid = 256; PSECURITY_DESCRIPTOR pSid = (PSECURITY_DESCRIPTOR) aSid; BOOL fFreePsid = FALSE; // Open the specified key err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey); // The key may not exist if (err == ERROR_FILE_NOT_FOUND) { if (hKey != hRoot) { RegCloseKey(hKey); } return TRUE; } if (err == ERROR_SUCCESS) { // Fetch the security descriptor on this key err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR) aSid, &cbSid); if (err == ERROR_INSUFFICIENT_BUFFER) { pSid = (PSECURITY_DESCRIPTOR) malloc(cbSid); if (pSid == NULL) { if (hKey != hRoot) { RegCloseKey(hKey); } return FALSE; } fFreePsid = TRUE; err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR) pSid, &cbSid); } // We've read the security descriptor - now try to write it if (err == ERROR_SUCCESS) { err = RegSetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSid); } if (hKey != hRoot) { RegCloseKey(hKey); } } if (fFreePsid) { free(pSid); } return err == ERROR_SUCCESS ? TRUE : FALSE; } void CUtility::PostErrorMessage(void) { TCHAR szMessage[256]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, szMessage, sizeof(szMessage) / sizeof(TCHAR), NULL); CString sCaption; sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, szMessage, sCaption, MB_OK); } void CUtility::PostErrorMessage(int err) { TCHAR szMessage[256]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, szMessage, sizeof(szMessage) / sizeof(TCHAR), NULL); CString sCaption; sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, szMessage, sCaption, MB_OK); } // Write a named string value to the registry int CUtility::WriteRegSzNamedValue(HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName, LPCTSTR szVal, DWORD dwSize) { int err; HKEY hKey; ULONG lSize; // Open the key err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey); // The key may not exist if (err == ERROR_FILE_NOT_FOUND) { DWORD dwDisp; err = RegCreateKeyEx(hRoot, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp); } if (err != ERROR_SUCCESS) return err; // Attempt to write the named value lSize = _tcslen(szVal) + 1; err = RegSetValueEx(hKey, szValueName, NULL, REG_SZ, (BYTE *) szVal, lSize*sizeof(TCHAR)); if (hKey != hRoot) RegCloseKey(hKey); return err; } // Write a named multi string value to the registry int CUtility::WriteRegMultiSzNamedValue(HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName, LPCTSTR szVal, DWORD dwSize) { int err = ERROR_SUCCESS; HKEY hKey; // Open the key err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey); // The key may not exist if (err == ERROR_FILE_NOT_FOUND) { DWORD dwDisp; err = RegCreateKeyEx(hRoot, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp); } if (err != ERROR_SUCCESS) return err; // Attempt to write the named value err = RegSetValueEx(hKey, szValueName, NULL, REG_MULTI_SZ, (BYTE *) szVal, dwSize*sizeof(TCHAR) ); if (hKey != hRoot) RegCloseKey(hKey); return err; } // Write a named DWORD value to the registry int CUtility::WriteRegDwordNamedValue(HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName, DWORD dwVal) { int err; HKEY hKey; // Open the key err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey); // The key may not exist if (err == ERROR_FILE_NOT_FOUND) { DWORD dwDisp; err = RegCreateKeyEx(hRoot, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp); } if (err != ERROR_SUCCESS) return err; // Attempt to write the named value if (RegSetValueEx(hKey, szValueName, NULL, REG_DWORD, (BYTE *) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS) { if (hKey != hRoot) { RegCloseKey(hKey); } return GetLastError(); } // Return the value if (hKey != hRoot) { RegCloseKey(hKey); } return ERROR_SUCCESS; } // Write an ACL as a registry named value int CUtility::WriteRegSingleACL(HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName, PSECURITY_DESCRIPTOR pSec) { int err; HKEY hKey = hRoot; PSrSecurityDescriptor pSrSec; PSrAcl pDacl; // Open the key unless the key path is NULL if (szKeyPath) { if ((err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey)) != ERROR_SUCCESS) { return err; } } ULONG cbLen; BOOL fIsIAC = SDisIAC((SECURITY_DESCRIPTOR * )pSec); // If there are no ACE's and this is DefaultAccessPermission, then // interpret this as activator access only which we indicate by // removing the named value if (!fIsIAC) { pSrSec = (PSrSecurityDescriptor) pSec; pDacl = (PSrAcl) (((BYTE *) pSec) + (pSrSec->Dacl)); if (_tcscmp(szValueName, TEXT("DefaultAccessPermission")) == 0 && pDacl->AceCount == 0) { err = RegDeleteValue(hKey, szValueName); return err; } cbLen = RtlLengthSecurityDescriptor(pSec); } else { cbLen = (ULONG) GlobalSize(pSec); } // Else write the ACL simply as a REG_SZ value err = RegSetValueEx(hKey, szValueName, 0, REG_BINARY, (BYTE *) pSec, cbLen); if (hKey != hRoot) { RegCloseKey(hKey); } return err; } // Write an ACL on a registry key int CUtility::WriteRegKeyACL(HKEY hKey, HKEY *phClsids, unsigned cClsids, PSECURITY_DESCRIPTOR pSec, PSECURITY_DESCRIPTOR pSecOrig) { int err; // The logic is somewhat different depending on whether we're starting // with HKEY_CLASSES_ROOT or a specific AppID if (hKey == HKEY_CLASSES_ROOT) { return WriteRegKeyACL2(hKey, hKey, pSec, pSecOrig); } // It's a specific AppID else { // Write the security on the AppID key if (err = RegSetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSec) != ERROR_SUCCESS) { return err; } // Iterate over the CLSID's covered by this AppID and recursively // write security on them and their subkeys for (UINT k = 0; k < cClsids; k++) { if (err = WriteRegKeyACL2(phClsids[k], phClsids[k], pSec, pSecOrig) != ERROR_SUCCESS) { return err; } } } return ERROR_SUCCESS; } // Write an ACL recursively on a registry key provided the current // security descriptor on the key is the same as the passed in // original security descriptor int CUtility::WriteRegKeyACL2(HKEY hRoot, HKEY hKey, PSECURITY_DESCRIPTOR pSec, PSECURITY_DESCRIPTOR pSecOrig) { BYTE aCurrSD[256] = {0}; DWORD cbCurrSD = 256; PSECURITY_DESCRIPTOR pCurrSD = (PSECURITY_DESCRIPTOR) aCurrSD; BOOL fFreePCurrSD = FALSE; int err; BOOL fProceed; // Read the current security descriptor on this key err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, aCurrSD, &cbCurrSD); if (err == ERROR_MORE_DATA || err == ERROR_INSUFFICIENT_BUFFER) { pCurrSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbCurrSD); if (pCurrSD == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } fFreePCurrSD = TRUE; } else if (err != ERROR_SUCCESS) { return err; } if ((err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pCurrSD, &cbCurrSD) != ERROR_SUCCESS)) { if (fFreePCurrSD) { GlobalFree(pCurrSD); } return err; } // Only proceed down this subtree if the current SD and the // original SD are the same fProceed = CompareSDs((PSrSecurityDescriptor) pCurrSD, (PSrSecurityDescriptor) pSecOrig); // We're done with the current security descriptor if (fFreePCurrSD) { GlobalFree(pCurrSD); } if (!fProceed) { if (hKey != hRoot) { RegCloseKey(hKey); } return ERROR_SUCCESS; } // Write the top level ACL err = RegSetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSec); // Now enumerate the subkeys and write ACL's on them DWORD iSubKey; TCHAR szSubKeyName[128]; HKEY hKey2; iSubKey = 0; while (err == ERROR_SUCCESS) { // Enumerate the next key err = RegEnumKey(hKey, iSubKey, szSubKeyName, 128); if (err != ERROR_SUCCESS) { break; } // Prepare for the next key iSubKey++; // Open this subkey and recursively write the ACL on it and // all of its subkeys if (RegOpenKeyEx(hKey, szSubKeyName, 0, KEY_ALL_ACCESS, &hKey2) == ERROR_SUCCESS) { err = WriteRegKeyACL2(hRoot, hKey2, pSec, pSecOrig); } } if (hKey != hRoot) { RegCloseKey(hKey); } return err == ERROR_NO_MORE_ITEMS ? ERROR_SUCCESS : err; } // Write a user's password to the private LSA database int CUtility::WriteLsaPassword(CLSID appid, LPCTSTR szPassword) { return ERROR_SUCCESS; } int CUtility::DeleteRegKey(HKEY hRoot, LPCTSTR szKeyPath) { return RegDeleteKey(hRoot, szKeyPath); } int CUtility::DeleteRegValue(HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName) { int err; HKEY hKey; if ((err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey)) == ERROR_SUCCESS) { err = RegDeleteValue(hKey, szValueName); if (hRoot != hKey) RegCloseKey(hKey); } return err; } // Change the identity under which a service runs int CUtility::WriteSrvIdentity(LPCTSTR szService, LPCTSTR szIdentity) { return ERROR_SUCCESS; } DWORD __stdcall callBackFunc(HWND hwndParent, HANDLE hInstance, ULONG_PTR CallBackContext, PSECURITY_DESCRIPTOR SecDesc, PSECURITY_DESCRIPTOR SecDescNewObjects, BOOLEAN ApplyToSubContainers, BOOLEAN ApplyToSubObjects, LPDWORD StatusReturn) { int err = ERROR_SUCCESS; PCallBackContext pCallBackContext = (PCallBackContext) CallBackContext; SECURITY_DESCRIPTOR* pSD = (SECURITY_DESCRIPTOR*) SecDesc; SECURITY_DESCRIPTOR_RELATIVE* pSDr = (SECURITY_DESCRIPTOR_RELATIVE*) SecDesc; PSrAcl pDacl; PSrAce pAce; DWORD cbAces; // Check whether the security descriptor is self-relative if (!(pSD->Control & SE_SELF_RELATIVE)) { pDacl = (PSrAcl) pSD->Dacl; } else { pDacl = (PSrAcl) (((BYTE *) pSDr) + (pSDr->Dacl)); } if (pDacl) { // Do over the ACE's for (pAce = (PSrAce) (((BYTE *) pDacl) + sizeof(SSrAcl)), cbAces = pDacl->AceCount;cbAces; pAce = (PSrAce) (((BYTE *) pAce) + pAce->AceSize),cbAces--) { if (pAce->Type == 1 && pAce->AccessMask == GENERIC_ALL) { pAce->AccessMask = COM_RIGHTS_EXECUTE; } } } // Set the inheritance flags on the new security descriptor if (pCallBackContext->pktType == RegKeyACL) { g_util.SetInheritanceFlags((SECURITY_DESCRIPTOR *) SecDesc); } if (pCallBackContext->fIsIAC) { // try to convert to a serialized IAccessControl SECURITY_DESCRIPTOR * pNewSD = g_util.IACfromSD((SECURITY_DESCRIPTOR *)SecDesc); if (pNewSD) { SecDesc = pNewSD; } else { pCallBackContext->fIsIAC = FALSE; // failed so treat it as if it is an old-style SD CString sMsg; CString sCaption; sMsg.LoadString(IDS_CANTCONVERT); sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, sMsg, sCaption, MB_OK); } } else { SECURITY_DESCRIPTOR * pNewSD; // just copy the security descriptor to get it into Global Memory if (!g_util.CopySD((SECURITY_DESCRIPTOR *)SecDesc, &pNewSD)) { *StatusReturn = ERROR_OUTOFMEMORY; return ERROR_OUTOFMEMORY; } SecDesc = pNewSD; } // Write the new or modified security descriptor if (*pCallBackContext->pIndex == -1) { if (pCallBackContext->pktType == SingleACL) { err = g_virtreg.NewRegSingleACL( pCallBackContext->info.single.hRoot, pCallBackContext->info.single.szKeyPath, pCallBackContext->info.single.szValueName, (SECURITY_DESCRIPTOR *) SecDesc, pCallBackContext->fIsIAC, // If it's an IAC then it's already SELF-RELATIVE pCallBackContext->pIndex); } else { err = g_virtreg.NewRegKeyACL( pCallBackContext->info.regKey.hKey, pCallBackContext->info.regKey.phClsids, pCallBackContext->info.regKey.cClsids, pCallBackContext->info.regKey.szTitle, pCallBackContext->origSD, (SECURITY_DESCRIPTOR *) SecDesc, pCallBackContext->fIsIAC, // If it's an IAC then it's already SELF-RELATIVE pCallBackContext->pIndex); } } else { g_virtreg.ChgRegACL(*pCallBackContext->pIndex, (SECURITY_DESCRIPTOR *) SecDesc, pCallBackContext->fIsIAC); // If it's an IAC then it's already SELF-RELATIVE } *StatusReturn = err; return err; } // Invoke the ACL editor on the specified named value. This method // writes an ACL data packet to the virtual registry. This method is for // Access and Launch security only (pktType SingleACL). int CUtility::ACLEditor(HWND hWnd, HKEY hRoot, LPCTSTR szKeyPath, LPCTSTR szValueName, int *pIndex, PACKETTYPE pktType, dcomAclType eAclType) { #if !defined(STANDALONE_BUILD) int err; HKEY hKey; BYTE aSD[128]; DWORD cbSD = 128; DWORD dwType; SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR *) aSD; BOOL fFreePSD = FALSE; SID *pSid; TCHAR szAllow[32]; TCHAR szDeny[32]; CString szAllow_; CString szDeny_; // Build the allow and deny strings switch (eAclType) { case dcomAclAccess: szAllow_.LoadString(IDS_ALLOW_ACCESS); szDeny_.LoadString(IDS_DENY_ACCESS); break; case dcomAclLaunch: szAllow_.LoadString(IDS_ALLOW_LAUNCH); szDeny_.LoadString(IDS_DENY_LAUNCH); break; case dcomAclConfig: szAllow_.LoadString(IDS_ALLOW_CONFIG); szDeny_.LoadString(IDS_DENY_CONFIG); break; } _tcscpy(szAllow, (LPCTSTR) szAllow_); _tcscpy(szDeny, (LPCTSTR) szDeny_); // Fetch the current SD, either from the registry, by default if the // named value doesn't exist or from the virtual registry if (*pIndex == -1) { // Open the specified key if ((err = RegOpenKeyEx(hRoot, szKeyPath, 0, KEY_ALL_ACCESS, &hKey)) != ERROR_SUCCESS) { return err; } // Attempt to read the specified named value err = RegQueryValueEx(hKey, szValueName, 0, &dwType, (BYTE *) aSD, &cbSD); if (err == ERROR_MORE_DATA || err == ERROR_INSUFFICIENT_BUFFER) { pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbSD); if (pSD == NULL) { RegCloseKey(hKey); return ERROR_NOT_ENOUGH_MEMORY; } fFreePSD = TRUE; err = RegQueryValueEx(hKey, szValueName, 0, &dwType, (BYTE *) pSD, &cbSD); } // The named valued doesn't exist. If this is // \\HKEY_CLASSES_ROOT\... // then use the default named value if it exists else if (err != ERROR_SUCCESS) { if (hRoot != HKEY_LOCAL_MACHINE) { RegCloseKey(hKey); if (err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\OLE"), 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) { return err; } // Attempt to read the specified named value TCHAR szDefault[32]; _tcscpy(szDefault, TEXT("Default")); _tcscat(szDefault, szValueName); err = RegQueryValueEx(hKey, szDefault, 0, &dwType, (BYTE *) aSD, &cbSD); if (err == ERROR_MORE_DATA) { pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbSD); if (pSD == NULL) { RegCloseKey(hKey); return ERROR_NOT_ENOUGH_MEMORY; } fFreePSD = TRUE; err = RegQueryValueEx(hKey, szDefault, 0, &dwType, (BYTE *) pSD, &cbSD); } } } RegCloseKey(hKey); // If still don't have an SD, then simply create one if (err != ERROR_SUCCESS) { if (!g_util.LookupProcessInfo(&pSid, NULL)) { return GetLastError(); } if (!g_util.MakeSecDesc(pSid, &pSD)) { delete pSid; return GetLastError(); } fFreePSD = TRUE; } } // Fetch the most recently edited SD else { CDataPacket *pCdp = g_virtreg.GetAt(*pIndex); pSD = pCdp -> pkt.acl.pSec; } // Initialize the callback context m_sCallBackContext.pktType = pktType; m_sCallBackContext.pIndex = pIndex; m_sCallBackContext.origSD = pSD; m_sCallBackContext.info.single.hRoot = hRoot; m_sCallBackContext.info.single.szKeyPath = (TCHAR*)szKeyPath; m_sCallBackContext.info.single.szValueName = (TCHAR*)szValueName; // Invoke the ACL editor DWORD dwStatus; GENERIC_MAPPING genericMapping; CString szObjectType; szObjectType.LoadString(IDS_Registry_value); SED_HELP_INFO helpInfo = { TEXT("dcomcnfg.hlp"), {HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG} }; SED_OBJECT_TYPE_DESCRIPTOR objTyp = {1, // Revision FALSE, // Is container? FALSE, // Allow new object perms? FALSE, // Specific to generic? &genericMapping, // Generic mapping NULL, // Generic mapping new (TCHAR *) ((LPCTSTR) szObjectType), // Object type name &helpInfo, // Help info TEXT(""), // Ckbox title TEXT(""), // Apply title TEXT(""), // NULL, // Special object access NULL // New special object access }; SED_APPLICATION_ACCESS appAccess[] = {{SED_DESC_TYPE_RESOURCE, COM_RIGHTS_EXECUTE, 0, szAllow}, {SED_DESC_TYPE_RESOURCE, 0, 0, szDeny}}; SED_APPLICATION_ACCESSES appAccesses = {2, // Count of access groups appAccess, // Access array szAllow // Default access name }; // Intialize the help contexts helpInfo.aulHelpContext[HC_MAIN_DLG] = IDH_REGISTRY_VALUE_PERMISSIONS; helpInfo.aulHelpContext[HC_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_GLOBAL; helpInfo.aulHelpContext[HC_NEW_ITEM_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_GLOBAL; helpInfo.aulHelpContext[HC_ADD_USER_DLG] = IDH_ADD_USERS_AND_GROUPS; helpInfo.aulHelpContext[HC_ADD_USER_MEMBERS_LG_DLG] = IDH_LOCAL_GROUP_MEMBERSHIP; helpInfo.aulHelpContext[HC_ADD_USER_MEMBERS_GG_DLG] = IDH_GLOBAL_GROUP_MEMBERSHIP; helpInfo.aulHelpContext[HC_ADD_USER_SEARCH_DLG] = IDH_FIND_ACCOUNT1; genericMapping.GenericRead = GENERIC_ALL; genericMapping.GenericWrite = GENERIC_ALL; genericMapping.GenericExecute = GENERIC_ALL; genericMapping.GenericAll = GENERIC_ALL; if (!CheckForValidSD(pSD)) { // make a valid security descriptor so we can continue if (!g_util.LookupProcessInfo(&pSid, NULL)) { return GetLastError(); } if (!g_util.MakeSecDesc(pSid, &pSD)) { delete pSid; return GetLastError(); } fFreePSD = TRUE; } m_sCallBackContext.fIsIAC = SDisIAC(pSD); if (m_sCallBackContext.fIsIAC) { // convert to a true security descriptor SECURITY_DESCRIPTOR * pNewSD = SDfromIAC(pSD); if (!pNewSD) { // failed so pop up an error box CString sMsg, sCaption; sMsg.LoadString(IDS_BADSD); sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, sMsg, sCaption, MB_OK); // make a valid security descriptor so we can continue if (!g_util.LookupProcessInfo(&pSid, NULL)) { return GetLastError(); } if (!g_util.MakeSecDesc(pSid, &pNewSD)) { delete pSid; return GetLastError(); } } if (fFreePSD) { GlobalFree(pSD); } pSD=pNewSD; fFreePSD = TRUE; } // If this is for Access or Launch permissons then check that the // SD contains only allows and deny's for COM_RIGHTS_EXECUTE if (!CheckSDForCOM_RIGHTS_EXECUTE(pSD)) { return IDCANCEL; } // Invoke the ACL editor SedDiscretionaryAclEditor(hWnd, // Owner hWnd GetModuleHandle(NULL), // Owner hInstance NULL, // Server &objTyp, // ObjectTyp, &appAccesses, // Application accesses (TCHAR*)szValueName, // Object name, callBackFunc, // Callback function (ULONG_PTR) &m_sCallBackContext, // Callback context pSD, // Security descriptor, FALSE, // Couldnt read Dacl, FALSE, // Can't write Dacl, &dwStatus, // SED status return, 0); // Flags // Check status return if (dwStatus != ERROR_SUCCESS) { // PostErrorMessage(dwStatus); } // We're done if (fFreePSD) { GlobalFree(pSD); } return dwStatus == 0 ? ERROR_SUCCESS : IDCANCEL; #else return IDCANCEL; #endif } // Invoke the ACL editor on the specified key. This method writes an ACL // data packet to the virtual registry. This method supports configuration // security only (pktType RegKeyACL). int CUtility::ACLEditor2(HWND hWnd, HKEY hKey, HKEY *phClsids, unsigned cClsids, TCHAR *szTitle, int *pIndex, PACKETTYPE pktType) { #if !defined(STANDALONE_BUILD) int err; BYTE aSD[128]; DWORD cbSD = 128; SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR *) aSD; BOOL fFreePSD = FALSE; TCHAR szKeyRead[32]; CString szKeyRead_; TCHAR szHkeyClassesRoot[32]; CString szHkeyClassesRoot_; // Initialize strings szKeyRead_.LoadString(IDS_Key_Read); _tcscpy(szKeyRead, (LPCTSTR) szKeyRead_); szHkeyClassesRoot_.LoadString(IDS_HKEY_CLASSES_ROOT); _tcscpy(szHkeyClassesRoot, (LPCTSTR) szHkeyClassesRoot_); if (*pIndex == -1) { // Read the security descriptor on this key err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, aSD, &cbSD); if (err == ERROR_MORE_DATA || err == ERROR_INSUFFICIENT_BUFFER) { pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbSD); if (pSD == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } fFreePSD = TRUE; } else if (err != ERROR_SUCCESS) { return err; } if ((err = RegGetKeySecurity(hKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSD, &cbSD) != ERROR_SUCCESS)) { if (fFreePSD) { GlobalFree(pSD); } return err; } } // Fetch the most recently edited SD else { CDataPacket *pCdp = g_virtreg.GetAt(*pIndex); pSD = pCdp -> pkt.racl.pSec; } // Initialize the callback context m_sCallBackContext.pktType = pktType; m_sCallBackContext.pIndex = pIndex; m_sCallBackContext.origSD = pSD; m_sCallBackContext.info.regKey.hKey = hKey; m_sCallBackContext.info.regKey.phClsids = phClsids; m_sCallBackContext.info.regKey.cClsids = cClsids; m_sCallBackContext.info.regKey.szTitle = szTitle; // Invoke the ACL editor DWORD dwStatus; GENERIC_MAPPING genericMapping; CString szObjectType; szObjectType.LoadString(IDS_Registry_Key); CString szQueryValue; szQueryValue.LoadString(IDS_Query_Value); CString szSetValue; szSetValue.LoadString(IDS_Set_Value); CString szCreateSubkeys; szCreateSubkeys.LoadString(IDS_Create_Subkey); CString szEnumerateSubkeys; szEnumerateSubkeys.LoadString(IDS_Enumerate_Subkeys); CString szNotify; szNotify.LoadString(IDS_Notify); CString szCreateLink; szCreateLink.LoadString(IDS_Create_Link); CString szDelete; szDelete.LoadString(IDS_Delete); CString szWriteDAC; szWriteDAC.LoadString(IDS_Write_DAC); CString szWriteOwner; szWriteOwner.LoadString(IDS_Write_Owner); CString szReadControl; szReadControl.LoadString(IDS_Read_Control); CString szRead; szRead.LoadString(IDS_Read); CString szFullControl; szFullControl.LoadString(IDS_Full_Control); CString szSpecialAccess; szSpecialAccess.LoadString(IDS_Special_AccessDotDotDot); SED_HELP_INFO helpInfo = { TEXT("dcomcnfg.hlp"), {HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG, HC_MAIN_DLG} }; SED_OBJECT_TYPE_DESCRIPTOR objTyp = {SED_REVISION1, // Revision FALSE, // Is container? FALSE, // Allow new object perms? FALSE, // Specific to generic? &genericMapping, // Generic mapping NULL, // Generic mapping new (TCHAR *) ((LPCTSTR) szObjectType), // Object type name &helpInfo, // Help info TEXT(""), // Ckbox title TEXT(""), // Apply title TEXT(""), // (TCHAR *) ((LPCTSTR) szSpecialAccess), // Special Access menu item NULL // New special object access }; SED_APPLICATION_ACCESS appAccess[] = { { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_QUERY_VALUE, 0, (TCHAR *) ((LPCTSTR) szQueryValue) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_SET_VALUE, 0, (TCHAR *) ((LPCTSTR) szSetValue) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_CREATE_SUB_KEY, 0, (TCHAR *) ((LPCTSTR) szCreateSubkeys) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_ENUMERATE_SUB_KEYS, 0, (TCHAR *) ((LPCTSTR) szEnumerateSubkeys) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_NOTIFY, 0, (TCHAR *) ((LPCTSTR) szNotify) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, KEY_CREATE_LINK, 0, (TCHAR *) ((LPCTSTR) szCreateLink) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, 0x00010000, /* DELETE, */ 0, (TCHAR *) ((LPCTSTR) szDelete) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, WRITE_DAC, 0, (TCHAR *) ((LPCTSTR) szWriteDAC) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, WRITE_OWNER, 0, (TCHAR *) ((LPCTSTR) szWriteOwner) }, { SED_DESC_TYPE_RESOURCE_SPECIAL, READ_CONTROL, 0, (TCHAR *) ((LPCTSTR) szReadControl) }, { SED_DESC_TYPE_RESOURCE, KEY_READ, 0, (TCHAR *) ((LPCTSTR) szRead) }, { SED_DESC_TYPE_RESOURCE, GENERIC_ALL, /* KEY_ALL_ACCESS, */ 0, (TCHAR *) ((LPCTSTR) szFullControl) } }; SED_APPLICATION_ACCESSES appAccesses = {12, // Count of access groups appAccess, // Access array szKeyRead // Default access name }; // Intialize the help contexts helpInfo.aulHelpContext[HC_MAIN_DLG] = IDH_REGISTRY_KEY_PERMISSIONS; if (hKey == HKEY_CLASSES_ROOT) { helpInfo.aulHelpContext[HC_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_GLOBAL; helpInfo.aulHelpContext[HC_NEW_ITEM_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_GLOBAL; } else { helpInfo.aulHelpContext[HC_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_PER_APPID; helpInfo.aulHelpContext[HC_NEW_ITEM_SPECIAL_ACCESS_DLG] = IDH_SPECIAL_ACCESS_PER_APPID; } helpInfo.aulHelpContext[HC_ADD_USER_DLG] = IDH_ADD_USERS_AND_GROUPS; helpInfo.aulHelpContext[HC_ADD_USER_MEMBERS_LG_DLG] = IDH_LOCAL_GROUP_MEMBERSHIP; helpInfo.aulHelpContext[HC_ADD_USER_MEMBERS_GG_DLG] = IDH_GLOBAL_GROUP_MEMBERSHIP; helpInfo.aulHelpContext[HC_ADD_USER_SEARCH_DLG] = IDH_FIND_ACCOUNT1; genericMapping.GenericRead = KEY_READ; genericMapping.GenericWrite = KEY_WRITE; genericMapping.GenericExecute = KEY_READ; genericMapping.GenericAll = KEY_ALL_ACCESS; if (!CheckForValidSD(pSD)) { return IDCANCEL; } m_sCallBackContext.fIsIAC = SDisIAC(pSD); if (m_sCallBackContext.fIsIAC) { // convert to a true security descriptor SECURITY_DESCRIPTOR * pNewSD = SDfromIAC(pSD); if (!pNewSD) { // failed so pop up an error box CString sMsg, sCaption; sMsg.LoadString(IDS_BADSD); sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, sMsg, sCaption, MB_OK); return IDCANCEL; } if (fFreePSD) { GlobalFree(pSD); } pSD=pNewSD; fFreePSD = TRUE; } // Invoke the ACL editor SedDiscretionaryAclEditor(hWnd, // Owner hWnd GetModuleHandle(NULL), // Owner hInstance NULL, // Server &objTyp, // ObjectTyp, &appAccesses, // Application accesses szTitle ? szTitle : szHkeyClassesRoot,// Object name, callBackFunc, // Callback function (ULONG_PTR) &m_sCallBackContext, // Callback context pSD, // Security descriptor, FALSE, // Couldnt read Dacl, FALSE, // Can't write Dacl, &dwStatus, // SED status return, 0); // Flags // Check status return if (dwStatus != ERROR_SUCCESS) { // PostErrorMessage(dwStatus); } // We're done if (fFreePSD) { GlobalFree(pSD); } return dwStatus == 0 ? ERROR_SUCCESS : IDCANCEL; #else return IDCANCEL; #endif } BOOL CUtility::InvokeUserBrowser(HWND hWnd, TCHAR *szUser) { BOOL fRet = FALSE; #if !defined(STANDALONE_BUILD) CString szTitle; szTitle.LoadString(IDS_Browse_for_users); IDsObjectPicker *pDsObjectPicker = NULL; HRESULT hr = CoCreateInstance(CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER, IID_IDsObjectPicker, (void**)&pDsObjectPicker); if (SUCCEEDED(hr)) { IDataObject *pDataObject = NULL; // Fill in the structures necessary to init the dialog. DSOP_SCOPE_INIT_INFO rgInitInfo[2]; ZeroMemory(rgInitInfo, sizeof(rgInitInfo)); rgInitInfo[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgInitInfo[0].flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER; rgInitInfo[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE | DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT; rgInitInfo[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS; rgInitInfo[0].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS; rgInitInfo[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgInitInfo[1].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN | DSOP_SCOPE_TYPE_GLOBAL_CATALOG | DSOP_SCOPE_TYPE_WORKGROUP | DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE | DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE | DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN; rgInitInfo[1].flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT; rgInitInfo[1].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS; rgInitInfo[1].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS; DSOP_INIT_INFO dsInitInfo = { sizeof(dsInitInfo) }; dsInitInfo.aDsScopeInfos = rgInitInfo; dsInitInfo.cDsScopeInfos = sizeof(rgInitInfo) / sizeof(rgInitInfo[0]); hr = pDsObjectPicker->Initialize(&dsInitInfo); if (SUCCEEDED(hr)) { hr = pDsObjectPicker->InvokeDialog(hWnd, &pDataObject); } // Yes, I mean S_OK. The return code is S_FALSE if the user hits CANCEL. if (hr == S_OK) { // Why, yes, this DOES _s_u_c_k_! CLIPFORMAT cfDsObjectPicker = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST); STGMEDIUM stgMed = { TYMED_HGLOBAL, NULL, NULL }; FORMATETC fmtEtc = { cfDsObjectPicker, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; hr = pDataObject->GetData(&fmtEtc, &stgMed); if (SUCCEEDED(hr)) { PDS_SELECTION_LIST pSelList = (PDS_SELECTION_LIST)GlobalLock(stgMed.hGlobal); // Only care about the first item (should be single select, see above). assert(pSelList->cItems <= 1); PWSTR pwszName = NULL; PWSTR pwszDomain = NULL; PWSTR pwszPath = pSelList->aDsSelection[0].pwzADsPath; if (pwszPath && pwszPath[0]) { // e.g. "WinNT://REDMOND/johndoty" pwszName = wcsrchr(pwszPath, L'/'); assert(pwszName != NULL); if (pwszName == NULL) pwszName = pwszPath; *pwszName = L'\0'; pwszDomain = wcsrchr(pwszPath, L'/'); assert(pwszDomain != NULL); if (pwszDomain == NULL) pwszDomain = pwszPath; else pwszDomain++; pwszName++; } else { // e.g. "Everyone" pwszName = pSelList->aDsSelection[0].pwzName; } if (pwszName && pwszName[0]) { // Text buffer has already been allocated (*gulp*) CString szBackslash; szBackslash.LoadString(IDS_backslash); if (pwszDomain) _tcscpy(szUser, pwszDomain); else szUser[0] = L'\0'; _tcscat(szUser, (LPCTSTR) szBackslash); _tcscat(szUser, pwszName); fRet = TRUE; } ::GlobalUnlock(stgMed.hGlobal); ReleaseStgMedium(&stgMed); } pDataObject->Release(); } pDsObjectPicker->Release(); } #endif return fRet; } BOOL CUtility::InvokeMachineBrowser(TCHAR *szMachine) { #if !defined(STANDALONE_BUILD) /////////////////////////////////////////////////// // If we end up not wanting to use I_SystemFocusDialog, then the code below // is the start for fetching machine resources ourselves /* DWORD dwErr; NETRESOURCE aNetResource[1000]; HANDLE hNetwork; DWORD dwEntries = 100; DWORD dwBufSize = sizeof(aNetResource); dwErr = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, NULL, &hNetwork); if (dwErr == NO_ERROR) { dwEntries = 0xffffffff; dwErr = WNetEnumResource(hNetwork, &dwEntries, aNetResource, &dwBufSize); } WNetCloseEnum(hNetwork); dwErr = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, aNetResource, &hNetwork); if (dwErr == NO_ERROR) { dwEntries = 0xffffffff; dwErr = WNetEnumResource(hNetwork, &dwEntries, &aNetResource[1], &dwBufSize); } return dwErr == NO_ERROR ? TRUE : FALSE; */ /////////////////////////////////////////////////////// UINT err; BOOL fOkPressed = FALSE; err = I_SystemFocusDialog(GetForegroundWindow(), // FOCUSDLG_BROWSE_LOGON_DOMAIN | // FOCUSDLG_BROWSE_WKSTA_DOMAIN, 0x30003, szMachine, 128, &fOkPressed, TEXT("dcomcnfg.hlp"), IDH_SELECT_DOMAIN); if (err == ERROR_SUCCESS && fOkPressed) { return TRUE; } else { return FALSE; } #else return FALSE; #endif } int CUtility::StringFromGUID(GUID &rguid, TCHAR *lpsz, int cbMax) { int i; LPWSTR p = lpsz; const BYTE * pBytes = (const BYTE *) &rguid; *p++ = L'{'; for (i = 0; i < sizeof(GuidMap); i++) { if (GuidMap[i] == '-') { *p++ = L'-'; } else { *p++ = wszDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ]; *p++ = wszDigits[ (pBytes[GuidMap[i]] & 0x0F) ]; } } *p++ = L'}'; *p = L'\0'; return GUIDSTR_MAX; } BOOL CUtility::IsEqualGuid(GUID &guid1, GUID &guid2) { return ( ((PLONG) &guid1)[0] == ((PLONG) &guid2)[0] && ((PLONG) &guid1)[1] == ((PLONG) &guid2)[1] && ((PLONG) &guid1)[2] == ((PLONG) &guid2)[2] && ((PLONG) &guid1)[3] == ((PLONG) &guid2)[3]); } BOOL CUtility::AdjustPrivilege(TCHAR *szPrivilege) { HANDLE hProcessToken = 0; BOOL bOK = FALSE; TOKEN_PRIVILEGES privileges; if( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hProcessToken ) ) { return FALSE; } privileges.PrivilegeCount = 1; privileges.Privileges[ 0 ].Attributes = SE_PRIVILEGE_ENABLED; if( !LookupPrivilegeValue(NULL, szPrivilege, &privileges.Privileges[ 0 ].Luid ) ) { return FALSE; } if( !AdjustTokenPrivileges( hProcessToken, FALSE, &privileges, 0L, NULL, NULL ) ) { return FALSE; } if( hProcessToken ) { CloseHandle( hProcessToken ); } return TRUE; } BOOL CUtility::VerifyRemoteMachine(TCHAR *szRemoteMachine) { NETRESOURCE sResource; NETRESOURCE sResource2; DWORD dwErr; HANDLE hEnum; DWORD cbEntries; DWORD cbBfr; // TODO: Get this function to work. Right now WNetEnumResource is // screwing up the stack, causing an AV and anyway returns the error // ERROR_NO_MORE_ITEMS which I don't understand. // // Also, it is not clear that we should verify the remote machine name. // It may have different formats, e.g. IP address or a URL specification. // It may not even be on an NT network. In any case it may be offline // currently. return TRUE; sResource.dwScope = RESOURCE_GLOBALNET; sResource.dwType = RESOURCETYPE_ANY; sResource.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC; sResource.dwUsage = RESOURCEUSAGE_CONTAINER; sResource.lpLocalName = NULL; sResource.lpRemoteName = szRemoteMachine; sResource.lpComment = NULL; sResource.lpProvider = NULL; dwErr = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, RESOURCEUSAGE_CONTAINER, &sResource, &hEnum); if (dwErr == NO_ERROR) { cbEntries = 1; cbBfr = sizeof(NETRESOURCE); dwErr = WNetEnumResource(hEnum, &cbEntries, &sResource2, &cbBfr); } CloseHandle(hEnum); return TRUE; } BOOL CUtility::RetrieveUserPassword(TCHAR *szAppid, CString &sPassword) { #if !defined(STANDALONE_BUILD) LSA_OBJECT_ATTRIBUTES sObjAttributes; HANDLE hPolicy = NULL; LSA_UNICODE_STRING sKey; PLSA_UNICODE_STRING psPassword; TCHAR szKey[4 + GUIDSTR_MAX + 1]; // Formulate the access key if (_tcslen(szAppid) > GUIDSTR_MAX) return FALSE; _tcscpy(szKey, TEXT("SCM:")); _tcscat(szKey, szAppid); // UNICODE_STRING length fields are in bytes and include the NULL // terminator sKey.Length = (_tcslen(szKey) + 1) * sizeof(TCHAR); sKey.MaximumLength = (GUIDSTR_MAX + 5) * sizeof(TCHAR); sKey.Buffer = szKey; // Open the local security policy InitializeObjectAttributes(&sObjAttributes, NULL, 0L, NULL, NULL); if (!NT_SUCCESS(LsaOpenPolicy(NULL, &sObjAttributes, POLICY_GET_PRIVATE_INFORMATION, &hPolicy))) { return FALSE; } // Read the user's password if (!NT_SUCCESS(LsaRetrievePrivateData(hPolicy, &sKey, &psPassword))) { LsaClose(hPolicy); return FALSE; } // Close the policy handle, we're done with it now. LsaClose(hPolicy); // Possible for LsaRetrievePrivateData to return success but with a NULL // psPassword. If this happens we fail. if (!psPassword) { return FALSE; } // Copy the password sPassword = psPassword->Buffer; // Clear and free lsa's buffer SecureZeroMemory(psPassword->Buffer, psPassword->Length); LsaFreeMemory( psPassword ); return TRUE; #else return FALSE; #endif } BOOL CUtility::StoreUserPassword(TCHAR *szAppid, CString &szPassword) { #if !defined(STANDALONE_BUILD) LSA_OBJECT_ATTRIBUTES sObjAttributes; HANDLE hPolicy = NULL; LSA_UNICODE_STRING sKey; LSA_UNICODE_STRING sPassword; TCHAR szKey[4 + GUIDSTR_MAX + 1]; // Formulate the access key if (_tcslen(szAppid) > GUIDSTR_MAX) return FALSE; _tcscpy(szKey, TEXT("SCM:")); _tcscat(szKey, szAppid); // UNICODE_STRING length fields are in bytes and include the NULL // terminator sKey.Length = (_tcslen(szKey) + 1) * sizeof(TCHAR); sKey.MaximumLength = (GUIDSTR_MAX + 5) * sizeof(TCHAR); sKey.Buffer = szKey; // Make the password a UNICODE string sPassword.Length = (_tcslen(LPCTSTR(szPassword)) + 1) * sizeof(TCHAR); sPassword.Buffer = (TCHAR *) LPCTSTR(szPassword); sPassword.MaximumLength = sPassword.Length; // Open the local security policy InitializeObjectAttributes(&sObjAttributes, NULL, 0L, NULL, NULL); if (!NT_SUCCESS(LsaOpenPolicy(NULL, &sObjAttributes, POLICY_CREATE_SECRET, &hPolicy))) { return FALSE; } // Store the user's password if (!NT_SUCCESS(LsaStorePrivateData(hPolicy, &sKey, &sPassword))) { g_util.PostErrorMessage(); LsaClose(hPolicy); return FALSE; } // Close the policy handle, we're done with it now. LsaClose(hPolicy); return TRUE; #else return FALSE; #endif } BOOL CUtility::LookupProcessInfo(SID **ppSid, TCHAR **ppszPrincName) { BYTE aMemory[SIZEOF_TOKEN_USER]; TOKEN_USER *pTokenUser = (TOKEN_USER *) &aMemory; HANDLE hToken = NULL; DWORD lIgnore; DWORD lSidLen; DWORD lNameLen = 0; DWORD lDomainLen = 0; TCHAR *pDomainName = NULL; SID_NAME_USE sIgnore; if (ppszPrincName != NULL) *ppszPrincName = NULL; // Open the process's token. if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { // Lookup SID of process token. if (GetTokenInformation( hToken, TokenUser, pTokenUser, sizeof(aMemory), &lIgnore )) { // Allocate memory to hold the SID. lSidLen = GetLengthSid( pTokenUser->User.Sid ); *ppSid = (SID *) new BYTE[lSidLen]; if (*ppSid == NULL) { return FALSE; } memcpy(*ppSid, pTokenUser->User.Sid, lSidLen); // Stop now if the caller doesn't want the user name. if (ppszPrincName != NULL) { // Find out how much memory to allocate for the name. LookupAccountSid(NULL, pTokenUser->User.Sid, NULL, &lNameLen, NULL, &lDomainLen, NULL ); if (lNameLen != 0) { // Allocate memory for the user's name. *ppszPrincName = (TCHAR *) new BYTE[lNameLen*sizeof(TCHAR)]; if (ppszPrincName == NULL) { CloseHandle( hToken ); return FALSE; } pDomainName = (TCHAR *) new BYTE[lDomainLen*sizeof(TCHAR)]; if (pDomainName == NULL) { delete ppszPrincName; CloseHandle( hToken ); return FALSE; } // Find the user's name. if (!LookupAccountSid( NULL, pTokenUser->User.Sid, *ppszPrincName, &lNameLen, pDomainName, &lDomainLen, &sIgnore)) { delete ppszPrincName; delete pDomainName; CloseHandle( hToken ); return FALSE; } } delete ppszPrincName; delete pDomainName; } } CloseHandle( hToken ); } return TRUE; } BOOL CUtility::MakeSecDesc(SID *pSid, SECURITY_DESCRIPTOR **ppSD) { ACL *pAcl; DWORD lSidLen; SID *pGroup; SID *pOwner; // In case we fail *ppSD = NULL; // Allocate the security descriptor. lSidLen = GetLengthSid( pSid ); *ppSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, sizeof(SECURITY_DESCRIPTOR) + 2*lSidLen + SIZEOF_ACL); if (*ppSD == NULL) { return FALSE; } pGroup = (SID *) (*ppSD + 1); pOwner = (SID *) (((BYTE *) pGroup) + lSidLen); pAcl = (ACL *) (((BYTE *) pOwner) + lSidLen); // Initialize a new security descriptor. if (!InitializeSecurityDescriptor(*ppSD, SECURITY_DESCRIPTOR_REVISION)) { GlobalFree(*ppSD); return FALSE; } // Initialize a new ACL. if (!InitializeAcl(pAcl, SIZEOF_ACL, ACL_REVISION2)) { GlobalFree(*ppSD); return FALSE; } // Comment out this code because the only time we create a default SD is // when attempting to edit // \\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE.DefaultAccessPermission // which we want to start with 0 ACE's /* // Allow the current user access. if (!AddAccessAllowedAce( pAcl, ACL_REVISION2, COM_RIGHTS_EXECUTE, pSid )) { GlobalFree(*ppSD); return FALSE; } // Allow local system access. if (!AddAccessAllowedAce( pAcl, ACL_REVISION2, COM_RIGHTS_EXECUTE, (void *) &LOCAL_SYSTEM_SID )) { GlobalFree(*ppSD); return FALSE; } */ // Add a new ACL to the security descriptor. if (!SetSecurityDescriptorDacl( *ppSD, TRUE, pAcl, FALSE )) { GlobalFree(*ppSD); return FALSE; } // Set the group. memcpy( pGroup, pSid, lSidLen ); if (!SetSecurityDescriptorGroup( *ppSD, pGroup, FALSE )) { GlobalFree(*ppSD); return FALSE; } // Set the owner. memcpy( pOwner, pSid, lSidLen ); if (!SetSecurityDescriptorOwner( *ppSD, pOwner, FALSE )) { GlobalFree(*ppSD); return FALSE; } // Check the security descriptor. assert(IsValidSecurityDescriptor(*ppSD)); return TRUE; } // Accepts either a traditional security descriptor or an IAccessControl BOOL CUtility::CheckForValidSD(SECURITY_DESCRIPTOR *pSD) { WORD dwType = 0; if (pSD) { dwType = *((WORD *)pSD); } if ((dwType != 1) && (dwType != 2)) { CString sMsg, sCaption; sMsg.LoadString(IDS_BADSD); sCaption.LoadString(IDS_SYSTEMMESSAGE); MessageBox(NULL, sMsg, sCaption, MB_OK); return FALSE; } return TRUE; } // Check to see if the security descriptor is really a serialized IAccessControl. BOOL CUtility::SDisIAC(SECURITY_DESCRIPTOR *pSD) { WORD dwType = *((WORD *)pSD); if (dwType == 2) { return TRUE; } return FALSE; } SECURITY_DESCRIPTOR * CUtility::SDfromIAC(SECURITY_DESCRIPTOR * pSD) { IStream * pStream; IAccessControl * pIAC; IPersistStream * pIPS; HRESULT hr; BOOL fReturn; // Un-serialize the IAccessControl hr = CreateStreamOnHGlobal((HGLOBAL)pSD, FALSE, &pStream); if (FAILED(hr)) { return NULL; } // skip version DWORD dwVersion; hr = pStream->Read(&dwVersion, sizeof(DWORD), NULL); if (FAILED(hr) || dwVersion != 2) { return NULL; } // skip CLSID CLSID clsid; hr = pStream->Read(&clsid, sizeof(CLSID), NULL); if (FAILED(hr)) { return NULL; } // create and IAccessControl and get an IPersistStream hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IAccessControl, (void **)&pIAC); if (FAILED(hr)) { pStream->Release(); return NULL; } hr = pIAC->QueryInterface(IID_IPersistStream, (void **) &pIPS); if (FAILED(hr)) { pIAC->Release(); pStream->Release(); return NULL; } hr = pIPS->Load(pStream); pIPS->Release(); pStream->Release(); if (FAILED(hr)) { pIAC->Release(); return NULL; } // Create an EXPLICIT_ACCESS list for each entry in the IAccessControl DWORD cAces; ACTRL_ACCESS_ENTRYW * rgAE; ACTRL_ACCESS * pAccess; // PTRUSTEE pOwner; // PTRUSTEE pGroup; // hr = pIAC->GetAllAccessRights(NULL, &pAccess, &pOwner, &pGroup); hr = pIAC->GetAllAccessRights(NULL, &pAccess, NULL, NULL); if (FAILED(hr) || (pAccess->cEntries == 0)) { pIAC->Release(); return NULL; } // we're assuming only one property entry cAces = pAccess->pPropertyAccessList->pAccessEntryList->cEntries; rgAE = pAccess->pPropertyAccessList->pAccessEntryList->pAccessList; EXPLICIT_ACCESS * rgEA = new EXPLICIT_ACCESS[cAces]; DWORD i; for (i = cAces; i--;) { LPTSTR szName = rgAE[i].Trustee.ptstrName; if (TRUSTEE_IS_NAME == rgAE[i].Trustee.TrusteeForm && 0 == wcscmp(rgAE[i].Trustee.ptstrName, L"*")) { szName = new WCHAR [wcslen(L"EVERYONE") + 1]; if (!szName) { pIAC->Release(); return NULL; } wcscpy(szName, L"EVERYONE"); } DWORD dwAccessPermissions = rgAE[i].Access; // should always be COM_RIGHTS_EXECUTE or GENERIC_ALL ACCESS_MODE AccessMode; switch (rgAE[i].fAccessFlags) { case ACTRL_ACCESS_ALLOWED: AccessMode = SET_ACCESS; dwAccessPermissions = COM_RIGHTS_EXECUTE; // HACK! Required to get ACL editor to work. break; case ACTRL_ACCESS_DENIED: default: AccessMode = DENY_ACCESS; dwAccessPermissions = GENERIC_ALL; // HACK! Required to get ACL editor to work. break; } DWORD dwInheritance = rgAE[i].Inheritance; // Carefull. May not be allowed. BuildExplicitAccessWithName( &rgEA[i], szName, dwAccessPermissions, AccessMode, dwInheritance); } SECURITY_DESCRIPTOR * pSDNew = NULL; ULONG cbSize = 0; // create the new Security descriptor hr = BuildSecurityDescriptor(NULL, //pOwner, NULL, //pGroup, cAces, rgEA, 0, NULL, NULL, &cbSize, (void **)&pSDNew); if (ERROR_SUCCESS != hr) { // For some reason this may fail with this error even when it appears to have worked // A subsequent call seems to have no affect (i.e. it doesn't work like // other security descriptor calls that expect you to allocate the buffer yourself) if (ERROR_INSUFFICIENT_BUFFER != hr) { return NULL; } } SECURITY_DESCRIPTOR * pSDCopy = (SECURITY_DESCRIPTOR *)GlobalAlloc(GMEM_FIXED, cbSize); if (!pSDCopy) { LocalFree(pSDNew); return NULL; } memcpy(pSDCopy, pSDNew, cbSize); LocalFree(pSDNew); //delete [] rgAE; pIAC->Release(); return pSDCopy; } SECURITY_DESCRIPTOR * CUtility::IACfromSD(SECURITY_DESCRIPTOR * pSD) { IAccessControl * pIAC = NULL; // create new IAccessControl object HRESULT hr; hr = CoCreateInstance(CLSID_DCOMAccessControl, NULL, CLSCTX_INPROC_SERVER, IID_IAccessControl, (void **) &pIAC); if (FAILED(hr)) { return (NULL); } IPersistStream * pIPS; hr = pIAC->QueryInterface(IID_IPersistStream, (void **) &pIPS); if (FAILED(hr)) { pIAC->Release(); return NULL; } hr = pIPS->Load(NULL); if (FAILED(hr)) { pIPS->Release(); pIAC->Release(); return NULL; } BOOL fReturn, fDaclPresent, fDaclDefaulted; ACL * pDacl; // get the ACL list fReturn = GetSecurityDescriptorDacl(pSD, &fDaclPresent, &pDacl, &fDaclDefaulted); if (fReturn && fDaclPresent) { PEXPLICIT_ACCESS rgEA; ULONG cAces; #if 0 // Set to 1 when GetExplicitEntriesFromAcl works DWORD dwReturn = GetExplicitEntriesFromAcl(pDacl, &cAces, &rgEA); // enumerate the ACL, building list of objects to add to IAccessControl object if (dwReturn != ERROR_SUCCESS) { pIAC->Release(); return NULL; } ACTRL_ACCESSW stAccess; ACTRL_PROPERTY_ENTRYW stProperty; ACTRL_ACCESS_ENTRY_LISTW stAccessList; stAccess.cEntries = 1; stAccess.pPropertyAccessList = &stProperty; stProperty.lpProperty = NULL; stProperty.pAccessEntryList = &stAccessList; stProperty.fListFlags = 0; stAccessList.cEntries = cAces; ACTRL_ACCESS_ENTRYW * rgAE = new ACTRL_ACCESS_ENTRYW[cAces]; stAccessList.pAccessList = rgAE; ULONG i; for (i = cAces; i--; ) { rgAE[i].Trustee = rgEA[i].Trustee; if (rgEA[i].Trustee.TrusteeForm == TRUSTEE_IS_SID) { // convert to a named trustee rgAE[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME; SID * pSid = (SID *)rgEA[i].Trustee.ptstrName; DWORD cbName = 0; DWORD cbDomain = 0; LPTSTR szName = NULL; LPTSTR szDomain = NULL; SID_NAME_USE snu; fReturn = LookupAccountSid(NULL, pSid, szName, &cbName, szDomain, &cbDomain, &snu); szName = (LPTSTR) new char [cbName]; szDomain = (LPTSTR) new char [cbDomain]; fReturn = LookupAccountSid(NULL, pSid, szName, &cbName, szDomain, &cbDomain, &snu); CString * pcs = new CString; (*pcs) = TEXT("\\\\"); (*pcs) += szDomain; (*pcs) += TEXT("\\"); (*pcs) += szName; rgAE[i].Trustee.ptstrName = (LPTSTR)(LPCTSTR)(*pcs); } else { #if 0 // REMOVE THIS HACK when GetExplicitEntriesFromAcl works as it should if (rgAE[i].Trustee.TrusteeType < TRUSTEE_IS_WELL_KNOWN_GROUP) { rgAE[i].Trustee.TrusteeType = (enum _TRUSTEE_TYPE)((unsigned)rgAE[i].Trustee.TrusteeType + 1); } #endif if (rgAE[i].Trustee.TrusteeType == TRUSTEE_IS_WELL_KNOWN_GROUP) { // IAccessControl::GrantAccessRights doesn't like TRUSTEE_IS_WELL_KNOWN_GROUP for some reason rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; } } // test for "the world" if (TRUSTEE_IS_WELL_KNOWN_GROUP == rgAE[i].Trustee.TrusteeType && 0 == _wcsicmp(L"Everyone", rgAE[i].Trustee.ptstrName)) { rgAE[i].Trustee.ptstrName[0] = L'*'; rgAE[i].Trustee.ptstrName[1] = 0; } rgAE[i].Access = rgEA[i].grfAccessPermissions; rgAE[i].ProvSpecificAccess = 0; rgAE[i].Inheritance = rgEA[i].grfInheritance; rgAE[i].lpInheritProperty = NULL; switch (rgEA[i].grfAccessMode) { case SET_ACCESS: rgAE[i].fAccessFlags = ACTRL_ACCESS_ALLOWED; break; case DENY_ACCESS: default: rgAE[i].fAccessFlags = ACTRL_ACCESS_DENIED; break; } } #else ACL_SIZE_INFORMATION aclInfo; fReturn = GetAclInformation(pDacl, &aclInfo, sizeof(aclInfo), AclSizeInformation); if (!fReturn) { pIPS->Release(); pIAC->Release(); return NULL; } cAces = aclInfo.AceCount; ACE_HEADER * pAceHeader; ACTRL_ACCESSW stAccess; ACTRL_PROPERTY_ENTRYW stProperty; ACTRL_ACCESS_ENTRY_LISTW stAccessList; stAccess.cEntries = 1; stAccess.pPropertyAccessList = &stProperty; stProperty.lpProperty = NULL; stProperty.pAccessEntryList = &stAccessList; stProperty.fListFlags = 0; stAccessList.cEntries = cAces; ACTRL_ACCESS_ENTRYW * rgAE = new ACTRL_ACCESS_ENTRYW[cAces]; if (!rgAE) { pIPS->Release(); pIAC->Release(); return NULL; } stAccessList.pAccessList = rgAE; ULONG i; for (i = cAces; i--; ) { rgAE[i].ProvSpecificAccess = 0; rgAE[i].Inheritance = NO_INHERITANCE; rgAE[i].lpInheritProperty = NULL; rgAE[i].Trustee.pMultipleTrustee = NULL; rgAE[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; rgAE[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME; rgAE[i].Access = COM_RIGHTS_EXECUTE; fReturn = GetAce(pDacl, i, (LPVOID *)&pAceHeader); if (!fReturn) { delete [] rgAE; pIPS->Release(); pIAC->Release(); return NULL; } SID * pSid = NULL; switch (pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: { rgAE[i].fAccessFlags = ACTRL_ACCESS_ALLOWED; ACCESS_ALLOWED_ACE * pAce = (ACCESS_ALLOWED_ACE *)pAceHeader; pSid = (SID *) &(pAce->SidStart); } break; case ACCESS_DENIED_ACE_TYPE: { rgAE[i].fAccessFlags = ACTRL_ACCESS_DENIED; ACCESS_DENIED_ACE * pAce = (ACCESS_DENIED_ACE *)pAceHeader; pSid = (SID *) &(pAce->SidStart); } break; default: break; } TCHAR szName[MAX_PATH]; TCHAR szDomain[MAX_PATH]; DWORD cbName = MAX_PATH; DWORD cbDomain = MAX_PATH; SID_NAME_USE use; if(pSid) { fReturn = LookupAccountSid(NULL, pSid, szName, &cbName, szDomain, &cbDomain, &use); } else { fReturn = FALSE; // i.e., we took default path above } if (!fReturn) { delete [] rgAE; pIPS->Release(); pIAC->Release(); return NULL; } switch (use) { case SidTypeUser: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_USER; break; case SidTypeGroup: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; break; case SidTypeAlias: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; break; case SidTypeWellKnownGroup: //rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; break; case SidTypeDeletedAccount: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_DELETED; break; case SidTypeInvalid: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_INVALID; break; case SidTypeDomain: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; //TRUSTEE_IS_DOMAIN; break; case SidTypeUnknown: default: rgAE[i].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; break; } CString sz; // test for "the world" if (0 == wcslen(szDomain) && 0 == _wcsicmp(L"Everyone", szName)) { sz = "*"; } else { sz = szDomain; sz += "\\"; sz += szName; } WCHAR * wsz = new WCHAR[sz.GetLength() + 1]; wcscpy(wsz, (LPCWSTR)sz); rgAE[i].Trustee.ptstrName = wsz; } #endif delete [] rgAE; hr = pIAC->GrantAccessRights(&stAccess); if (FAILED(hr)) { pIPS->Release(); pIAC->Release(); return NULL; } // free up structures // LocalFree(rgEA); } // serialize the IAccessControl object // Find out how big it is ULARGE_INTEGER size; hr = pIPS->GetSizeMax(&size); if (FAILED(hr)) { pIPS->Release(); pIAC->Release(); return NULL; } size.QuadPart += sizeof(DWORD) + sizeof (CLSID); HANDLE hMem = GlobalAlloc(GMEM_FIXED, size.LowPart); if (hMem != NULL) { IStream * pStream; hr = CreateStreamOnHGlobal(hMem, FALSE, &pStream); if (FAILED(hr)) { pIPS->Release(); pIAC->Release(); return NULL; } DWORD dwVersion = 2; CLSID clsid = CLSID_DCOMAccessControl; hr = pStream->Write(&dwVersion, sizeof(DWORD), NULL); if (FAILED(hr)) { pStream->Release(); pIPS->Release(); pIAC->Release(); return NULL; } hr = pStream->Write(&clsid, sizeof(CLSID), NULL); if (FAILED(hr)) { pStream->Release(); pIPS->Release(); pIAC->Release(); return NULL; } hr = pIPS->Save(pStream, TRUE); pStream->Release(); if (FAILED(hr)) { pIPS->Release(); pIAC->Release(); return NULL; } } pIPS->Release(); pIAC->Release(); return (SECURITY_DESCRIPTOR *) hMem; } BOOL CUtility::CheckSDForCOM_RIGHTS_EXECUTE(SECURITY_DESCRIPTOR *pSD) { PSrAcl pDacl; PSrAce pAce; DWORD cbAces; SECURITY_DESCRIPTOR_RELATIVE* pSDr = (SECURITY_DESCRIPTOR_RELATIVE*) pSD; // Check whether the security descriptor is self-relative if (!(pSD->Control & SE_SELF_RELATIVE)) { pDacl = (PSrAcl) pSD->Dacl; // Check for a deny ALL if (pDacl == NULL) { return TRUE; } } else { // First check for a deny ALL if (pSDr->Dacl == 0) { return TRUE; } pDacl = (PSrAcl) (((BYTE *) pSDr) + (pSDr->Dacl)); } // Do over the ACE's for (pAce = (PSrAce) (((BYTE *) pDacl) + sizeof(SSrAcl)), cbAces = pDacl->AceCount; cbAces; pAce = (PSrAce) (((BYTE *) pAce) + pAce->AceSize), cbAces--) { // workaround for the ACL editor bug. If the ACL editor sees a non GENERIC_ALL deny ACE, it // complains. So we convert COM_RIGHTS_EXECUTE to GENERIC_ALL. On the way back we will // do the reverse. See CallBackFunc for the other half of this fix. if (pAce->Type == 1 && pAce->AccessMask == COM_RIGHTS_EXECUTE) { pAce->AccessMask = GENERIC_ALL; } // Check that it is // a) an allow on COM_RIGHTS_EXECUTE // b) a deny on GENERIC_ALL, // c) a deny on COM_RIGHTS_EXECUTE, // d) a deny ALL (handled above if the DACL is NULL) or // e) an allow everyone (handled implicitly if cbAces == 0) if (!(((pAce->Type == 0 && pAce->AccessMask == COM_RIGHTS_EXECUTE) || (pAce->Type == 1 && pAce->AccessMask == GENERIC_ALL) || (pAce->Type == 1 && pAce->AccessMask == COM_RIGHTS_EXECUTE)))) { CString szText; CString szTitle; szText.LoadString(IDS_The_security_); szTitle.LoadString(IDS_DCOM_Configuration_Warning); if (MessageBox(GetForegroundWindow(), (LPCTSTR) szText, (LPCTSTR) szTitle, MB_YESNO) == IDYES) { pAce->Flags = 0; pAce->Type = 0; pAce->AccessMask = COM_RIGHTS_EXECUTE; } else { return FALSE; } } } return TRUE; } BOOL CUtility::ChangeService(LPCTSTR szService, LPCTSTR szIdentity, LPCTSTR szPassword, LPCTSTR szDisplay) { SC_HANDLE hSCManager; SC_HANDLE hService; QUERY_SERVICE_CONFIG qsc; DWORD dwBytesNeeded = 0; LPTSTR lpszTmpDisplay = (LPTSTR)szDisplay; // Open the service control manager if (hSCManager = OpenSCManager(NULL, NULL, GENERIC_READ | GENERIC_WRITE)) { // Try to open a handle to the requested service if (!(hService = OpenService(hSCManager, szService, GENERIC_READ | GENERIC_WRITE))) { g_util.PostErrorMessage(); CloseServiceHandle(hSCManager); return FALSE; } // Close the service manager's database CloseServiceHandle(hSCManager); if (QueryServiceConfig(hService, &qsc, sizeof(qsc), &dwBytesNeeded)) lpszTmpDisplay = qsc.lpDisplayName; // Change service identity parameters if (ChangeServiceConfig(hService, SERVICE_NO_CHANGE, // SERVICE_WIN32_OWN_PROCESS, SERVICE_NO_CHANGE, // SERVICE_DEMAND_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, szIdentity, szPassword, NULL)) { // Return success CloseServiceHandle(hService); return TRUE; } else { g_util.PostErrorMessage(); CloseServiceHandle(hService); return FALSE; } } else { g_util.PostErrorMessage(); return FALSE; } } // defined in ole32 extern "C" void UpdateDCOMSettings(); BOOL CUtility::UpdateDCOMInfo(void) { UpdateDCOMSettings(); return TRUE; } LRESULT CALLBACK ControlFixProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // This is a work-around because there is a bug in msdev 4.1: Cannot get // WM_HELP message processed by a control on which DDX_Control data exchange // is done because of subclassing problem. See msdn Q145865 for a discussion // plus work-around code. void CUtility::FixHelp(CWnd* pWnd) { // search all child windows. If their window proc // is AfxWndProc, then subclass with our window proc CWnd* pWndChild = pWnd->GetWindow(GW_CHILD); while(pWndChild != NULL) { if (GetWindowLongPtr(pWndChild->GetSafeHwnd(), GWLP_WNDPROC) == (LONG_PTR)AfxWndProc) { SetWindowLongPtr(pWndChild->GetSafeHwnd(), GWLP_WNDPROC, (LONG_PTR)ControlFixProc); } pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); } } LRESULT CALLBACK ControlFixProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_HELP) { // bypass MFC's handler, message will be sent to parent return DefWindowProc(hwnd, uMsg, wParam, lParam); } return AfxWndProc(hwnd,uMsg,wParam,lParam); } // Compare two security descriptors in self-relative form to // determine if they're the same BOOL CUtility::CompareSDs(PSrSecurityDescriptor pSD1, PSrSecurityDescriptor pSD2) { PSID pSid1, pSid2; PSrAcl pDacl1, pDacl2; PSrAce pAce1, pAce2; BYTE *p1, *p2; // Compare the owners pSid1 = (PSID) (((BYTE *) pSD1) + pSD1->Owner); pSid2 = (PSID) (((BYTE *) pSD2) + pSD2->Owner); if (!EqualSid(pSid1, pSid2)) { return FALSE; } // Compare the groups pSid1 = (PSID) (((BYTE *) pSD1) + pSD1->Group); pSid2 = (PSID) (((BYTE *) pSD2) + pSD2->Group); if (!EqualSid(pSid1, pSid2)) { return FALSE; } // Compare the DACL's pDacl1 = (PSrAcl) (((BYTE *) pSD1) + pSD1->Dacl); pDacl2 = (PSrAcl) (((BYTE *) pSD2) + pSD2->Dacl); // Check first that they are the same size and have the same // number of ACE's if (! (pDacl1->AclSize == pDacl2->AclSize && pDacl1->AceCount == pDacl2->AceCount)) { return FALSE; } // Now compare the ACL ACE by ACE pAce1 = (PSrAce) (((BYTE *) pDacl1) + sizeof(SSrAcl)); pAce2 = (PSrAce) (((BYTE *) pDacl2) + sizeof(SSrAcl)); for (int k = 0; k < pDacl1->AceCount; k++) { // Check the ACE headers if (! (pAce1->Type == pAce2->Type && pAce1->AceSize == pAce2->AceSize && pAce1->AccessMask == pAce2->AccessMask)) { return FALSE; } // Check the SID's p1 = (BYTE *) (((BYTE *) pAce1) + sizeof(ACE_HEADER)); p2 = (BYTE *) (((BYTE *) pAce2) + sizeof(ACE_HEADER)); for (ULONG j = 0; j < pAce1->AceSize - sizeof(ACE_HEADER); j++) { if (p1[j] != p2[j]) { return FALSE; } } // Go to the next ACE pAce1 = (PSrAce) (((BYTE *) pAce1) + pAce1->AceSize); pAce2 = (PSrAce) (((BYTE *) pAce2) + pAce2->AceSize); } return TRUE; } int CUtility::SetAccountRights(LPCTSTR szUser, TCHAR *szPrivilege) { #if !defined(STANDALONE_BUILD) int err; LSA_HANDLE hPolicy; LSA_OBJECT_ATTRIBUTES objAtt; DWORD cbSid = 1; TCHAR szDomain[MAX_PATH]; DWORD cbDomain = MAX_PATH * sizeof(TCHAR); PSID pSid = NULL; SID_NAME_USE snu; LSA_UNICODE_STRING privStr; // Fetch the SID for the specified user if ((err = GetPrincipalSID(szUser, &pSid)) != ERROR_SUCCESS) return err; memset(&objAtt, 0, sizeof(LSA_OBJECT_ATTRIBUTES)); if (IsBackupDC()) { TCHAR* pszPDC; LSA_UNICODE_STRING lsaPDC; pszPDC = PrimaryDCName(); lsaPDC.Length = _tcslen (pszPDC) * sizeof (TCHAR)-2; lsaPDC.MaximumLength = lsaPDC.Length + sizeof (TCHAR); lsaPDC.Buffer = &pszPDC[2]; err = LsaOpenPolicy(&lsaPDC, &objAtt, POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy); } else err = LsaOpenPolicy(NULL, &objAtt, POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy); if (err != ERROR_SUCCESS) { return GetLastError(); } // Set the specified privilege on this account privStr.Length = _tcslen(szPrivilege) * sizeof(TCHAR); privStr.MaximumLength = privStr.Length + sizeof(TCHAR); privStr.Buffer = szPrivilege; err = LsaAddAccountRights(hPolicy, pSid, &privStr, 1); // We're done delete pSid; LsaClose(hPolicy); if (err != ERROR_SUCCESS) { return GetLastError(); } #endif return ERROR_SUCCESS; } // NOTE: Cannot handle IAccessControl style SDs void CUtility::CopyAbsoluteSD( SECURITY_DESCRIPTOR *pSDSrc, SECURITY_DESCRIPTOR **pSDDest) { (*pSDDest)->Revision = pSDSrc->Revision; (*pSDDest)->Sbz1 = pSDSrc->Sbz1; (*pSDDest)->Control = pSDSrc->Control; (*pSDDest)->Group = (*pSDDest)->Owner = (*pSDDest)->Dacl = (*pSDDest)->Sacl = NULL; BYTE* pOffSet=(BYTE*)(*pSDDest)+sizeof(SECURITY_DESCRIPTOR); if (pSDSrc->Dacl != NULL) { memcpy(pOffSet,pSDSrc->Dacl,pSDSrc->Dacl->AclSize); (*pSDDest)->Dacl = (PACL)pOffSet; pOffSet += pSDSrc->Dacl->AclSize; } if (pSDSrc->Owner != NULL) { memcpy(pOffSet,pSDSrc->Owner,GetLengthSid(pSDSrc->Owner)); (*pSDDest)->Owner = (PSID)pOffSet; pOffSet += GetLengthSid(pSDSrc->Owner); } if (pSDSrc->Group != NULL) { memcpy(pOffSet,pSDSrc->Group,GetLengthSid(pSDSrc->Group)); (*pSDDest)->Group = (PSID)pOffSet; } } // This method is included only because in the debug version when using // MFC they validate the C++ heap, whereas RtlCopySecurityDescriptor uses // the standard process heap, causing MFC to throw a breakpoint // The return value indicates the success or failure of the operation // NTBUG 310004. This function is called to copy both self-relative and absolute // SDs. In its previous incarnation, it corrupted heap when called to // copy an absolute SD. Now I do the right thing. // NOTE: This function can not handle IAccessControl style SDs despite // appearances to the contrary. This is OK, because dcomcnfg.exe does // not handle such SDs at all. BOOL CUtility::CopySD(SECURITY_DESCRIPTOR *pSrc, SECURITY_DESCRIPTOR **pDest) { #if !defined(STANDALONE_BUILD) ULONG cbLen; *pDest = NULL; if (IsValidSecurityDescriptor(pSrc)) { if (SDisIAC(pSrc)) { cbLen = (ULONG) GlobalSize(pSrc); } else { cbLen = RtlLengthSecurityDescriptor(pSrc); } *pDest = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbLen); if (*pDest) { // if the SD is already self-relative, just copy if ((pSrc)->Control & SE_SELF_RELATIVE ) { memcpy(*pDest, pSrc, cbLen); return TRUE; } else { // workaround an ACLEDIT bug (NT 352977). When the DACL has no ACES, // ACLEDIT returns incorrect AclSize, causing an AV // when I copy it. So fix it here. if ((pSrc)->Dacl != NULL && ((pSrc)->Dacl->AceCount == 0)) (pSrc)->Dacl->AclSize=sizeof(ACL); CopyAbsoluteSD(pSrc,pDest); return TRUE; } GlobalFree(*pDest); } } #endif return FALSE; } // Set the inheritance flags on a security descriptor so keys created // under the key having this security descriptor will inherit all its // ACE's. We do this as a utility routine rather than via the ACL // editor because doing that adds check boxes and such to the ACL editor, // so it's cleaner this way. // // Note. The security descriptor is expected to be in absolute form void CUtility::SetInheritanceFlags(SECURITY_DESCRIPTOR *pSec) { PSrAcl pAcl = (PSrAcl) pSec->Dacl; PSrAce pAce; int k; // Do over the ACE's this DACL for (k = pAcl->AceCount, pAce = (PSrAce) (((BYTE *) pAcl) + sizeof(SSrAcl)); k; k--, pAce = (PSrAce) (((BYTE *) pAce) + pAce->AceSize)) { pAce->Flags |= CONTAINER_INHERIT_ACE; } } HRESULT CUtility::GetPrincipalSID (LPCTSTR Principal, PSID *Sid) { DWORD sidSize = 0; TCHAR refDomain [256]; DWORD refDomainSize = 0; DWORD returnValue; SID_NAME_USE snu; BOOL bSuccess; bSuccess = LookupAccountName (NULL, Principal, *Sid, &sidSize, refDomain, &refDomainSize, &snu); // codework - we need to check if this is correct // what about multisuer machines - ie hydra if ((returnValue = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) return returnValue; if ((*Sid = new BYTE[sidSize]) == NULL) return ERROR_OUTOFMEMORY; if (!LookupAccountName (NULL, Principal, *Sid, &sidSize, refDomain, &refDomainSize, &snu)) { delete *Sid; *Sid = NULL; return GetLastError(); } return ERROR_SUCCESS; } // this method, on first execution, checks if the current machine is a backup domain controller and if so, // caches the value and returns TRUE. Subsequent executions will use cached value. BOOL CUtility::IsBackupDC() { #if !defined(STANDALONE_BUILD) USER_MODALS_INFO_2 *umi2 = NULL; SERVER_INFO_101 *si101 = NULL; DWORD dw; if (!m_bCheckedDC) { if ((dw = NetServerGetInfo (NULL, 101, (LPBYTE *) &si101)) == 0) { if (si101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) { if ((dw = NetUserModalsGet (NULL, 2, (LPBYTE *) &umi2)) == 0) { if(umi2) { NetGetDCName (NULL, umi2->usrmod2_domain_name, (LPBYTE *) &m_pszDomainController); NetApiBufferFree (umi2); } m_bIsBdc = TRUE; } } } m_bCheckedDC = TRUE; if (si101) NetApiBufferFree (si101); } return m_bIsBdc; #else return FALSE; #endif } TCHAR* CUtility::PrimaryDCName() { static TCHAR s_tszUnknownDomainName[] = _T("UnknownDCName"); #if !defined(STANDALONE_BUILD) if (IsBackupDC()) { if(m_pszDomainController){ return m_pszDomainController; } else { return s_tszUnknownDomainName; } } #endif return NULL; }