//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: owner.cpp // // This file contains the implementation of the Owner page. // //-------------------------------------------------------------------------- #include "aclpriv.h" #include "sddl.h" // ConvertSidToStringSid // // Context Help IDs. // const static DWORD aOwnerHelpIDs[] = { IDC_OWN_CURRENTOWNER_STATIC, IDH_OWN_CURRENTOWNER, IDC_OWN_CURRENTOWNER, IDH_OWN_CURRENTOWNER, IDC_OWN_OWNERLIST_STATIC, IDH_OWN_OWNERLIST, IDC_OWN_OWNERLIST, IDH_OWN_OWNERLIST, IDC_OWN_RECURSE, IDH_OWN_RECURSE, IDC_OWN_RESET, IDH_OWN_RESET, IDC_ACEL_STATIC, -1, 0, 0 }; // // These SIDs are always added to the list of possible owners // const static UI_TokenSid g_uiTokenSids[] = { UI_TSID_CurrentProcessUser, UI_TSID_CurrentProcessOwner, //UI_TSID_CurrentProcessPrimaryGroup, }; class COwnerPage : public CSecurityPage { private: PSID m_psidOriginal; PSID m_psidNetID; HANDLE m_hSidThread; public: COwnerPage(LPSECURITYINFO psi, SI_OBJECT_INFO *psiObjectInfo); virtual ~COwnerPage(void); private: virtual BOOL DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); void InitDlg(HWND hDlg); int AddSid(HWND hOwner, PSID psid, LPCTSTR pszServerName = NULL); void OnApply(HWND hDlg, BOOL bClose); void OnReset(HWND hDlg); }; HPROPSHEETPAGE CreateOwnerPage(LPSECURITYINFO psi, SI_OBJECT_INFO *psiObjectInfo) { HPROPSHEETPAGE hPage = NULL; COwnerPage *pPage; TraceEnter(TRACE_OWNER, "CreateOwnerPage"); pPage = new COwnerPage(psi, psiObjectInfo); if (pPage) { hPage = pPage->CreatePropSheetPage(MAKEINTRESOURCE(IDD_OWNER_PAGE)); if (!hPage) delete pPage; } TraceLeaveValue(hPage); } COwnerPage::COwnerPage(LPSECURITYINFO psi, SI_OBJECT_INFO *psiObjectInfo) : CSecurityPage(psi, SI_PAGE_OWNER), m_psidOriginal(NULL), m_psidNetID(NULL), m_hSidThread(NULL) { // Lookup known SIDs asynchronously so the dialog // will initialize faster HDPA hSids = DPA_Create(ARRAYSIZE(g_uiTokenSids)); if (hSids) { USES_CONVERSION; LPCWSTR pszServer = NULL; if (psiObjectInfo) pszServer = psiObjectInfo->pszServerName; for (int i = 0; i < ARRAYSIZE(g_uiTokenSids); i++) DPA_AppendPtr(hSids, QueryTokenSid(g_uiTokenSids[i])); m_psidNetID = GetAuthenticationID(pszServer); if (m_psidNetID) DPA_AppendPtr(hSids, m_psidNetID); LookupSidsAsync(hSids, W2CT(pszServer), m_psi2, NULL, 0, &m_hSidThread); DPA_Destroy(hSids); } } COwnerPage::~COwnerPage(void) { if (m_hSidThread) CloseHandle(m_hSidThread); if (m_psidOriginal) LocalFree(m_psidOriginal); if (m_psidNetID) LocalFree(m_psidNetID); } int COwnerPage::AddSid(HWND hOwner, PSID psid, LPCTSTR pszServerName) { PUSER_LIST pUserList = NULL; SID_NAME_USE sidType = SidTypeUnknown; LPCTSTR pszName = NULL; LPCTSTR pszLogonName = NULL; int iItem = -1; int cItems; LV_ITEM lvItem; TraceEnter(TRACE_OWNER, "COwnerPage::AddSid"); TraceAssert(!m_bAbortPage); if (!psid || !IsValidSid(psid)) ExitGracefully(iItem, -1, "Bad SID parameter"); // Get the name for this SID if (LookupSid(psid, pszServerName, m_psi2, &pUserList)) { TraceAssert(NULL != pUserList); TraceAssert(1 == pUserList->cUsers); sidType = pUserList->rgUsers[0].SidType; pszName = pUserList->rgUsers[0].pszName; pszLogonName = pUserList->rgUsers[0].pszLogonName; } switch (sidType) { case SidTypeDomain: case SidTypeDeletedAccount: case SidTypeInvalid: case SidTypeUnknown: case SidTypeComputer: ExitGracefully(iItem, -1, "SID invalid on target"); break; } cItems = ListView_GetItemCount(hOwner); lvItem.mask = LVIF_PARAM; lvItem.iSubItem = 0; // See if this SID is already in the list for (iItem = 0; iItem < cItems; iItem++) { lvItem.iItem = iItem; lvItem.lParam = NULL; ListView_GetItem(hOwner, &lvItem); if (lvItem.lParam && EqualSid(psid, (PSID)lvItem.lParam)) { // This is a hack. We often see alias sids more than once when // filling the list, e.g. BUILTIN\Administrators. We want to use // the version of the name that includes the target domain, if // provided. That is, if pszServerName is non-NULL here, switch // to the version of the name that goes with pszServerName. if (pszServerName) { lvItem.mask = LVIF_TEXT; lvItem.pszText = NULL; if (BuildUserDisplayName(&lvItem.pszText, pszName, pszLogonName) || ConvertSidToStringSid(psid, &lvItem.pszText)) { ListView_SetItem(hOwner, &lvItem); LocalFreeString(&lvItem.pszText); } } break; } } if (iItem == cItems) { // The SID doesn't exist in the list. Add a new entry. PSID psidCopy = LocalAllocSid(psid); if (psidCopy) { lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvItem.iItem = 0; lvItem.iSubItem = 0; lvItem.pszText = NULL; if (!BuildUserDisplayName(&lvItem.pszText, pszName, pszLogonName)) ConvertSidToStringSid(psid, &lvItem.pszText); lvItem.iImage = GetSidImageIndex(psid, sidType); lvItem.lParam = (LPARAM)psidCopy; // Insert principal into list iItem = ListView_InsertItem(hOwner, &lvItem); LocalFreeString(&lvItem.pszText); } } exit_gracefully: if (NULL != pUserList) LocalFree(pUserList); TraceLeaveValue(iItem); } void COwnerPage::InitDlg(HWND hDlg) { TCHAR szBuffer[MAX_PATH]; BOOL bReadOnly; HWND hOwner = GetDlgItem(hDlg, IDC_OWN_OWNERLIST); HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT)); TraceEnter(TRACE_OWNER, "COwnerPage::InitDlg"); // Hide the Reset button if it isn't supported. if (!(m_siObjectInfo.dwFlags & SI_RESET) && !(m_siObjectInfo.dwFlags & SI_RESET_OWNER)) { ShowWindow(GetDlgItem(hDlg, IDC_OWN_RESET), SW_HIDE); } // Hide the Recurse checkbox if it isn't supported. if ((m_siObjectInfo.dwFlags & (SI_OWNER_RECURSE | SI_CONTAINER)) != (SI_OWNER_RECURSE | SI_CONTAINER)) { m_siObjectInfo.dwFlags &= ~SI_OWNER_RECURSE; HWND hwndRecurse = GetDlgItem(hDlg, IDC_OWN_RECURSE); ShowWindow(hwndRecurse, SW_HIDE); EnableWindow(hwndRecurse, FALSE); } if (m_bAbortPage) { // // Disable everything // bReadOnly = TRUE; } else { // Create & set the image list for the listview ListView_SetImageList(hOwner, LoadImageList(::hModule, MAKEINTRESOURCE(IDB_SID_ICONS)), LVSIL_SMALL); // // Add the "Name" column (the only column on this page) // RECT rc; GetClientRect(hOwner, &rc); LoadString(::hModule, IDS_NAME, szBuffer, ARRAYSIZE(szBuffer)); LV_COLUMN col; col.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH; col.fmt = LVCFMT_LEFT; col.pszText = szBuffer; col.iSubItem = 0; col.cx = rc.right; ListView_InsertColumn(hOwner, 0, &col); // // Make a copy of the current owner sid // PSECURITY_DESCRIPTOR pSD = NULL; HRESULT hr = m_psi->GetSecurity(OWNER_SECURITY_INFORMATION, &pSD, FALSE); if (pSD) { PSID psidOwner = NULL; BOOL bDefaulted; GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted); if (psidOwner) { UINT iLength = GetLengthSid(psidOwner); m_psidOriginal = LocalAlloc(LPTR, iLength); if (m_psidOriginal) CopyMemory(m_psidOriginal, psidOwner, iLength); } LocalFree(pSD); } // Test for writeability bReadOnly = !!(m_siObjectInfo.dwFlags & SI_OWNER_READONLY); } // !m_bAbortPage // // Iterate through the groups on this process's token looking for // the SE_GROUP_OWNER attribute. // if (!bReadOnly) { HANDLE hProcessToken = NULL; // // Wait for the known SIDs to be resolved so we don't try // to look them up twice. // if (m_hSidThread) { WaitForSingleObject(m_hSidThread, INFINITE); CloseHandle(m_hSidThread); m_hSidThread = NULL; } if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) { // Allocate a buffer for the TOKEN_GROUPS information ULONG cbBuffer = 1024; // start with 1k LPVOID pBuffer = LocalAlloc(LPTR, cbBuffer); if (pBuffer) { if (!GetTokenInformation(hProcessToken, TokenGroups, pBuffer, cbBuffer, &cbBuffer)) { LocalFree(pBuffer); pBuffer = NULL; if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pBuffer = LocalAlloc(LPTR, cbBuffer);// size returned above if (pBuffer && !GetTokenInformation(hProcessToken, TokenGroups, pBuffer, cbBuffer, &cbBuffer)) { LocalFree(pBuffer); pBuffer = NULL; } } } if (pBuffer) { PTOKEN_GROUPS ptg = (PTOKEN_GROUPS)pBuffer; for (ULONG i = 0; i < ptg->GroupCount; i++) { DWORD dwAttr = ptg->Groups[i].Attributes; if ((dwAttr & SE_GROUP_OWNER) && !(dwAttr & SE_GROUP_LOGON_ID)) { AddSid(hOwner, ptg->Groups[i].Sid, m_siObjectInfo.pszServerName); } } } if (pBuffer != NULL) LocalFree(pBuffer); } CloseHandle(hProcessToken); } // // Now add in the additional possible sids // for (int i = 0; i < ARRAYSIZE(g_uiTokenSids); i++) AddSid(hOwner, QueryTokenSid(g_uiTokenSids[i])); AddSid(hOwner, m_psidNetID, m_siObjectInfo.pszServerName); } if (!m_bAbortPage) { PUSER_LIST pUserList = NULL; LoadString(::hModule, IDS_OWNER_CANT_DISPLAY, szBuffer, ARRAYSIZE(szBuffer)); // Finally, look up a name for the original SID. if (m_psidOriginal) { LPTSTR pszName = NULL; // Get the "S-1-5-blah" form of the SID in case the lookup fails if (ConvertSidToStringSid(m_psidOriginal, &pszName)) { lstrcpyn(szBuffer, pszName, ARRAYSIZE(szBuffer)); LocalFreeString(&pszName); } if (LookupSid(m_psidOriginal, m_siObjectInfo.pszServerName, m_psi2, &pUserList)) { TraceAssert(NULL != pUserList); TraceAssert(1 == pUserList->cUsers); if (BuildUserDisplayName(&pszName, pUserList->rgUsers[0].pszName, pUserList->rgUsers[0].pszLogonName)) { lstrcpyn(szBuffer, pszName, ARRAYSIZE(szBuffer)); LocalFreeString(&pszName); } LocalFree(pUserList); } } SetDlgItemText(hDlg, IDC_OWN_CURRENTOWNER, szBuffer); } // // If the current user cannot change owners, gray out the list box. // if (bReadOnly) { // Disable the list and notify the user that it's read-only. EnableWindow(hOwner, FALSE); EnableWindow(GetDlgItem(hDlg, IDC_OWN_RESET), FALSE); EnableWindow(GetDlgItem(hDlg, IDC_OWN_RECURSE), FALSE); // // If we're aborting, then the user should have been notified // during the propsheetpage callback. Don't put up another // message here. // if (S_OK == m_hrLastPSPCallbackResult) { MsgPopup(hDlg, MAKEINTRESOURCE(IDS_OWNER_READONLY), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONINFORMATION, ::hModule, m_siObjectInfo.pszObjectName); } } SetCursor(hcur); TraceLeaveVoid(); } void COwnerPage::OnApply(HWND hDlg, BOOL bClose) { int iSelected = -1; HWND hwndOwnerList; PSID psid; BOOL bRecurse = FALSE; SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION; BOOL bEqualSid = FALSE; TraceEnter(TRACE_OWNER, "COwnerPage::OnApply"); hwndOwnerList = GetDlgItem(hDlg, IDC_OWN_OWNERLIST); psid = (PSID)GetSelectedItemData(hwndOwnerList, &iSelected); // If there is no selection, use the original if (!psid) psid = m_psidOriginal; // If no selection and no original, then we can't do anything if (!psid) TraceLeaveVoid(); if ((m_siObjectInfo.dwFlags & SI_OWNER_RECURSE) && IsDlgButtonChecked(hDlg, IDC_OWN_RECURSE) == BST_CHECKED) { bRecurse = TRUE; } // Has anything changed? if (m_psidOriginal && ( (m_psidOriginal == psid) || EqualSid(m_psidOriginal, psid) ) && !bRecurse) { // Nothing has changed TraceLeaveVoid(); } SECURITY_DESCRIPTOR sd = {0}; DWORD dwPriv = SE_TAKE_OWNERSHIP_PRIVILEGE; HANDLE hToken = INVALID_HANDLE_VALUE; HRESULT hr; TraceAssert(!m_bAbortPage); InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorOwner(&sd, psid, FALSE); // // ISecurityInformation::SetSecurity doesn't have a parameter to indicate // that the owner should be recursively applied. We could add a parameter, // but for now, just use one of the unused SECURITY_INFORMATION bits. // The security descriptor structure is unlikely to change so this should // be ok for now. if (bRecurse) si |= SI_OWNER_RECURSE; hToken = EnablePrivileges(&dwPriv, 1); hr = m_psi->SetSecurity(si, &sd); ReleasePrivileges(hToken); 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); } else if (S_OK == hr && !bClose) { //Inform the Effective Permission tab that //Permissions are changed PropSheet_QuerySiblings(GetParent(hDlg),0,0); UINT iLength = GetLengthSid(psid); if (-1 != iSelected) { TCHAR szName[MAX_PATH]; szName[0] = TEXT('\0'); ListView_GetItemText(hwndOwnerList, iSelected, 0, szName, ARRAYSIZE(szName)); SetDlgItemText(hDlg, IDC_OWN_CURRENTOWNER, szName); } if (!(m_psidOriginal && ((m_psidOriginal == psid) || EqualSid(m_psidOriginal, psid)))) { if (m_psidOriginal) { UINT iLengthOriginal = (UINT)LocalSize(m_psidOriginal); if (iLengthOriginal < iLength) { LocalFree(m_psidOriginal); m_psidOriginal = NULL; } else { ZeroMemory(m_psidOriginal, iLengthOriginal); } } if (!m_psidOriginal) m_psidOriginal = LocalAlloc(LPTR, iLength); if (m_psidOriginal) { CopyMemory(m_psidOriginal, psid, iLength); } else { hr = E_OUTOFMEMORY; } } if (m_siObjectInfo.dwFlags & SI_OWNER_RECURSE) CheckDlgButton(hDlg, IDC_OWN_RECURSE, BST_UNCHECKED); } if (FAILED(hr)) { SysMsgPopup(hDlg, MAKEINTRESOURCE(IDS_OWNER_WRITE_FAILED), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONERROR, ::hModule, hr, m_siObjectInfo.pszObjectName); } TraceLeaveVoid(); } void COwnerPage::OnReset(HWND hDlg) { PSECURITY_DESCRIPTOR pSD = NULL; HWND hOwner; PSID psid; HRESULT hr; TraceEnter(TRACE_OWNER, "COwnerPage::OnReset"); TraceAssert(!m_bAbortPage); hOwner = GetDlgItem(hDlg, IDC_OWN_OWNERLIST); psid = (PSID)GetSelectedItemData(hOwner, NULL); hr = m_psi->GetSecurity(OWNER_SECURITY_INFORMATION, &pSD, TRUE); if (SUCCEEDED(hr)) { PSID psidDefault = NULL; BOOL bDefaulted; if (pSD) GetSecurityDescriptorOwner(pSD, &psidDefault, &bDefaulted); if (psidDefault && !EqualSid(psidDefault, psid)) { int iSel = AddSid(hOwner, psidDefault, m_siObjectInfo.pszServerName); if (iSel != -1) { ListView_SetItemState(hOwner, iSel, LVIS_SELECTED, LVIS_SELECTED); PropSheet_Changed(GetParent(hDlg), hDlg); } } LocalFree(pSD); } else { SysMsgPopup(hDlg, MAKEINTRESOURCE(IDS_OPERATION_FAILED), MAKEINTRESOURCE(IDS_SECURITY), MB_OK | MB_ICONERROR, ::hModule, hr); } TraceLeaveVoid(); } BOOL COwnerPage::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { BOOL bResult = TRUE; switch(uMsg) { case WM_INITDIALOG: InitDlg(hDlg); break; case WM_NOTIFY: { LPNM_LISTVIEW pnmlv = (LPNM_LISTVIEW)lParam; switch (((LPNMHDR)lParam)->code) { case LVN_ITEMCHANGED: if (pnmlv->uChanged & LVIF_STATE) { // item *gaining* selection if ((pnmlv->uNewState & LVIS_SELECTED) && !(pnmlv->uOldState & LVIS_SELECTED)) { PropSheet_Changed(GetParent(hDlg), hDlg); } } break; case LVN_DELETEITEM: if (pnmlv->lParam) LocalFree((PSID)pnmlv->lParam); break; case NM_SETFOCUS: if (((LPNMHDR)lParam)->idFrom == IDC_OWN_OWNERLIST) { // Make sure the listview is always focused on something, // otherwise you can't tab into the control. HWND hwndLV = GetDlgItem(hDlg, IDC_OWN_OWNERLIST); if (-1 == ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED)) ListView_SetItemState(hwndLV, 0, LVIS_FOCUSED, LVIS_FOCUSED); } break; case PSN_QUERYINITIALFOCUS: { // Set initial focus to the list of potential owners HWND hwndLV = GetDlgItem(hDlg, IDC_OWN_OWNERLIST); if (IsWindowEnabled(hwndLV)) SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)hwndLV); else bResult = FALSE; } break; case PSN_APPLY: OnApply(hDlg, (BOOL)(((LPPSHNOTIFY)lParam)->lParam)); break; default: bResult = FALSE; } } break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_OWN_RECURSE: if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) PropSheet_Changed(GetParent(hDlg), hDlg); break; case IDC_OWN_RESET: OnReset(hDlg); break; default: bResult = FALSE; } break; case WM_HELP: if (IsWindowEnabled(hDlg)) { WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, c_szAcluiHelpFile, HELP_WM_HELP, (DWORD_PTR)aOwnerHelpIDs); } break; case WM_CONTEXTMENU: if (IsWindowEnabled(hDlg)) { WinHelp(hDlg, c_szAcluiHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)aOwnerHelpIDs); } break; default: bResult = FALSE; } return bResult; }