#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(this); else if (IsEqualIID(riid, IID_ISecurityInformation2)) { if (m_pPage->m_psi2) *ppv = static_cast(this); } else if (IsEqualIID(riid, IID_IEffectivePermission)) { if(m_pPage->m_pei) *ppv = static_cast(this); } else if (IsEqualIID(riid, IID_ISecurityObjectTypeInfo)) { if(m_pPage->m_psoti) *ppv = static_cast(this); } #if(_WIN32_WINNT >= 0x0500) else if (IsEqualIID(riid, IID_IDsObjectPicker)) *ppv = static_cast(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); } //