// Copyright (c) 1997-1999 Microsoft Corporation
#include "precomp.h"
#include "SI.h"
#include "..\common\util.h"
#include "..\common\ServiceThread.h"
#include "..\MMFUtil\MsgDlg.h"
#include "resource.h"
#include <winbase.h>

//----------------------------------------------------------------------------
EXTERN_C const GUID IID_ISecurityInformation =
	{ 0x965fc360, 0x16ff, 0x11d0, 0x91, 0xcb, 0x0, 0xaa, 0x0, 0xbb, 0xb7, 0x23 };

#define MY_FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED                    \
                            | SYNCHRONIZE                               \
                            | FILE_READ_DATA    | FILE_LIST_DIRECTORY   \
                            | FILE_WRITE_DATA   | FILE_ADD_FILE         \
                            | FILE_APPEND_DATA  | FILE_ADD_SUBDIRECTORY \
                            | FILE_CREATE_PIPE_INSTANCE                 \
                            | FILE_READ_EA                              \
                            | FILE_WRITE_EA                             \
                            | FILE_EXECUTE      | FILE_TRAVERSE         \
                            | FILE_DELETE_CHILD                         \
                            | FILE_READ_ATTRIBUTES                      \
                            | FILE_WRITE_ATTRIBUTES)

#if(FILE_ALL_ACCESS != MY_FILE_ALL_ACCESS)
#error ACL editor needs to sync with file permissions changes in ntioapi.h (or ntioapi.h is broken)
#endif

#define INHERIT_FULL            (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)

//
// Treat SYNCHRONIZE specially. In particular, always allow SYNCHRONIZE and
// never Deny SYNCHRONIZE. Do this by removing it from the Generic Mapping,
// turning it off in all ACEs and SI_ACCESS entries, and then adding it to
// all Allow ACEs before saving a new ACL.
//
#define FILE_GENERIC_READ_      (FILE_GENERIC_READ    & ~SYNCHRONIZE)
#define FILE_GENERIC_WRITE_     (FILE_GENERIC_WRITE   & ~(SYNCHRONIZE | READ_CONTROL))
#define FILE_GENERIC_EXECUTE_   (FILE_GENERIC_EXECUTE & ~SYNCHRONIZE)
#define FILE_GENERIC_ALL_       (FILE_ALL_ACCESS      & ~SYNCHRONIZE)

#define FILE_GENERAL_MODIFY     (FILE_GENERIC_READ_  | FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_ | DELETE)
#define FILE_GENERAL_PUBLISH    (FILE_GENERIC_READ_  | FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_)
#define FILE_GENERAL_DEPOSIT    (FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_)
#define FILE_GENERAL_READ_EX    (FILE_GENERIC_READ_  | FILE_GENERIC_EXECUTE_)

// The following array defines the permission names for NTFS objects.
SI_ACCESS siNTFSAccesses[] =
{
    { &GUID_NULL, FILE_GENERIC_ALL_,    MAKEINTRESOURCE(IDS_NTFS_GENERIC_ALL),      SI_ACCESS_GENERAL | INHERIT_FULL },
    { &GUID_NULL, FILE_GENERAL_MODIFY,  MAKEINTRESOURCE(IDS_NTFS_GENERAL_MODIFY),   SI_ACCESS_GENERAL | INHERIT_FULL },
    { &GUID_NULL, FILE_GENERAL_READ_EX, MAKEINTRESOURCE(IDS_NTFS_GENERAL_READ),     SI_ACCESS_GENERAL | INHERIT_FULL },
    { &GUID_NULL, FILE_GENERAL_READ_EX, MAKEINTRESOURCE(IDS_NTFS_GENERAL_LIST),     SI_ACCESS_CONTAINER | CONTAINER_INHERIT_ACE },
    { &GUID_NULL, FILE_GENERIC_READ_,   MAKEINTRESOURCE(IDS_NTFS_GENERIC_READ),     SI_ACCESS_GENERAL | INHERIT_FULL },
    { &GUID_NULL, FILE_GENERIC_WRITE_,  MAKEINTRESOURCE(IDS_NTFS_GENERIC_WRITE),    SI_ACCESS_GENERAL | INHERIT_FULL },
    { &GUID_NULL, FILE_EXECUTE,         MAKEINTRESOURCE(IDS_NTFS_FILE_EXECUTE),     SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_READ_DATA,       MAKEINTRESOURCE(IDS_NTFS_FILE_READ_DATA),   SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_READ_ATTRIBUTES, MAKEINTRESOURCE(IDS_NTFS_FILE_READ_ATTR),   SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_READ_EA,         MAKEINTRESOURCE(IDS_NTFS_FILE_READ_EA),     SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_WRITE_DATA,      MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_DATA),  SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_APPEND_DATA,     MAKEINTRESOURCE(IDS_NTFS_FILE_APPEND_DATA), SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_WRITE_ATTRIBUTES,MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_ATTR),  SI_ACCESS_SPECIFIC },
    { &GUID_NULL, FILE_WRITE_EA,        MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_EA),    SI_ACCESS_SPECIFIC },
