|
|
#include "aclpriv.h"
//Function for checking the Custom checkbox
VOID CheckCustom(HWND hwndList, WORD wColumn, //Allow or Deny column
DWORD dwState ) { //Custom is always the last checkbox
UINT cRights = (UINT)SendMessage(hwndList, CLM_GETITEMCOUNT, 0, 0);
//Don't check if the column is already checked. This fucntion is first
//called for explicit and then for inherited aces. Effect is if explict aces are there
//checkbox is enabled.
DWORD dwStateCurrent = (DWORD)SendMessage(hwndList, CLM_GETSTATE, MAKELONG((WORD)(cRights -1), wColumn), 0);
if (dwStateCurrent & CLST_CHECKED) return; //
//Custom Checkbox is always disabled
//
SendMessage(hwndList, CLM_SETSTATE, MAKELONG((WORD)(cRights -1), wColumn), dwState|CLST_DISABLED); } VOID ClearCustom(HWND hwndList, WORD wColumn) //Allow or Deny column
{ //Custom is always the last checkbox
UINT cRights = (UINT)SendMessage(hwndList, CLM_GETITEMCOUNT, 0, 0); //
//Custom Checkbox is always disabled
//
SendMessage(hwndList, CLM_SETSTATE, MAKELONG((WORD)(cRights -1), wColumn), CLST_DISABLED); }
//
// CPrincipal implementation
//
CPrincipal::~CPrincipal() { if (NULL != m_pSID) LocalFree(m_pSID);
LocalFreeString(&m_pszName); LocalFreeString(&m_pszDisplayName); if( m_hAdditionalAllow != NULL ) DSA_Destroy( m_hAdditionalAllow ); if( m_hAdditionalDeny != NULL ) DSA_Destroy( m_hAdditionalDeny ); }
BOOL CPrincipal::SetPrincipal(PSID pSID, SID_NAME_USE sidType, LPCTSTR pszName, LPCTSTR pszLogonName) { DWORD dwLength;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::SetPrincipal"); TraceAssert(pSID != NULL); TraceAssert(IsValidSid(pSID));
if (NULL != m_pSID) LocalFree(m_pSID);
m_pSID = LocalAllocSid(pSID);
SetSidType(sidType); SetName(pszName, pszLogonName);
TraceLeaveValue(NULL != m_pSID); }
BOOL CPrincipal::SetName(LPCTSTR pszName, LPCTSTR pszLogonName) { LocalFreeString(&m_pszName); m_bHaveRealName = FALSE;
if (BuildUserDisplayName(&m_pszName, pszName, pszLogonName)) m_bHaveRealName = TRUE; else ConvertSidToStringSid(m_pSID, &m_pszName); if(pszName) { LocalFreeString(&m_pszDisplayName); LocalAllocString(&m_pszDisplayName, pszName); }
return (NULL != m_pszName); }
CPermissionSet* CPrincipal::GetPermSet(DWORD dwType, BOOL bInherited) { CPermissionSet *pPermSet = NULL;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::GetPermSet");
switch (dwType) { case ACCESS_DENIED_ACE_TYPE: if (bInherited) pPermSet = &m_permInheritedDeny; else pPermSet = &m_permDeny; break;
case ACCESS_ALLOWED_ACE_TYPE: if (bInherited) pPermSet = &m_permInheritedAllow; else pPermSet = &m_permAllow; break;
case ACCESS_ALLOWED_COMPOUND_ACE_TYPE: // We don't handle compound ACEs
TraceMsg("Ignoring ACCESS_ALLOWED_COMPOUND_ACE"); break;
#ifdef DEBUG
case SYSTEM_AUDIT_ACE_TYPE: case SYSTEM_ALARM_ACE_TYPE: case SYSTEM_AUDIT_OBJECT_ACE_TYPE: case SYSTEM_ALARM_OBJECT_ACE_TYPE: default: // We only process the various ACCESS_ALLOWED_* and ACCESS_DENIED_*
// ACE types, except for ACCESS_ALLOWED_COMPOUND_ACE_TYPE, and these
// are all accounted for above. Something is very wrong if we get
// an audit/alarm ACE or some unknown/future ACE type.
TraceAssert(FALSE); break; #endif
}
TraceLeaveValue(pPermSet); }
BOOL CPrincipal::AddNormalAce(DWORD dwType, DWORD dwFlags, ACCESS_MASK mask, const GUID *pObjectType) { BOOL fResult = FALSE;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::AddNormalAce");
CPermissionSet *pPermSet = GetPermSet(dwType, (BOOL)(dwFlags & INHERITED_ACE)); if (pPermSet) fResult = pPermSet->AddAce(pObjectType, mask, dwFlags);
TraceLeaveValue(fResult); }
BOOL CPrincipal::AddAdvancedAce(DWORD dwType, PACE_HEADER pAce) { BOOL fResult = FALSE;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::AddAdvancedAce");
CPermissionSet *pPermSet = GetPermSet(dwType, AceInherited(pAce)); if (pPermSet) fResult = pPermSet->AddAdvancedAce(pAce);
TraceLeaveValue(fResult); }
BOOL CPrincipal::AddAce(PACE_HEADER pAce) { TraceEnter(TRACE_PRINCIPAL, "CPrincipal::AddAce"); TraceAssert(pAce != NULL);
BOOL fResult = FALSE; const GUID *pObjectType = NULL; UCHAR AceType = pAce->AceType; UCHAR AceFlags = pAce->AceFlags; ACCESS_MASK AccessMask = ((PKNOWN_ACE)pAce)->Mask; ULONG ulObjectFlags = 0;
// Get the object type GUID from object ACEs
if (IsObjectAceType(pAce)) { AceType -= (ACCESS_ALLOWED_OBJECT_ACE_TYPE - ACCESS_ALLOWED_ACE_TYPE); ulObjectFlags = ((PKNOWN_OBJECT_ACE)pAce)->Flags;
if (m_pPage->m_wDaclRevision < ACL_REVISION_DS) m_pPage->m_wDaclRevision = ACL_REVISION_DS;
pObjectType = RtlObjectAceObjectType(pAce); }
if (!pObjectType) pObjectType = &GUID_NULL;
// Map any generic bits to standard & specific bits.
m_pPage->m_psi->MapGeneric(pObjectType, &AceFlags, &AccessMask);
// Can't have INHERIT_ONLY_ACE without either CONTAINER_INHERIT_ACE or
// OBJECT_INHERIT_ACE, so if we find one of these, skip it.
if ((AceFlags & (INHERIT_ONLY_ACE | ACE_INHERIT_ALL)) != INHERIT_ONLY_ACE) { //
//ACE_INHERITED_OBJECT_TYPE_PRESENT is invalid without
//either of container inherit or object inherit flags.
//NTRAID#NTBUG9-287737-2001/01/23-hiteshr
//
if (ulObjectFlags & ACE_INHERITED_OBJECT_TYPE_PRESENT && AceFlags & ACE_INHERIT_ALL) { // If we have an inherit object type without INHERIT_ONLY_ACE,
// and the inherit object type matches the current object,
// then it applies to this object. Simulate this (per the
// ACL inheritance spec) with 2 ACEs: one with no inheritance
// at all, and one with the inherit type + INHERIT_ONLY_ACE.
// Does it apply to this object?
if ((m_pPage->m_siObjectInfo.dwFlags & SI_OBJECT_GUID) && !(AceFlags & INHERIT_ONLY_ACE) && IsSameGUID(&m_pPage->m_siObjectInfo.guidObjectType, RtlObjectAceInheritedObjectType(pAce))) { // Mask out all flags except INHERITED_ACE and add it
AddNormalAce(AceType, (AceFlags & INHERITED_ACE), AccessMask, pObjectType);
// Turn on INHERIT_ONLY_ACE before adding the "advanced" ACE.
pAce->AceFlags |= INHERIT_ONLY_ACE; }
// The ACE does not apply directly to this object
fResult = AddAdvancedAce(AceType, pAce); } else { fResult = AddNormalAce(AceType, AceFlags, AccessMask, pObjectType); } }
TraceLeaveValue(fResult); }
ULONG CPrincipal::GetAclLength(DWORD dwFlags) { // Return an estimate of the buffer size needed to hold the
// requested ACEs. The size of the ACL header is NOT INCLUDED.
// The following flags are always assumed:
// ACL_DENY | ACL_ALLOW | ACL_NONOBJECT | ACL_OBJECT
ULONG nAclLength = 0; ULONG nSidLength;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::GetAclLength"); TraceAssert(NULL != m_pSID);
if (NULL == m_pSID) TraceLeaveValue(0);
nSidLength = GetLengthSid(m_pSID);
if (dwFlags & ACL_NONINHERITED) { nAclLength += m_permDeny.GetAclLength(nSidLength); nAclLength += m_permAllow.GetAclLength(nSidLength); }
if (dwFlags & ACL_INHERITED) { nAclLength += m_permInheritedDeny.GetAclLength(nSidLength); nAclLength += m_permInheritedAllow.GetAclLength(nSidLength); }
TraceLeaveValue(nAclLength); }
BOOL CPrincipal::AppendToAcl(PACL pAcl, DWORD dwFlags, PACE_HEADER *ppAcePos) // position to copy first ACE
{ PACE_HEADER pAceT;
TraceEnter(TRACE_PRINCIPAL, "CPrincipal::AppendToAcl"); TraceAssert(pAcl != NULL && IsValidAcl(pAcl)); TraceAssert(ppAcePos != NULL); TraceAssert(NULL != m_pSID);
if (NULL == m_pSID) TraceLeaveValue(FALSE);
pAceT = *ppAcePos;
// Build the ACL in the following order:
// Deny
// Allow
// Inherited Deny
// Inherited Allow
if (dwFlags & ACL_NONINHERITED) { if (dwFlags & ACL_DENY) m_permDeny.AppendToAcl(pAcl, ppAcePos, m_pSID, FALSE, dwFlags);
if (dwFlags & ACL_ALLOW) m_permAllow.AppendToAcl(pAcl, ppAcePos, m_pSID, TRUE, dwFlags); }
if (dwFlags & ACL_INHERITED) { if (dwFlags & ACL_DENY) m_permInheritedDeny.AppendToAcl(pAcl, ppAcePos, m_pSID, FALSE, dwFlags);
if (dwFlags & ACL_ALLOW) m_permInheritedAllow.AppendToAcl(pAcl, ppAcePos, m_pSID, TRUE, dwFlags); }
if ((dwFlags & ACL_CHECK_CREATOR) && IsCreatorSid(m_pSID)) { //
// Special case for CreatorOwner/CreatorGroup,
// which are only useful if inherit bits are set.
//
for (; pAceT < *ppAcePos; pAceT = (PACE_HEADER)NextAce(pAceT)) { pAceT->AceFlags |= INHERIT_ONLY_ACE; if (!(pAceT->AceFlags & ACE_INHERIT_ALL)) { pAceT->AceFlags |= ACE_INHERIT_ALL;
//
// Give the client a chance to adjust the flags.
// E.g. DS always turns off OBJECT_INHERIT_ACE
//
UCHAR AceFlags = pAceT->AceFlags; ACCESS_MASK Mask = GENERIC_ALL; m_pPage->m_psi->MapGeneric(NULL, &AceFlags, &Mask); pAceT->AceFlags = AceFlags; } } }
TraceAssert(IsValidAcl(pAcl)); TraceLeaveValue(TRUE); }
BOOL CPrincipal::HaveInheritedAces(void) { return (m_permInheritedAllow.GetPermCount(TRUE) || m_permInheritedDeny.GetPermCount(TRUE)); }
void CPrincipal::ConvertInheritedAces(BOOL bDelete) { if (bDelete) { m_permInheritedDeny.Reset(); m_permInheritedAllow.Reset(); } else { m_permDeny.ConvertInheritedAces(m_permInheritedDeny); m_permAllow.ConvertInheritedAces(m_permInheritedAllow); } }
void CPrincipal::AddPermission(BOOL bAllow, PPERMISSION pperm) { if (bAllow) m_permAllow.AddPermission(pperm); else m_permDeny.AddPermission(pperm); }
void CPrincipal::RemovePermission(BOOL bAllow, PPERMISSION pperm) { if (bAllow) m_permAllow.RemovePermission(pperm); else m_permDeny.RemovePermission(pperm); }
//
// CPermPage implementation
//
void CPermPage::InitPrincipalList(HWND hDlg, PACL pDacl) { TraceEnter(TRACE_PERMPAGE, "CPermPage::InitPrincipalList"); TraceAssert(hDlg != NULL);
HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); TraceAssert(hwndList != NULL);
// Save the DACL revision
if (pDacl != NULL) { m_wDaclRevision = pDacl->AclRevision; }
// If we have a selection, remember the SID for later
PSID psidTemp = NULL; LPPRINCIPAL pPrincipal = (LPPRINCIPAL)GetSelectedItemData(hwndList, NULL); if (pPrincipal != NULL) psidTemp = LocalAllocSid(pPrincipal->GetSID());
// Empty out the list
ListView_DeleteAllItems(hwndList);
// Enumerate the new DACL and fill the list
EnumerateAcl(hwndList, pDacl);
// Try to re-select the previously selection
if (psidTemp != NULL) { int cItems = ListView_GetItemCount(hwndList);
LV_ITEM lvItem; lvItem.iSubItem = 0; lvItem.mask = LVIF_PARAM;
// Look for the previously selected principal in the list
while (cItems > 0) { --cItems; lvItem.iItem = cItems;
ListView_GetItem(hwndList, &lvItem); pPrincipal = (LPPRINCIPAL)lvItem.lParam;
if (EqualSid(psidTemp, pPrincipal->GetSID())) { SelectListViewItem(hwndList, cItems); break; } }
LocalFree(psidTemp); }
TraceLeaveVoid(); }
STDMETHODIMP _InitCheckList(HWND hwndList, LPSECURITYINFO psi, const GUID* pguidObjectType, DWORD dwFlags, HINSTANCE hInstance, DWORD dwType, PSI_ACCESS *ppDefaultAccess) { HRESULT hr; PSI_ACCESS pAccess; ULONG cAccesses; ULONG iDefaultAccess; TCHAR szName[MAX_PATH];
TraceEnter(TRACE_MISC, "_InitCheckList"); TraceAssert(psi != NULL);
//
// Retrieve the permission list
//
hr = psi->GetAccessRights(pguidObjectType, dwFlags, &pAccess, &cAccesses, &iDefaultAccess); if (SUCCEEDED(hr) && cAccesses > 0) { if (ppDefaultAccess != NULL) *ppDefaultAccess = &pAccess[iDefaultAccess];
// Enumerate the permissions and add to the checklist
for (ULONG i = 0; i < cAccesses; i++, pAccess++) { LPCTSTR pszName;
// Only add permissions that have any of the flags specified in dwType
if (!(pAccess->dwFlags & dwType)) continue;
pszName = pAccess->pszName; if (IS_INTRESOURCE(pszName)) { TraceAssert(hInstance != NULL);
if (LoadString(hInstance, (UINT)((ULONG_PTR)pszName), szName, ARRAYSIZE(szName)) == 0) { LoadString(::hModule, IDS_UNKNOWN, szName, ARRAYSIZE(szName)); } pszName = szName; }
if (SendMessage(hwndList, CLM_ADDITEM, (WPARAM)pszName, (LPARAM)pAccess) == -1) { DWORD dwErr = GetLastError(); ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Failed to add item to checklist"); } } }
exit_gracefully:
TraceLeaveResult(hr); }
HRESULT CPermPage::InitCheckList(HWND hDlg) { HRESULT hr; TCHAR szName[MAX_PATH]; PSI_ACCESS pAccess;
TraceEnter(TRACE_PERMPAGE, "CPermPage::InitCheckList"); TraceAssert(hDlg != NULL);
HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PERMS); // checklist window
TraceAssert(hwndList != NULL);
DWORD dwType = SI_ACCESS_GENERAL; if (m_siObjectInfo.dwFlags & SI_CONTAINER) dwType |= SI_ACCESS_CONTAINER;
// Enumerate the permissions and add to the checklist
hr = _InitCheckList(hwndList, m_psi, NULL, 0, m_siObjectInfo.hInstance, dwType, &m_pDefaultAccess); if (SUCCEEDED(hr)) { //Add Custom Checkbox at the bottom of checklist. Custom checkbox is added only if
//Advanced Page is there
if(m_bCustomPermission) { if( ( pAccess = (PSI_ACCESS)LocalAlloc( LPTR, sizeof( SI_ACCESS ) ) ) == NULL ) ExitGracefully(hr, HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY), "Failed to allocate Memeory");
pAccess->dwFlags = SI_ACCESS_CUSTOM;
LoadString(::hModule, IDS_CUSTOM, szName, ARRAYSIZE(szName));
if (SendMessage(hwndList, CLM_ADDITEM, (WPARAM)szName, (LPARAM)pAccess) == -1) { DWORD dwErr = GetLastError(); ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Failed to add item to checklist"); } //
//Disable the custom checkbox
//
ClearCustom(hwndList,1); ClearCustom(hwndList,2); } } exit_gracefully: TraceLeaveResult(hr); }
//
// CAUTION - This function modifies the ACEs in the ACL by setting
// the AceType to 0xff (an invalid ACE type).
//
//This function goes through the ACL and groups the aces
//according to SID in PRINCIPAL objects.
void CPermPage::EnumerateAcl(HWND hwndList, PACL pAcl) { LPPRINCIPAL pPrincipal; PACE_HEADER pAce; int iEntry; int iTemp; PACE_HEADER paceTemp; HDPA hSids = NULL;
if (pAcl == NULL) return;
TraceEnter(TRACE_PERMPAGE, "CPermPage::EnumerateAcl");
TraceAssert(IsValidAcl(pAcl)); TraceAssert(hwndList != NULL);
hSids = DPA_Create(4);
if (NULL == hSids) TraceLeaveVoid();
for (iEntry = 0, pAce = (PACE_HEADER)FirstAce(pAcl); iEntry < pAcl->AceCount; iEntry++, pAce = (PACE_HEADER)NextAce(pAce)) { // Skip ACEs that we've already seen
if (pAce->AceType == 0xff) continue;
// Found an ACE we haven't seen yet, must be a new principal
pPrincipal = new CPrincipal(this); if (pPrincipal == NULL) continue; // memory error (try to continue)
// Initialize new principal
if (!pPrincipal->SetPrincipal(GetAceSid(pAce))) { delete pPrincipal; continue; // probably memory error (try to continue)
}
// Remember the SIDs so that later we can look up all the names
// at once and then add them to the listview.
DPA_AppendPtr(hSids, pPrincipal->GetSID());
// The current ACE belongs to this principal, so add it
pPrincipal->AddAce(pAce);
// Mark the ACE so we don't look at it again
pAce->AceType = 0xff;
// Loop through the rest of the ACEs in the ACL looking
// for the same SID
paceTemp = pAce; for (iTemp = iEntry + 1; iTemp < pAcl->AceCount; iTemp++) { // Move pointer to the current ACE
paceTemp = (PACE_HEADER)NextAce(paceTemp);
// If this ACE belongs to the current principal, add it
if (paceTemp->AceType != 0xff && EqualSid(GetAceSid(paceTemp), pPrincipal->GetSID())) { // Same principal, add the ACE
pPrincipal->AddAce(paceTemp);
// Mark the ACE so we don't look at it again
paceTemp->AceType = 0xff; } }
if (-1 == AddPrincipalToList(hwndList, pPrincipal)) { delete pPrincipal; } }
// Launch thread to look up sids
m_fBusy = TRUE; LookupSidsAsync(hSids, m_siObjectInfo.pszServerName, m_psi2, GetParent(hwndList), UM_SIDLOOKUPCOMPLETE); DPA_Destroy(hSids);
TraceLeaveVoid(); }
HRESULT CPermPage::SetPrincipalNamesInList(HWND hwndList, PSID pSid) { TraceEnter(TRACE_PERMPAGE, "CPermPage::SetPrincipalNamesInList");
HRESULT hr = S_OK; PUSER_LIST pUserList = NULL; LPPRINCIPAL pPrincipal = NULL; LVITEM lvItem = {0}; int cListItems; int iListItem; HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
// Enumerate through each entry in the list view
cListItems = ListView_GetItemCount(hwndList); for (iListItem = 0; iListItem < cListItems; iListItem++) { lvItem.mask = LVIF_PARAM; lvItem.iItem = iListItem; lvItem.iSubItem = 0;
if (ListView_GetItem(hwndList, &lvItem)) { pPrincipal = (LPPRINCIPAL) lvItem.lParam;
if (pPrincipal != NULL) { // Are we looking for a particular principal?
if (pSid && !EqualSid(pSid, pPrincipal->GetSID())) continue;
// Do we already have a good name?
if (pPrincipal->HaveRealName()) { if (pSid) break; // only care about this principal, stop here
else continue; // skip this one and check the rest
}
// Lookup the SID for this principal in the cache
LookupSid(pPrincipal->GetSID(), m_siObjectInfo.pszServerName, m_psi2, &pUserList);
if ((pUserList != NULL) && (pUserList->cUsers == 1)) { // The list should contain a single item
PUSER_INFO pUserInfo = &pUserList->rgUsers[0];
// Update the principal with this new name information
pPrincipal->SetSidType(pUserInfo->SidType); pPrincipal->SetName(pUserInfo->pszName, pUserInfo->pszLogonName);
// Set the text of this item to the name we've found
lvItem.mask = LVIF_TEXT | LVIF_IMAGE; lvItem.pszText = (LPTSTR)pPrincipal->GetName(); lvItem.iImage = pPrincipal->GetImageIndex(); ListView_SetItem(hwndList, &lvItem);
LocalFree(pUserList); } } } }
SetCursor(hcur);
TraceLeaveResult(hr); }
int CPermPage::AddPrincipalToList(HWND hwndList, LPPRINCIPAL pPrincipal) { LVITEM lvItem; int iIndex = -1;
TraceEnter(TRACE_PERMPAGE, "CPermPage::AddPrincipalToList"); TraceAssert(hwndList != NULL); TraceAssert(pPrincipal != NULL);
// Insert new principal into listview
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvItem.iItem = 0; lvItem.iSubItem = 0; lvItem.lParam = (LPARAM)pPrincipal; lvItem.pszText = (LPTSTR)pPrincipal->GetName(); lvItem.iImage = pPrincipal->GetImageIndex();
iIndex = ListView_InsertItem(hwndList, &lvItem);
TraceLeaveValue(iIndex); }
VOID CPermPage::SetPermLabelText(HWND hDlg) { RECT rc; WCHAR szBuffer[MAX_COLUMN_CHARS]; HWND hwndLabel; HWND hwndList; LPTSTR pszCaption = NULL; SIZE size; LPCWSTR pszUserName = NULL; CPrincipal * pPrincipal = NULL; int iIndex = 0; //Get Label Dimension
hwndLabel = GetDlgItem(hDlg, IDC_SPP_ACCESS); GetClientRect(hwndLabel, &rc); MapDialogRect(hDlg,&rc); //Get Text Dimension
HDC hdc = GetDC(hDlg);
hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); pPrincipal = (LPPRINCIPAL)GetSelectedItemData(hwndList, &iIndex); if(pPrincipal) pszUserName = pPrincipal->GetDisplayName();
if(pszUserName) FormatStringID(&pszCaption, ::hModule, IDS_DYNAMIC_PERMISSION,pszUserName); else FormatStringID(&pszCaption, ::hModule, IDS_PERMISSIONS, NULL);
GetTextExtentPoint32(hdc,pszCaption,wcslen(pszCaption),&size);
if(size.cx > rc.right) { hwndLabel = GetDlgItem(hDlg, IDC_SPP_ACCESS); EnableWindow(hwndLabel, FALSE); ShowWindow(hwndLabel,SW_HIDE); hwndLabel = GetDlgItem(hDlg, IDC_SPP_ACCESS_BIG); EnableWindow(hwndLabel, TRUE); ShowWindow(hwndLabel,SW_SHOW); SetWindowText(hwndLabel,pszCaption); } else { hwndLabel = GetDlgItem(hDlg, IDC_SPP_ACCESS_BIG); EnableWindow(hwndLabel, FALSE); ShowWindow(hwndLabel,SW_HIDE); hwndLabel = GetDlgItem(hDlg, IDC_SPP_ACCESS); EnableWindow(hwndLabel, TRUE); ShowWindow(hwndLabel,SW_SHOW); SetWindowText(hwndLabel,pszCaption); } hwndLabel = GetDlgItem(hDlg, IDC_EDIT1); SetWindowText(hwndLabel,pszCaption);
LocalFreeString(&pszCaption); ReleaseDC(hDlg, hdc); }
BOOL CPermPage::InitDlg(HWND hDlg) { HRESULT hr = S_OK; HWND hwnd; HWND hwndList; RECT rc; LV_COLUMN col; TCHAR szBuffer[MAX_COLUMN_CHARS]; PSECURITY_DESCRIPTOR pSD = NULL; BOOL bUserNotified = FALSE; HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
TraceEnter(TRACE_PERMPAGE, "CPermPage::InitDlg"); TraceAssert(hDlg != NULL); TraceAssert(m_psi != NULL);
hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); //
// Create & set the image list for the listview. If there is a
// problem CreateSidImageList will return NULL which won't hurt
// anything. In that case we'll just continue without an image list.
//
ListView_SetImageList(hwndList, LoadImageList(::hModule, MAKEINTRESOURCE(IDB_SID_ICONS)), LVSIL_SMALL);
// Set extended LV style for whole line selection with InfoTips
ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
//
// Add appropriate listview columns
//
GetClientRect(hwndList, &rc);
col.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_WIDTH; col.fmt = LVCFMT_LEFT; col.iSubItem = 0; col.cx = rc.right; ListView_InsertColumn(hwndList, 0, &col);
if (!(m_siObjectInfo.dwFlags & SI_ADVANCED)) { // Hide the Advanced button
hwnd = GetDlgItem(hDlg, IDC_SPP_ADVANCED); ShowWindow(hwnd, SW_HIDE); EnableWindow(hwnd, FALSE); hwnd = GetDlgItem(hDlg, IDC_SPP_STATIC_ADV); ShowWindow(hwnd, SW_HIDE); EnableWindow(hwnd, FALSE); }
if (S_FALSE == m_hrLastPSPCallbackResult) { // The propsheetpage callback told us to not show any messages here.
bUserNotified = TRUE; }
//Additional Permissions?
m_bCustomPermission = (m_siObjectInfo.dwFlags & SI_ADVANCED) && !(m_siObjectInfo.dwFlags & SI_NO_ADDITIONAL_PERMISSION); if (m_bAbortPage) { // Disable everything except the Advanced button
m_siObjectInfo.dwFlags |= SI_READONLY; EnableWindow(hwndList, FALSE);
// The user should have been notified during the propsheetpage
// callback, so don't put up another message now.
bUserNotified = TRUE; } else { //
// Initialize the checklist window
//
hr = InitCheckList(hDlg); FailGracefully(hr, "Failed to initialize checklist");
//
// Retrieve the DACL from the object and set it into the dialog
//
hr = m_psi->GetSecurity(DACL_SECURITY_INFORMATION, &pSD, FALSE);
if (SUCCEEDED(hr)) { // We always disable the advanced button until the SID name cache
// is filled on our other thread. See the DlgProc handler for
// UM_SIDLOOKUPCOMPLETE
EnableWindow(GetDlgItem(hDlg, IDC_SPP_ADVANCED), FALSE);
hr = SetDacl(hDlg, pSD); FailGracefully(hr, "SetDacl failed"); } else if (hr == E_ACCESSDENIED) { if (!bUserNotified) { //
// Can't read the DACL or Owner, figure out what we CAN do.
//
UINT idMsg = IDS_PERM_NO_ACCESS; UINT mbType = MB_OK | MB_ICONWARNING;
if (!(m_siObjectInfo.dwFlags & SI_READONLY)) { if(!( m_siObjectInfo.dwFlags & SI_MAY_WRITE)) idMsg = IDS_PERM_CANT_READ_CAN_WRITE_DACL; else idMsg = IDS_PERM_CANT_READ_MAY_WRITE_DACL; } else { //
// Can't write the DACL, can we write the owner or edit the SACL?
//
DWORD dwFlags = m_siObjectInfo.dwFlags & (SI_EDIT_AUDITS | SI_OWNER_READONLY);
// If we're not editing the owner, then we can't write it.
if (!(m_siObjectInfo.dwFlags & SI_EDIT_OWNER)) dwFlags |= SI_OWNER_READONLY;
switch(dwFlags) { case 0: // Can write the Owner but can't edit the SACL
idMsg = IDS_PERM_CANT_READ_CAN_WRITE_OWNER; break;
case SI_EDIT_AUDITS: // Can edit the SACL and write the Owner
idMsg = IDS_PERM_CANT_READ_CAN_AUDIT_WRITE_OWNER; break;
case SI_OWNER_READONLY: // No Access
break;
case SI_OWNER_READONLY | SI_EDIT_AUDITS: // Can edit the SACL but can't write the Owner
idMsg = IDS_PERM_CANT_READ_CAN_AUDIT; break; } }
if (idMsg == IDS_PERM_NO_ACCESS) mbType = MB_OK | MB_ICONERROR;
MsgPopup(hDlg, MAKEINTRESOURCE(idMsg), MAKEINTRESOURCE(IDS_SECURITY), mbType, ::hModule, m_siObjectInfo.pszObjectName); bUserNotified = TRUE; }
EnablePrincipalControls(hDlg, FALSE); hr = S_OK; } else { FailGracefully(hr, "GetSecurity failed"); } } // !m_bAbortPage
if (m_siObjectInfo.dwFlags & SI_READONLY) { EnableWindow(GetDlgItem(hDlg, IDC_SPP_ADD), FALSE); EnablePrincipalControls(hDlg, FALSE);
// Tell the user that we're in read-only mode
//Do not show this dialog box
//Windows Bug 181665
/*if (!bUserNotified)
{ MsgPopup(hDlg, MAKEINTRESOURCE(IDS_PERM_READONLY), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONINFORMATION, ::hModule, m_siObjectInfo.pszObjectName);
bUserNotified = TRUE; } */ }
exit_gracefully:
if (pSD != NULL) LocalFree(pSD);
SetCursor(hcur);
if (FAILED(hr)) { // Hide and disable everything
for (hwnd = GetWindow(hDlg, GW_CHILD); hwnd != NULL; hwnd = GetWindow(hwnd, GW_HWNDNEXT)) { ShowWindow(hwnd, SW_HIDE); EnableWindow(hwnd, FALSE); }
// Enable and show the "No Security" message
hwnd = GetDlgItem(hDlg, IDC_SPP_NO_SECURITY); EnableWindow(hwnd, TRUE); ShowWindow(hwnd, SW_SHOW); }
TraceLeaveValue(TRUE); }
BOOL CPermPage::OnNotify(HWND hDlg, int /*idCtrl*/, LPNMHDR pnmh) { LPNM_LISTVIEW pnmlv = (LPNM_LISTVIEW)pnmh;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnNotify"); TraceAssert(hDlg != NULL); TraceAssert(pnmh != NULL);
// Set default return value
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
switch (pnmh->code) { case LVN_ITEMCHANGED: if (pnmlv->uChanged & LVIF_STATE) { OnSelChange(hDlg); // item *gaining* selection
if ((pnmlv->uNewState & LVIS_SELECTED) && !(pnmlv->uOldState & LVIS_SELECTED)) { //here bClearCustom should be False. We don't need to clear
//Custom when we select another principal.
//Build Additional List for it.
} // item *losing* selection
else if (!(pnmlv->uNewState & LVIS_SELECTED) && (pnmlv->uOldState & LVIS_SELECTED)) { // Post ourselves a message to check for a new selection later.
// If we haven't gotten a new selection by the time we process
// this message, then assume the user clicked inside the listview
// but not on an item, thus causing the listview to remove the
// selection. In that case, disable the combobox & Remove button.
//
// Do this via WM_COMMAND rather than WM_NOTIFY so we don't
// have to allocate/free a NMHDR structure.
PostMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(pnmh->idFrom, pnmh->hwndFrom, IDN_CHECKSELECTION)); } } break;
case LVN_DELETEITEM: delete (LPPRINCIPAL)pnmlv->lParam; break;
case LVN_KEYDOWN: if (((LPNMLVKEYDOWN)pnmh)->wVKey == VK_DELETE) { //Get the status of Remove button. Only if remove is
//enabled do something bug 390243
if( IsWindowEnabled( GetDlgItem( hDlg,IDC_SPP_REMOVE )) ) OnRemovePrincipal(hDlg); } break;
#ifdef UNUSED
case NM_DBLCLK: if (pnmh->idFrom == IDC_SPP_PRINCIPALS) { // Must have a selection to get here
TraceAssert(ListView_GetSelectedCount(pnmh->hwndFrom) == 1);
// do something here
} break; #endif
case CLN_CLICK: if (pnmh->idFrom == IDC_SPP_PERMS) { LPPRINCIPAL pPrincipal; int iIndex = -1; pPrincipal = (LPPRINCIPAL)GetSelectedItemData(GetDlgItem(hDlg, IDC_SPP_PRINCIPALS), &iIndex); if (pPrincipal) { PNM_CHECKLIST pnmc = (PNM_CHECKLIST)pnmh; PSI_ACCESS pAccess = (PSI_ACCESS)pnmc->dwItemData; //Custom checkbox is clicked, reqiures special handling
if( pAccess->dwFlags & SI_ACCESS_CUSTOM ) { if (pnmc->dwState & CLST_CHECKED) { //Uncheck the Checkbox. Can checkbox be prevented from checked?
SendMessage(pnmc->hdr.hwndFrom, CLM_SETSTATE, MAKELONG((WORD)pnmc->iItem, (WORD)pnmc->iSubItem), 0 ); //Show the message box
MsgPopup(hDlg, MAKEINTRESOURCE(IDS_CUSTOM_CHECKBOX_WARNING), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONINFORMATION, ::hModule); } else { SetDirty(hDlg); //Clear the Special Checkbox and Permissions
BOOL bClearAllow = (1 == pnmc->iSubItem); // 1 = Allow, 2 = Deny
OnSelChange(hDlg, TRUE, bClearAllow, !bClearAllow); } //Break out of Switch
break; } }
//
// HandleListClick decides which boxes should be checked and
// unchecked, however, we can't rely only on that to generate
// ACLs (we used to). Suppose the principal has Full Control and
// the user unchecks "Delete" which is a single bit. If there is
// no checkbox corresponding to "Full Control minus Delete" then
// the principal would also lose other bits, such as WRITE_DAC.
//
// So let HandleListClick do its thing. Then remove permission
// bits according to what was checked or unchecked.
//
// But wait, there's more. Removing permission bits turns off
// too much. For example, if the principal has Full Control and
// the user turns off Full Control, then the principal ends up
// with nothing, even though HandleListClick leaves Modify
// checked.
//
// So after removing what was just (un)checked, build new
// permissions from what is still checked and add them.
//
// This yields the correct results, and also keeps the principal
// up-to-date so we don't need to call CommitCurrent anywhere else.
//
// Raid 260952
//
//HandleListClick decides which boxes should be checked and unchecked.
//If FullControl was intially Checked, we uncheck Read, Full Control is
//also unchecked. If a checkbox was intially checked and unchecked in
//HandleListClick, its added to h[Allow/Deny]UncheckedAccess list.
//Permission corresponding to these checkboxes is removed.
// Check/uncheck appropriate boxes in both columns
HDSA hAllowUncheckedAccess = NULL; HDSA hDenyUncheckedAccess = NULL;
//Does appropriate check-uncheck.
HandleListClick((PNM_CHECKLIST)pnmh, SI_PAGE_PERM, m_siObjectInfo.dwFlags & SI_CONTAINER, &hAllowUncheckedAccess, &hDenyUncheckedAccess, m_bCustomPermission);
pPrincipal = (LPPRINCIPAL)GetSelectedItemData(GetDlgItem(hDlg, IDC_SPP_PRINCIPALS), &iIndex); if (pPrincipal) { PNM_CHECKLIST pnmc = (PNM_CHECKLIST)pnmh; PSI_ACCESS pAccess = (PSI_ACCESS)pnmc->dwItemData; PERMISSION perm = { pAccess->mask, 0, 0 };
//If we uncheck Allow Read, Allow Read Checkbox goes into HandleListClick as
//unchecked and is not in hAllowUncheckedAccess. Perm Corresponding to it
//especially removed.
if(!(pnmc->dwState & CLST_CHECKED)) { // Which column was clicked?
BOOL bRemoveFromAllow = (1 == pnmc->iSubItem); // 1 = Allow, 2 = Deny
if (pAccess->pguid) perm.guid = *pAccess->pguid;
if (m_siObjectInfo.dwFlags & SI_CONTAINER) perm.dwFlags = pAccess->dwFlags & VALID_INHERIT_FLAGS;
pPrincipal->RemovePermission(bRemoveFromAllow, &perm); }
if( hAllowUncheckedAccess ) { UINT cItems = DSA_GetItemCount(hAllowUncheckedAccess); PERMISSION permTemp; while (cItems) { --cItems; DSA_GetItem(hAllowUncheckedAccess, cItems, &pAccess); permTemp.mask = pAccess->mask; if (m_siObjectInfo.dwFlags & SI_CONTAINER) permTemp.dwFlags = pAccess->dwFlags & VALID_INHERIT_FLAGS; if( pAccess->pguid ) permTemp.guid = *pAccess->pguid; pPrincipal->RemovePermission(TRUE, &permTemp); } DSA_Destroy(hAllowUncheckedAccess); }
if( hDenyUncheckedAccess ) { UINT cItems = DSA_GetItemCount(hDenyUncheckedAccess); PERMISSION permTemp; PSI_ACCESS pAccess2 = NULL; while (cItems) { --cItems; DSA_GetItem(hDenyUncheckedAccess, cItems, &pAccess2); permTemp.mask = pAccess2->mask; if (m_siObjectInfo.dwFlags & SI_CONTAINER) permTemp.dwFlags = pAccess2->dwFlags & VALID_INHERIT_FLAGS; if( pAccess2->pguid ) permTemp.guid = *pAccess2->pguid; pPrincipal->RemovePermission(FALSE, &permTemp); } DSA_Destroy(hDenyUncheckedAccess); } }
SetDirty(hDlg);
// Add perms according to what is still checked. This is required, since
// when i uncheck Read, full control is also unchecked and permission corresponding to
// it is removed. This will remove Write also, though its still checked.
//CommitCurrent will add permission for checkboxes which are still checked.
CommitCurrent(hDlg, iIndex);
//Here i should add additional list to main list, but no need to rebuild addtional list
// Reset the "There is more stuff" message
OnSelChange(hDlg, FALSE); } break;
case CLN_GETCOLUMNDESC: { PNM_CHECKLIST pnmc = (PNM_CHECKLIST)pnmh; GetDlgItemText(hDlg, IDC_SPP_ALLOW - 1 + pnmc->iSubItem, pnmc->pszText, pnmc->cchTextMax); } break;
case PSN_APPLY: OnApply(hDlg, (BOOL)(((LPPSHNOTIFY)pnmh)->lParam)); break; default: TraceLeaveValue(FALSE); // message not handled
}
TraceLeaveValue(TRUE); // message handled
}
BOOL CheckPermissions(HWND hwndList, CPermissionSet &PermSet, WORD wColumn, BOOL bDisabled, BOOL bInheritFlags, BOOL bCustom, //Does Custom Checkbox exist?
BOOL bClearCustom, HDSA hAdditional )//Clear Custom Permissions?
{ UINT cRights = (UINT)SendMessage(hwndList, CLM_GETITEMCOUNT, 0, 0);
//Custom Checkbox is handled separately the end.
if( bCustom ) --cRights; UINT cAces = PermSet.GetPermCount(); BOOL bMorePresent = FALSE; WORD wOtherColumn; DWORD dwState = CLST_CHECKED;
TraceEnter(TRACE_MISC, "CheckPermissions");
HDSA hPermList; //Temp List of PPERMISSION pointers
if( bClearCustom ) { hPermList = DSA_Create(SIZEOF(PPERMISSION), 4); if (hPermList == NULL) { TraceMsg("DSA_Create failed"); TraceLeaveValue(FALSE); } }
if (wColumn == 1) wOtherColumn = 2; else wOtherColumn = 1;
if (bDisabled) dwState |= CLST_DISABLED;
for (UINT j = 0; j < cAces; j++) { ACCESS_MASK maskChecked = 0; PPERMISSION pPerm = PermSet[j]; BOOL bIsNullGuid = IsNullGUID(&pPerm->guid); //Igonre custom here
for (UINT i = 0; i < cRights ; i++) { PSI_ACCESS pAccess = (PSI_ACCESS)SendMessage(hwndList, CLM_GETITEMDATA, i, 0); //
// The below expression tests to see if this access mask enables
// this access "rights" line. It could have more bits enabled, but
// as long as it has all of the ones from the pAccess[i].mask then
// it effectively has that option enabled.
//
if ( (pPerm->mask & pAccess->mask) == pAccess->mask && (bIsNullGuid || IsSameGUID(&pPerm->guid, pAccess->pguid)) ) { DWORD dwStateCompare;
//
// Next, check the inherit flags.
//
if (bInheritFlags) { DWORD dwCommonFlags = pPerm->dwFlags & pAccess->dwFlags;
//
// This expression tests to see whether the ACE applies
// to all objects that this access rights line applies to.
// The ACE must have at least as many of (CONTAINER_INHERIT_ACE,
// OBJECT_INHERIT_ACE) turned on as the rights line, and
// if the ACE has INHERIT_ONLY_ACE, then so must the rights line.
//
if (!((dwCommonFlags & ACE_INHERIT_ALL) == (pAccess->dwFlags & ACE_INHERIT_ALL) && (dwCommonFlags & INHERIT_ONLY_ACE) == (pPerm->dwFlags & INHERIT_ONLY_ACE))) continue; }
// The bits say it's checked. We may not actually check the box
// below, but for other reasons. In any case, we don't want the
// "Additional stuff is here but I can't show it" message to
// display because of this perm.
maskChecked |= pAccess->mask;
//
// Ok, the bits say that this box should be checked, but
// if the other column is already checked and has the same
// enabled/disabled state, then we don't check this one.
// This keeps us from having both Allow and Deny checked &
// enabled on the same line (nonsense) or checked & disabled
// on the same line (both inherited; we must show both as
// Allow Inherited can preceede Deny Inherited and we
// don't know the order at this point.
//
if( !(pPerm->dwFlags & INHERITED_ACE) ) { dwStateCompare = (DWORD)SendMessage(hwndList, CLM_GETSTATE, MAKELONG((WORD)i, wOtherColumn), 0); if ((dwStateCompare & CLST_CHECKED) && ((dwStateCompare & CLST_DISABLED) == (dwState & CLST_DISABLED))) continue; } //
// Next, see if the box is already checked. If so, leave it
// alone. Note that we don't compare the enabled/disabled
// state. The effect here is that the first check wins.
// Raid 326000
//
dwStateCompare = (DWORD)SendMessage(hwndList, CLM_GETSTATE, MAKELONG((WORD)i, wColumn), 0); if (dwStateCompare & CLST_CHECKED) continue;
//
// Finally, check the box.
//
SendMessage(hwndList, CLM_SETSTATE, MAKELONG((WORD)i, wColumn), dwState); } }
if( bClearCustom ) { //If an ace don't check anyof the checkboxes,( i.e. maskchecked = 0 ),
//it should be removed when custom is unchecked.
if( !maskChecked ) { DSA_AppendItem(hPermList, &pPerm); maskChecked = pPerm->mask; //this is done to make sure maskchecked is false
} //Ace checks some checkbox ( maskChecked), so it mask should be maskChecked
else pPerm->mask = maskChecked; }
// Does this ACE have bits that aren't shown on this dialog?
if (maskChecked != pPerm->mask) { ACCESS_MASK maskTemp = 0; //Add this ace to the list of additional aces,
//but only the bits which are additional
if( hAdditional ) { maskTemp = pPerm->mask; pPerm->mask &= ~maskChecked; DSA_AppendItem(hAdditional, pPerm); pPerm->mask = maskTemp; } bMorePresent = TRUE; } }
if( bClearCustom ) { UINT cItems = DSA_GetItemCount(hPermList); PPERMISSION pTemp = NULL; while (cItems) { --cItems; DSA_GetItem(hPermList, cItems, &pTemp); //Removes only the permission which match its inheritance flag, not others.
//For example it this permission is read applied to subobjects and hence appear as
//custom permission. On clearing the custom checkbox, only read permission applied to
//subobjects should go, not the read permission applied to this object ( and/or subobjects)
// which can be shown in other checkboxes.
PermSet.RemovePermission(pTemp, TRUE); --cAces; } PermSet.ResetAdvanced(); DSA_Destroy(hPermList); }
// Does this permission set have "advanced" ACEs that aren't shown
// on this dialog?
if (!bMorePresent && cAces != PermSet.GetPermCount(TRUE)) bMorePresent = TRUE;
if( bMorePresent && bCustom ) CheckCustom( hwndList, wColumn, dwState );
TraceLeaveValue(bMorePresent); }
void CPermPage::OnSelChange(HWND hDlg, BOOL bClearFirst, BOOL bClearCustomAllow, BOOL bClearCustomDeny) { BOOL bDisabled = m_siObjectInfo.dwFlags & SI_READONLY;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnSelChange"); TraceAssert(hDlg != NULL);
//
// If the principal list is empty or there is no selection, then we need
// to disable all of the controls that operate on items in the listbox.
//
HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); TraceAssert(hwndList != NULL);
// Get the selected principal
LPPRINCIPAL pPrincipal = (LPPRINCIPAL)GetSelectedItemData(hwndList, NULL);
// Enable/disable the other controls
if (!bDisabled) EnablePrincipalControls(hDlg, pPrincipal != NULL);
//Change the permission label to reflect the new User/Group
SetPermLabelText(hDlg);
if (pPrincipal == NULL) TraceLeaveVoid(); // no selection or empty list
//
// Check/uncheck the permission boxes
//
hwndList = GetDlgItem(hDlg, IDC_SPP_PERMS); TraceAssert(hwndList != NULL);
if (bClearFirst) { // First need to uncheck everything
ClearPermissions(hwndList, bDisabled); }
BOOL bIsContainer = m_siObjectInfo.dwFlags & SI_CONTAINER; BOOL bMorePresent = FALSE;
//Clear the Custom Checkboxes. This is the only place where Custom Checkbox is cleared
if(m_bCustomPermission) { ClearCustom(hwndList,1); ClearCustom(hwndList,2); }
if( !pPrincipal->m_hAdditionalAllow ) { pPrincipal->m_hAdditionalAllow = DSA_Create(SIZEOF(PERMISSION), 4); if (pPrincipal->m_hAdditionalAllow == NULL) { TraceMsg("DSA_Create failed"); TraceLeaveVoid(); }
} if( !pPrincipal->m_hAdditionalDeny ) { pPrincipal->m_hAdditionalDeny = DSA_Create(SIZEOF(PERMISSION), 4); if (pPrincipal->m_hAdditionalDeny == NULL) { TraceMsg("DSA_Create failed"); TraceLeaveVoid(); }
} UINT cItems = DSA_GetItemCount(pPrincipal->m_hAdditionalAllow); PPERMISSION pPermTemp; while (cItems) { --cItems; pPermTemp = (PPERMISSION)DSA_GetItemPtr(pPrincipal->m_hAdditionalAllow, cItems ); if(pPermTemp) pPrincipal->AddPermission(TRUE, pPermTemp); } DSA_DeleteAllItems(pPrincipal->m_hAdditionalAllow);
cItems = DSA_GetItemCount(pPrincipal->m_hAdditionalDeny); while (cItems) { --cItems; pPermTemp = (PPERMISSION)DSA_GetItemPtr(pPrincipal->m_hAdditionalDeny, cItems ); if(pPermTemp) pPrincipal->AddPermission(FALSE, pPermTemp); } DSA_DeleteAllItems(pPrincipal->m_hAdditionalDeny);
bMorePresent |= CheckPermissions(hwndList, pPrincipal->m_permDeny, 2, bDisabled, bIsContainer, m_bCustomPermission, bClearCustomDeny, pPrincipal->m_hAdditionalDeny); bMorePresent |= CheckPermissions(hwndList, pPrincipal->m_permAllow, 1, bDisabled, bIsContainer, m_bCustomPermission, bClearCustomAllow, pPrincipal->m_hAdditionalAllow); bMorePresent |= CheckPermissions(hwndList, pPrincipal->m_permInheritedDeny, 2, TRUE, bIsContainer, m_bCustomPermission, FALSE, NULL); bMorePresent |= CheckPermissions(hwndList, pPrincipal->m_permInheritedAllow, 1, TRUE, bIsContainer, m_bCustomPermission, FALSE,NULL);
if (m_siObjectInfo.dwFlags & SI_ADVANCED) { ShowWindow(GetDlgItem(hDlg, IDC_SPP_MORE_MSG), (bMorePresent ? SW_SHOW : SW_HIDE));
} else if (bMorePresent) { TraceMsg("Ignoring unknown permissions"); }
TraceLeaveVoid(); }
void CPermPage::OnApply(HWND hDlg, BOOL bClose) { HRESULT hr = S_OK; PSECURITY_DESCRIPTOR pSD;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnApply");
// Build a new DACL without the inherited ACEs.
if (m_fPageDirty && SUCCEEDED(hr = BuildDacl(hDlg, &pSD, FALSE)) && (hr != S_FALSE)) { PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR)pSD; DWORD dwWarning = 0; SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
TraceAssert(pSD != NULL); TraceAssert(m_psi != NULL);
// Check for Deny ACEs in the ACL
if (!m_bWasDenyAcl) { DWORD dwFullControl = GENERIC_ALL; UCHAR aceFlags = 0;
m_psi->MapGeneric(NULL, &aceFlags, &dwFullControl); if (IsDenyACL(psd->Dacl, (psd->Control & SE_DACL_PROTECTED), dwFullControl, &dwWarning)) { TraceAssert(dwWarning != 0);
// Warn the user about Deny ACEs
if (IDNO == MsgPopup(hDlg, MAKEINTRESOURCE(dwWarning), MAKEINTRESOURCE(IDS_SECURITY), MB_YESNO | MB_ICONWARNING, ::hModule, m_siObjectInfo.pszObjectName)) { hr = S_FALSE; } } }
if (S_FALSE != hr) { if(!IsAclBloated(hDlg, si, pSD, m_cInheritableAces, m_siObjectInfo.dwFlags & SI_EDIT_PROPERTIES)) { // Apply the new security descriptor on the object
hr = m_psi->SetSecurity(si, pSD); } else hr = S_FALSE; }
if (S_OK == hr) { LocalFree(pSD); pSD = NULL; m_fPageDirty = FALSE;
if (!bClose) { //
// Read the new DACL back from the object. This ensures that we
// have the "real" current DACL in case it was modified by the
// object. For example, inherited aces may have been added.
//
// This also resets the dialog to the initial state if the
// user chose No in the confirmation dialog above.
//
if (SUCCEEDED(m_psi->GetSecurity(DACL_SECURITY_INFORMATION, &pSD, FALSE))) SetDacl(hDlg, pSD); } } else if (S_FALSE == hr) { // S_FALSE is silent failure (the client should put up UI
// during SetSecurity before returning S_FALSE).
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID); }
if (pSD != NULL) LocalFree(pSD); }
if (FAILED(hr)) { // Tell the user there was a problem. If they choose to cancel
// and the dialog is closing, do nothing (let the dialog close).
// Otherwise, tell the property sheet that we had a problem.
if (IDCANCEL != SysMsgPopup(hDlg, MAKEINTRESOURCE(IDS_PERM_WRITE_FAILED), MAKEINTRESOURCE(IDS_SECURITY), (bClose ? MB_RETRYCANCEL : MB_OK) | MB_ICONERROR, ::hModule, hr, m_siObjectInfo.pszObjectName)) { // Return PSNRET_INVALID to abort the Apply and cause the sheet to
// select this page as the active page.
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID); } }
TraceLeaveVoid(); }
/*-----------------------------------------------------------------------------
/ BuildDacl / ------- / Convert the listbox entries into SD. If the size of security descriptor / is more than Max allowed shows a dialog box. / ppSD can be NULL for the cases where we want to verify if the SD size is / not execeeding the max size. / /----------------------------------------------------------------------------*/
HRESULT CPermPage::BuildDacl(HWND hDlg, PSECURITY_DESCRIPTOR *ppSD, BOOL fIncludeInherited) { PISECURITY_DESCRIPTOR pSD; ULONG nAclSize; LPPRINCIPAL pPrincipal; int cPrincipals = 0; DWORD dwFlags; int i, j; HCURSOR hcur = NULL; HWND hwndList = NULL; LV_ITEM lvItem; lvItem.mask = LVIF_PARAM; lvItem.iSubItem = 0;
static DWORD dwCanonicalFlags[] = { ACL_DENY | ACL_NONOBJECT, ACL_DENY | ACL_OBJECT, ACL_ALLOW | ACL_NONOBJECT, ACL_ALLOW | ACL_OBJECT };
TraceEnter(TRACE_PERMPAGE, "CPermPage::BuildDacl"); TraceAssert(hDlg != NULL);
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
//
// Estimate the size of the buffer necessary to build the
// Security Descriptor.
//
hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); cPrincipals = ListView_GetItemCount(hwndList);
dwFlags = ACL_NONINHERITED; if (fIncludeInherited) dwFlags |= ACL_INHERITED;
WORD nMaxAclSize = 0xffff; nAclSize = SIZEOF(ACL);
for (i = 0; i < cPrincipals; i++) { lvItem.iItem = i; if (ListView_GetItem(hwndList, &lvItem)) { pPrincipal = (LPPRINCIPAL)lvItem.lParam; nAclSize += pPrincipal->GetAclLength(dwFlags); } if(nAclSize > nMaxAclSize) { //
//itow converts upto 33 bytes so 34bytes is fine
//
WCHAR buffer[34]; _itow((cPrincipals - i),buffer,10); ULONG nMsgId = IDS_ACL_SIZE_ERROR; if(!ppSD) nMsgId = IDS_ACL_SIZE_ERROR_ADV;
MsgPopup(hDlg, MAKEINTRESOURCE(nMsgId), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONERROR, ::hModule, buffer); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID); //
//Do a silent failure since we have already shown the error message
//
return S_FALSE; } }
if(!ppSD) return S_OK;
*ppSD = NULL;
//
// Now that we have the size estimate, allocate the buffer. Note that
// we allocate enough memory for a self-relative security descriptor, but
// don't set the SE_SELF_RELATIVE flag in pSD->Control. This lets us
// use pSD->Dacl, etc. as pointers rather than offsets.
//
*ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH + nAclSize); if (*ppSD == NULL) TraceLeaveResult(E_OUTOFMEMORY);
InitializeSecurityDescriptor(*ppSD, SECURITY_DESCRIPTOR_REVISION);
pSD = (PISECURITY_DESCRIPTOR)*ppSD;
//
// Finally, build the security descriptor
//
pSD->Control |= SE_DACL_PRESENT | SE_DACL_AUTO_INHERIT_REQ | (m_wSDControl & (SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED));
if (nAclSize > 0) { pSD->Dacl = (PACL)(pSD + 1); pSD->Dacl->AclRevision = ACL_REVISION; pSD->Dacl->AclSize = (WORD)nAclSize; pSD->Dacl->AceCount = 0;
PACE_HEADER pAcePos = (PACE_HEADER)FirstAce(pSD->Dacl);
DWORD dwExtraFlags = fIncludeInherited ? 0 : ACL_CHECK_CREATOR;
// Build the DACL in the following order:
// Deny
// Allow
// Inherited Deny
// Inherited Allow
for (j = 0; j < ARRAYSIZE(dwCanonicalFlags); j++) { for (i = 0; i < cPrincipals; i++) { lvItem.iItem = i; if (ListView_GetItem(hwndList, &lvItem)) { pPrincipal = (LPPRINCIPAL)lvItem.lParam; pPrincipal->AppendToAcl(pSD->Dacl, ACL_NONINHERITED | dwCanonicalFlags[j] | dwExtraFlags, &pAcePos); } } }
if (fIncludeInherited) { for (j = 0; j < ARRAYSIZE(dwCanonicalFlags); j++) { for (i = 0; i < cPrincipals; i++) { lvItem.iItem = i; if (ListView_GetItem(hwndList, &lvItem)) { pPrincipal = (LPPRINCIPAL)lvItem.lParam; pPrincipal->AppendToAcl(pSD->Dacl, ACL_INHERITED | dwCanonicalFlags[j] | dwExtraFlags, &pAcePos); } } } }
// Set accurate size information for the ACL
nAclSize = (ULONG)((PBYTE)pAcePos - (PBYTE)pSD->Dacl); TraceAssert(nAclSize >= SIZEOF(ACL)); TraceAssert(pSD->Dacl->AclSize >= nAclSize);
if (pSD->Dacl->AclSize > nAclSize) pSD->Dacl->AclSize = (WORD)nAclSize;
TraceAssert(m_psi2 || IsDACLCanonical(pSD->Dacl)); }
TraceAssert(pSD && IsValidSecurityDescriptor(pSD));
SetCursor(hcur);
TraceLeaveResult(S_OK); }
HRESULT CPermPage::SetDacl(HWND hDlg, PSECURITY_DESCRIPTOR pSD, BOOL bDirty) { HRESULT hr = S_OK; PACL pAcl = NULL; PACL paclAllowAll = NULL; BOOL bDefaulted; BOOL bPresent; SECURITY_DESCRIPTOR_CONTROL wSDControl = 0; PSECURITY_DESCRIPTOR pSDDefault = NULL; DWORD dwRevision;
TraceEnter(TRACE_PERMPAGE, "CPermPage::SetDacl"); TraceAssert(hDlg != NULL);
if (pSD != NULL && !IsValidSecurityDescriptor(pSD)) TraceLeaveResult(E_INVALIDARG);
if (pSD != NULL) GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision);
// Save the DACL protection and auto-inherited bits
m_wSDControl &= ~(SE_DACL_DEFAULTED | SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED); m_wSDControl |= (wSDControl & (SE_DACL_DEFAULTED | SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED));
// Get a pointer to the new DACL
if (pSD != NULL) GetSecurityDescriptorDacl(pSD, &bPresent, &pAcl, &bDefaulted);
if (!(m_siObjectInfo.dwFlags & SI_READONLY)) { // Check for canonical ordering (Deny, Allow, Inherited Deny, Inherited Allow)
if ((m_psi2 && !m_psi2->IsDaclCanonical(pAcl)) || (!m_psi2 && !IsDACLCanonical(pAcl))) { TraceMsg("DACL not in canonical order!");
// Ask the user whether to canonicalize the DACL or
// blow it away completely.
if (IDCANCEL == MsgPopup(hDlg, MAKEINTRESOURCE(IDS_PERM_NOT_CANONICAL), MAKEINTRESOURCE(IDS_SECURITY), MB_OKCANCEL | MB_ICONWARNING, ::hModule, m_siObjectInfo.pszObjectName)) { // Blow it away and start over.
pAcl = NULL;
// Does the caller support a default ACL? If so, get it now.
if (m_siObjectInfo.dwFlags & SI_RESET) { hr = m_psi->GetSecurity(DACL_SECURITY_INFORMATION, &pSDDefault, TRUE);
if (SUCCEEDED(hr) && pSDDefault != NULL) { // Save the DACL control bits
GetSecurityDescriptorControl(pSDDefault, &wSDControl, &dwRevision); m_wSDControl &= ~(SE_DACL_DEFAULTED | SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED); m_wSDControl |= SE_DACL_DEFAULTED | (wSDControl & (SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED));
// Get a pointer to the new DACL
GetSecurityDescriptorDacl(pSDDefault, &bPresent, &pAcl, &bDefaulted); } // else go with a NULL DACL
} } // else simply continuing and re-saving will
// cause the DACL to get sorted correctly
// This causes a PropSheet_Changed notification to be sent below
bDirty = TRUE; } }
m_bWasDenyAcl = FALSE;
// A NULL ACL implies "Everyone Full control", so
// create such an ACL here
if (pAcl == NULL) { PSID psidWorld = QuerySystemSid(UI_SID_World); DWORD dwSidLength = GetLengthSid(psidWorld); DWORD dwAclLength = SIZEOF(ACL) + SIZEOF(ACCESS_ALLOWED_ACE) - SIZEOF(DWORD) + dwSidLength;
m_wDaclRevision = ACL_REVISION;
paclAllowAll = (PACL)LocalAlloc(LPTR, dwAclLength); if (paclAllowAll != NULL) { paclAllowAll->AclRevision = ACL_REVISION; paclAllowAll->AclSize = (WORD)dwAclLength; #if 0 //(_WIN32_WINNT >= 0x0500)
paclAllowAll->AceCount = 0;
AddAccessAllowedAceEx(paclAllowAll, ACL_REVISION, ACE_INHERIT_ALL, GENERIC_ALL, psidWorld); #else
paclAllowAll->AceCount = 1;
PACE_HEADER pAce = (PACE_HEADER)FirstAce(paclAllowAll); pAce->AceType = ACCESS_ALLOWED_ACE_TYPE; pAce->AceFlags = ACE_INHERIT_ALL; pAce->AceSize = (WORD)dwAclLength - SIZEOF(ACL); ((PACCESS_ALLOWED_ACE)pAce)->Mask = GENERIC_ALL; CopyMemory(&((PACCESS_ALLOWED_ACE)pAce)->SidStart, psidWorld, dwSidLength); #endif
pAcl = paclAllowAll; } } else { DWORD dwFullControl = GENERIC_ALL; UCHAR aceFlags = 0;
m_psi->MapGeneric(NULL, &aceFlags, &dwFullControl); if (IsDenyACL(pAcl, (m_wSDControl & SE_DACL_PROTECTED), dwFullControl, NULL)) { // Already have Deny ACEs, don't bother warning again later.
m_bWasDenyAcl = TRUE; } }
// Reset the list of principals
InitPrincipalList(hDlg, pAcl);
//Get the count of inheritable aces
m_cInheritableAces = GetCountOfInheritableAces(pAcl);
// If there aren't any entries, fake a sel change to update
// (i.e. disable) the other controls.
if (pAcl == NULL || pAcl->AceCount == 0) OnSelChange(hDlg);
if (bDirty) SetDirty(hDlg, TRUE);
if (paclAllowAll != NULL) LocalFree(paclAllowAll);
if (pSDDefault != NULL) LocalFree(pSDDefault);
TraceLeaveResult(hr); }
void CPermPage::OnAddPrincipal(HWND hDlg) { PUSER_LIST pUserList = NULL;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnAddPrincipal"); TraceAssert(hDlg != NULL); if (S_OK == GetUserGroup(hDlg, TRUE, &pUserList)) { PUSER_INFO pUserInfo; DWORD i; BOOL fPageModified = FALSE; int iItem = -1;
TraceAssert(NULL != pUserList);
HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); TraceAssert(hwndList != NULL);
for (i = 0; i < pUserList->cUsers; i++) { int cItems; LV_ITEM lvItem; LPPRINCIPAL pPrincipal; BYTE buffer[SIZEOF(KNOWN_OBJECT_ACE) + SIZEOF(GUID)]; PACE_HEADER pAce = (PACE_HEADER)buffer;
pUserInfo = &pUserList->rgUsers[i]; iItem = -1;
// Check whether the new principal is already in our list.
// If so, don't add it again.
cItems = ListView_GetItemCount(hwndList); lvItem.iSubItem = 0; lvItem.mask = LVIF_PARAM; while (cItems > 0) { LPPRINCIPAL pPrincipal2 = NULL;
--cItems; lvItem.iItem = cItems;
ListView_GetItem(hwndList, &lvItem); pPrincipal2 = (LPPRINCIPAL)lvItem.lParam;
if (EqualSid(pPrincipal2->GetSID(), pUserInfo->pSid)) { iItem = lvItem.iItem; break; } }
// Did we find it?
if (iItem != -1) continue;
// ListView_FindItem failed to find a match. Add a
// new principal.
pPrincipal = new CPrincipal(this); if (!pPrincipal) continue;
// Initialize principal
if (!pPrincipal->SetPrincipal(pUserInfo->pSid, pUserInfo->SidType, pUserInfo->pszName, pUserInfo->pszLogonName)) { delete pPrincipal; continue; }
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvItem.iItem = 0; lvItem.iSubItem = 0; lvItem.pszText = (LPTSTR)pPrincipal->GetName(); lvItem.iImage = pPrincipal->GetImageIndex(); lvItem.lParam = (LPARAM)pPrincipal;
// Insert principal into list
iItem = ListView_InsertItem(hwndList, &lvItem); if (-1 == iItem) { delete pPrincipal; continue; }
// Add ace with default access
pAce->AceType = ACCESS_ALLOWED_ACE_TYPE; pAce->AceFlags = 0; pAce->AceSize = SIZEOF(ACCESS_ALLOWED_ACE); ((PACCESS_ALLOWED_ACE)pAce)->Mask = m_pDefaultAccess->mask;
if (m_siObjectInfo.dwFlags & SI_CONTAINER) { // Pick up inherit bits from the default access
pAce->AceFlags = (UCHAR)(m_pDefaultAccess->dwFlags & (VALID_INHERIT_FLAGS & ~INHERITED_ACE));
//
// Special case for CreatorOwner/CreatorGroup,
// which are only useful if inherit bits are set.
//
if (IsCreatorSid(pUserInfo->pSid)) { // Make sure it inherits onto something
if (!(pAce->AceFlags & ACE_INHERIT_ALL)) pAce->AceFlags = ACE_INHERIT_ALL;
// It never applies to the current object
pAce->AceFlags |= INHERIT_ONLY_ACE;
// Set it up so whoever creates an object
// gets full control by default
((PACCESS_ALLOWED_ACE)pAce)->Mask = GENERIC_ALL; } }
if (!IsNullGUID(m_pDefaultAccess->pguid)) { pAce->AceType = ACCESS_ALLOWED_OBJECT_ACE_TYPE; pAce->AceSize = SIZEOF(KNOWN_OBJECT_ACE) + SIZEOF(GUID); ((PKNOWN_OBJECT_ACE)pAce)->Flags = ACE_OBJECT_TYPE_PRESENT; *RtlObjectAceObjectType(pAce) = *m_pDefaultAccess->pguid; }
pPrincipal->AddAce(pAce); fPageModified = TRUE; }
// Done with this now
LocalFree(pUserList);
if (fPageModified) { // If we've added items, resize the Name column
//ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
SetDirty(hDlg); }
if (iItem != -1) { // Select the last one inserted.
SelectListViewItem(hwndList, iItem); } }
TraceLeaveVoid(); }
void CPermPage::OnRemovePrincipal(HWND hDlg) { HWND hwndList; int iIndex; LPPRINCIPAL pPrincipal;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnRemovePrincipal");
hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); pPrincipal = (LPPRINCIPAL)GetSelectedItemData(hwndList, &iIndex);
if (pPrincipal) { BOOL bDirty = FALSE;
if (pPrincipal->GetAclLength(ACL_INHERITED) > 0) { // This principal has inherited ACEs so we can't remove the principal
// from the list. Instead, simply remove the non-inherited ACEs from
// the principal.
if (pPrincipal->GetAclLength(ACL_NONINHERITED) > 0) { pPrincipal->m_permDeny.Reset(); pPrincipal->m_permAllow.Reset(); DSA_DeleteAllItems(pPrincipal->m_hAdditionalAllow); DSA_DeleteAllItems(pPrincipal->m_hAdditionalDeny);
bDirty = TRUE;
// Update the other controls (this happens automatically in the
// ListView_DeleteItem case below).
OnSelChange(hDlg); } else { // Notify the user that we can't remove inherited ACEs.
MsgPopup(hDlg, MAKEINTRESOURCE(IDS_PERM_CANT_REMOVE), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONWARNING, ::hModule, pPrincipal->GetName()); } } else { ListView_DeleteItem(hwndList, iIndex); //
// If we just removed the only item, move the focus to the Add button
// (the Remove button will be disabled in OnSelChange).
//
int cItems = ListView_GetItemCount(hwndList); if (cItems == 0) SetFocus(GetDlgItem(hDlg, IDC_SPP_ADD)); else { // If we deleted the last one, select the previous one
if (cItems <= iIndex) --iIndex;
SelectListViewItem(hwndList, iIndex); //
//Key board focus is getting lost at this point
//set it to REMOVE button.
//
SetFocus(GetDlgItem(hDlg, IDC_SPP_REMOVE)); } bDirty = TRUE; }
// Notify the property sheet that we've changed
if (bDirty) SetDirty(hDlg); }
TraceLeaveVoid(); }
void CPermPage::OnAdvanced(HWND hDlg) { LPSECURITYINFO psi;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnAdvanced");
//
//Don't go to Advanced page, if DACL size is more than
//maximum allowed.
//
if (m_fPageDirty && (S_FALSE == BuildDacl(hDlg, NULL, FALSE))) TraceLeaveVoid();
//
// Create an ISecurityInformation wrapper to give to the advanced
// dialog. The wrapper intercepts GetSecurity & SetSecurity.
//
psi = new CSecurityInfo(this, hDlg);
if (psi != NULL) { // Invoke the advanced ACL editor
EditSecurityEx(hDlg, psi,this, 0); psi->Release(); // release initial reference
} else { MsgPopup(hDlg, MAKEINTRESOURCE(IDS_OUT_OF_MEMORY), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONERROR, ::hModule); }
TraceLeaveVoid(); }
void CPermPage::EnablePrincipalControls(HWND hDlg, BOOL fEnable) { TraceEnter(TRACE_PERMPAGE, "CPermPage::EnablePrincipalControls");
EnableWindow(GetDlgItem(hDlg, IDC_SPP_PERMS), fEnable);
if (!fEnable) { ShowWindow(GetDlgItem(hDlg, IDC_SPP_MORE_MSG), SW_HIDE); } else { #if 0
LPPRINCIPAL pPrincipal = (LPPRINCIPAL)GetSelectedItemData(GetDlgItem(hDlg, IDC_SPP_PRINCIPALS), NULL);
// If the selected principal has only inherited ACEs, then disable
// the Remove button.
if (pPrincipal && pPrincipal->GetAclLength(ACL_INHERITED) > 0 && pPrincipal->GetAclLength(ACL_NONINHERITED) == 0) { fEnable = FALSE; } #endif
} EnableWindow(GetDlgItem(hDlg, IDC_SPP_REMOVE), fEnable);
TraceLeaveVoid(); }
void CPermPage::CommitCurrent(HWND hDlg, int iPrincipal) { // Commit any outstanding bit changes
HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS);
TraceEnter(TRACE_PERMPAGE, "CPermPage::CommitCurrent");
// If an index isn't provided, get the index of the currently
// selected principal.
if (iPrincipal == -1) iPrincipal = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
if (iPrincipal != -1) { // Get the Principal from the selection.
LV_ITEM lvItem; lvItem.mask = LVIF_PARAM; lvItem.iItem = iPrincipal; lvItem.iSubItem = 0; lvItem.lParam = 0;
ListView_GetItem(hwndList, &lvItem); LPPRINCIPAL pPrincipal = (LPPRINCIPAL)lvItem.lParam;
if (pPrincipal != NULL) { // Get new ACEs from the checklist window
HDPA hAceEntries = DPA_Create(4);
if (hAceEntries != NULL) { hwndList = GetDlgItem(hDlg, IDC_SPP_PERMS); UINT iCount = GetAcesFromCheckList(hwndList, pPrincipal->GetSID(), TRUE, FALSE, 0, &GUID_NULL, hAceEntries);
// Merge new ACEs into the principal
while (iCount != 0) { --iCount; PACE_HEADER pAce = (PACE_HEADER)DPA_FastGetPtr(hAceEntries, iCount); // Shouldn't get any inherited ACEs here
TraceAssert(!(pAce->AceFlags & INHERITED_ACE)); pPrincipal->AddAce(pAce); LocalFree(pAce); DPA_DeletePtr(hAceEntries, iCount); }
TraceAssert(DPA_GetPtrCount(hAceEntries) == 0); DPA_Destroy(hAceEntries); } } }
TraceLeaveVoid(); }
void CPermPage::OnSize(HWND hDlg, DWORD dwSizeType, ULONG /*nWidth*/, ULONG /*nHeight*/) { RECT rc; RECT rcDlg; LONG dx; LONG dy; HWND hwndAdvButton; HWND hwndPermList; HWND hwndPrincipalList; HWND hwndBottom; HWND hwnd; LONG i;
TraceEnter(TRACE_PERMPAGE, "CPermPage::OnSize");
if (dwSizeType != SIZE_RESTORED) TraceLeaveVoid();
hwndPrincipalList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); hwndPermList = GetDlgItem(hDlg, IDC_SPP_PERMS); hwndAdvButton = GetDlgItem(hDlg, IDC_SPP_ADVANCED); GetClientRect(hDlg, &rcDlg);
GetWindowRect(hwndPrincipalList, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); // map from screen to dlg
InflateRect(&rcDlg, -rc.left, -rc.top); // account for margins
if (GetWindowLong(hwndAdvButton, GWL_STYLE) & WS_VISIBLE) { hwndBottom = hwndAdvButton; } else { hwndBottom = hwndPermList; }
GetWindowRect(hwndBottom, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);
dy = rcDlg.bottom - rc.bottom;
GetWindowRect(hwndPermList, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);
dx = rcDlg.right - rc.right;
//
// Never make things smaller, and only make things
// bigger if the change is worthwhile.
//
dx = max(dx, 0); if (dx < 5) dx = 0; dy = max(dy, 0); if (dy < 5) dy = 0;
//
// Reposition and/or resize all controls
//
if (dx > 0 || dy > 0) { // Add, Remove, Reset buttons
for (i = IDC_SPP_ADD; i <= IDC_SPP_REMOVE; i++) { hwnd = GetDlgItem(hDlg, i); GetWindowRect(hwnd, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwnd, NULL, rc.left + dx, rc.top + dy/2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); } }
if (dx > 0 || dy > 0) { // Listview containing User/Group names
GetWindowRect(hwndPrincipalList, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwndPrincipalList, NULL, 0, 0, rc.right - rc.left + dx, rc.bottom - rc.top + dy/2, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
// Widen the name column if necessary
GetClientRect(hwndPrincipalList, &rc); if (ListView_GetColumnWidth(hwndPrincipalList, 0) < rc.right) ListView_SetColumnWidth(hwndPrincipalList, 0, rc.right); }
if (dy > 0 || dx > 0) { // Static control "Access"
hwnd = GetDlgItem(hDlg, IDC_SPP_ACCESS); GetWindowRect(hwnd, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwnd, NULL, rc.left, rc.top + dy/2, rc.right - rc.left + dx, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); //Static control Big Permission Label
hwnd = GetDlgItem(hDlg, IDC_SPP_ACCESS_BIG); GetWindowRect(hwnd, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwnd, NULL, rc.left, rc.top + dy/2, rc.right - rc.left + dx, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
}
if (dx > 0 || dy > 0) { // Static controls "Allow" and "Deny"
for (i = IDC_SPP_ALLOW; i <= IDC_SPP_DENY; i++) { hwnd = GetDlgItem(hDlg, i); GetWindowRect(hwnd, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwnd, NULL, rc.left + dx, rc.top + dy/2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); }
// List of permission checkboxes
GetWindowRect(hwndPermList, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwndPermList, NULL, rc.left, rc.top + dy/2, rc.right - rc.left + dx, rc.bottom - rc.top + dy/2, SWP_NOACTIVATE | SWP_NOZORDER); }
if (dy > 0 || dx > 0) { // Advanced button
GetWindowRect(hwndAdvButton, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwndAdvButton, NULL, rc.left + dx, rc.top + dy, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
// "More stuff is present but not viewable" message
hwnd = GetDlgItem(hDlg, IDC_SPP_STATIC_ADV); GetWindowRect(hwnd, &rc); MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2); SetWindowPos(hwnd, NULL, rc.left, rc.top + dy, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
TraceLeaveVoid(); }
void CPermPage::ClearPermissions(HWND hwndList, BOOL bDisabled) { // Uncheck everything
UINT cRights = 0; DWORD dwState = CLST_UNCHECKED;
if (bDisabled) dwState |= CLST_DISABLED;
if (hwndList) cRights = (UINT)SendMessage(hwndList, CLM_GETITEMCOUNT, 0, 0);
while (cRights > 0) { cRights--; SendMessage(hwndList, CLM_SETSTATE, MAKELONG((WORD)cRights, 1), dwState); SendMessage(hwndList, CLM_SETSTATE, MAKELONG((WORD)cRights, 2), dwState); }
if(m_bCustomPermission) { ClearCustom(hwndList,1); ClearCustom(hwndList,2); } }
void CPermPage::SetDirty(HWND hDlg, BOOL bDefault) { if (!bDefault) m_wSDControl &= ~SE_DACL_DEFAULTED; m_fPageDirty = TRUE; PropSheet_Changed(GetParent(hDlg), hDlg); }
BOOL CPermPage::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { // First check to see if its time to update listview names
if (uMsg == UM_SIDLOOKUPCOMPLETE) { HWND hwndList = GetDlgItem(hDlg, IDC_SPP_PRINCIPALS); SetPrincipalNamesInList(hwndList, (PSID)lParam); SetPermLabelText(hDlg);
// lParam is zero when all remaining names are looked up
if (0 == lParam) { // Sort using the real names
ListView_SortItems(hwndList, NULL, 0);
// Make sure the selected item is visible
int iSelItem; if (NULL == GetSelectedItemData(hwndList, &iSelItem)) { // No selection, select the first item
SelectListViewItem(hwndList, 0); } else { ListView_EnsureVisible(hwndList, iSelItem, FALSE); }
// Show normal cursor now
m_fBusy = FALSE; SetCursor(LoadCursor(NULL, IDC_ARROW));
// Enable the Advanced button if appropriate
EnableWindow(GetDlgItem(hDlg, IDC_SPP_ADVANCED), (m_siObjectInfo.dwFlags & SI_ADVANCED)); } return TRUE; }
switch(uMsg) { case WM_SETCURSOR: if (m_fBusy) { SetCursor(m_hcurBusy); SetWindowLong(hDlg, DWLP_MSGRESULT, TRUE); return TRUE; } else return FALSE; break;
case WM_INITDIALOG: return InitDlg(hDlg);
case WM_NOTIFY: return OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam);
case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_SPP_ADD: OnAddPrincipal(hDlg); break;
case IDC_SPP_REMOVE: OnRemovePrincipal(hDlg); break;
case IDC_SPP_ADVANCED: OnAdvanced(hDlg); break;
case IDC_SPP_PRINCIPALS: if (GET_WM_COMMAND_CMD(wParam, lParam) == IDN_CHECKSELECTION) { // See if we have gotten a new selection. If not, then the
// user must have clicked inside the listview but not on an item,
// thus causing the listview to remove the selection. In that
// case, disable the other controls
if (ListView_GetSelectedCount(GET_WM_COMMAND_HWND(wParam, lParam)) == 0) { // Uncheck everything first
ClearPermissions(GetDlgItem(hDlg, IDC_SPP_PERMS)); EnablePrincipalControls(hDlg, FALSE); } } break;
default: // Command not handled
return FALSE; } break;
case WM_SIZE: OnSize(hDlg, (LONG)wParam, (ULONG)LOWORD(lParam), (ULONG)HIWORD(lParam)); break;
case WM_HELP: if (IsWindowEnabled(hDlg)) { WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, c_szAcluiHelpFile, HELP_WM_HELP, (DWORD_PTR)aPermPageHelpIDs); } break;
case WM_CONTEXTMENU: if (IsWindowEnabled(hDlg)) { HWND hwnd = (HWND)wParam;
//
// Some of the checkboxes may be scrolled out of view, but
// they are still detected by WinHelp, so we jump through
// a few extra hoops here.
//
if (hwnd == hDlg) { POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam);
ScreenToClient(hDlg, &pt); hwnd = ChildWindowFromPoint(hDlg, pt); if (hDlg == hwnd) break; }
//
// WinHelp looks for child windows, but we don't have help id's
// for the permission checkboxes. If the request is for the
// checklist window, fake out WinHelp by referring to one of
// the static labels just above the list.
//
if (GetDlgCtrlID(hwnd) == IDC_SPP_PERMS) hwnd = GetDlgItem(hDlg, IDC_SPP_ACCESS);
WinHelp(hwnd, c_szAcluiHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)aPermPageHelpIDs); } break;
default: // Message not handled
return FALSE; }
return TRUE; }
//
// CSecurityInfo implementation
//
STDMETHODIMP_(ULONG) CSecurityInfo::AddRef() { return ++m_cRef; }
STDMETHODIMP_(ULONG) CSecurityInfo::Release() { if (--m_cRef == 0) { delete this; return 0; }
return m_cRef; }
STDMETHODIMP CSecurityInfo::QueryInterface(REFIID riid, LPVOID FAR* ppv) { *ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ISecurityInformation)) *ppv = static_cast<LPSECURITYINFO>(this); else if (IsEqualIID(riid, IID_ISecurityInformation2)) { if (m_pPage->m_psi2) *ppv = static_cast<LPSECURITYINFO2>(this); } else if (IsEqualIID(riid, IID_IEffectivePermission)) { if(m_pPage->m_pei) *ppv = static_cast<LPEFFECTIVEPERMISSION>(this); } else if (IsEqualIID(riid, IID_ISecurityObjectTypeInfo)) { if(m_pPage->m_psoti) *ppv = static_cast<LPSecurityObjectTypeInfo>(this);
}
#if(_WIN32_WINNT >= 0x0500)
else if (IsEqualIID(riid, IID_IDsObjectPicker)) *ppv = static_cast<IDsObjectPicker*>(this); #endif
if (*ppv) { m_cRef++; return S_OK; }
return E_NOINTERFACE; }
STDMETHODIMP CSecurityInfo::GetObjectInformation(PSI_OBJECT_INFO pObjectInfo) { TraceEnter(TRACE_SI, "CSecurityInfo::GetObjectInformation"); TraceAssert(m_pPage != NULL);
*pObjectInfo = m_pPage->m_siObjectInfo;
TraceLeaveResult(S_OK); }
STDMETHODIMP CSecurityInfo::GetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR *ppSD, BOOL fDefault) { HRESULT hr;
TraceEnter(TRACE_SI, "CSecurityInfo::GetSecurity"); TraceAssert(si != 0); TraceAssert(ppSD != NULL); TraceAssert(m_pPage != NULL); TraceAssert(m_hDlg != NULL);
*ppSD = NULL;
//Effective permission page calls with si = DACL + OWNER + GROUP and it should
//return Actual Security Descriptor. Other pages calls with only one thing at a time
//and we build dacl and return it if its dirty.
if (!fDefault && (si == DACL_SECURITY_INFORMATION) && m_pPage->m_fPageDirty) { // We only get asked for one thing at a time
TraceAssert(si == DACL_SECURITY_INFORMATION);
// Return current DACL, including inherited ACEs
hr = m_pPage->BuildDacl(m_hDlg, ppSD, TRUE); } else { TraceAssert(m_pPage->m_psi != NULL);
// Get it from the object
hr = m_pPage->m_psi->GetSecurity(si, ppSD, fDefault); }
TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::SetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD) { HRESULT hr = E_FAIL;
TraceEnter(TRACE_SI, "CSecurityInfo::SetSecurity"); TraceAssert(si != 0); TraceAssert(pSD != NULL); TraceAssert(m_pPage != NULL); TraceAssert(m_hDlg != NULL);
// Write out the new security descriptor
if (m_pPage->m_psi != NULL) hr = m_pPage->m_psi->SetSecurity(si, pSD);
if (S_OK == hr && (si & DACL_SECURITY_INFORMATION)) { PSECURITY_DESCRIPTOR psd = NULL;
m_pPage->m_fPageDirty = FALSE;
// Read the new DACL back from the object, that is, don't use the one
// from the passed-in security descriptor. This ensures that we have
// the "real" current DACL in case it was modified somewhere en route.
if (SUCCEEDED(m_pPage->m_psi->GetSecurity(DACL_SECURITY_INFORMATION, &psd, FALSE))) pSD = psd;
// Reinitialize the dialog using the new DACL
m_pPage->SetDacl(m_hDlg, pSD);
if (psd != NULL) LocalFree(psd); }
TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::GetAccessRights(const GUID* pguidObjectType, DWORD dwFlags, PSI_ACCESS *ppAccess, ULONG *pcAccesses, ULONG *piDefaultAccess) { HRESULT hr = E_FAIL;
TraceEnter(TRACE_SI, "CSecurityInfo::GetAccessRights"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_psi != NULL) hr = m_pPage->m_psi->GetAccessRights(pguidObjectType, dwFlags, ppAccess, pcAccesses, piDefaultAccess); TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::MapGeneric(const GUID* pguidObjectType, UCHAR *pAceFlags, ACCESS_MASK *pmask) { HRESULT hr = E_FAIL;
TraceEnter(TRACE_SI, "CSecurityInfo::MapGeneric"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_psi != NULL) hr = m_pPage->m_psi->MapGeneric(pguidObjectType, pAceFlags, pmask);
TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes) { HRESULT hr = E_FAIL;
TraceEnter(TRACE_SI, "CSecurityInfo::GetInheritTypes"); TraceAssert(m_pPage != NULL); TraceAssert(ppInheritTypes != NULL); TraceAssert(pcInheritTypes != NULL);
*ppInheritTypes = NULL; *pcInheritTypes = 0;
if (m_pPage->m_psi != NULL) hr = m_pPage->m_psi->GetInheritTypes(ppInheritTypes, pcInheritTypes); TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::PropertySheetPageCallback(HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage) { HRESULT hr = S_OK;
TraceEnter(TRACE_SI, "CSecurityInfo::PropertySheetPageCallback"); TraceAssert(m_pPage != NULL);
//
// Pass the call on to the client
//
if (m_pPage->m_psi != NULL) hr = m_pPage->m_psi->PropertySheetPageCallback(hwnd, uMsg, uPage);
//
// If the simple perm page is disabled, make sure the advanced perm
// page is as well.
//
if (SUCCEEDED(hr) && uPage == SI_PAGE_ADVPERM && m_pPage->m_bAbortPage) hr = E_FAIL;
TraceLeaveResult(hr); }
//
// ISecurityInformation2 methods
//
STDMETHODIMP_(BOOL) CSecurityInfo::IsDaclCanonical(PACL pDacl) { BOOL bResult = TRUE;
TraceEnter(TRACE_SI, "CSecurityInfo::IsDaclCanonical"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_psi2 != NULL) bResult = m_pPage->m_psi2->IsDaclCanonical(pDacl);
TraceLeaveValue(bResult); }
STDMETHODIMP CSecurityInfo::LookupSids(ULONG cSids, PSID *rgpSids, LPDATAOBJECT *ppdo) { HRESULT hr = E_NOTIMPL;
TraceEnter(TRACE_SI, "CSecurityInfo::LookupSids"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_psi2 != NULL) hr = m_pPage->m_psi2->LookupSids(cSids, rgpSids, ppdo);
TraceLeaveResult(hr); }
//
// IDsObjectPicker methods
//
#if(_WIN32_WINNT >= 0x0500)
STDMETHODIMP CSecurityInfo::Initialize(PDSOP_INIT_INFO pInitInfo) { HRESULT hr; IDsObjectPicker *pObjectPicker = NULL;
hr = m_pPage->GetObjectPicker(&pObjectPicker);
if (SUCCEEDED(hr)) { if (m_pPage->m_flLastOPOptions != pInitInfo->flOptions) { m_pPage->m_flLastOPOptions = (DWORD)-1;
hr = pObjectPicker->Initialize(pInitInfo);
if (SUCCEEDED(hr)) { m_pPage->m_flLastOPOptions = pInitInfo->flOptions; } } pObjectPicker->Release(); }
return hr; }
STDMETHODIMP CSecurityInfo::InvokeDialog(HWND hwndParent, IDataObject **ppdoSelection) { HRESULT hr; IDsObjectPicker *pObjectPicker = NULL;
hr = m_pPage->GetObjectPicker(&pObjectPicker);
if (SUCCEEDED(hr)) { hr = pObjectPicker->InvokeDialog(hwndParent, ppdoSelection); pObjectPicker->Release(); }
return hr; } #endif // _WIN32_WINNT >= 0x0500
STDMETHODIMP CSecurityInfo::GetInheritSource(SECURITY_INFORMATION si, PACL pACL, PINHERITED_FROM *ppInheritArray) { HRESULT hr = E_NOTIMPL;
TraceEnter(TRACE_SI, "CSecurityInfo::GetInheritSource"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_psoti) hr = m_pPage->m_psoti->GetInheritSource(si, pACL, ppInheritArray);
TraceLeaveResult(hr); }
STDMETHODIMP CSecurityInfo::GetEffectivePermission( THIS_ const GUID* pguidObjectType, PSID pUserSid, LPCWSTR pszServerName, PSECURITY_DESCRIPTOR pSD, POBJECT_TYPE_LIST *ppObjectTypeList, ULONG *pcObjectTypeListLength, PACCESS_MASK *ppGrantedAccessList, ULONG *pcGrantedAccessListLength) { HRESULT hr = E_NOTIMPL;
TraceEnter(TRACE_SI, "CSecurityInfo::GetEffectivePermission"); TraceAssert(m_pPage != NULL);
if (m_pPage->m_pei) hr = m_pPage->m_pei->GetEffectivePermission(pguidObjectType, pUserSid, pszServerName, pSD, ppObjectTypeList, pcObjectTypeListLength, ppGrantedAccessList, pcGrantedAccessListLength);
TraceLeaveResult(hr); }
//
// Expose an api to get at the simple permission editor
//
HPROPSHEETPAGE ACLUIAPI CreateSecurityPage(LPSECURITYINFO psi) { HPROPSHEETPAGE hPage = NULL; PPERMPAGE pPage; PSIDCACHE pSidCache;
TraceEnter(TRACE_PERMPAGE, "CreateSecurityPage");
// Create the global SID Cache
pSidCache = GetSidCache();
if (NULL == psi) { SetLastError(ERROR_INVALID_PARAMETER); TraceLeaveValue(NULL); }
pPage = new CPermPage(psi);
if (pPage) { SI_OBJECT_INFO siObjectInfo = {0}; LPCTSTR pszTitle = NULL;
if (SUCCEEDED(psi->GetObjectInformation(&siObjectInfo)) && (siObjectInfo.dwFlags & SI_PAGE_TITLE)) { pszTitle = siObjectInfo.pszPageTitle; }
hPage = pPage->CreatePropSheetPage(MAKEINTRESOURCE(IDD_SIMPLE_PERM_PAGE), pszTitle);
if (!hPage) delete pPage; }
if (pSidCache) pSidCache->Release();
TraceLeaveValue(hPage); }
BOOL ACLUIAPI EditSecurity( HWND hwndOwner, LPSECURITYINFO psi ) { HPROPSHEETPAGE hPage[1]; UINT cPages = 0; BOOL bResult = FALSE; SI_OBJECT_INFO siObjectInfo = {0}; HRESULT hr;
TraceEnter(TRACE_PERMPAGE, "EditSecurity");
// Get object name for dialog title
hr = psi->GetObjectInformation(&siObjectInfo);
if (FAILED(hr)) { if (!GetLastError()) SetLastError(hr);
TraceLeaveValue(FALSE); }
hPage[cPages] = CreateSecurityPage( psi ); if (hPage[cPages]) cPages++;
if (cPages) { // Build dialog title string
LPTSTR pszCaption = NULL;
PROPSHEETHEADER psh; psh.dwSize = SIZEOF(psh); psh.dwFlags = PSH_DEFAULT; psh.hwndParent = hwndOwner; psh.hInstance = ::hModule; psh.nPages = cPages; psh.nStartPage = 0; psh.phpage = &hPage[0];
// There has been a request for customization of this dialog title,
// but this probably isn't the best way to do it, since the dlg title
// and page title will be the same.
#if 0
if ((siObjectInfo.dwFlags & SI_PAGE_TITLE) && siObjectInfo.pszPageTitle && siObjectInfo.pszPageTitle[0]) { psh.pszCaption = siObjectInfo.pszPageTitle; } else #endif
{ FormatStringID(&pszCaption, ::hModule, IDS_SPP_TITLE, siObjectInfo.pszObjectName); psh.pszCaption = pszCaption; }
bResult = (BOOL)(PropertySheet(&psh) + 1);
LocalFreeString(&pszCaption); }
TraceLeaveValue(bResult); } //
|