windows-nt/Source/XPSP1/NT/shell/osshell/security/aclui/owner.cpp
2020-09-26 16:20:57 +08:00

745 lines
21 KiB
C++

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