Leaked source code of windows server 2003
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

//+-------------------------------------------------------------------------
//
// 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;
}