#if(FILE_CREATE_PIPE_INSTANCE != FILE_APPEND_DATA)
    { &GUID_NULL, FILE_CREATE_PIPE_INSTANCE, MAKEINTRESOURCE(IDS_NTFS_FILE_CREATE_PIPE), SI_ACCESS_SPECIFIC },
#endif
    { &GUID_NULL, FILE_DELETE_CHILD,    MAKEINTRESOURCE(IDS_NTFS_FILE_DELETE_CHILD),SI_ACCESS_SPECIFIC },
    { &GUID_NULL, DELETE,               MAKEINTRESOURCE(IDS_NTFS_STD_DELETE),       SI_ACCESS_SPECIFIC },
    { &GUID_NULL, READ_CONTROL,         MAKEINTRESOURCE(IDS_NTFS_STD_READ_CONTROL), SI_ACCESS_SPECIFIC },
    { &GUID_NULL, WRITE_DAC,            MAKEINTRESOURCE(IDS_NTFS_STD_WRITE_DAC),    SI_ACCESS_SPECIFIC },
    { &GUID_NULL, WRITE_OWNER,          MAKEINTRESOURCE(IDS_NTFS_STD_WRITE_OWNER),  SI_ACCESS_SPECIFIC },
//    { &GUID_NULL, SYNCHRONIZE,          MAKEINTRESOURCE(IDS_NTFS_STD_SYNCHRONIZE),  SI_ACCESS_SPECIFIC },
    { &GUID_NULL, 0,                    MAKEINTRESOURCE(IDS_NONE),                  0 },
    { &GUID_NULL, FILE_GENERIC_EXECUTE_,MAKEINTRESOURCE(IDS_NTFS_GENERIC_EXECUTE),  0 },
    { &GUID_NULL, FILE_GENERAL_DEPOSIT, MAKEINTRESOURCE(IDS_NTFS_GENERAL_DEPOSIT),  0 },
    { &GUID_NULL, FILE_GENERAL_PUBLISH, MAKEINTRESOURCE(IDS_NTFS_GENERAL_PUBLISH),  0 },
};
#define iNTFSDefAccess      2   // FILE_GENERAL_READ_EX


// The following array defines the inheritance types for NTFS directories.
SI_INHERIT_TYPE siNTFSInheritTypes[] =
{
    &GUID_NULL, 0,                                                             MAKEINTRESOURCE(IDS_NTFS_FOLDER),
    &GUID_NULL, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,                    MAKEINTRESOURCE(IDS_NTFS_FOLDER_SUBITEMS),
    &GUID_NULL, CONTAINER_INHERIT_ACE,                                         MAKEINTRESOURCE(IDS_NTFS_FOLDER_SUBFOLDER),
    &GUID_NULL, OBJECT_INHERIT_ACE,                                            MAKEINTRESOURCE(IDS_NTFS_FOLDER_FILE),
    &GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_SUBITEMS_ONLY),
    &GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE,                      MAKEINTRESOURCE(IDS_NTFS_SUBFOLDER_ONLY),
    &GUID_NULL, INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE,                         MAKEINTRESOURCE(IDS_NTFS_FILE_ONLY),
};

