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.
2508 lines
82 KiB
2508 lines
82 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995 - 2000.
|
|
//
|
|
// File: shrpage2.cxx
|
|
//
|
|
// Contents: "Simple Sharing" shell property page extension
|
|
//
|
|
// History: 06-Oct-00 jeffreys Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "headers.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include "resource.h"
|
|
#include "helpids.h"
|
|
#include "dlgnew.hxx"
|
|
#include "cache.hxx"
|
|
#include "share.hxx"
|
|
#include "acl.hxx"
|
|
#include "shrinfo.hxx"
|
|
#include "shrpage2.hxx"
|
|
#include "util.hxx"
|
|
#include <userenv.h> // GetProfilesDirectory
|
|
#undef ClearFlag // #defined in both ccstock.h and seopaque.h
|
|
#include <seopaque.h> // FirstAce, etc.
|
|
#include <shgina.h> // ILocalMachine, ILogonUser
|
|
#include <shpriv.h> // IHomeNetworkWizard
|
|
|
|
extern GENERIC_MAPPING ShareMap; // permpage.cpp
|
|
|
|
//
|
|
// Forward Decl.
|
|
//
|
|
|
|
INT_PTR
|
|
WarningDlgProc(
|
|
IN HWND hWnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
);
|
|
|
|
// Security descriptor stuff used on this page
|
|
//
|
|
// (A;OICI;GA;;;SY) == Allow GENERIC_ALL to SYSTEM
|
|
// (A;OICI;GA;;;%s) == Allow GENERIC_ALL to <current user>
|
|
// (A;OICI;GA;;;BA) == Allow GENERIC_ALL to Builtin (local) Administrators
|
|
// (A;OICI;GRGWGXSD;;;WD) == Allow Modify access to Everyone (Read, Write, eXecute, Delete)
|
|
//
|
|
// "OI" == Object Inherit (inherit onto files)
|
|
// "CI" == Container Inherit (inherit onto subfolders)
|
|
//
|
|
// See sddl.h for more info.
|
|
|
|
// Share permissions
|
|
const WCHAR c_szFullShareSD[] = L"D:(A;;GRGWGXSD;;;WD)";
|
|
const WCHAR c_szReadonlyShareSD[] = L"D:(A;;GRGX;;;WD)";
|
|
|
|
// Folder permissions (used only on profile folders)
|
|
const WCHAR c_szPrivateFolderSD[] = L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;%1)";
|
|
const WCHAR c_szDefaultProfileSD[]= L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;%1)(A;OICI;GA;;;BA)";
|
|
|
|
// Root folder permissions (see SDDLRoot in ds\security\services\scerpc\headers.h)
|
|
const WCHAR c_szRootSDSecure[] = L"D:(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)(A;OICIIO;GA;;;CO)(A;CIOI;GRGX;;;BU)(A;CI;4;;;BU)(A;CIIO;2;;;BU)(A;;GRGX;;;WD)";
|
|
const WCHAR c_szRootSDUnsecure[]= L"D:P(A;OICI;GA;;;WD)";
|
|
|
|
typedef struct
|
|
{
|
|
SID sid; // contains 1 subauthority
|
|
DWORD dwSubAuth[1]; // we currently need at most 2 subauthorities
|
|
} _SID2;
|
|
|
|
const SID g_WorldSid = {SID_REVISION,1,SECURITY_WORLD_SID_AUTHORITY, {SECURITY_WORLD_RID} };
|
|
const _SID2 g_AdminsSid = {{SID_REVISION,2,SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID}}, {DOMAIN_ALIAS_RID_ADMINS}};
|
|
const _SID2 g_PowerUSid = {{SID_REVISION,2,SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID}}, {DOMAIN_ALIAS_RID_POWER_USERS}};
|
|
const _SID2 g_UsersSid = {{SID_REVISION,2,SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID}}, {DOMAIN_ALIAS_RID_USERS}};
|
|
const _SID2 g_GuestsSid = {{SID_REVISION,2,SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID}}, {DOMAIN_ALIAS_RID_GUESTS}};
|
|
|
|
static const UINT g_rgHideTheseControlsOnDriveBlockade[] = {
|
|
IDC_GB_SECURITY
|
|
, IDC_GB_NETWORK_SHARING
|
|
, IDC_SIMPLE_SHARE_SECURITY_STATIC
|
|
, IDC_SHARE_NOTSHARED
|
|
, IDC_LNK_SHARE_PARENT_PROTECTED
|
|
, IDC_SHARE_ICON
|
|
, IDC_SIMPLE_SHARE_NETWORKING_STATIC
|
|
, IDC_SHARE_SHAREDAS
|
|
, IDC_SHARE_SHARENAME_TEXT
|
|
, IDC_SHARE_SHARENAME
|
|
, IDC_SHARE_PERMISSIONS
|
|
, IDC_I_SHARE_INFORMATION
|
|
, IDC_LNK_SHARE_NETWORK_WIZARD
|
|
, IDC_LNK_SHARE_OPEN_SHARED_DOCS
|
|
, IDC_LNK_SHARE_HELP_SHARING_AND_SECURITY
|
|
, IDC_LNK_SHARE_HELP_ON_SECURITY
|
|
, IDC_S_SHARE_SYSTEM_FOLDER
|
|
, IDC_LNK_SHARE_SECURITY_OVERRIDE
|
|
};
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: _GetUserSid
|
|
//
|
|
// Synopsis: Get the current user's SID from the thread or process token.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
_GetUserSid(
|
|
OUT PWSTR *ppszSID
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
HANDLE hToken;
|
|
|
|
*ppszSID = NULL;
|
|
|
|
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)
|
|
|| OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
{
|
|
BYTE buffer[sizeof(TOKEN_USER) + sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD)];
|
|
ULONG cbBuffer = sizeof(buffer);
|
|
|
|
if (GetTokenInformation(hToken,
|
|
TokenUser,
|
|
buffer,
|
|
cbBuffer,
|
|
&cbBuffer))
|
|
{
|
|
PTOKEN_USER ptu = (PTOKEN_USER)buffer;
|
|
bResult = ConvertSidToStringSidW(ptu->User.Sid, ppszSID);
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: _GetUserProfilePath
|
|
//
|
|
// Synopsis: Retrieve the profile path for a particular user.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT _GetUserProfilePath(PCWSTR pszUserSID, PWSTR szPath, DWORD cchPath)
|
|
{
|
|
WCHAR szKey[MAX_PATH];
|
|
DWORD dwErr;
|
|
|
|
if (PathCombineW(szKey, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList", pszUserSID))
|
|
{
|
|
DWORD cbSize = cchPath * sizeof(WCHAR);
|
|
dwErr = SHGetValue(HKEY_LOCAL_MACHINE, szKey, L"ProfileImagePath", NULL, szPath, &cbSize);
|
|
}
|
|
else
|
|
{
|
|
dwErr = ERROR_FILENAME_EXCED_RANGE;
|
|
}
|
|
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: _CheckFolderType
|
|
//
|
|
// Synopsis: Check whether the target folder is contained in a
|
|
// special folder, such as the user's profile.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
static const struct
|
|
{
|
|
int csidl;
|
|
BOOL bTestSubfolder;
|
|
BOOL bUserSpecific;
|
|
DWORD dwFlags;
|
|
PCWSTR pszDefaultSD; // needed if CFT_FLAG_CAN_MAKE_PRIVATE is on
|
|
} c_rgFolderInfo[] =
|
|
{
|
|
// NOTE: Order is important here!
|
|
{CSIDL_SYSTEM, TRUE, FALSE, CFT_FLAG_ALWAYS_SHARED | CFT_FLAG_SYSTEM_FOLDER, NULL},
|
|
{CSIDL_PROGRAM_FILES, FALSE, FALSE, CFT_FLAG_ALWAYS_SHARED | CFT_FLAG_SYSTEM_FOLDER, NULL},
|
|
{CSIDL_COMMON_DOCUMENTS, TRUE, FALSE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ALWAYS_SHARED, NULL},
|
|
{CSIDL_COMMON_DESKTOPDIRECTORY, TRUE, FALSE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ALWAYS_SHARED, NULL},
|
|
{CSIDL_COMMON_PICTURES, TRUE, FALSE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ALWAYS_SHARED, NULL},
|
|
{CSIDL_COMMON_MUSIC, TRUE, FALSE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ALWAYS_SHARED, NULL},
|
|
{CSIDL_COMMON_VIDEO, TRUE, FALSE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ALWAYS_SHARED, NULL},
|
|
{CSIDL_PROFILE, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_DESKTOPDIRECTORY, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_PERSONAL, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_MYPICTURES, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_MYMUSIC, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_MYVIDEO, TRUE, TRUE, CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_CAN_MAKE_PRIVATE, c_szDefaultProfileSD},
|
|
{CSIDL_WINDOWS, TRUE, FALSE, CFT_FLAG_ALWAYS_SHARED | CFT_FLAG_SYSTEM_FOLDER, NULL},
|
|
};
|
|
// Some of the folders above are normally contained within another, e.g. MyDocs
|
|
// inside Profile, but may be redirected elsewhere. In such cases, the child
|
|
// folder should be listed *after* the parent folder. This is important for
|
|
// correctly finding the point at which the protected ACL is set.
|
|
//
|
|
// Also, upgrades from previous OS's can leave profiles under CSIDL_WINDOWS.
|
|
// We don't allow sharing under CSIDL_WINDOWS, except we want to allow the user
|
|
// to share folders in their profile. So leave CSIDL_WINDOWS last.
|
|
|
|
|
|
BOOL
|
|
_PathIsEqualOrSubFolder(
|
|
PWSTR pszParent,
|
|
PCWSTR pszSubFolder
|
|
)
|
|
{
|
|
WCHAR szCommon[MAX_PATH];
|
|
|
|
// PathCommonPrefix() always removes the slash on common
|
|
return (pszParent[0] && PathRemoveBackslashW(pszParent)
|
|
&& PathCommonPrefixW(pszParent, pszSubFolder, szCommon)
|
|
&& lstrcmpiW(pszParent, szCommon) == 0);
|
|
}
|
|
|
|
DWORD
|
|
_CheckFolderType(
|
|
PCWSTR pszFolder,
|
|
PCWSTR pszUserSID,
|
|
BOOL *pbFolderRoot,
|
|
PCWSTR *ppszDefaultAcl
|
|
)
|
|
{
|
|
// Default is to allow sharing, unless there is a reason not to
|
|
DWORD dwSharingFlags = CFT_FLAG_SHARING_ALLOWED;
|
|
|
|
if (pbFolderRoot)
|
|
{
|
|
*pbFolderRoot = FALSE;
|
|
}
|
|
|
|
if (ppszDefaultAcl)
|
|
{
|
|
*ppszDefaultAcl = NULL;
|
|
}
|
|
|
|
// Note that we don't mess with UNC paths
|
|
|
|
if (NULL == pszFolder ||
|
|
L'\0' == *pszFolder ||
|
|
PathIsUNC(pszFolder))
|
|
{
|
|
return CFT_FLAG_NO_SHARING;
|
|
}
|
|
|
|
// We warn about sharing out the root folder of drives.
|
|
if (PathIsRoot(pszFolder))
|
|
{
|
|
return CFT_FLAG_ROOT_FOLDER;
|
|
}
|
|
|
|
WCHAR szPath[MAX_PATH];
|
|
BOOL bFolderRoot = FALSE;
|
|
int i;
|
|
HRESULT hr;
|
|
|
|
if (NULL != pszUserSID)
|
|
{
|
|
LPWSTR pszCurrentSID = NULL;
|
|
if (_GetUserSid(&pszCurrentSID))
|
|
{
|
|
appAssert(NULL != pszCurrentSID);
|
|
if (0 == lstrcmpiW(pszUserSID, pszCurrentSID))
|
|
{
|
|
// Use NULL for current user to avoid E_NOTIMPL cases below
|
|
pszUserSID = NULL;
|
|
}
|
|
LocalFree(pszCurrentSID);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYLEN(c_rgFolderInfo); i++)
|
|
{
|
|
// If the user is specified, need to check the correct profile
|
|
if (c_rgFolderInfo[i].bUserSpecific && NULL != pszUserSID)
|
|
{
|
|
switch (c_rgFolderInfo[i].csidl)
|
|
{
|
|
case CSIDL_PROFILE:
|
|
hr = _GetUserProfilePath(pszUserSID, szPath, ARRAYLEN(szPath));
|
|
break;
|
|
|
|
case CSIDL_DESKTOPDIRECTORY:
|
|
case CSIDL_PERSONAL:
|
|
case CSIDL_MYPICTURES:
|
|
case CSIDL_MYMUSIC:
|
|
case CSIDL_MYVIDEO:
|
|
default:
|
|
// Need to load the user's hive and read the shell folder
|
|
// path from there.
|
|
//
|
|
// For now, we don't really need these, so just skip them.
|
|
appAssert(FALSE);
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = SHGetFolderPathW(NULL, c_rgFolderInfo[i].csidl | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath);
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
bFolderRoot = (lstrcmpiW(szPath, pszFolder) == 0);
|
|
if (bFolderRoot ||
|
|
(c_rgFolderInfo[i].bTestSubfolder && _PathIsEqualOrSubFolder(szPath, pszFolder)))
|
|
{
|
|
if (bFolderRoot && ppszDefaultAcl)
|
|
{
|
|
*ppszDefaultAcl = c_rgFolderInfo[i].pszDefaultSD;
|
|
}
|
|
dwSharingFlags = c_rgFolderInfo[i].dwFlags;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ARRAYLEN(c_rgFolderInfo) == i)
|
|
{
|
|
// Check for other profile dirs. If there were a CSIDL for this we
|
|
// could just add it to the list above.
|
|
DWORD cchPath = ARRAYLEN(szPath);
|
|
if (GetProfilesDirectoryW(szPath, &cchPath))
|
|
{
|
|
bFolderRoot = (lstrcmpiW(szPath, pszFolder) == 0);
|
|
if (bFolderRoot || _PathIsEqualOrSubFolder(szPath, pszFolder))
|
|
{
|
|
// No sharing
|
|
dwSharingFlags = CFT_FLAG_SYSTEM_FOLDER;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pbFolderRoot)
|
|
{
|
|
*pbFolderRoot = bFolderRoot;
|
|
}
|
|
|
|
return dwSharingFlags;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: IsGuestEnabledForNetworkAccess
|
|
//
|
|
// Synopsis: Test whether the Guest account can be used for incoming
|
|
// network connections.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL IsGuestEnabledForNetworkAccess()
|
|
{
|
|
BOOL bResult = FALSE;
|
|
ILocalMachine *pLM;
|
|
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_ILocalMachine, (void**)&pLM)))
|
|
{
|
|
VARIANT_BOOL vbEnabled = VARIANT_FALSE;
|
|
bResult = (SUCCEEDED(pLM->get_isGuestEnabled(ILM_GUEST_NETWORK_LOGON, &vbEnabled)) && VARIANT_TRUE == vbEnabled);
|
|
pLM->Release();
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CSimpleSharingPage::CSimpleSharingPage()
|
|
:
|
|
CShareBase(FALSE),
|
|
_bSharingEnabled(TRUE),
|
|
_bShareNameChanged(FALSE),
|
|
_bSecDescChanged(FALSE),
|
|
_bIsPrivateVisible(FALSE),
|
|
_bDriveRootBlockade(TRUE),
|
|
_dwPermType(0),
|
|
_pszInheritanceSource(NULL)
|
|
{
|
|
INIT_SIG(CSimpleSharingPage);
|
|
}
|
|
|
|
CSimpleSharingPage::~CSimpleSharingPage()
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
if (NULL != _pszInheritanceSource)
|
|
{
|
|
LocalFree(_pszInheritanceSource);
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_PageProc, private
|
|
//
|
|
// Synopsis: Dialog Procedure for this object
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_PageProc(
|
|
IN HWND hwnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_SETTINGCHANGE:
|
|
// Reinitialize the dialog
|
|
_InitializeControls(hwnd);
|
|
break;
|
|
}
|
|
|
|
return CShareBase::_PageProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_OnInitDialog, private
|
|
//
|
|
// Synopsis: WM_INITDIALOG handler
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_OnInitDialog(
|
|
IN HWND hwnd,
|
|
IN HWND /*hwndFocus*/,
|
|
IN LPARAM /*lInitParam*/
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
appDebugOut((DEB_ITRACE, "_OnInitDialog\n"));
|
|
|
|
// use LanMan API constant to set maximum share name length
|
|
SendDlgItemMessage(hwnd, IDC_SHARE_SHARENAME, EM_LIMITTEXT, NNLEN, 0L);
|
|
|
|
_InitializeControls(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_OnCommand, private
|
|
//
|
|
// Synopsis: WM_COMMAND handler
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_OnCommand(
|
|
IN HWND hwnd,
|
|
IN WORD wNotifyCode,
|
|
IN WORD wID,
|
|
IN HWND hwndCtl
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
switch (wID)
|
|
{
|
|
case IDC_SHARE_SHAREDAS:
|
|
if (BST_UNCHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_SHAREDAS))
|
|
{
|
|
_ReadControls(hwnd);
|
|
}
|
|
// Fall through...
|
|
case IDC_SHARE_NOTSHARED:
|
|
if (BN_CLICKED == wNotifyCode)
|
|
{
|
|
_SetControlsFromData(hwnd);
|
|
_MarkPageDirty();
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_SHARE_SHARENAME:
|
|
if (EN_CHANGE == wNotifyCode && !_fInitializingPage)
|
|
{
|
|
_bShareNameChanged = TRUE;
|
|
_MarkPageDirty();
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_SHARE_PERMISSIONS:
|
|
if (BN_CLICKED == wNotifyCode)
|
|
{
|
|
_bSecDescChanged = TRUE;
|
|
_MarkPageDirty();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return CShareBase::_OnCommand(hwnd, wNotifyCode, wID, hwndCtl);
|
|
}
|
|
|
|
BOOL
|
|
RunTheNetworkSharingWizard(
|
|
HWND hwnd
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IHomeNetworkWizard *pHNW;
|
|
|
|
hr = CoCreateInstance( CLSID_HomeNetworkWizard, NULL, CLSCTX_INPROC_SERVER, IID_IHomeNetworkWizard, (void**)&pHNW );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BOOL bRebootRequired = FALSE;
|
|
|
|
hr = pHNW->ShowWizard(hwnd, &bRebootRequired);
|
|
if ( SUCCEEDED(hr) && bRebootRequired )
|
|
{
|
|
RestartDialogEx(hwnd, NULL, EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG);
|
|
}
|
|
|
|
pHNW->Release();
|
|
}
|
|
|
|
return (SUCCEEDED(hr));
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_OnPropertySheetNotify, private
|
|
//
|
|
// Synopsis: WM_NOTIFY handler
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_OnPropertySheetNotify(
|
|
IN HWND hwnd,
|
|
IN LPNMHDR phdr
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
switch (phdr->code)
|
|
{
|
|
case NM_RETURN:
|
|
case NM_CLICK:
|
|
switch (phdr->idFrom)
|
|
{
|
|
case IDC_LNK_SHARE_PARENT_PROTECTED:
|
|
{
|
|
HWND hwndFrame = _GetFrameWindow();
|
|
|
|
// Close the current propsheet
|
|
PropSheet_PressButton(hwndFrame, PSBTN_CANCEL);
|
|
|
|
appAssert(NULL != _pszInheritanceSource);
|
|
|
|
// Show the sharing page for the ancestor folder
|
|
WCHAR szCaption[50];
|
|
LoadStringW(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
|
|
SHObjectProperties(GetParent(hwndFrame), SHOP_FILEPATH, _pszInheritanceSource, szCaption);
|
|
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_LNK_SHARE_NETWORK_WIZARD:
|
|
appAssert(!_bSharingEnabled);
|
|
if ( RunTheNetworkSharingWizard( hwnd ) )
|
|
{
|
|
// Reinitialize the dialog
|
|
_InitializeControls(hwnd);
|
|
}
|
|
break;
|
|
|
|
case IDC_LNK_SHARE_SECURITY_OVERRIDE:
|
|
{
|
|
UINT iRet = (UINT) DialogBox( g_hInstance, MAKEINTRESOURCE(IDD_SIMPLE_SHARE_ENABLE_WARNING), hwnd, WarningDlgProc );
|
|
if ( IDC_RB_RUN_THE_WIZARD == iRet )
|
|
{
|
|
appAssert(!_bSharingEnabled);
|
|
if ( RunTheNetworkSharingWizard( hwnd ) )
|
|
{
|
|
//
|
|
// Now that we changed the "networking state," re-initialize the dialog
|
|
// and update the control to the new state.
|
|
//
|
|
|
|
_InitializeControls(hwnd);
|
|
}
|
|
}
|
|
else if ( IDC_RB_ENABLE_FILE_SHARING == iRet )
|
|
{
|
|
ILocalMachine *pLM;
|
|
HRESULT hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_ILocalMachine, (void**)&pLM);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pLM->EnableGuest(ILM_GUEST_NETWORK_LOGON);
|
|
pLM->Release();
|
|
|
|
SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
|
|
}
|
|
|
|
//
|
|
// Now that we changed the "networking state," re-initialize the dialog
|
|
// and update the control to the new state.
|
|
//
|
|
|
|
_InitializeControls(hwnd);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_LNK_SHARE_DRIVE_BLOCADE:
|
|
if (_bDriveRootBlockade)
|
|
{
|
|
// Unhide the other controls
|
|
for (ULONG idx = 0; idx < ARRAYLEN(g_rgHideTheseControlsOnDriveBlockade); idx ++ )
|
|
{
|
|
ShowWindow(GetDlgItem(hwnd, g_rgHideTheseControlsOnDriveBlockade[idx]), SW_SHOW);
|
|
}
|
|
_bDriveRootBlockade = FALSE;
|
|
_InitializeControls( hwnd );
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_LNK_SHARE_OPEN_SHARED_DOCS:
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
BOOL b = SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_DOCUMENTS, TRUE);
|
|
if (b)
|
|
{
|
|
DWORD_PTR dwRet = (DWORD_PTR) ShellExecute(hwnd, L"Open", szPath, NULL, NULL, SW_SHOW);
|
|
if ( 32 < dwRet )
|
|
{
|
|
HWND hwndFrame = _GetFrameWindow();
|
|
|
|
// Close the current propsheet
|
|
PropSheet_PressButton(hwndFrame, PSBTN_CANCEL);
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_LNK_SHARE_HELP_SHARING_AND_SECURITY:
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
if (LoadString(g_hInstance,
|
|
IsOS(OS_PERSONAL) ? IDS_SHARE_HELP_SHARING_AND_SECURITY_PER : IDS_SHARE_HELP_SHARING_AND_SECURITY_WKS,
|
|
szPath, ARRAYLEN(szPath)))
|
|
{
|
|
HWND hwndFrame = _GetFrameWindow();
|
|
ShellExecute(hwndFrame, NULL, szPath, NULL, NULL, SW_NORMAL);
|
|
}
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CShareBase::_OnPropertySheetNotify(hwnd, phdr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_OnHelp, private
|
|
//
|
|
// Synopsis: WM_HELP handler
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
static const DWORD aHelpIds[] =
|
|
{
|
|
IDC_SHARE_SHARENAME, IDH_SHARE2_ShareName,
|
|
IDC_SHARE_SHARENAME_TEXT, IDH_SHARE2_ShareName,
|
|
IDC_SHARE_NOTSHARED, IDH_SHARE2_MakePrivate,
|
|
IDC_SHARE_SHAREDAS, IDH_SHARE2_ShareOnNet,
|
|
IDC_SHARE_PERMISSIONS, IDH_SHARE2_ReadOnly,
|
|
IDC_LNK_SHARE_DRIVE_BLOCADE,(DWORD)-1, // no help
|
|
0,0
|
|
};
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_OnHelp(
|
|
IN HWND /*hwnd*/,
|
|
IN LPHELPINFO lphi
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
if (lphi->iContextType == HELPINFO_WINDOW) // a control
|
|
{
|
|
WCHAR szHelp[50];
|
|
LoadString(g_hInstance, IDS_SIMPLE_SHARE_HELPFILE, szHelp, ARRAYLEN(szHelp));
|
|
WinHelp(
|
|
(HWND)lphi->hItemHandle,
|
|
szHelp,
|
|
HELP_WM_HELP,
|
|
(DWORD_PTR)aHelpIds);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_OnContextMenu, private
|
|
//
|
|
// Synopsis: WM_CONTEXTMENU handler
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_OnContextMenu(
|
|
IN HWND /*hwnd*/,
|
|
IN HWND hwndCtl,
|
|
IN int /*xPos*/,
|
|
IN int /*yPos*/
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
WCHAR szHelp[50];
|
|
LoadString(g_hInstance, IDS_SIMPLE_SHARE_HELPFILE, szHelp, ARRAYLEN(szHelp));
|
|
WinHelp(
|
|
hwndCtl,
|
|
szHelp,
|
|
HELP_CONTEXTMENU,
|
|
(DWORD_PTR)aHelpIds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_InitializeControls, private
|
|
//
|
|
// Synopsis: Initialize the controls from scratch
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CSimpleSharingPage::_InitializeControls(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
_fInitializingPage++;
|
|
|
|
_dwPermType = IFPFU_NOT_NTFS;
|
|
_bIsPrivateVisible = FALSE;
|
|
if (NULL != _pszInheritanceSource)
|
|
{
|
|
LocalFree(_pszInheritanceSource);
|
|
_pszInheritanceSource = NULL;
|
|
}
|
|
|
|
// Check whether to show the "Make Private" stuff
|
|
DWORD dwFolderFlags = _CheckFolderType(_pszPath, NULL, NULL, NULL);
|
|
if (dwFolderFlags & CFT_FLAG_CAN_MAKE_PRIVATE)
|
|
{
|
|
_dwPermType = IFPFU_NOT_PRIVATE;
|
|
|
|
LPWSTR pszSID = NULL;
|
|
if (_GetUserSid(&pszSID))
|
|
{
|
|
appAssert(NULL != pszSID);
|
|
IsFolderPrivateForUser(_pszPath, pszSID, &_dwPermType, &_pszInheritanceSource);
|
|
LocalFree(pszSID);
|
|
}
|
|
|
|
if ((_dwPermType & IFPFU_NOT_NTFS) == 0)
|
|
{
|
|
_bIsPrivateVisible = TRUE;
|
|
}
|
|
}
|
|
CheckDlgButton(hwnd, IDC_SHARE_NOTSHARED, (_bIsPrivateVisible && (_dwPermType & IFPFU_PRIVATE) != 0) ? BST_CHECKED : BST_UNCHECKED);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_NOTSHARED), _bIsPrivateVisible);
|
|
|
|
BOOL bIsFolderNetworkShared = FALSE;
|
|
|
|
if ( g_fSharingEnabled )
|
|
{
|
|
// Is there a net share?
|
|
if (_cShares > 0)
|
|
{
|
|
// It's shared, but check for hidden (admin$) shares and
|
|
// ignore them by removing them from the list.
|
|
|
|
appAssert(_bNewShare == FALSE);
|
|
|
|
for (CShareInfo* p = (CShareInfo*)_pInfoList->Next();
|
|
p != _pInfoList && _cShares > 0;
|
|
)
|
|
{
|
|
CShareInfo* pNext = (CShareInfo*)p->Next();
|
|
|
|
if (STYPE_SPECIAL & p->GetType())
|
|
{
|
|
// remove p from the list
|
|
p->Unlink();
|
|
delete p;
|
|
_cShares--;
|
|
}
|
|
|
|
p = pNext;
|
|
}
|
|
if (_cShares == 0)
|
|
{
|
|
// No shares left, so construct an element to be used
|
|
// by the UI to stash the new share's data.
|
|
_ConstructNewShareInfo();
|
|
}
|
|
}
|
|
|
|
// Now is it shared?
|
|
if (_cShares > 0)
|
|
{
|
|
CheckDlgButton(hwnd, IDC_SHARE_SHAREDAS, BST_CHECKED);
|
|
bIsFolderNetworkShared = TRUE;
|
|
}
|
|
else
|
|
{
|
|
SetDlgItemText(hwnd, IDC_SHARE_SHARENAME, L"");
|
|
CheckDlgButton(hwnd, IDC_SHARE_SHAREDAS, BST_UNCHECKED);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The Simple Sharing page (shrpage2.cxx) assumes ForceGuest
|
|
// mode is in effect for incoming network access. This mode uses
|
|
// the Guest account for all network connections.
|
|
//
|
|
// Out of the box, the Guest account is disabled, effectively
|
|
// disabling network sharing. The Home Networking Wizard is
|
|
// used to enable network sharing (and the Guest account).
|
|
//
|
|
// So we test whether the Guest account is enabled for network
|
|
// logon to determine whether to enable the sharing UI. If
|
|
// network sharing is disabled, we disable the UI and offer
|
|
// to launch the Home Networking Wizard.
|
|
//
|
|
// Note that it is possible for a net share to exist even though
|
|
// the Guest account is disabled.
|
|
//
|
|
_bSharingEnabled = IsGuestEnabledForNetworkAccess();
|
|
|
|
BOOL bShowPrivateWarning = (_bIsPrivateVisible && (_dwPermType & IFPFU_PRIVATE_INHERITED));
|
|
BOOL bInheritanceSource = (NULL == _pszInheritanceSource);
|
|
BOOL bIsRootFolder = (dwFolderFlags & CFT_FLAG_ROOT_FOLDER);
|
|
BOOL bIsSystemFolder = (dwFolderFlags & CFT_FLAG_SYSTEM_FOLDER);
|
|
//BOOL bIsInSharedFolder = (dwFolderFlags & CFT_FLAG_ALWAYS_SHARED);
|
|
|
|
// see if the path is the root of a drive. if so, put up the blockade.
|
|
if (_bDriveRootBlockade && bIsRootFolder && !bIsFolderNetworkShared)
|
|
{
|
|
_MyShow(hwnd, IDC_LNK_SHARE_DRIVE_BLOCADE, TRUE);
|
|
|
|
// Hide all the other controls when the blockade is up.
|
|
for (ULONG idx = 0; idx < ARRAYLEN(g_rgHideTheseControlsOnDriveBlockade); idx ++ )
|
|
{
|
|
ShowWindow(GetDlgItem(hwnd, g_rgHideTheseControlsOnDriveBlockade[idx]), SW_HIDE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL bShowInfoIcon = FALSE;
|
|
BOOL bShowNetworkWizard = FALSE;
|
|
BOOL bShowParentProteced = FALSE;
|
|
BOOL bShowSystemFolder = FALSE;
|
|
|
|
// Hide the blockade
|
|
_MyShow(hwnd, IDC_LNK_SHARE_DRIVE_BLOCADE, FALSE );
|
|
|
|
// Turn on the "special info" as nessecary.
|
|
if (bIsSystemFolder)
|
|
{
|
|
_bSharingEnabled = FALSE;
|
|
bShowSystemFolder = TRUE;
|
|
bShowInfoIcon = TRUE;
|
|
}
|
|
else if (bShowPrivateWarning && !bInheritanceSource)
|
|
{
|
|
bShowParentProteced = TRUE;
|
|
bShowInfoIcon = TRUE;
|
|
}
|
|
else if (!bShowPrivateWarning && !_bSharingEnabled && g_fSharingEnabled)
|
|
{
|
|
bShowNetworkWizard = TRUE;
|
|
}
|
|
|
|
_MyShow(hwnd, IDC_LNK_SHARE_PARENT_PROTECTED , bShowParentProteced);
|
|
_MyShow(hwnd, IDC_LNK_SHARE_NETWORK_WIZARD , bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_LNK_SHARE_SECURITY_OVERRIDE , bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_SIMPLE_SHARE_NETWORKING_STATIC, !bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_SHARE_SHAREDAS , !bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_SHARE_SHARENAME_TEXT , !bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_SHARE_SHARENAME , !bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_SHARE_PERMISSIONS , !bShowNetworkWizard);
|
|
_MyShow(hwnd, IDC_S_SHARE_SYSTEM_FOLDER , bShowSystemFolder);
|
|
_MyShow(hwnd, IDC_I_SHARE_INFORMATION , bShowInfoIcon);
|
|
}
|
|
|
|
_SetControlsFromData(hwnd);
|
|
|
|
_fInitializingPage--;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::SetControlsFromData, private
|
|
//
|
|
// Synopsis: From the class variables and current state of the radio
|
|
// buttons, set the enabled/disabled state of the buttons, as
|
|
// well as filling the controls with the appropriate values.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CSimpleSharingPage::_SetControlsFromData(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
_fInitializingPage++;
|
|
|
|
BOOL bIsPrivate = (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_NOTSHARED));
|
|
BOOL bIsShared = (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_SHAREDAS));
|
|
|
|
// We don't allow both to be checked at the same time
|
|
appAssert(!(bIsPrivate && bIsShared));
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_NOTSHARED), !bIsShared && _bIsPrivateVisible && !(_dwPermType & IFPFU_PRIVATE_INHERITED));
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_SHAREDAS), _bSharingEnabled && !bIsPrivate);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_SHARENAME_TEXT), _bSharingEnabled && bIsShared);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_SHARENAME), _bSharingEnabled && bIsShared);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SHARE_PERMISSIONS), _bSharingEnabled && bIsShared);
|
|
|
|
if (bIsShared)
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_SetControlsFromData: path is shared\n"));
|
|
|
|
_pCurInfo = (CShareInfo*)_pInfoList->Next();
|
|
if (NULL != _pCurInfo)
|
|
{
|
|
// Note that EM_LIMITTEXT doesn't prevent us from putting
|
|
// too much text into the control here.
|
|
appAssert(wcslen(_pCurInfo->GetNetname()) <= NNLEN);
|
|
SetDlgItemText(hwnd, IDC_SHARE_SHARENAME, _pCurInfo->GetNetname());
|
|
|
|
// If the share really exists, then make the name read-only.
|
|
// This corresponds to the non-editable combo-box on the full
|
|
// sharing page.
|
|
SendDlgItemMessage(hwnd, IDC_SHARE_SHARENAME, EM_SETREADONLY, (_cShares > 0), 0);
|
|
|
|
CheckDlgButton(hwnd, IDC_SHARE_PERMISSIONS, _IsReadonlyShare(_pCurInfo) ? BST_UNCHECKED : BST_CHECKED);
|
|
}
|
|
else
|
|
{
|
|
CheckDlgButton(hwnd, IDC_SHARE_SHAREDAS, BST_UNCHECKED );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_SetControlsFromData: path is not shared\n"));
|
|
_pCurInfo = NULL;
|
|
}
|
|
|
|
_fInitializingPage--;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_ReadControls, private
|
|
//
|
|
// Synopsis: Load the data in the controls into internal data structures.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_ReadControls(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
if (_bShareNameChanged)
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_ReadControls: share name changed\n"));
|
|
if (NULL != _pCurInfo)
|
|
{
|
|
appAssert(GetWindowTextLength(GetDlgItem(hwnd, IDC_SHARE_SHARENAME)) <= NNLEN);
|
|
|
|
WCHAR szShareName[NNLEN + 1];
|
|
GetDlgItemText(hwnd, IDC_SHARE_SHARENAME, szShareName, ARRAYLEN(szShareName));
|
|
TrimLeadingAndTrailingSpaces(szShareName);
|
|
_pCurInfo->SetNetname(szShareName);
|
|
_bShareNameChanged = FALSE;
|
|
}
|
|
else
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_ReadControls: _pCurInfo is NULL\n"));
|
|
}
|
|
}
|
|
|
|
if (_bSecDescChanged)
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_ReadControls: permissions changed\n"));
|
|
if(NULL != _pCurInfo)
|
|
{
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
BOOL bIsReadonly = (BST_UNCHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_PERMISSIONS));
|
|
|
|
if (ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
|
bIsReadonly ? c_szReadonlyShareSD : c_szFullShareSD,
|
|
SDDL_REVISION_1,
|
|
&pSD,
|
|
NULL))
|
|
{
|
|
appAssert(IsValidSecurityDescriptor(pSD));
|
|
|
|
// _pCurInfo takes ownership of pSD; no need to free on success
|
|
if (FAILED(_pCurInfo->TransferSecurityDescriptor(pSD)))
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
}
|
|
|
|
_bSecDescChanged = FALSE;
|
|
}
|
|
else
|
|
{
|
|
appDebugOut((DEB_ITRACE, "_ReadControls: _pCurInfo is NULL\n"));
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_ValidatePage, private
|
|
//
|
|
// Synopsis: Return TRUE if the current page is valid
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_ValidatePage(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
_ReadControls(hwnd); // read current stuff
|
|
|
|
if (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_SHAREDAS))
|
|
{
|
|
// If the user is creating a share on the property sheet (as
|
|
// opposed to using the "new share" dialog), we must validate the
|
|
// share.... Note that _bNewShare is still TRUE if the the user has
|
|
// clicked on "Not Shared", so we must check that too.
|
|
|
|
// Validate the share
|
|
|
|
if (!_ValidateNewShareName())
|
|
{
|
|
SetErrorFocus(hwnd, IDC_SHARE_SHARENAME);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#if DBG == 1
|
|
Dump(L"_ValidatePage finished");
|
|
#endif // DBG == 1
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_DoApply, private
|
|
//
|
|
// Synopsis: If anything has changed, apply the data
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_DoApply(
|
|
IN HWND hwnd,
|
|
IN BOOL bClose
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
if (_bDirty)
|
|
{
|
|
HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
BOOL bIsShared = (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_SHAREDAS));
|
|
|
|
if (bIsShared)
|
|
{
|
|
_ReadControls(hwnd);
|
|
|
|
//
|
|
// NTRAID#NTBUG9-382492-2001/05/05-jeffreys
|
|
// NTRAID#NTBUG9-479914-2001/11/07-jeffreys
|
|
//
|
|
// Win9x boxes can't see the share if the name is longer than LM20_NNLEN
|
|
//
|
|
if (NULL != _pCurInfo)
|
|
{
|
|
if (_pCurInfo->GetFlag() == SHARE_FLAG_ADDED)
|
|
{
|
|
PCWSTR pszName = _pCurInfo->GetNetname();
|
|
if (NULL != pszName &&
|
|
!IsValidWin9xShareLength(pszName) &&
|
|
IDNO == MyConfirmationDialog(hwnd, MSG_LONGNAMECONFIRM, MB_YESNO | MB_ICONWARNING, pszName))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_CommitShares(!bIsShared);
|
|
|
|
if (_bDirty)
|
|
{
|
|
DWORD dwLevel;
|
|
BOOL bIsPrivate = (_bIsPrivateVisible && BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_NOTSHARED));
|
|
|
|
appAssert(!(bIsShared && bIsPrivate));
|
|
|
|
if (bIsPrivate)
|
|
{
|
|
// Private to the current user
|
|
dwLevel = 0;
|
|
}
|
|
else if (!bIsShared)
|
|
{
|
|
// Default ACL (neither private nor shared)
|
|
dwLevel = 1;
|
|
}
|
|
else if (BST_UNCHECKED == IsDlgButtonChecked(hwnd, IDC_SHARE_PERMISSIONS))
|
|
{
|
|
// Read-only share
|
|
dwLevel = 2;
|
|
}
|
|
else
|
|
{
|
|
// Read-write share
|
|
dwLevel = 3;
|
|
}
|
|
|
|
_SetFolderPermissions(dwLevel);
|
|
}
|
|
|
|
CShareBase::_DoApply(hwnd, bClose);
|
|
|
|
if (!bClose)
|
|
{
|
|
_InitializeControls(hwnd);
|
|
}
|
|
|
|
SetCursor(hcur);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_DoCancel, private
|
|
//
|
|
// Synopsis: Do whatever is necessary to cancel the changes
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_DoCancel(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
if (_bDirty)
|
|
{
|
|
_bShareNameChanged = FALSE;
|
|
}
|
|
|
|
return CShareBase::_DoCancel(hwnd);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_SetFolderPermissions, private
|
|
//
|
|
// Synopsis: Set new permissions on the subtree, either restricting
|
|
// access to the current user or making the folder accessible
|
|
// to everyone.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
typedef struct _SET_PERM_THREAD_DATA
|
|
{
|
|
DWORD dwLevel;
|
|
HWND hwndOwner;
|
|
WCHAR szPath[1];
|
|
} SET_PERM_THREAD_DATA;
|
|
|
|
DWORD WINAPI _SetFolderPermissionsThread(LPVOID pv)
|
|
{
|
|
DWORD dwError = ERROR_INVALID_DATA;
|
|
SET_PERM_THREAD_DATA *ptd = (SET_PERM_THREAD_DATA*)pv;
|
|
if (ptd)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
if (!SetFolderPermissionsForSharing(ptd->szPath, NULL, ptd->dwLevel, ptd->hwndOwner))
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
LocalFree(ptd);
|
|
}
|
|
FreeLibraryAndExitThread(g_hInstance, dwError);
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_SetFolderPermissions(
|
|
IN DWORD dwLevel
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
BOOL bResult = FALSE;
|
|
|
|
IActionProgressDialog *papd = NULL;
|
|
IActionProgress *pap = NULL;
|
|
|
|
// Show progress UI so the user understands that we're doing a lengthy
|
|
// operation, even though there's no way to cancel SetNamedSecurityInfo
|
|
// or get progress from it. This requires us to call SetNamedSecurityInfo
|
|
// on a different thread.
|
|
//
|
|
// Also, if the user cancels the progress dialog, we'll release the UI
|
|
// thread even though we can't stop the SetNamedSecurityInfo call. Just
|
|
// abandon the thread and let it run.
|
|
//
|
|
// This can lead to weird results when toggling the "Make private" checkbox
|
|
// on a large subtree:
|
|
// 1. toggle "Make private" and click Apply
|
|
// 2. cancel the progress UI
|
|
// 3. the "Make private" checkbox reverts to the previous state
|
|
// 4. Cancel the property sheet and reopen after the disk stops grinding
|
|
// 5. the "Make private" checkbox shows the new state
|
|
// Apparently, SetNamedSecurityInfo sets folder security in post-order, so
|
|
// the top folder doesn't get the new permissions until then end.
|
|
//
|
|
// Hopefully, this is rare enough that we don't need to do anything about it.
|
|
|
|
HRESULT hr = CoCreateInstance(CLSID_ProgressDialog,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IActionProgressDialog,
|
|
(void**)&papd);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szTitle[64];
|
|
|
|
IUnknown_SetSite(papd, this); // needed for modality
|
|
|
|
LoadStringW(g_hInstance, IDS_PERM_PROGRESS_TITLE, szTitle, ARRAYLEN(szTitle));
|
|
|
|
hr = papd->Initialize(SPINITF_MODAL | SPINITF_NOMINIMIZE, szTitle, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = papd->QueryInterface(IID_IActionProgress, (void**)&pap);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pap->Begin(SPACTION_APPLYINGATTRIBS, SPBEGINF_MARQUEEPROGRESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kick off a thread to do the grunge work
|
|
|
|
int cchPath = lstrlenW(_pszPath);
|
|
// SET_PERM_THREAD_DATA includues room for the terminating '\0' already
|
|
SET_PERM_THREAD_DATA *ptd = (SET_PERM_THREAD_DATA*)LocalAlloc(LPTR, sizeof(SET_PERM_THREAD_DATA) + cchPath*sizeof(WCHAR));
|
|
if (NULL != ptd)
|
|
{
|
|
DWORD dwT;
|
|
|
|
// It is possible to make a folder private with net sharing disabled.
|
|
// It is also possible that net sharing was previously enabled and net
|
|
// shares may still exist. Let's not confuse the user with a warning
|
|
// about deleting net shares on child folders if we happen to have this
|
|
// rare combination. That is, pass NULL for the HWND when sharing is
|
|
// disabled.
|
|
|
|
ptd->dwLevel = dwLevel;
|
|
ptd->hwndOwner = _bSharingEnabled ? _hwndPage : NULL;
|
|
lstrcpynW(ptd->szPath, _pszPath, cchPath+1); // +1 for '\0'
|
|
|
|
LoadLibraryW(L"ntshrui.dll");
|
|
|
|
HANDLE hThread = CreateThread(NULL, 0, _SetFolderPermissionsThread, ptd, 0, &dwT);
|
|
if (NULL == hThread)
|
|
{
|
|
// CreateThread failed? Do it synchronously
|
|
bResult = SetFolderPermissionsForSharing(ptd->szPath, NULL, ptd->dwLevel, ptd->hwndOwner);
|
|
LocalFree(ptd);
|
|
FreeLibrary(g_hInstance);
|
|
}
|
|
else
|
|
{
|
|
// Poll for cancel every 1/2 second
|
|
dwT = pap ? 500 : INFINITE;
|
|
while (WAIT_TIMEOUT == WaitForSingleObject(hThread, dwT))
|
|
{
|
|
BOOL bCancelled;
|
|
hr = pap->QueryCancel(&bCancelled);
|
|
|
|
// QueryCancel pumps messages, which somehow resets
|
|
// the cursor to normal. (_DoApply sets the hourglass)
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
if (SUCCEEDED(hr) && bCancelled)
|
|
{
|
|
// Abandon the worker thread
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check the result
|
|
|
|
bResult = TRUE;
|
|
dwT = ERROR_SUCCESS;
|
|
GetExitCodeThread(hThread, &dwT);
|
|
|
|
// If the exit code is STILL_ACTIVE, assume success.
|
|
// (failure tends to happen quickly -- access denied, etc.)
|
|
if (STILL_ACTIVE == dwT)
|
|
{
|
|
dwT = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwT)
|
|
{
|
|
SetLastError(dwT);
|
|
bResult = FALSE;
|
|
}
|
|
|
|
CloseHandle(hThread);
|
|
}
|
|
}
|
|
|
|
if (pap)
|
|
{
|
|
pap->End();
|
|
pap->Release();
|
|
}
|
|
|
|
if (papd)
|
|
{
|
|
IUnknown_SetSite(papd, NULL);
|
|
papd->Stop();
|
|
papd->Release();
|
|
}
|
|
|
|
// If we just made the folder private, check whether the user has
|
|
// a password. If not, offer to launch the User Accounts CPL.
|
|
|
|
if (bResult && 0 == dwLevel && !_UserHasPassword())
|
|
{
|
|
WCHAR szMsg[MAX_PATH];
|
|
WCHAR szCaption[50];
|
|
LoadStringW(g_hInstance, IDS_PRIVATE_CREATE_PASSWORD, szMsg, ARRAYLEN(szMsg));
|
|
LoadStringW(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
|
|
|
|
if (IDYES == MessageBoxW(_hwndPage, szMsg, szCaption, MB_YESNO | MB_ICONWARNING))
|
|
{
|
|
// Launch the User Accounts CPL to the password page
|
|
SHELLEXECUTEINFO sei = {0};
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.fMask = 0;
|
|
sei.hwnd = _hwndPage;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = TEXT("nusrmgr.cpl");
|
|
sei.lpParameters = TEXT(",initialTask=ChangePassword");
|
|
sei.lpDirectory = NULL;
|
|
ShellExecuteEx(&sei);
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_IsReadonlyShare, private
|
|
//
|
|
// Synopsis: Test whether the share ACL grants more than read access to
|
|
// Everyone or Guest.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_IsReadonlyShare(
|
|
IN CShareInfo *pShareInfo
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
BOOL bReadonly = TRUE;
|
|
|
|
// Get the current share ACL and check for read-only
|
|
PSECURITY_DESCRIPTOR pSD = pShareInfo->GetSecurityDescriptor();
|
|
if (NULL == pSD)
|
|
{
|
|
// Default security allows anyone to connect with Full Control
|
|
bReadonly = FALSE;
|
|
}
|
|
else
|
|
{
|
|
PACL pDacl;
|
|
BOOL bPresent;
|
|
BOOL bDefault;
|
|
|
|
if (GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefault) && NULL != pDacl)
|
|
{
|
|
TRUSTEE tEveryone;
|
|
TRUSTEE tGuests;
|
|
ACCESS_MASK dwAllMask = 0;
|
|
ACCESS_MASK dwGuestMask = 0;
|
|
|
|
// The masks are all initialized to zero. If one or more of the
|
|
// calls to GetEffectiveRightsFromAcl fails, then it will look like
|
|
// that trustee has no rights and the UI will adjust accordingly.
|
|
// There is nothing we could do better by trapping errors from
|
|
// GetEffectiveRightsFromAcl, so don't bother.
|
|
|
|
BuildTrusteeWithSid(&tEveryone, (PSID)&g_WorldSid);
|
|
tEveryone.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
GetEffectiveRightsFromAcl(pDacl, &tEveryone, &dwAllMask);
|
|
|
|
BuildTrusteeWithSid(&tGuests, (PSID)&g_GuestsSid);
|
|
tGuests.TrusteeType = TRUSTEE_IS_ALIAS;
|
|
GetEffectiveRightsFromAcl(pDacl, &tGuests, &dwGuestMask);
|
|
|
|
if ((dwAllMask & ~(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE)) != 0
|
|
|| (dwGuestMask & ~(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE)) != 0)
|
|
{
|
|
bReadonly = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// NULL DACL means no security
|
|
bReadonly = FALSE;
|
|
}
|
|
}
|
|
|
|
return bReadonly;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::_UserHasPassword, private
|
|
//
|
|
// Synopsis: Test whether the current user has a non-blank password.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CSimpleSharingPage::_UserHasPassword(
|
|
VOID
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
// If anything fails, we assume the user has a password
|
|
BOOL bHasPassword = TRUE;
|
|
ILogonEnumUsers *pEnumUsers;
|
|
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_ShellLogonEnumUsers,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ILogonEnumUsers,
|
|
(void**)&pEnumUsers);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ILogonUser *pUser;
|
|
|
|
// Currently, this function always returns S_OK, so need to check NULL
|
|
hr = pEnumUsers->get_currentUser(&pUser);
|
|
if (SUCCEEDED(hr) && NULL != pUser)
|
|
{
|
|
VARIANT_BOOL vb = VARIANT_TRUE;
|
|
|
|
hr = pUser->get_passwordRequired(&vb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (VARIANT_FALSE == vb)
|
|
{
|
|
bHasPassword = FALSE;
|
|
}
|
|
}
|
|
|
|
pUser->Release();
|
|
}
|
|
pEnumUsers->Release();
|
|
}
|
|
|
|
return bHasPassword;
|
|
}
|
|
|
|
|
|
#if DBG == 1
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CSimpleSharingPage::Dump, private
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
CSimpleSharingPage::Dump(
|
|
IN PWSTR pszCaption
|
|
)
|
|
{
|
|
CHECK_SIG(CSimpleSharingPage);
|
|
|
|
appDebugOut((DEB_TRACE,
|
|
"CSimpleSharingPage::Dump, %ws\n",
|
|
pszCaption));
|
|
|
|
appDebugOut((DEB_TRACE | DEB_NOCOMPNAME,
|
|
"\t This: 0x%08lx\n"
|
|
"\t Path: %ws\n"
|
|
"\t Page: 0x%08lx\n"
|
|
"\t Initializing?: %ws\n"
|
|
"\t Dirty?: %ws\n"
|
|
"\t Share changed?: %ws\n"
|
|
"\tPrivate visible?: %ws\n"
|
|
"\t _dwPermType: 0x%08lx\n"
|
|
"\t _pInfoList: 0x%08lx\n"
|
|
"\t _pCurInfo: 0x%08lx\n"
|
|
"\t Shares: %d\n"
|
|
,
|
|
this,
|
|
_pszPath,
|
|
_hwndPage,
|
|
_fInitializingPage ? L"yes" : L"no",
|
|
_bDirty ? L"yes" : L"no",
|
|
_bShareNameChanged ? L"yes" : L"no",
|
|
_bIsPrivateVisible ? L"yes" : L"no",
|
|
_dwPermType,
|
|
_pInfoList,
|
|
_pCurInfo,
|
|
_cShares
|
|
));
|
|
|
|
CShareInfo* p;
|
|
|
|
for (p = (CShareInfo*) _pInfoList->Next();
|
|
p != _pInfoList;
|
|
p = (CShareInfo*) p->Next())
|
|
{
|
|
p->Dump(L"Prop page list");
|
|
}
|
|
|
|
for (p = (CShareInfo*) _pReplaceList->Next();
|
|
p != _pReplaceList;
|
|
p = (CShareInfo*) p->Next())
|
|
{
|
|
p->Dump(L"Replace list");
|
|
}
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: _IsDaclPrivateForUser
|
|
//
|
|
// Synopsis: See whether the DACL grants Full Control to the user
|
|
// and locks everyone else out
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL WINAPI
|
|
_IsDaclPrivateForUser(
|
|
IN PACL pDacl,
|
|
IN PCWSTR pszUserSID
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
|
|
static const struct
|
|
{
|
|
PSID psid;
|
|
TRUSTEE_TYPE type;
|
|
} rgTrustees[] =
|
|
{
|
|
{(PSID)&g_WorldSid, TRUSTEE_IS_WELL_KNOWN_GROUP},
|
|
{(PSID)&g_AdminsSid, TRUSTEE_IS_ALIAS},
|
|
{(PSID)&g_PowerUSid, TRUSTEE_IS_ALIAS},
|
|
{(PSID)&g_UsersSid, TRUSTEE_IS_ALIAS},
|
|
};
|
|
|
|
if (pDacl)
|
|
{
|
|
PSID psidUser = NULL;
|
|
TRUSTEE tTemp;
|
|
ACCESS_MASK dwUserMask = 0;
|
|
|
|
// The masks are all initialized to zero. If one or more of the
|
|
// calls to GetEffectiveRightsFromAcl fails, then it will look like
|
|
// that trustee has no rights and the UI will adjust accordingly.
|
|
// There is nothing we could do better by trapping errors from
|
|
// GetEffectiveRightsFromAcl, so don't bother.
|
|
|
|
if (ConvertStringSidToSid(pszUserSID, &psidUser))
|
|
{
|
|
BuildTrusteeWithSid(&tTemp, psidUser);
|
|
tTemp.TrusteeType = TRUSTEE_IS_USER;
|
|
GetEffectiveRightsFromAcl(pDacl, &tTemp, &dwUserMask);
|
|
LocalFree(psidUser);
|
|
}
|
|
|
|
//
|
|
// These tests may need some fine tuning
|
|
//
|
|
if ((dwUserMask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)
|
|
{
|
|
ACCESS_MASK dwOtherMask = 0;
|
|
UINT i;
|
|
|
|
for (i = 0; i < ARRAYLEN(rgTrustees); i++)
|
|
{
|
|
ACCESS_MASK dwTempMask = 0;
|
|
BuildTrusteeWithSid(&tTemp, rgTrustees[i].psid);
|
|
tTemp.TrusteeType = rgTrustees[i].type;
|
|
GetEffectiveRightsFromAcl(pDacl, &tTemp, &dwTempMask);
|
|
dwOtherMask |= dwTempMask;
|
|
}
|
|
|
|
if ((dwOtherMask & ~(READ_CONTROL | SYNCHRONIZE)) == 0)
|
|
{
|
|
// Looks like the folder is private for this user
|
|
bResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
BOOL _IsVolumeNTFS(PCWSTR pszFolder)
|
|
{
|
|
WCHAR szVolume[MAX_PATH];
|
|
DWORD dwFSFlags = 0;
|
|
|
|
return (GetVolumePathNameW(pszFolder, szVolume, ARRAYLEN(szVolume)) &&
|
|
GetVolumeInformationW(szVolume, NULL, 0, NULL, NULL, &dwFSFlags, NULL, 0) &&
|
|
0 != (FS_PERSISTENT_ACLS & dwFSFlags));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: IsFolderPrivateForUser, exported
|
|
//
|
|
// Synopsis: Check the DACL on a folder
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
STDAPI_(BOOL)
|
|
IsFolderPrivateForUser(
|
|
IN PCWSTR pszFolderPath,
|
|
IN PCWSTR pszUserSID,
|
|
OUT PDWORD pdwPrivateType,
|
|
OUT PWSTR* ppszInheritanceSource
|
|
)
|
|
{
|
|
if (NULL != ppszInheritanceSource)
|
|
{
|
|
*ppszInheritanceSource = NULL;
|
|
}
|
|
|
|
if (NULL == pdwPrivateType)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwPrivateType = IFPFU_NOT_PRIVATE;
|
|
|
|
if (NULL == pszFolderPath || NULL == pszUserSID)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// One would think that we could call GetNamedSecurityInfo without first
|
|
// checking for NTFS, and just let it fail on FAT volumes. However,
|
|
// GetNamedSecurityInfo succeeds on FAT and returns a valid security
|
|
// descriptor with a NULL DACL. This is actually correct in that
|
|
// a NULL DACL means no security, which is true on FAT.
|
|
//
|
|
// We then have the problem of trying to differentiate between a NULL
|
|
// DACL on an NTFS volume (it can happen), and a NULL DACL from a FAT
|
|
// volume. Let's just check for NTFS first.
|
|
|
|
if (!_IsVolumeNTFS(pszFolderPath))
|
|
{
|
|
// No ACLs, so we're done
|
|
*pdwPrivateType = IFPFU_NOT_NTFS;
|
|
return TRUE;
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
PACL pDacl = NULL;
|
|
DWORD dwErr = GetNamedSecurityInfoW(
|
|
(PWSTR)pszFolderPath,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
&pDacl,
|
|
NULL,
|
|
&pSD);
|
|
|
|
if (ERROR_SUCCESS == dwErr)
|
|
{
|
|
appAssert(NULL != pSD);
|
|
|
|
if (_IsDaclPrivateForUser(pDacl, pszUserSID))
|
|
{
|
|
SECURITY_DESCRIPTOR_CONTROL wControl = 0;
|
|
DWORD dwRevision;
|
|
|
|
*pdwPrivateType = IFPFU_PRIVATE;
|
|
|
|
// Check the control bits to see if we are inheriting
|
|
GetSecurityDescriptorControl(pSD, &wControl, &dwRevision);
|
|
|
|
if ((wControl & SE_DACL_PROTECTED) == 0)
|
|
{
|
|
// The DACL is not protected; assume the rights are inherited.
|
|
//
|
|
// When making a folder private, we always protect the DACL
|
|
// on the folder and reset child ACLs, so the assumption
|
|
// about inheriting is correct when using the simple UI.
|
|
//
|
|
// If someone uses the old Security page or cacls.exe to
|
|
// modify ACLs, then the safest thing is to disable the
|
|
// page and only let them reset everything from higher up.
|
|
// Well, it turns out that that's exactly what happens when
|
|
// we set IFPFU_PRIVATE_INHERITED.
|
|
|
|
*pdwPrivateType |= IFPFU_PRIVATE_INHERITED;
|
|
|
|
// Does the caller want the ancestor that made this
|
|
// subtree private?
|
|
if (NULL != ppszInheritanceSource)
|
|
{
|
|
PINHERITED_FROMW pInheritedFrom = (PINHERITED_FROMW)LocalAlloc(LPTR, sizeof(INHERITED_FROMW)*pDacl->AceCount);
|
|
|
|
if (pInheritedFrom != NULL)
|
|
{
|
|
dwErr = GetInheritanceSourceW(
|
|
(PWSTR)pszFolderPath,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
TRUE,
|
|
NULL,
|
|
0,
|
|
pDacl,
|
|
NULL,
|
|
&ShareMap,
|
|
pInheritedFrom);
|
|
|
|
if (ERROR_SUCCESS == dwErr)
|
|
{
|
|
PACE_HEADER pAceHeader;
|
|
UINT i;
|
|
|
|
PSID psidUser = NULL;
|
|
if (ConvertStringSidToSid(pszUserSID, &psidUser))
|
|
{
|
|
// Enumerate the ACEs looking for the ACE that grants
|
|
// Full Control to the current user
|
|
|
|
for (i = 0, pAceHeader = (PACE_HEADER)FirstAce(pDacl);
|
|
i < pDacl->AceCount;
|
|
i++, pAceHeader = (PACE_HEADER)NextAce(pAceHeader))
|
|
{
|
|
PKNOWN_ACE pAce = (PKNOWN_ACE)pAceHeader;
|
|
if (IsKnownAceType(pAceHeader) &&
|
|
EqualSid(psidUser, &pAce->SidStart) &&
|
|
(pAce->Mask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)
|
|
{
|
|
// Found it. But we only want the inheritance
|
|
// source if it's not explicit.
|
|
if (pInheritedFrom[i].GenerationGap > 0)
|
|
{
|
|
int cch = lstrlenW(pInheritedFrom[i].AncestorName) + 1;
|
|
*ppszInheritanceSource = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*cch);
|
|
if (NULL != *ppszInheritanceSource)
|
|
{
|
|
lstrcpynW(*ppszInheritanceSource, pInheritedFrom[i].AncestorName, cch);
|
|
}
|
|
}
|
|
|
|
// Stop looking
|
|
break;
|
|
}
|
|
}
|
|
LocalFree(psidUser);
|
|
}
|
|
}
|
|
|
|
LocalFree(pInheritedFrom);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pSD);
|
|
}
|
|
else
|
|
{
|
|
// GetNamedSecurityInfo failed. The path may not exist, or it may
|
|
// be FAT. In any case, assume permissions are not available.
|
|
*pdwPrivateType = IFPFU_NOT_NTFS;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: _MakeSecurityDescriptorForUser
|
|
//
|
|
// Synopsis: Insert a SID string into a string SD and convert it
|
|
// to a binary SD.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
_MakeSecurityDescriptorForUser(PCWSTR pszSDFormat, PCWSTR pszUserSID, PSECURITY_DESCRIPTOR *ppSD, PACL *ppDacl)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
LPWSTR pszSD = NULL;
|
|
|
|
*ppDacl = NULL;
|
|
|
|
DWORD dwResult = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
pszSDFormat,
|
|
0,
|
|
0,
|
|
(LPWSTR)&pszSD,
|
|
1,
|
|
(va_list*)&pszUserSID);
|
|
if (dwResult)
|
|
{
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptorW(pszSD, SDDL_REVISION_1, &pSD, NULL);
|
|
if (bResult)
|
|
{
|
|
*ppSD = pSD;
|
|
|
|
if (ppDacl)
|
|
{
|
|
BOOL bPresent;
|
|
BOOL bDefault;
|
|
|
|
GetSecurityDescriptorDacl(pSD, &bPresent, ppDacl, &bDefault);
|
|
}
|
|
}
|
|
LocalFree(pszSD);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
int _ShowDeleteShareWarning(HWND hwndParent)
|
|
{
|
|
WCHAR szMsg[MAX_PATH];
|
|
WCHAR szCaption[50];
|
|
LoadStringW(g_hInstance, IDS_PRIVATE_CONFIRM_DELSHARE, szMsg, ARRAYLEN(szMsg));
|
|
LoadStringW(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
|
|
|
|
return MessageBoxW(hwndParent, szMsg, szCaption, MB_YESNO | MB_ICONWARNING);
|
|
}
|
|
|
|
|
|
BOOL _IsRootACLSecure(PACL pDacl)
|
|
{
|
|
TRUSTEE tTemp;
|
|
ACCESS_MASK dwMask = 0;
|
|
BuildTrusteeWithSid(&tTemp, (PSID)&g_WorldSid);
|
|
tTemp.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
GetEffectiveRightsFromAcl(pDacl, &tTemp, &dwMask);
|
|
return !(dwMask & (WRITE_DAC | WRITE_OWNER));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: SetFolderPermissionsForSharing, exported
|
|
//
|
|
// Parameters:
|
|
// pszFolderPath - Folder to adjust permissions on
|
|
// pszUserSID - User SID (NULL for current user)
|
|
// dwLevel - 0 = "private". Only the user and local system get access.
|
|
// 1 = "not shared". Remove explicit Everyone ACE.
|
|
// 2 = "shared read-only". Grant explicit RX to Everyone.
|
|
// 3 = "shared read/write". Grant explicit RWXD to Everyone.
|
|
// hwndParent - MessageBox parent. Set to NULL to prevent warnings.
|
|
//
|
|
// Synopsis: Set the DACL on a folder according to the sharing level
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#define SIZEOF_EVERYONE_ACE (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + sizeof(g_WorldSid))
|
|
|
|
static const struct
|
|
{
|
|
DWORD AceFlags;
|
|
DWORD AccessMask;
|
|
} c_rgEveryoneAces[] =
|
|
{
|
|
{0, 0},
|
|
{CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE},
|
|
{CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE},
|
|
};
|
|
|
|
//
|
|
// Hash algorithm borrowed from shell32\hash.c
|
|
//
|
|
ULONG _HashString(PCWSTR psz)
|
|
{
|
|
UINT hash = 314159269;
|
|
for(; *psz; psz++)
|
|
{
|
|
hash ^= (hash<<11) + (hash<<5) + (hash>>2) + (UINT)*psz;
|
|
}
|
|
return (hash & 0x7FFFFFFF);
|
|
}
|
|
|
|
STDAPI_(BOOL)
|
|
SetFolderPermissionsForSharing(
|
|
IN PCWSTR pszFolderPath,
|
|
IN PCWSTR pszUserSID,
|
|
IN DWORD dwLevel,
|
|
IN HWND hwndParent
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
DWORD dwFolderFlags;
|
|
BOOL bSpecialFolderRoot = FALSE;
|
|
PCWSTR pszDefaultSD = NULL;
|
|
LPWSTR pszUserSIDToFree = NULL;
|
|
|
|
appDebugOut((DEB_ITRACE, "SetFolderPermissionsForSharing\n"));
|
|
|
|
if (dwLevel > 3)
|
|
{
|
|
appDebugOut((DEB_ITRACE, "Invalid sharing level\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
dwFolderFlags = _CheckFolderType(pszFolderPath, pszUserSID, &bSpecialFolderRoot, &pszDefaultSD);
|
|
|
|
if (0 == (dwFolderFlags & (CFT_FLAG_SHARING_ALLOWED | CFT_FLAG_ROOT_FOLDER)))
|
|
{
|
|
appDebugOut((DEB_ITRACE, "Sharing not allowed on this folder\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == (dwFolderFlags & CFT_FLAG_CAN_MAKE_PRIVATE) && 0 == dwLevel)
|
|
{
|
|
appDebugOut((DEB_ITRACE, "Can't make this folder private\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// One would think that we could call GetNamedSecurityInfo without first
|
|
// checking for NTFS, and just let it fail on FAT volumes. However,
|
|
// GetNamedSecurityInfo succeeds on FAT and returns a valid security
|
|
// descriptor with a NULL DACL. This is actually correct in that
|
|
// a NULL DACL means no security, which is true on FAT.
|
|
//
|
|
// We then have the problem of trying to differentiate between a NULL
|
|
// DACL on an NTFS volume (it can happen), and a NULL DACL from a FAT
|
|
// volume. Let's just check for NTFS first.
|
|
|
|
if (!_IsVolumeNTFS(pszFolderPath))
|
|
{
|
|
// No ACLs, so we're done
|
|
return (0 != dwLevel);
|
|
}
|
|
|
|
// If we are making the folder private, first check whether any child
|
|
// folders are shared on the net. If so, warn that we are going to nuke them.
|
|
|
|
CShareInfo* pWarnList = NULL;
|
|
if (0 == dwLevel &&
|
|
SUCCEEDED(g_ShareCache.ConstructParentWarnList(pszFolderPath, &pWarnList)) &&
|
|
NULL != pWarnList &&
|
|
NULL != hwndParent)
|
|
{
|
|
if (IDNO == _ShowDeleteShareWarning(hwndParent))
|
|
{
|
|
DeleteShareInfoList(pWarnList, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
// JonN 4/04/01 328512
|
|
// Explorer Sharing Tab (NTSHRUI) should popup warning on deleting
|
|
// SYSVOL,NETLOGON and C$, D$... shares
|
|
for (CShareInfo* p = (CShareInfo*)pWarnList->Next();
|
|
p != pWarnList;
|
|
)
|
|
{
|
|
CShareInfo* pNext = (CShareInfo*)p->Next();
|
|
|
|
DWORD id = ConfirmStopShare( hwndParent, MB_YESNO, p->GetNetname() );
|
|
if ( IDYES != id )
|
|
{
|
|
DeleteShareInfoList(pWarnList, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
p = pNext;
|
|
}
|
|
|
|
}
|
|
// No more early returns after this point (have to free pWarnList)
|
|
|
|
if (NULL == pszUserSID || L'\0' == pszUserSID[0])
|
|
{
|
|
_GetUserSid(&pszUserSIDToFree);
|
|
pszUserSID = pszUserSIDToFree;
|
|
}
|
|
|
|
// Use a mutex to prevent multiple threads from setting permissions on the
|
|
// same folder at the same time. The mutex name cannot contain '\' so hash
|
|
// the path to obtain a name unique to this folder.
|
|
|
|
WCHAR szMutex[30] = L"";
|
|
wnsprintfW(szMutex, ARRAYLEN(szMutex), L"share perms %x", _HashString(pszFolderPath));
|
|
|
|
HANDLE hMutex = CreateMutex(NULL, FALSE, szMutex);
|
|
if (NULL != hMutex)
|
|
{
|
|
// Wait at most 30 seconds for any abandoned thread (or if someone is
|
|
// squatting on the mutex), then blindly continue. This wait normally
|
|
// completes quickly and prevents 2 threads from trying to ACL the
|
|
// folder at the same time.
|
|
WaitForSingleObject(hMutex, 30*1000);
|
|
|
|
if (pszUserSID)
|
|
{
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
PACL pDacl = NULL;
|
|
DWORD dwErr = GetNamedSecurityInfoW(
|
|
(PWSTR)pszFolderPath,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
&pDacl,
|
|
NULL,
|
|
&pSD);
|
|
|
|
if (ERROR_SUCCESS == dwErr)
|
|
{
|
|
PACL pDaclToFree = NULL;
|
|
|
|
appAssert(NULL != pSD);
|
|
|
|
if (dwFolderFlags & CFT_FLAG_CAN_MAKE_PRIVATE)
|
|
{
|
|
if (_IsDaclPrivateForUser(pDacl, pszUserSID))
|
|
{
|
|
// _IsDaclPrivateForUser returns FALSE if pDacl is NULL
|
|
appAssert(NULL != pDacl);
|
|
|
|
if (0 == dwLevel)
|
|
{
|
|
// Already private, nothing to do
|
|
bResult = TRUE;
|
|
pDacl = NULL;
|
|
}
|
|
else // making public
|
|
{
|
|
if (bSpecialFolderRoot)
|
|
{
|
|
// Taking a special folder that was private, and making
|
|
// it public. First need to reset the DACL to default.
|
|
// (Special folders often have protected DACLs.)
|
|
if (pszDefaultSD)
|
|
{
|
|
LocalFree(pSD);
|
|
pSD = NULL;
|
|
pDacl = NULL;
|
|
|
|
// If this fails, pDacl will be NULL and we will fail below
|
|
_MakeSecurityDescriptorForUser(pszDefaultSD, pszUserSID, &pSD, &pDacl);
|
|
|
|
appDebugOut((DEB_ITRACE, "Using default security descriptor\n"));
|
|
}
|
|
}
|
|
else // not root of special folder
|
|
{
|
|
SECURITY_DESCRIPTOR_CONTROL wControl = 0;
|
|
DWORD dwRevision;
|
|
|
|
// Check the control bits to see if we are inheriting
|
|
GetSecurityDescriptorControl(pSD, &wControl, &dwRevision);
|
|
|
|
if ((wControl & SE_DACL_PROTECTED) == 0)
|
|
{
|
|
// Inheriting from parent, assume the parent folder
|
|
// is private. Can't make a subfolder public.
|
|
pDacl = NULL;
|
|
|
|
appDebugOut((DEB_ITRACE, "Can't make private subfolder public\n"));
|
|
}
|
|
else
|
|
{
|
|
// This folder is private and we're making it public.
|
|
// Eliminate all explicit ACEs and reset the protected
|
|
// bit so it inherits normal permissions from its parent.
|
|
|
|
pDacl->AceCount = 0;
|
|
SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // Not currently private
|
|
{
|
|
if (0 == dwLevel)
|
|
{
|
|
// Reset the DACL to private before continuing below
|
|
LocalFree(pSD);
|
|
pSD = NULL;
|
|
pDacl = NULL;
|
|
|
|
// If this fails, pDacl will be NULL and we will fail below
|
|
_MakeSecurityDescriptorForUser(c_szPrivateFolderSD, pszUserSID, &pSD, &pDacl);
|
|
}
|
|
}
|
|
}
|
|
else // can't make private
|
|
{
|
|
// We check for this above
|
|
appAssert(0 != dwLevel);
|
|
}
|
|
|
|
if ((dwFolderFlags & CFT_FLAG_ROOT_FOLDER) && NULL != pDacl)
|
|
{
|
|
// Currently can't make root folders private
|
|
appAssert(0 != dwLevel);
|
|
|
|
//
|
|
// NTRAID#NTBUG9-378617-2001/05/04-jeffreys
|
|
//
|
|
// Root ACLs tend to have an explicit Everyone ACE, which
|
|
// screws us up in some cases. Easiest thing is to start
|
|
// with a new ACL and don't touch the Everyone entry below.
|
|
//
|
|
|
|
BOOL bRootIsSecure = _IsRootACLSecure(pDacl);
|
|
|
|
LocalFree(pSD);
|
|
pSD = NULL;
|
|
pDacl = NULL;
|
|
|
|
// If this fails, pDacl will be NULL and we will fail below
|
|
_MakeSecurityDescriptorForUser(bRootIsSecure ? c_szRootSDSecure : c_szRootSDUnsecure, pszUserSID, &pSD, &pDacl);
|
|
|
|
appDebugOut((DEB_ITRACE, "Using default security descriptor\n"));
|
|
}
|
|
|
|
//
|
|
// If we're making the folder public, adjust the existing ACL
|
|
//
|
|
if (NULL != pDacl && 0 != dwLevel)
|
|
{
|
|
PKNOWN_ACE pAce;
|
|
int iEntry;
|
|
USHORT cAces = 0;
|
|
ULONG cbExplicitAces = 0;
|
|
|
|
// Adjust the level to use as an index into c_rgEveryoneAces
|
|
DWORD dwPublicLevel = dwLevel - 1;
|
|
appAssert(dwPublicLevel < ARRAYLEN(c_rgEveryoneAces));
|
|
|
|
for (iEntry = 0, pAce = (PKNOWN_ACE)FirstAce(pDacl);
|
|
iEntry < pDacl->AceCount;
|
|
iEntry++, pAce = (PKNOWN_ACE)NextAce(pAce))
|
|
{
|
|
// Assuming the ACL is canonical, we can stop as soon as we find
|
|
// an inherited ACE, since the rest will all be inherited and we
|
|
// can't modify those.
|
|
if (AceInherited(&pAce->Header))
|
|
break;
|
|
|
|
cAces++;
|
|
cbExplicitAces += pAce->Header.AceSize;
|
|
|
|
if (!(dwFolderFlags & CFT_FLAG_ROOT_FOLDER) &&
|
|
IsKnownAceType(pAce) &&
|
|
EqualSid((PSID)&pAce->SidStart, (PSID)&g_WorldSid))
|
|
{
|
|
pAce->Header.AceFlags = (UCHAR)c_rgEveryoneAces[dwPublicLevel].AceFlags;
|
|
pAce->Mask = c_rgEveryoneAces[dwPublicLevel].AccessMask;
|
|
|
|
// We don't need to add another Everyone ACE below
|
|
dwPublicLevel = 0;
|
|
}
|
|
}
|
|
|
|
// Trim off inherited ACEs. We don't need to include them when
|
|
// saving the new ACL, and this generally leaves enough space
|
|
// in the ACL to add an Everyone ACE if we need to.
|
|
pDacl->AceCount = cAces;
|
|
|
|
if (0 != dwPublicLevel)
|
|
{
|
|
// Need to add an explicit entry for Everyone.
|
|
|
|
ULONG cbAclSize = sizeof(ACL) + SIZEOF_EVERYONE_ACE + cbExplicitAces;
|
|
|
|
if (cbAclSize > (ULONG)pDacl->AclSize)
|
|
{
|
|
// No room in the existing ACL. Allocate a new
|
|
// ACL and copy existing entries (if any)
|
|
pDaclToFree = (PACL)LocalAlloc(LPTR, cbAclSize);
|
|
if (NULL != pDaclToFree)
|
|
{
|
|
CopyMemory(pDaclToFree, pDacl, pDacl->AclSize);
|
|
pDaclToFree->AclSize = (USHORT)cbAclSize;
|
|
pDacl = pDaclToFree;
|
|
}
|
|
else
|
|
{
|
|
// Fail
|
|
pDacl = NULL;
|
|
appDebugOut((DEB_ITRACE, "Unable to alloc buffer for new ACL\n"));
|
|
}
|
|
}
|
|
|
|
if (NULL != pDacl)
|
|
{
|
|
appAssert(cbAclSize <= (ULONG)pDacl->AclSize);
|
|
|
|
if (!AddAccessAllowedAceEx(pDacl,
|
|
ACL_REVISION2,
|
|
c_rgEveryoneAces[dwPublicLevel].AceFlags,
|
|
c_rgEveryoneAces[dwPublicLevel].AccessMask,
|
|
(PSID)&g_WorldSid))
|
|
{
|
|
// Fail
|
|
pDacl = NULL;
|
|
appDebugOut((DEB_ITRACE, "Unable to add Everyone ACE\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the new DACL on the folder
|
|
//
|
|
if (NULL != pDacl)
|
|
{
|
|
SECURITY_INFORMATION si;
|
|
SECURITY_DESCRIPTOR_CONTROL wControl = 0;
|
|
DWORD dwRevision;
|
|
|
|
GetSecurityDescriptorControl(pSD, &wControl, &dwRevision);
|
|
|
|
if (SE_DACL_PROTECTED & wControl)
|
|
{
|
|
// The security descriptor specifies SE_DACL_PROTECTED
|
|
si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
|
|
}
|
|
else
|
|
{
|
|
// Prevent the system from automagically protecting the DACL
|
|
si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
if (0 == dwLevel)
|
|
{
|
|
// To make the folder private, we have to make sure we blow
|
|
// away any explicit permissions on children, so use
|
|
// TreeResetNamedSecurityInfo with KeepExplicit = FALSE.
|
|
|
|
// TreeResetNamedSecurityInfo has a callback mechanism, but
|
|
// we currently don't use it. Note that the paths passed to
|
|
// the callback look like
|
|
// "\Device\HarddiskVolume1\dir\name"
|
|
|
|
appDebugOut((DEB_ITRACE, "Making folder private; resetting child ACLs\n"));
|
|
appAssert(si == (DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION));
|
|
|
|
dwErr = TreeResetNamedSecurityInfoW(
|
|
(PWSTR)pszFolderPath,
|
|
SE_FILE_OBJECT,
|
|
si,
|
|
NULL,
|
|
NULL,
|
|
pDacl,
|
|
NULL,
|
|
FALSE, // KeepExplicit (perms on children)
|
|
NULL,
|
|
ProgressInvokeNever,
|
|
NULL
|
|
);
|
|
|
|
if (ERROR_SUCCESS == dwErr && NULL != pWarnList)
|
|
{
|
|
// Nuke child shares
|
|
for (CShareInfo* p = (CShareInfo*)pWarnList->Next();
|
|
p != pWarnList;
|
|
)
|
|
{
|
|
CShareInfo* pNext = (CShareInfo*)p->Next();
|
|
|
|
if (p->GetFlag() != SHARE_FLAG_ADDED)
|
|
{
|
|
p->SetDirtyFlag(SHARE_FLAG_REMOVE);
|
|
p->Commit(NULL);
|
|
SHChangeNotify(SHCNE_NETSHARE, SHCNF_PATH, p->GetPath(), NULL);
|
|
}
|
|
|
|
// get rid of p
|
|
p->Unlink();
|
|
delete p;
|
|
|
|
p = pNext;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// To make the folder public, we grant access at this level
|
|
// without blowing away child permissions, including DACL
|
|
// protection. This means that a private subfolder will still
|
|
// be private. Use SetNamedSecurityInfo for these, since
|
|
// TreeResetNamedSecurityInfo always removes SE_DACL_PROTECTED
|
|
// from children.
|
|
|
|
dwErr = SetNamedSecurityInfoW(
|
|
(PWSTR)pszFolderPath,
|
|
SE_FILE_OBJECT,
|
|
si,
|
|
NULL,
|
|
NULL,
|
|
pDacl,
|
|
NULL);
|
|
}
|
|
|
|
if (ERROR_SUCCESS == dwErr)
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
}
|
|
|
|
LocalFree(pDaclToFree);
|
|
LocalFree(pSD);
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(hMutex);
|
|
CloseHandle(hMutex);
|
|
}
|
|
|
|
LocalFree(pszUserSIDToFree);
|
|
|
|
if (NULL != pWarnList)
|
|
{
|
|
DeleteShareInfoList(pWarnList, TRUE);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//
|
|
// Description:
|
|
// Dialog proc for the enabling sharing warning dialog.
|
|
//
|
|
INT_PTR
|
|
WarningDlgProc(
|
|
IN HWND hWnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM /*lParam*/
|
|
)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
//
|
|
// Load warning icon from USER32.
|
|
//
|
|
|
|
HICON hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_WARNING));
|
|
if (hIcon)
|
|
{
|
|
SendDlgItemMessage(hWnd, IDC_ICON_INFO, STM_SETICON, (WPARAM )hIcon, 0L);
|
|
}
|
|
|
|
//
|
|
// Set default radio item.
|
|
//
|
|
|
|
SendDlgItemMessage(hWnd, IDC_RB_RUN_THE_WIZARD, BM_SETCHECK, BST_CHECKED, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
if ( BN_CLICKED == HIWORD(wParam) )
|
|
{
|
|
UINT iRet = (UINT) SendDlgItemMessage(hWnd, IDC_RB_RUN_THE_WIZARD, BM_GETCHECK, 0, 0 );
|
|
if ( BST_CHECKED == iRet )
|
|
{
|
|
EndDialog(hWnd, IDC_RB_RUN_THE_WIZARD );
|
|
}
|
|
else
|
|
{
|
|
EndDialog(hWnd, IDC_RB_ENABLE_FILE_SHARING );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
if ( BN_CLICKED == HIWORD(wParam) )
|
|
{
|
|
EndDialog(hWnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|