You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1241 lines
36 KiB
1241 lines
36 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: util.cpp
|
|
//
|
|
// This file contains misc functions.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "rshx32.h"
|
|
#include <shlobjp.h> // SHFree
|
|
#include <shlwapip.h> // IsOS
|
|
#include <safeboot.h> // SAFEBOOT_* flags
|
|
|
|
|
|
STDMETHODIMP
|
|
IDA_BindToFolder(LPIDA pIDA, LPSHELLFOLDER *ppsf)
|
|
{
|
|
HRESULT hr;
|
|
LPSHELLFOLDER psfDesktop;
|
|
|
|
TraceEnter(TRACE_UTIL, "IDA_BindToFolder");
|
|
TraceAssert(pIDA != NULL);
|
|
TraceAssert(ppsf != NULL);
|
|
|
|
*ppsf = NULL;
|
|
|
|
hr = SHGetDesktopFolder(&psfDesktop);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPCITEMIDLIST pidlFolder = (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[0]);
|
|
|
|
if (ILIsEmpty(pidlFolder))
|
|
{
|
|
// We're binding to the desktop
|
|
*ppsf = psfDesktop;
|
|
}
|
|
else
|
|
{
|
|
hr = psfDesktop->BindToObject(pidlFolder,
|
|
NULL,
|
|
IID_IShellFolder,
|
|
(PVOID*)ppsf);
|
|
psfDesktop->Release();
|
|
}
|
|
}
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
IDA_GetItemName(LPSHELLFOLDER psf,
|
|
LPCITEMIDLIST pidl,
|
|
LPTSTR pszName,
|
|
UINT cchName,
|
|
SHGNO uFlags)
|
|
{
|
|
STRRET str;
|
|
HRESULT hr;
|
|
|
|
hr = psf->GetDisplayNameOf(pidl, uFlags, &str);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD dwErr;
|
|
LPSTR psz;
|
|
|
|
switch (str.uType)
|
|
{
|
|
case STRRET_WSTR:
|
|
lstrcpyn(pszName, str.pOleStr, cchName);
|
|
|
|
//
|
|
// Since this string was alocated from the shell's IMalloc heap,
|
|
// we must free it to the same place.
|
|
//
|
|
SHFree(str.pOleStr);
|
|
break;
|
|
|
|
case STRRET_OFFSET:
|
|
psz = (LPSTR)ByteOffset(pidl, str.uOffset);
|
|
goto GetItemName_ANSI;
|
|
|
|
case STRRET_CSTR:
|
|
psz = str.cStr;
|
|
GetItemName_ANSI:
|
|
|
|
if (!MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
psz,
|
|
-1,
|
|
pszName,
|
|
cchName))
|
|
{
|
|
dwErr = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
IDA_GetItemName(LPSHELLFOLDER psf,
|
|
LPCITEMIDLIST pidl,
|
|
LPTSTR *ppszName,
|
|
SHGNO uFlags)
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
HRESULT hr = IDA_GetItemName(psf, pidl, szName, ARRAYSIZE(szName));
|
|
if (SUCCEEDED(hr))
|
|
hr = LocalAllocString(ppszName, szName);
|
|
else
|
|
*ppszName = NULL;
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Helper functions used by DPA_CompareSecurityIntersection
|
|
//
|
|
BOOL
|
|
IsEqualSID(PSID pSid1, PSID pSid2)
|
|
{
|
|
//
|
|
// Are they both NULL?
|
|
//
|
|
if (pSid1 || pSid2)
|
|
{
|
|
//
|
|
// At least one is non-NULL, so if one is NULL then they can't
|
|
// be equal.
|
|
//
|
|
if (pSid1 == NULL || pSid2 == NULL)
|
|
return FALSE;
|
|
|
|
//
|
|
// Both are non-NULL. Check the SIDs.
|
|
//
|
|
if (!EqualSid(pSid1, pSid2))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
IsEqualACL(PACL pA1, PACL pA2)
|
|
{
|
|
//
|
|
// Are they both NULL?
|
|
//
|
|
if (pA1 || pA2)
|
|
{
|
|
//
|
|
// At least one is non-NULL, so if one is NULL then they can't
|
|
// be equal.
|
|
//
|
|
if (pA1 == NULL || pA2 == NULL)
|
|
return FALSE;
|
|
|
|
//
|
|
// At this point we know that both are non-NULL. Check the
|
|
// sizes and contents.
|
|
//
|
|
// could do a lot more here
|
|
if (pA1->AclSize != pA2->AclSize || memcmp(pA1, pA2, pA1->AclSize))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CompareSecurityDescriptors
|
|
//
|
|
// Synopsis: Determines if 2 security descriptors are identical. It does
|
|
// this by comparing control fields, owner/group, and acls.
|
|
//
|
|
// Arguments: [IN] pSD1 - 1st SD to compare
|
|
// [IN] pSD2 - 2nd SD to compare
|
|
// [OUT] pfOwnerConflict - (optional) Set to TRUE if the Owner SIDs are not equal
|
|
// [OUT] pfGroupConflict - (optional) Set to TRUE if the Group SIDs are not equal
|
|
// [OUT] pfDACLConflict - (optional) Set to TRUE if the DACLs are not equal
|
|
// [OUT] pfSACLConflict - (optional) Set to TRUE if the SACLs are not equal
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define DACL_CONTROL_MASK (SE_DACL_PRESENT | SE_DACL_DEFAULTED | SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED)
|
|
#define SACL_CONTROL_MASK (SE_SACL_PRESENT | SE_SACL_DEFAULTED | SE_SACL_AUTO_INHERITED | SE_SACL_PROTECTED)
|
|
|
|
void
|
|
CompareSecurityDescriptors(PSECURITY_DESCRIPTOR pSD1,
|
|
PSECURITY_DESCRIPTOR pSD2,
|
|
BOOL *pfOwnerConflict,
|
|
BOOL *pfGroupConflict,
|
|
BOOL *pfSACLConflict,
|
|
BOOL *pfDACLConflict)
|
|
{
|
|
PISECURITY_DESCRIPTOR pS1 = (PISECURITY_DESCRIPTOR)pSD1;
|
|
PISECURITY_DESCRIPTOR pS2 = (PISECURITY_DESCRIPTOR)pSD2;
|
|
|
|
//
|
|
// Are the pointers identical?
|
|
// This includes the case where both are NULL.
|
|
//
|
|
if (pS1 == pS2)
|
|
{
|
|
if (pfOwnerConflict)
|
|
*pfOwnerConflict = FALSE;
|
|
if (pfGroupConflict)
|
|
*pfGroupConflict = FALSE;
|
|
if (pfSACLConflict)
|
|
*pfSACLConflict = FALSE;
|
|
if (pfDACLConflict)
|
|
*pfDACLConflict = FALSE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Is (only) one of them NULL? If so, then we can't compare so
|
|
// assume that nothing matches.
|
|
//
|
|
if (!pS1 || !pS2)
|
|
{
|
|
if (pfOwnerConflict)
|
|
*pfOwnerConflict = TRUE;
|
|
if (pfGroupConflict)
|
|
*pfGroupConflict = TRUE;
|
|
if (pfSACLConflict)
|
|
*pfSACLConflict = TRUE;
|
|
if (pfDACLConflict)
|
|
*pfDACLConflict = TRUE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Owner
|
|
//
|
|
if (pfOwnerConflict)
|
|
{
|
|
if ((pS1->Control & SE_OWNER_DEFAULTED) != (pS2->Control & SE_OWNER_DEFAULTED))
|
|
{
|
|
*pfOwnerConflict = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfOwnerConflict = !IsEqualSID(RtlpOwnerAddrSecurityDescriptor(pS1),
|
|
RtlpOwnerAddrSecurityDescriptor(pS2));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Group
|
|
//
|
|
if (pfGroupConflict)
|
|
{
|
|
if ((pS1->Control & SE_GROUP_DEFAULTED) != (pS2->Control & SE_GROUP_DEFAULTED))
|
|
{
|
|
*pfGroupConflict = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfGroupConflict = !IsEqualSID(RtlpGroupAddrSecurityDescriptor(pS1),
|
|
RtlpGroupAddrSecurityDescriptor(pS2));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sacl
|
|
//
|
|
if (pfSACLConflict)
|
|
{
|
|
if ((pS1->Control & SACL_CONTROL_MASK) != (pS2->Control & SACL_CONTROL_MASK))
|
|
{
|
|
*pfSACLConflict = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfSACLConflict = !IsEqualACL(RtlpSaclAddrSecurityDescriptor(pS1),
|
|
RtlpSaclAddrSecurityDescriptor(pS2));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dacl
|
|
//
|
|
if (pfDACLConflict)
|
|
{
|
|
if ((pS1->Control & DACL_CONTROL_MASK) != (pS2->Control & DACL_CONTROL_MASK))
|
|
{
|
|
*pfDACLConflict = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfDACLConflict = !IsEqualACL(RtlpDaclAddrSecurityDescriptor(pS1),
|
|
RtlpDaclAddrSecurityDescriptor(pS2));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: DPA_CompareSecurityIntersection
|
|
|
|
SYNOPSIS: Determines if the selected objects have
|
|
equivalent security descriptors
|
|
|
|
ENTRY: hItemList - DPA containing item names
|
|
pfnReadSD - callback function to read the Security Descriptor
|
|
for a single item.
|
|
pfOwnerConflict - (optional) Set to TRUE if not all Owner SIDs are equal
|
|
pfGroupConflict - (optional) Set to TRUE if not all Group SIDs are equal
|
|
pfDACLConflict - (optional) Set to TRUE if not all DACLs are equal
|
|
pfSACLConflict - (optional) Set to TRUE if not all SACLs are equal
|
|
ppszOwnerConflict - (optional) The name of the first item
|
|
with a different Owner is returned here.
|
|
Free with LocalFreeString.
|
|
ppszGroupConflict - (optional) similar to ppszOwnerConflict
|
|
ppszDaclConflict - (optional) similar to ppszOwnerConflict
|
|
ppszSaclConflict - (optional) similar to ppszOwnerConflict
|
|
ppszFailureMsg - Message to display in case of failure.
|
|
|
|
RETURNS: S_OK if successful, HRESULT error code otherwise
|
|
|
|
NOTES: The function may exit without checking all objects if all of
|
|
the requested flags become FALSE. All out parameters are
|
|
valid if the function succeeds, and undetermined otherwise.
|
|
|
|
HISTORY:
|
|
JeffreyS 18-Feb-1997 Created
|
|
|
|
********************************************************************/
|
|
|
|
STDMETHODIMP
|
|
DPA_CompareSecurityIntersection(HDPA hItemList,
|
|
PFN_READ_SD pfnReadSD,
|
|
BOOL *pfOwnerConflict,
|
|
BOOL *pfGroupConflict,
|
|
BOOL *pfSACLConflict,
|
|
BOOL *pfDACLConflict,
|
|
LPTSTR *ppszOwnerConflict,
|
|
LPTSTR *ppszGroupConflict,
|
|
LPTSTR *ppszSaclConflict,
|
|
LPTSTR *ppszDaclConflict,
|
|
LPTSTR *ppszFailureMsg,
|
|
LPBOOL pbCancel)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwErr;
|
|
SECURITY_INFORMATION si = 0;
|
|
DWORD dwPriv = SE_SECURITY_PRIVILEGE;
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
LPTSTR pszItem;
|
|
LPTSTR pszFile;
|
|
PSECURITY_DESCRIPTOR pSD1 = NULL;
|
|
PSECURITY_DESCRIPTOR pSD2 = NULL;
|
|
int i;
|
|
|
|
#if DBG
|
|
DWORD dwTimeStart = GetTickCount();
|
|
#endif
|
|
|
|
TraceEnter(TRACE_UTIL, "DPA_CompareSecurityIntersection");
|
|
TraceAssert(hItemList != NULL);
|
|
TraceAssert(pfnReadSD != NULL);
|
|
|
|
if (pfOwnerConflict)
|
|
{
|
|
*pfOwnerConflict = FALSE;
|
|
si |= OWNER_SECURITY_INFORMATION;
|
|
}
|
|
if (pfGroupConflict)
|
|
{
|
|
*pfGroupConflict = FALSE;
|
|
si |= GROUP_SECURITY_INFORMATION;
|
|
}
|
|
if (pfSACLConflict)
|
|
{
|
|
*pfSACLConflict = FALSE;
|
|
|
|
// SeAuditPrivilege must be enabled to read the SACL.
|
|
hToken = EnablePrivileges(&dwPriv, 1);
|
|
if (INVALID_HANDLE_VALUE != hToken)
|
|
{
|
|
si |= SACL_SECURITY_INFORMATION;
|
|
}
|
|
else
|
|
{
|
|
// Leave *pfSACLConflict set to FALSE
|
|
pfSACLConflict = NULL;
|
|
TraceMsg("Security privilege not enabled -- not checking SACL");
|
|
}
|
|
}
|
|
if (pfDACLConflict)
|
|
{
|
|
*pfDACLConflict = FALSE;
|
|
si |= DACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
if (ppszOwnerConflict != NULL)
|
|
*ppszOwnerConflict = NULL;
|
|
if (ppszGroupConflict != NULL)
|
|
*ppszGroupConflict = NULL;
|
|
if (ppszSaclConflict != NULL)
|
|
*ppszSaclConflict = NULL;
|
|
if (ppszDaclConflict != NULL)
|
|
*ppszDaclConflict = NULL;
|
|
|
|
if (si == 0 || DPA_GetPtrCount(hItemList) < 2)
|
|
ExitGracefully(hr, S_OK, "Nothing requested or list contains only one item");
|
|
|
|
if (pbCancel && *pbCancel)
|
|
ExitGracefully(hr, S_OK, "DPA_CompareSecurityIntersection cancelled");
|
|
|
|
//
|
|
// Get the first item name and load its security descriptor.
|
|
//
|
|
pszItem = (LPTSTR)DPA_FastGetPtr(hItemList, 0);
|
|
if (NULL == pszItem)
|
|
ExitGracefully(hr, E_UNEXPECTED, "Item list is empty");
|
|
|
|
dwErr = (*pfnReadSD)(pszItem, si, &pSD1);
|
|
if (dwErr)
|
|
{
|
|
//Incase of multiple of selection, if we fail to read the security descriptor
|
|
//of single item, security page should be disabled.
|
|
LPTSTR pszSystemError = NULL;
|
|
LPTSTR pszFailureMsg = NULL;
|
|
if (GetSystemErrorText(&pszSystemError, dwErr))
|
|
{
|
|
//often last two chars are \r\n sequence which break our formatting.
|
|
DWORD dwLen = wcslen(pszSystemError);
|
|
if(dwLen >= 2 && pszSystemError[dwLen-2] == 0x0d && pszSystemError[dwLen-1] ==0x0a)
|
|
{
|
|
pszSystemError[dwLen-2] = 0;
|
|
}
|
|
FormatStringID(&pszFailureMsg, g_hInstance, IDS_MULTIPLE_SELECTION_READ_ERROR, pszItem, pszSystemError);
|
|
}
|
|
else
|
|
{
|
|
FormatStringID(&pszFailureMsg, g_hInstance, IDS_MULTIPLE_SELECTION_READ_ERROR_1, pszItem);
|
|
}
|
|
|
|
*ppszFailureMsg = pszFailureMsg;
|
|
ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Unable to read Security Descriptor");
|
|
}
|
|
|
|
//
|
|
// Go through the rest of the list and compare their security
|
|
// descriptors to the first one.
|
|
//
|
|
for (i = 1; i < DPA_GetPtrCount(hItemList) && si != 0; i++)
|
|
{
|
|
if (pbCancel && *pbCancel)
|
|
ExitGracefully(hr, S_OK, "DPA_CompareSecurityIntersection cancelled");
|
|
|
|
pszItem = (LPTSTR)DPA_FastGetPtr(hItemList, i);
|
|
if (NULL == pszItem)
|
|
ExitGracefully(hr, E_UNEXPECTED, "Unable to retrieve item name from list");
|
|
|
|
dwErr = (*pfnReadSD)(pszItem, si, &pSD2);
|
|
if (dwErr)
|
|
{
|
|
//Incase of multiple of selection, if we fail to read the security descriptor
|
|
//of single item, security page should be disabled.
|
|
LPTSTR pszSystemError = NULL;
|
|
LPTSTR pszFailureMsg = NULL;
|
|
if (GetSystemErrorText(&pszSystemError, dwErr))
|
|
{
|
|
//often last two chars are \r\n sequence which break our formatting.
|
|
DWORD dwLen = wcslen(pszSystemError);
|
|
if(dwLen >= 2 && pszSystemError[dwLen-2] == 0x0d && pszSystemError[dwLen-1] ==0x0a)
|
|
{
|
|
pszSystemError[dwLen-2] = 0;
|
|
}
|
|
FormatStringID(&pszFailureMsg, g_hInstance, IDS_MULTIPLE_SELECTION_READ_ERROR, pszItem, pszSystemError);
|
|
}
|
|
else
|
|
{
|
|
FormatStringID(&pszFailureMsg, g_hInstance, IDS_MULTIPLE_SELECTION_READ_ERROR_1, pszItem);
|
|
}
|
|
|
|
*ppszFailureMsg = pszFailureMsg;
|
|
ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Unable to read Security Descriptor");
|
|
}
|
|
|
|
CompareSecurityDescriptors(pSD1,
|
|
pSD2,
|
|
pfOwnerConflict,
|
|
pfGroupConflict,
|
|
pfSACLConflict,
|
|
pfDACLConflict);
|
|
if (pSD2 != NULL)
|
|
{
|
|
LocalFree(pSD2);
|
|
pSD2 = NULL;
|
|
}
|
|
|
|
//
|
|
// Get the leaf name of the item to return as the conflict name
|
|
//
|
|
pszFile = PathFindFileName(pszItem);
|
|
if (!pszFile)
|
|
pszFile = pszItem;
|
|
|
|
// If we find an owner that doesn't match, we can stop checking owners
|
|
if (pfOwnerConflict && *pfOwnerConflict)
|
|
{
|
|
pfOwnerConflict = NULL;
|
|
si &= ~OWNER_SECURITY_INFORMATION;
|
|
|
|
if (ppszOwnerConflict)
|
|
LocalAllocString(ppszOwnerConflict, pszFile);
|
|
}
|
|
|
|
// Ditto for the group
|
|
if (pfGroupConflict && *pfGroupConflict)
|
|
{
|
|
pfGroupConflict = NULL;
|
|
si &= ~GROUP_SECURITY_INFORMATION;
|
|
|
|
if (ppszGroupConflict)
|
|
LocalAllocString(ppszGroupConflict, pszFile);
|
|
}
|
|
|
|
// Same for SACLs
|
|
if (pfSACLConflict && *pfSACLConflict)
|
|
{
|
|
pfSACLConflict = NULL;
|
|
si &= ~SACL_SECURITY_INFORMATION;
|
|
|
|
if (ppszSaclConflict)
|
|
LocalAllocString(ppszSaclConflict, pszFile);
|
|
}
|
|
|
|
// Same for DACLs
|
|
if (pfDACLConflict && *pfDACLConflict)
|
|
{
|
|
pfDACLConflict = NULL;
|
|
si &= ~DACL_SECURITY_INFORMATION;
|
|
|
|
if (ppszDaclConflict)
|
|
LocalAllocString(ppszDaclConflict, pszFile);
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
// Release any privileges we enabled
|
|
ReleasePrivileges(hToken);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LocalFreeString(ppszOwnerConflict);
|
|
LocalFreeString(ppszGroupConflict);
|
|
LocalFreeString(ppszSaclConflict);
|
|
LocalFreeString(ppszDaclConflict);
|
|
}
|
|
|
|
if (pSD1 != NULL)
|
|
LocalFree(pSD1);
|
|
|
|
#if DBG
|
|
Trace((TEXT("DPA_CompareSecurityIntersection done: %d"), GetTickCount() - dwTimeStart));
|
|
#endif
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// GetRemotePath
|
|
//
|
|
// Purpose: Return UNC version of a path
|
|
//
|
|
// Parameters: pszInName - initial path
|
|
// ppszOutName - UNC path returned here
|
|
//
|
|
//
|
|
// Return: HRESULT
|
|
// S_OK - UNC path returned
|
|
// S_FALSE - drive not connected (UNC not returned)
|
|
// or failure code
|
|
//
|
|
// Notes: The function fails is the path is not a valid
|
|
// network path. If the path is already UNC,
|
|
// a copy is made without validating the path.
|
|
// *ppszOutName must be LocalFree'd by the caller.
|
|
//
|
|
//*************************************************************
|
|
|
|
DWORD _WNetGetConnection(LPCTSTR pszLocal, LPTSTR pszRemote, LPDWORD pdwLen)
|
|
{
|
|
DWORD dwErr = ERROR_PROC_NOT_FOUND;
|
|
|
|
// This is the only function we call in mpr.dll, and it's delay-loaded
|
|
// so wrap it with SEH.
|
|
__try
|
|
{
|
|
dwErr = WNetGetConnection(pszLocal, pszRemote, pdwLen);
|
|
}
|
|
__finally
|
|
{
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
GetRemotePath(LPCTSTR pszInName, LPTSTR *ppszOutName)
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
|
|
|
|
TraceEnter(TRACE_UTIL, "GetRemotePath");
|
|
TraceAssert(pszInName);
|
|
TraceAssert(ppszOutName);
|
|
|
|
*ppszOutName = NULL;
|
|
|
|
|
|
|
|
if (pszInName[1] == TEXT(':'))
|
|
{
|
|
DWORD dwErr;
|
|
TCHAR szLocalName[3];
|
|
TCHAR szRemoteName[MAX_PATH];
|
|
DWORD dwLen = ARRAYSIZE(szRemoteName);
|
|
|
|
szLocalName[0] = pszInName[0];
|
|
szLocalName[1] = pszInName[1];
|
|
szLocalName[2] = TEXT('\0');
|
|
|
|
dwErr = _WNetGetConnection(szLocalName, szRemoteName, &dwLen);
|
|
|
|
if (NO_ERROR == dwErr)
|
|
{
|
|
hr = S_OK;
|
|
dwLen = lstrlen(szRemoteName);
|
|
}
|
|
else if (ERROR_NOT_CONNECTED == dwErr)
|
|
{
|
|
ExitGracefully(hr, S_FALSE, "Drive not connected");
|
|
}
|
|
else if (ERROR_MORE_DATA != dwErr)
|
|
ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "WNetGetConnection failed");
|
|
// if dwErr == ERROR_MORE_DATA, dwLen already has the correct value
|
|
|
|
// Skip the drive letter and add the length of the rest of the path
|
|
// (including NULL)
|
|
pszInName += 2;
|
|
DWORD dwOutputLen = dwLen + lstrlen(pszInName) + 1;
|
|
|
|
// We should never get incomplete paths, so we should always
|
|
// see a backslash after the "X:". If this isn't true, then
|
|
// we should call GetFullPathName above.
|
|
TraceAssert(TEXT('\\') == *pszInName);
|
|
|
|
// Allocate the return buffer
|
|
*ppszOutName = (LPTSTR)LocalAlloc(LPTR, dwOutputLen * SIZEOF(TCHAR));
|
|
if (!*ppszOutName)
|
|
ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
|
|
|
|
if (ERROR_MORE_DATA == dwErr)
|
|
{
|
|
// Try again with the bigger buffer
|
|
dwErr = _WNetGetConnection(szLocalName, *ppszOutName, &dwLen);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
FailGracefully(hr, "WNetGetConnection failed");
|
|
}
|
|
else
|
|
{
|
|
// WNetGetConnection succeeded. Copy the result
|
|
lstrcpy(*ppszOutName, szRemoteName);
|
|
}
|
|
|
|
// Copy the rest of the path
|
|
hr = StringCchCat(*ppszOutName,dwOutputLen,pszInName);
|
|
}
|
|
else if (PathIsUNC(pszInName))
|
|
{
|
|
// Just copy the path without validating it
|
|
hr = LocalAllocString(ppszOutName, pszInName);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LocalFreeString(ppszOutName);
|
|
}
|
|
else
|
|
{
|
|
if(*ppszOutName)
|
|
{
|
|
//Trailing backshash causes IsShareRoot to fail.
|
|
//NTRAID#NTBUG9-533576-2002/05/06-hiteshr
|
|
PathRemoveBackslash(*ppszOutName);
|
|
}
|
|
}
|
|
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: LocalFreeDPA
|
|
|
|
SYNOPSIS: LocalFree's all pointers in a Dynamic Pointer
|
|
Array and then frees the DPA.
|
|
|
|
ENTRY: hList - handle of list to destroy
|
|
|
|
RETURNS: nothing
|
|
|
|
********************************************************************/
|
|
int CALLBACK
|
|
_LocalFreeCB(LPVOID pVoid, LPVOID /*pData*/)
|
|
{
|
|
if (pVoid)
|
|
LocalFree(pVoid);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
LocalFreeDPA(HDPA hList)
|
|
{
|
|
if (hList != NULL)
|
|
DPA_DestroyCallback(hList, _LocalFreeCB, 0);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsSafeMode
|
|
//
|
|
// Synopsis: Checks the registry to see if the system is in safe mode.
|
|
//
|
|
// History: 06-Oct-00 JeffreyS Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
IsSafeMode(void)
|
|
{
|
|
BOOL fIsSafeMode = FALSE;
|
|
LONG ec;
|
|
HKEY hkey;
|
|
|
|
ec = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hkey
|
|
);
|
|
|
|
if (ec == NO_ERROR)
|
|
{
|
|
DWORD dwValue;
|
|
DWORD dwValueSize = sizeof(dwValue);
|
|
|
|
ec = RegQueryValueEx(hkey,
|
|
TEXT("OptionValue"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwValueSize);
|
|
|
|
if (ec == NO_ERROR)
|
|
{
|
|
fIsSafeMode = (dwValue == SAFEBOOT_MINIMAL || dwValue == SAFEBOOT_NETWORK);
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
return fIsSafeMode;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsGuestAccessMode
|
|
//
|
|
// Synopsis: Checks the registry to see if the system is using the
|
|
// Guest-only network access mode.
|
|
//
|
|
// History: 06-Oct-00 JeffreyS Created
|
|
// 19-Apr-00 GPease Modified and changed name
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
IsForcedGuestModeOn(void)
|
|
{
|
|
BOOL fIsForcedGuestModeOn = FALSE;
|
|
|
|
if (IsOS(OS_PERSONAL))
|
|
{
|
|
// Guest mode is always on for Personal
|
|
fIsForcedGuestModeOn = TRUE;
|
|
}
|
|
else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
|
|
{
|
|
LONG ec;
|
|
HKEY hkey;
|
|
|
|
// Professional, not in a domain. Check the ForceGuest value.
|
|
|
|
ec = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hkey
|
|
);
|
|
|
|
if (ec == NO_ERROR)
|
|
{
|
|
DWORD dwValue;
|
|
DWORD dwValueSize = sizeof(dwValue);
|
|
|
|
ec = RegQueryValueEx(hkey,
|
|
TEXT("ForceGuest"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwValueSize);
|
|
|
|
if (ec == NO_ERROR && 1 == dwValue)
|
|
{
|
|
fIsForcedGuestModeOn = TRUE;
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
return fIsForcedGuestModeOn;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsSimpleUI
|
|
//
|
|
// Synopsis: Checks whether to show the simple version of the UI.
|
|
//
|
|
// History: 06-Oct-00 JeffreyS Created
|
|
// 19-Apr-00 GPease Removed CTRL key check
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
IsSimpleUI(void)
|
|
{
|
|
// Show old UI in safe mode and anytime network access involves
|
|
// true user identity (server, pro with GuestMode off).
|
|
|
|
// Show simple UI anytime network access is done using the Guest
|
|
// account (personal, pro with GuestMode on) except in safe mode.
|
|
|
|
return (!IsSafeMode() && IsForcedGuestModeOn());
|
|
}
|
|
|
|
|
|
HRESULT BindToObjectEx(IShellFolder *psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
IShellFolder *psfRelease = NULL;
|
|
|
|
if (!psf)
|
|
{
|
|
hr = SHGetDesktopFolder(&psf);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
psfRelease = psf;
|
|
}
|
|
}
|
|
|
|
if (psf)
|
|
{
|
|
if (!pidl || ILIsEmpty(pidl))
|
|
{
|
|
hr = psf->QueryInterface(riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
hr = psf->BindToObject(pidl, pbc, riid, ppv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (psfRelease)
|
|
{
|
|
psfRelease->Release();
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (*ppv == NULL))
|
|
{
|
|
// Some shell extensions (eg WS_FTP) will return success and a null out pointer
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
LPITEMIDLIST ILCloneParent(LPCITEMIDLIST pidl)
|
|
{
|
|
LPITEMIDLIST pidlParent = ILClone(pidl);
|
|
if (pidlParent)
|
|
ILRemoveLastID(pidlParent);
|
|
|
|
return pidlParent;
|
|
}
|
|
// psfRoot is the base of the bind. If NULL, then we use the shell desktop.
|
|
// If you want to bind relative to the explorer root (e.g., CabView, MSN),
|
|
// then use SHBindToIDListParent.
|
|
HRESULT BindToFolderIDListParent(IShellFolder *psfRoot, LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Old shell32 code in some cases simply whacked the pidl,
|
|
// but this is unsafe. Do what shdocvw does and clone/remove:
|
|
//
|
|
LPITEMIDLIST pidlParent = ILCloneParent(pidl);
|
|
if (pidlParent)
|
|
{
|
|
hr = BindToObjectEx(psfRoot, pidlParent, NULL, riid, ppv);
|
|
ILFree(pidlParent);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (ppidlLast)
|
|
*ppidlLast = ILFindLastID(pidl);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsUIHiddenByPrivacyPolicy
|
|
//
|
|
// Synopsis: Checks if Security Tab is hidden by privacy policy
|
|
// NTRAID#NTBUG9-223899-2001/03/06-hiteshr
|
|
// History: 06-March-01 hiteshr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
IsUIHiddenByPrivacyPolicy(void)
|
|
{
|
|
BOOL fIsUIHiddenByPrivacyPolicy = FALSE;
|
|
|
|
LONG ec;
|
|
HKEY hkey = NULL;
|
|
|
|
|
|
ec = RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hkey
|
|
);
|
|
|
|
if (ec == NO_ERROR)
|
|
{
|
|
DWORD dwValue = 0;
|
|
DWORD dwValueSize = sizeof(dwValue);
|
|
|
|
ec = RegQueryValueEx(hkey,
|
|
TEXT("NoSecurityTab"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwValue,
|
|
&dwValueSize);
|
|
|
|
if (ec == NO_ERROR && 1 == dwValue)
|
|
{
|
|
fIsUIHiddenByPrivacyPolicy = TRUE;
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
return fIsUIHiddenByPrivacyPolicy;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
// Function:SetAclOnRemoteNetworkDrive
|
|
// Synopsis: If Z: is a mapped drive( mapped to \\machineA\share), when
|
|
// we set DACL/SACL on Z:, Security API's cannot determine
|
|
// the parent of "share" on machineA and so we lose all the
|
|
// inherited aces. All UI can do is to detect such cases and
|
|
// show a warning. Thats what this function does.
|
|
// Arguments:hItemList List of items on which security is going to be set
|
|
// si: SECURITY_INFORMATION
|
|
// pSD: Security Descriptor to be set
|
|
// hWndPopupOwner: Owner window for message box
|
|
// Returns:
|
|
//-----------------------------------------------------------------------------
|
|
BOOL SetAclOnRemoteNetworkDrive(HDPA hItemList,
|
|
SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
HWND hWndPopupOwner)
|
|
{
|
|
if(!hItemList || !pSD)
|
|
{
|
|
ASSERT(hItemList);
|
|
ASSERT(pSD);
|
|
return FALSE;
|
|
}
|
|
|
|
//We care only about DACL and SACL
|
|
if(!(si & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
SECURITY_DESCRIPTOR_CONTROL Control = 0;
|
|
DWORD dwRevision = 0;
|
|
//Check if DACL or SACL is protected
|
|
if(!GetSecurityDescriptorControl(pSD,&Control,&dwRevision))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//If inheritance is prevented for this object,there is no loss of inheritance
|
|
//so we are fine
|
|
if( ((si & DACL_SECURITY_INFORMATION) && (Control & SE_DACL_PROTECTED)) ||
|
|
((si & SACL_SECURITY_INFORMATION) && (Control & SE_SACL_PROTECTED)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
LPTSTR pszPath = (LPTSTR)DPA_FastGetPtr(hItemList, 0);
|
|
|
|
int nMsgId = (si & DACL_SECURITY_INFORMATION) ? IDS_SET_PERM_ON_NETWORK_DRIVE : IDS_SET_SACL_ON_NETWORK_DRIVE;
|
|
|
|
//Ok, this is a remote network drive, display Warning.
|
|
//We display only one warning per list
|
|
if (IDNO == MsgPopup(hWndPopupOwner,
|
|
MAKEINTRESOURCE(nMsgId),
|
|
MAKEINTRESOURCE(IDS_PROPPAGE_TITLE),
|
|
MB_YESNO|MB_DEFBUTTON2| MB_ICONWARNING | MB_SETFOREGROUND,
|
|
g_hInstance,
|
|
pszPath))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
// Function:GetSystemPaths
|
|
// Synopsis:Extract the value for environment variables SystemDrive
|
|
// and SystemRoot
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
GetSystemPaths(LPWSTR * ppszSystemDrive,
|
|
LPWSTR * ppszSystemRoot)
|
|
{
|
|
static WCHAR szSystemDrive[] = L"%SystemDrive%";
|
|
static WCHAR szSystemRoot[] = L"%SystemRoot%";
|
|
|
|
do
|
|
{
|
|
//Get SystemDrive
|
|
DWORD dwLen = ExpandEnvironmentStrings(szSystemDrive,
|
|
NULL,
|
|
0);
|
|
if(dwLen)
|
|
{
|
|
//dwLen contains space for NULL char. 1 is added for "\".
|
|
//SystemDrive is in format C:, while we want in the format C:"\"
|
|
DWORD dwArrayLen = dwLen + 1;
|
|
*ppszSystemDrive = (LPWSTR)LocalAlloc(LPTR,dwArrayLen*sizeof(WCHAR));
|
|
if(!*ppszSystemDrive)
|
|
break;
|
|
|
|
dwLen = ExpandEnvironmentStrings(szSystemDrive,
|
|
*ppszSystemDrive,
|
|
dwArrayLen);
|
|
if(!dwLen)
|
|
{
|
|
LocalFree(*ppszSystemDrive);
|
|
*ppszSystemDrive = NULL;
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr = StringCchCat(*ppszSystemDrive,
|
|
dwArrayLen,
|
|
L"\\");
|
|
if(FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr));
|
|
LocalFree(*ppszSystemDrive);
|
|
*ppszSystemDrive = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//Get SystemRoot
|
|
dwLen = ExpandEnvironmentStrings(szSystemRoot,
|
|
NULL,
|
|
0);
|
|
if(dwLen)
|
|
{
|
|
*ppszSystemRoot = (LPWSTR)LocalAlloc(LPTR,dwLen*sizeof(WCHAR));
|
|
if(!*ppszSystemRoot)
|
|
break;
|
|
|
|
dwLen = ExpandEnvironmentStrings(szSystemRoot,
|
|
*ppszSystemRoot,
|
|
dwLen);
|
|
if(!dwLen)
|
|
{
|
|
ASSERT(dwLen);
|
|
LocalFree(*ppszSystemRoot);
|
|
*ppszSystemRoot = NULL;
|
|
}
|
|
}
|
|
|
|
}while(0);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
// Function:SetAclOnSystemPaths
|
|
// Synopsis:changing acl of SystemDrive or on or under SystemRoot results in
|
|
// problem. This function detects if user is changing acl on these
|
|
// system folder and shows appropriate warning.
|
|
// Returns: TRUE if its fine to set ACL , FALSE if not.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL SetAclOnSystemPaths(HDPA hItemList,
|
|
LPCWSTR pszSystemDrive,
|
|
LPCWSTR pszSystemRoot,
|
|
SECURITY_INFORMATION si,
|
|
HWND hWndPopupOwner)
|
|
{
|
|
if(!hItemList)
|
|
{
|
|
ASSERT(hItemList);
|
|
return FALSE;
|
|
}
|
|
|
|
//We care only about DACL
|
|
if(!(si & DACL_SECURITY_INFORMATION))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD dwLenSystemRoot = 0;
|
|
if(pszSystemRoot)
|
|
{
|
|
dwLenSystemRoot = (DWORD)wcslen(pszSystemRoot);
|
|
}
|
|
|
|
//Now Check each item in hItemList
|
|
for (int i = 0; i < DPA_GetPtrCount(hItemList); i++)
|
|
{
|
|
LPTSTR pszPath = (LPTSTR)DPA_FastGetPtr(hItemList, i);
|
|
|
|
if(!pszPath)
|
|
continue;
|
|
|
|
//Check for system drive
|
|
if(pszSystemDrive &&
|
|
CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
|
|
0,
|
|
pszSystemDrive,
|
|
-1,
|
|
pszPath,
|
|
-1))
|
|
{
|
|
if (IDNO == MsgPopup(hWndPopupOwner,
|
|
MAKEINTRESOURCE(IDS_ACL_ON_SYSTEMROOT),
|
|
MAKEINTRESOURCE(IDS_PROPPAGE_TITLE),
|
|
MB_YESNO|MB_DEFBUTTON2| MB_ICONWARNING | MB_SETFOREGROUND,
|
|
g_hInstance))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//Check if its under systemroot
|
|
DWORD dwlenPath = wcslen(pszPath);
|
|
|
|
if(dwLenSystemRoot && (dwlenPath >= dwLenSystemRoot) &&
|
|
(CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pszSystemRoot,
|
|
dwLenSystemRoot,
|
|
pszPath,
|
|
dwLenSystemRoot)))
|
|
{
|
|
|
|
if (IDNO == MsgPopup(hWndPopupOwner,
|
|
MAKEINTRESOURCE(IDS_ACL_ON_UNDER_SYSTEM_DRIVE),
|
|
MAKEINTRESOURCE(IDS_PROPPAGE_TITLE),
|
|
MB_YESNO|MB_DEFBUTTON2| MB_ICONWARNING | MB_SETFOREGROUND,
|
|
g_hInstance))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|