GENERIC_MAPPING NTFSMap =
{
    FILE_GENERIC_READ,
    FILE_GENERIC_WRITE,
    FILE_GENERIC_EXECUTE,
    FILE_ALL_ACCESS
};

//---------------------------------------------------------------
CSecurity::CSecurity() :m_initingDlg(false), 
						g_serviceThread(0)
{
	m_dwprevSize = 0;
	m_prevTokens = NULL;
	m_prevBuffer = NULL;

}

//---------------------------------------------------------------
void CSecurity::Initialize(WbemServiceThread *serviceThread,
						CWbemClassObject &inst,
						bstr_t deviceID,
						LPWSTR idiotName,
						LPWSTR provider,
						LPWSTR mangled)
{
	m_deviceID = deviceID;
	wcsncpy(m_idiotName, idiotName, 100);
	wcsncpy(m_provider, provider, 100);
	wcsncpy(m_mangled, mangled, 255);

	g_serviceThread = serviceThread;
	m_inst = inst;
}

//------------------ Accessors to the above arrays---------------
//---------------------------------------------------------------
HRESULT CSecurity::MapGeneric(const GUID *pguidObjectType,
								  UCHAR *pAceFlags,
								  ACCESS_MASK *pMask)
{
	ATLASSERT(pMask);

    MapGenericMask(pMask, &NTFSMap);
   *pMask &= ~SYNCHRONIZE;

	return S_OK;
}

//-----------------------------------------------------------------------------
HRESULT CSecurity::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes,
										ULONG *pcInheritTypes)
{
    ATLASSERT(ppInheritTypes != NULL);
    ATLASSERT(pcInheritTypes != NULL);

    *ppInheritTypes = siNTFSInheritTypes;
    *pcInheritTypes = ARRAYSIZE(siNTFSInheritTypes);

	return S_OK;
}

//-----------------------------------------------------------------------------
#define ALL_SECURITY_ACCESS (READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY)

DWORD CSecurity::GetAccessMask(CWbemClassObject &inst)
{
	CWbemClassObject paramCls, dummy;

	DWORD dwAccessGranted = 0;
    DWORD dwAccessDesired[] = { ALL_SECURITY_ACCESS,
                                READ_CONTROL,
                                WRITE_DAC,
                                WRITE_OWNER,
                                ACCESS_SYSTEM_SECURITY};

	paramCls = m_WbemServices.GetObject("CIM_LogicalFile");

	if(paramCls)
	{
		CWbemClassObject param;
		CWbemClassObject setting;

		HRESULT hr = paramCls.GetMethod(L"GetEffectivePermission", param, dummy);

		if(SUCCEEDED(hr) && (bool)param)
		{
			bstr_t path(L"CIM_LogicalFile=\"");
			path += m_deviceID;
			path += L"\\\\\"";

			for(int i = 0; i < ARRAYSIZE(dwAccessDesired); i++)
			{
				if((dwAccessDesired[i] & dwAccessGranted) == dwAccessDesired[i])
					continue;   // already have this access

				CWbemClassObject retCls;

				hr = S_OK;
				hr = param.Put(L"Permissions", (long)dwAccessDesired[i]);

				if(SUCCEEDED(hr))
				{
					hr = m_WbemServices.ExecMethod(path, 
													L"GetEffectivePermission",
													param, retCls);
					if(SUCCEEDED(hr))
					{
						// extract the real error code.
						bool granted = retCls.GetBool("ReturnValue");

						if(granted)
							dwAccessGranted |= dwAccessDesired[i];
					}
				}

			} //endfor

		} //endif (SUCCEEDED
	}

	return dwAccessGranted;
}

//-----------------------------------------------------------------------------
#define ALL_SI (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION|\
				DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION)


