3026 lines
88 KiB
C++
3026 lines
88 KiB
C++
//+---------------------------------------------------------------------
|
|
//
|
|
// 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 <getuser.h>
|
|
}
|
|
#endif
|
|
|
|
#include "util.h"
|
|
#include "virtreg.h"
|
|
|
|
extern "C"
|
|
{
|
|
#if !defined(STANDALONE_BUILD)
|
|
#include <ntlsa.h>
|
|
#include <ntseapi.h>
|
|
#include <sedapi.h>
|
|
#endif
|
|
|
|
#include <winnetwk.h>
|
|
|
|
#if !defined(STANDALONE_BUILD)
|
|
#include <uiexport.h>
|
|
#include <lm.h>
|
|
#endif
|
|
|
|
#include <rpc.h>
|
|
#include <rpcdce.h>
|
|
#include <aclapi.h>
|
|
}
|
|
|
|
#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}};
|
|
|
|
#if !defined(STANDALONE_BUILD)
|
|
extern "C"
|
|
{
|
|
int _stdcall UpdateActivationSettings(HANDLE hRpc, RPC_STATUS *status);
|
|
}
|
|
#endif
|
|
|
|
|
|
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_hRpc = 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();
|
|
if (m_hRpc != NULL)
|
|
{
|
|
RpcBindingFree(&m_hRpc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
#if 0 // set to 0 to remove this test code
|
|
SECURITY_DESCRIPTOR * pTempSD = g_util.IACfromSD((SECURITY_DESCRIPTOR *)SecDesc);
|
|
pNewSD = g_util.SDfromIAC((SECURITY_DESCRIPTOR *)pTempSD);
|
|
BOOL fResult = g_util.CompareSDs((PSrSecurityDescriptor)pNewSD, (PSrSecurityDescriptor)SecDesc);
|
|
GlobalFree(pTempSD);
|
|
#else
|
|
// just copy the security descriptor to get it into Global Memory
|
|
g_util.CopySD((SECURITY_DESCRIPTOR *)SecDesc, &pNewSD);
|
|
#endif
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
HUSERBROW hUser;
|
|
USERBROWSER sUserBrowser;
|
|
SUserDetailsPlus sUserDetailsPlus;
|
|
ULONG ulSize = USER_DETAILS_BUFFER_SIZE;
|
|
CString szTitle;
|
|
|
|
szTitle.LoadString(IDS_Browse_for_users);
|
|
|
|
sUserBrowser.ulStructSize = sizeof(USERBROWSER);
|
|
sUserBrowser.fUserCancelled = FALSE;
|
|
sUserBrowser.fExpandNames = TRUE;
|
|
sUserBrowser.hwndOwner = hWnd;
|
|
sUserBrowser.pszTitle = (TCHAR *) ((LPCTSTR) szTitle);
|
|
sUserBrowser.pszInitialDomain = NULL;
|
|
sUserBrowser.Flags = USRBROWS_SINGLE_SELECT |
|
|
USRBROWS_INCL_ALL |
|
|
USRBROWS_SHOW_USERS;
|
|
sUserBrowser.ulHelpContext = IDH_BROWSE_FOR_USERS;
|
|
sUserBrowser.pszHelpFileName = TEXT("dcomcnfg.hlp");
|
|
|
|
|
|
hUser = OpenUserBrowser(&sUserBrowser);
|
|
if (hUser == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
CString szBackslash;
|
|
|
|
szBackslash.LoadString(IDS_backslash);
|
|
|
|
if (EnumUserBrowserSelection(hUser,
|
|
&sUserDetailsPlus.sUserDetails,
|
|
&ulSize))
|
|
{
|
|
_tcscpy(szUser, sUserDetailsPlus.sUserDetails.pszDomainName);
|
|
_tcscat(szUser, (LPCTSTR) szBackslash);
|
|
_tcscat(szUser, sUserDetailsPlus.sUserDetails.pszAccountName);
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
CloseUserBrowser(hUser);
|
|
#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
|
|
_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
|
|
memset(psPassword->Buffer, 0, 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
|
|
_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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CUtility::UpdateDCOMInfo(void)
|
|
{
|
|
RPC_STATUS status;
|
|
TCHAR *pszBindString;
|
|
|
|
// Get a binding handle to the SCM if we haven't yet
|
|
if (m_hRpc == NULL)
|
|
{
|
|
status = RpcStringBindingCompose(NULL,
|
|
TEXT("ncalrpc"),
|
|
NULL,
|
|
TEXT("epmapper"),
|
|
NULL,
|
|
&pszBindString);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = RpcBindingFromStringBinding(pszBindString, &m_hRpc);
|
|
RpcStringFree(&pszBindString);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
}
|
|
|
|
#if !defined(STANDALONE_BUILD)
|
|
// Call over to the SCM to get the global registry values read
|
|
// into memory
|
|
UpdateActivationSettings(m_hRpc, &status);
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
{
|
|
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;
|
|
}
|