// NOTE: To efficiently get the SD ONLY ONCE, GetObjectInformation calls 
// CacheSecurity() which keeps and SD ptr in m_ppCachedSD. GetSecurity()
// simply returns this pointer and zero's m_ppCachedSD. Aclui will clean up
// the actually memory as usual.
HRESULT CSecurity::GetObjectInformation(PSI_OBJECT_INFO pObjectInfo)
{
    ATLASSERT(pObjectInfo != NULL &&
             !IsBadWritePtr(pObjectInfo, sizeof(*pObjectInfo)));

	wchar_t fmt[100];
	_bstr_t temp;
    _bstr_t driveName;
	TCHAR driveLtr[3] = {0};
    TCHAR szTemp[30] = {0};
	HRESULT hr = S_OK;

	if(m_initingDlg)
	{
		memset(fmt, 0, 100 * sizeof(wchar_t));
		memset(m_idiotName, 0, 100 * sizeof(wchar_t));

		HRESULT hr2 = E_FAIL;
		IWbemServices *service = 0;

		if(g_serviceThread)
		{
			m_WbemServices = g_serviceThread->GetPtr();
			m_WbemServices.GetServices(&service);
			SetPriv(service);

			// NOTE: This also sets m_hAccessToken as a side effect.
			hr2 = CacheSecurity(ALL_SI, (PSECURITY_DESCRIPTOR *)&m_ppCachedSD, false);
		}

		if(SUCCEEDED(hr2) && (bool)m_inst)
		{
			DWORD granted = 0;

			pObjectInfo->dwFlags |= SI_EDIT_ALL |		// dacl, sacl, owner pages.
									SI_ADVANCED;	// more than generic rights.
									
			granted = GetAccessMask(m_inst);

		    if (!(granted & WRITE_DAC))
				pObjectInfo->dwFlags |= SI_READONLY;

			if (!(granted & WRITE_OWNER))
			{
				if (!(granted & READ_CONTROL))
					pObjectInfo->dwFlags &= ~SI_EDIT_OWNER;
				else
					pObjectInfo->dwFlags |= SI_OWNER_READONLY;
			}

			if (!(granted & ACCESS_SYSTEM_SECURITY))
				pObjectInfo->dwFlags &= ~SI_EDIT_AUDITS;


			pObjectInfo->dwFlags |= SI_NO_ACL_PROTECT |	// no parent.
									 SI_CONTAINER;		// it's a container since I
														// only do the root dir.

			long driveType = m_inst.GetLong(L"DriveType");
			
			// if its a network connection (remote)....
			if(driveType == 4)
			{
				// use the leftovers from the mangling routine.
				pObjectInfo->pszServerName = CloneWideString(m_provider);
			}
			else
			{
				bstr_t server;
				server = m_inst.GetString(L"__SERVER");
				pObjectInfo->pszServerName = CloneWideString(server);
			}

			driveName = m_inst.GetString("Name");
			
			// isolate the drive letter for later.
			_tcsncpy(driveLtr, driveName, driveName.length());
		}
		else
		{
			pObjectInfo->pszServerName = NULL;
		}

		ClearPriv(service);

		pObjectInfo->hInstance = HINST_THISDLL;

		if(::LoadString(HINST_THISDLL, 
						IDS_IDIOT_CAPTION_FMT,
						fmt, 100) == 0)
		{
			wcscpy(fmt, L"%s (%c:)");
		}


		// if its locally mapped...
		if(wcslen(m_mangled) == 0)
		{
			// format sheet caption using volumeName (temp).
			if(temp.length() == 0)
			{
				// if not volume name... just say "Local Disk".
				if(::LoadString(HINST_THISDLL, 
								IDS_LOCAL_DISK,
								szTemp, 30) == 0)
				{
					wcscpy(szTemp, L"Local Disk");
				}
				temp = szTemp;
			}
			swprintf(m_idiotName, fmt, (wchar_t *)temp, driveLtr[0]);
		}
		else
		{
			// format sheet caption using m_mangled. (network drives)
			swprintf(m_idiotName, fmt, m_mangled, driveLtr[0]);
		}
		pObjectInfo->pszObjectName = m_idiotName;
	}
	else  // just the initial creation.
	{
		// its only looking for the _TITLE bit which we dont set anyway.
		pObjectInfo->dwFlags = 0;		
	}

    return hr;
}

//-----------------------------------------------------------------------------
HRESULT CSecurity::GetAccessRights(const GUID *pguidObjectType,
									  DWORD dwFlags,
									  PSI_ACCESS *ppAccess,
									  ULONG *pcAccesses,
									  ULONG *piDefaultAccess)
{
	ATLASSERT(ppAccess);
	ATLASSERT(pcAccesses);
	ATLASSERT(piDefaultAccess);

    *ppAccess = siNTFSAccesses;
    *pcAccesses = ARRAYSIZE(siNTFSAccesses);
    *piDefaultAccess = iNTFSDefAccess;

	return S_OK;
}

//------------------ Real workers -------------------------------
//---------------------------------------------------------------
void CSecurity::SetPriv(IWbemServices *service)
{
    ImpersonateSelf(SecurityImpersonation);

	if(OpenThreadToken( GetCurrentThread(), 
						TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
						FALSE, &m_hAccessToken ) )
	{
		m_fClearToken = true;

		// Now, get the LUID for the privilege from the local system
		ZeroMemory(&m_luid, sizeof(m_luid));
		ZeroMemory(&m_luid, sizeof(m_luid2));

		LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &m_luid);
		LookupPrivilegeValue(NULL, SE_TAKE_OWNERSHIP_NAME, &m_luid2);
		m_WbemServices.m_cloak = true;

		EnablePriv(service, true);
	}
	else
	{
		DWORD err = GetLastError();
	}
}

//---------------------------------------------------------------
#define EOAC_DYNAMIC_CLOAKING 0x40

DWORD CSecurity::EnablePriv(IWbemServices *service, bool fEnable )
{
	DWORD				dwError = ERROR_SUCCESS;
	DWORD				dwSize = 2 * sizeof(TOKEN_PRIVILEGES);
	BYTE				*buffer; 
	BYTE				*prevbuffer; 
	TOKEN_PRIVILEGES	*tokenPrivileges;

	//Itz better to save the previous state and restore it on EnablePriv(...,false);

	if((fEnable == false) && (m_prevTokens != NULL))
	{
		dwSize = m_dwprevSize;
		buffer = new BYTE[dwSize];
		tokenPrivileges = (TOKEN_PRIVILEGES*)buffer;
		memcpy(tokenPrivileges,m_prevTokens,dwSize);
	}
	else
	{
		buffer = new BYTE[dwSize];
		tokenPrivileges = (TOKEN_PRIVILEGES*)buffer;
		tokenPrivileges->PrivilegeCount = 2;
		tokenPrivileges->Privileges[0].Luid = m_luid;
		tokenPrivileges->Privileges[0].Attributes = (fEnable ? SE_PRIVILEGE_ENABLED : 0);
		tokenPrivileges->Privileges[1].Luid = m_luid2;
		tokenPrivileges->Privileges[1].Attributes = (fEnable ? SE_PRIVILEGE_ENABLED : 0);
		if(m_prevTokens != NULL)
		{
			delete []m_prevBuffer;
			m_prevTokens = NULL;
		}
		m_dwprevSize = 0;
		//Find the size of the PrevState Buffer
		AdjustTokenPrivileges(m_hAccessToken, 
								FALSE, 
								tokenPrivileges, 
								dwSize, 
								m_prevTokens, &m_dwprevSize);
		m_prevBuffer = new BYTE[m_dwprevSize];
		m_prevTokens = (TOKEN_PRIVILEGES*)m_prevBuffer;
	}

	if(AdjustTokenPrivileges(m_hAccessToken, 
								FALSE, 
								tokenPrivileges, 
								dwSize, 
								m_prevTokens, &m_dwprevSize))
	{
		long lRes = GetLastError();

		IClientSecurity* pSec;
		HRESULT hres = service->QueryInterface(IID_IClientSecurity, (void**)&pSec);
		if(FAILED(hres))
		{
			return E_FAIL;
		}

		hres = pSec->SetBlanket(service, RPC_C_AUTHN_WINNT,
						RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
						RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DYNAMIC_CLOAKING);
		pSec->Release();

	}
	else
	{
		dwError = ::GetLastError();
	}

	delete[] buffer;

	if(fEnable == false)
	{
		delete []m_prevBuffer;
		m_prevTokens = NULL;
	}

	return dwError;
}

//---------------------------------------------------------------
void CSecurity::ClearPriv(IWbemServices *service)
{

	EnablePriv(service, false);
	m_WbemServices.m_cloak = false;

	if(m_fClearToken)
	{
		CloseHandle(m_hAccessToken);
		m_hAccessToken = 0;
		m_fClearToken = false;
	}
}

//---------------------------------------------------------------
#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
#define NextAce(Ace) ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize))

void CSecurity::ProtectACLs(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD)
{
    SECURITY_DESCRIPTOR_CONTROL wSDControl;
    DWORD dwRevision;
    PACL pAcl;
    BOOL bDefaulted;
    BOOL bPresent;
    PACE_HEADER pAce;
    UINT cAces;


    if (0 == si || NULL == pSD)
        return;   // Nothing to do

    // Get the ACL protection control bits
    GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision);
    wSDControl &= SE_DACL_PROTECTED | SE_SACL_PROTECTED;

    if ((si & DACL_SECURITY_INFORMATION) && !(wSDControl & SE_DACL_PROTECTED))
    {
        wSDControl |= SE_DACL_PROTECTED;
        pAcl = NULL;
        GetSecurityDescriptorDacl(pSD, &bPresent, &pAcl, &bDefaulted);

        // Theoretically, modifying the DACL in this way can cause it to be
        // no longer canonical.  However, the only way this can happen is if
        // there is an inherited Deny ACE and a non-inherited Allow ACE.
        // Since this function is only called for root objects, this means
        // a) the server DACL must have a Deny ACE and b) the DACL on this
        // object must have been modified later.  But if the DACL was
        // modified through the UI, then we would have eliminated all of the
        // Inherited ACEs already.  Therefore, it must have been modified
        // through some other means.  Considering that the DACL originally
        // inherited from the server never has a Deny ACE, this situation
        // should be extrememly rare.  If it ever does happen, the ACL
        // Editor will just tell the user that the DACL is non-canonical.
        //
        // Therefore, let's ignore the possibility here.

        if (NULL != pAcl)
        {
            for (cAces = pAcl->AceCount, pAce = (PACE_HEADER)FirstAce(pAcl);
                 cAces > 0;
                 --cAces, pAce = (PACE_HEADER)NextAce(pAce))
            {
                pAce->AceFlags &= ~INHERITED_ACE;
            }
        }
    }

    if ((si & SACL_SECURITY_INFORMATION) && !(wSDControl & SE_SACL_PROTECTED))
    {
        wSDControl |= SE_SACL_PROTECTED;
        pAcl = NULL;
        GetSecurityDescriptorSacl(pSD, &bPresent, &pAcl, &bDefaulted);

        if (NULL != pAcl)
        {
            for (cAces = pAcl->AceCount, pAce = (PACE_HEADER)FirstAce(pAcl);
                 cAces > 0;
                 --cAces, pAce = (PACE_HEADER)NextAce(pAce))
            {
                pAce->AceFlags &= ~INHERITED_ACE;
            }
        }
    }

    SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED | SE_SACL_PROTECTED, wSDControl);
}

//---------------------------------------------------------------
HRESULT CSecurity::GetSecurity(THIS_ SECURITY_INFORMATION RequestedInformation,
									PSECURITY_DESCRIPTOR *ppSecurityDescriptor,
									BOOL fDefault )
{
    if(fDefault)
	{
        ATLTRACE(L"Default security descriptor not supported");
		return E_NOTIMPL;
	}

	// NOTE: After apply, aclui comes right here. When initially starting up, it'll go
	// through GetObjectInformation() first.

	// so, make sure to get a fresh SD from AFTER to apply.
	if(m_ppCachedSD == 0)
	{
		HRESULT hr2 = CacheSecurity(ALL_SI, (PSECURITY_DESCRIPTOR *)&m_ppCachedSD, false);
	}

	// if there's an SD available...
	if(m_ppCachedSD && SUCCEEDED(m_cacheHR))
	{
		// give ownership of the ptr.
		*ppSecurityDescriptor = m_ppCachedSD;
		m_ppCachedSD = NULL;
	} 
	return m_cacheHR;
}

//---------------------------------------------------------------
HRESULT CSecurity::CacheSecurity(THIS_ SECURITY_INFORMATION RequestedInformation,
									PSECURITY_DESCRIPTOR *ppSecurityDescriptor,
									BOOL fDefault )
{
    ATLASSERT(ppSecurityDescriptor != NULL);

    *ppSecurityDescriptor = NULL;

	// does it want something?
    if(RequestedInformation != 0)
    {
		CWbemClassObject dummy, setting, param;

		// call wbem.GetSecurityDescriptor() method
		m_settingPath = L"Win32_LogicalFileSecuritySetting=\"";
		m_settingPath += m_deviceID;
		m_settingPath += L"\\\\\"";

		m_cacheHR = m_WbemServices.ExecMethod(m_settingPath, 
										L"GetSecurityDescriptor",
										dummy, param);

		if(SUCCEEDED(m_cacheHR))
		{
			setting = param.GetEmbeddedObject("Descriptor");

			if(setting)
			{
				// translate SD to CInstance
				m_sec.Wbem2SD(RequestedInformation,
								setting, 
								m_WbemServices, 
								(SECURITY_DESCRIPTOR **)ppSecurityDescriptor);

				SECURITY_DESCRIPTOR **test = (SECURITY_DESCRIPTOR **)ppSecurityDescriptor;

			}
			else
			{
				return E_NOTIMPL;
			}
		}
		else
		{
			DisplayUserMessage(NULL, HINST_THISDLL,
								IDS_DISPLAY_NAME, BASED_ON_SRC, 
								GetSecurityDescriptor,
								m_cacheHR, MB_ICONWARNING);
		    return m_cacheHR;
		}
    }
    else
    {
        *ppSecurityDescriptor = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);

        if(*ppSecurityDescriptor)
            InitializeSecurityDescriptor(*ppSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
        else
            m_cacheHR = E_OUTOFMEMORY;
    }

    ProtectACLs(RequestedInformation & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION), 
				*ppSecurityDescriptor);

    return m_cacheHR;
}

//
// See comments about SYNCHRONIZE at the top of this file
//
void CSecurity::FixSynchronizeAccess(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD)
{
    if (NULL != pSD && 0 != (si & DACL_SECURITY_INFORMATION))
    {
        BOOL bPresent;
        BOOL bDefault;
        PACL pDacl = NULL;

        GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefault);

        if (pDacl)
        {
            PACE_HEADER pAce;
            int i;

            for (i = 0, pAce = (PACE_HEADER)FirstAce(pDacl);
                 i < pDacl->AceCount;
                 i++, pAce = (PACE_HEADER)NextAce(pAce))
            {
                if (ACCESS_ALLOWED_ACE_TYPE == pAce->AceType)
                    ((ACCESS_ALLOWED_ACE *)pAce)->Mask |= SYNCHRONIZE;
            }
        }
    }
}


//-----------------------------------------------------------------------------
HRESULT CSecurity::SetSecurity(SECURITY_INFORMATION SecurityInformation,
									PSECURITY_DESCRIPTOR pSecurityDescriptor)
{
	HRESULT hr = S_OK;

	try 
	{
		// since taking ownership grants implicit WRITE_DAC access, which may
		// be helpful when a new DACL is propagated downward.
	//    if ((SecurityInformation & (OWNER_SECURITY_INFORMATION | SI_OWNER_RECURSE))
	//            == (OWNER_SECURITY_INFORMATION | SI_OWNER_RECURSE))
	//    {
	//        BOOL bNotAllApplied = FALSE;

			// dont pass the SI_OWNER_RECURSE bit to wbem.
			SecurityInformation &= ~(SI_OWNER_RECURSE);

			// wbem cant do recursive ownership.
	//        hr = TakeOwnership(pSD, &bNotAllApplied);

	//        if (FAILED(hr) || bNotAllApplied)
	//        {
	//			ATLTRACE(L"Unable to take ownership of all items in tree.");
	//        }

	//    }

		// if something was changed...
		if(SecurityInformation != 0)
		{
			CWbemClassObject dummy, paramCls;

			paramCls = m_WbemServices.GetObject("Win32_LogicalFileSecuritySetting");

			if(paramCls)
			{
				CWbemClassObject param;
				CWbemClassObject setting;

				hr = paramCls.GetMethod(L"SetSecurityDescriptor",
										param, dummy);

				if(SUCCEEDED(hr) && (bool)param)
				{
			        // See comments about SYNCHRONIZE at the top of this file.
					if(SecurityInformation & DACL_SECURITY_INFORMATION)
						FixSynchronizeAccess(SecurityInformation, pSecurityDescriptor);

					// translate SD to CInstance
					m_sec.SD2Wbem(SecurityInformation,
									(SECURITY_DESCRIPTOR *)pSecurityDescriptor, 
									m_WbemServices,
									setting);

					hr = S_OK;
					hr = param.PutEmbeddedObject(L"Descriptor", setting);
					if(SUCCEEDED(hr))
					{

						IWbemServices *service;
						m_WbemServices.GetServices(&service);

						// NOTE: m_settingPath was built during GetSecurity.

						// call wbem.SetSecurityDescriptor() method
						SetPriv(service);
						hr = m_WbemServices.ExecMethod(m_settingPath, 
														L"SetSecurityDescriptor",
														param, dummy);
						if(SUCCEEDED(hr))
						{
							// extract the real error code.
							DWORD err = (DWORD)dummy.GetLong("ReturnValue");

							// NOTE: there's a issue about which error values should be
							// returned in 'returnValue'. 
							if(err == 0x5)
							{
								// Access Denied at the OS level.
								DisplayUserMessage(NULL, HINST_THISDLL,
													IDS_DISPLAY_NAME, BASED_ON_SRC, 
													SetSecurityDescriptor,
													WBEM_E_ACCESS_DENIED, MB_ICONWARNING);

							}
						}

						ClearPriv(service);
						service->Release();
						service = NULL;
					}
				}
				else
				{
					hr = E_FAIL;
				}
			}
			else
			{
				hr = WBEM_E_INVALID_METHOD;
			}
		}
	}
	catch( ... )
	{
		ATLASSERT(FALSE);
		hr = WBEM_E_UNEXPECTED;
	}

    return hr;
}

//-----------------------------------------------------------------------------
HRESULT CSecurity::PropertySheetPageCallback(HWND hwnd, 
												  UINT uMsg, 
												  SI_PAGE_TYPE uPage)
{
	if(uMsg == PSPCB_SI_INITDIALOG)
	{
		// make GetObjectInformation() cache the SD cuz we're going to display RSN.
		m_initingDlg = true;
	}
	else if((uMsg == PSPCB_CREATE) || uMsg == 0)
	{
	}
	else if(uMsg == PSPCB_RELEASE)
	{
	}

    return S_OK;
}
//---------------------------------------------------
LPWSTR CSecurity::CloneWideString(_bstr_t pszSrc ) 
{
    LPWSTR pszDst = NULL;

    pszDst = new WCHAR[(lstrlen(pszSrc) + 1)];
    if (pszDst) 
	{
        wcscpy( pszDst, pszSrc );
    }

    return pszDst;
}