1915 lines
50 KiB
C
1915 lines
50 KiB
C
/** Module Header **\
|
|
* Module Name: Upeutil.c
|
|
|
|
* Copyright (c) 1992, Microsoft Corporation
|
|
|
|
* Handles notification of key value changes in the registry that affect
|
|
* the Program Manager's groups.
|
|
* All security handling (ACLs on user profile).
|
|
* User Browser call to obtain user sid.
|
|
|
|
* History:
|
|
* 04-16-92 JohanneC Created.
|
|
*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include "upedit.h"
|
|
|
|
|
|
|
|
// Global varaibles and constants used in this module
|
|
|
|
|
|
|
|
SID_IDENTIFIER_AUTHORITY gNtAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY gLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
|
|
PSID gSystemSid; // Initialized in 'InitializeGlobalSids'
|
|
PSID gAdminsLocalGroup; // Initialized in 'InitializeGlobalSids'
|
|
|
|
|
|
|
|
|
|
|
|
// Private Prototypes
|
|
|
|
|
|
|
|
void GetGroupName(HKEY hkeyGroups, LPTSTR lpSubKey, LPTSTR lpGroupName);
|
|
|
|
/*
|
|
* CentreWindow
|
|
|
|
* Purpose : Positions a window so that it is centred in its parent
|
|
|
|
* History:
|
|
* 11-14-92 JohanneC Created.
|
|
*/
|
|
void CentreWindow(HWND hwnd)
|
|
{
|
|
RECT rect;
|
|
RECT rectParent;
|
|
HWND hwndParent;
|
|
LONG dx, dy;
|
|
LONG dxParent, dyParent;
|
|
|
|
// Get window rect
|
|
GetWindowRect(hwnd, &rect);
|
|
|
|
dx = rect.right - rect.left;
|
|
dy = rect.bottom - rect.top;
|
|
|
|
hwndParent = GetDesktopWindow();
|
|
GetWindowRect(hwndParent, &rectParent);
|
|
|
|
dxParent = rectParent.right - rectParent.left;
|
|
dyParent = rectParent.bottom - rectParent.top;
|
|
|
|
// Centre the child in the parent
|
|
rect.left = (dxParent - dx) / 2;
|
|
rect.top = (dyParent - dy) / 3;
|
|
|
|
// Move the child into position
|
|
SetWindowPos(hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
|
|
|
|
//SetForegroundWindow(hwnd);
|
|
}
|
|
|
|
/*
|
|
* CreateProgramGroupsEvent
|
|
|
|
* Create an event to handle a change in the program groups.
|
|
|
|
* History:
|
|
* 04-16-92 JohanneC Created
|
|
*/
|
|
HANDLE APIENTRY CreateProgramGroupsEvent()
|
|
{
|
|
TCHAR szGroups[] = TEXT("ProgmanGroups");
|
|
|
|
return(CreateEvent(NULL, FALSE, FALSE, szGroups));
|
|
}
|
|
|
|
/*
|
|
* ResetProgramGroupsEvent
|
|
|
|
|
|
* History:
|
|
* 04-16-92 JohanneC Created
|
|
*/
|
|
VOID APIENTRY ResetProgramGroupsEvent()
|
|
{
|
|
if (hProgramGroupsEvent && hkeyProgramGroups) {
|
|
ResetEvent(hProgramGroupsEvent);
|
|
RegNotifyChangeKeyValue(hkeyProgramGroupsCurrent, TRUE, REG_NOTIFY_CHANGE_NAME,
|
|
hProgramGroupsEvent, TRUE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HasProgramGroupsKeyChanged
|
|
|
|
* returns TRUE if a program group was modified.
|
|
|
|
* History:
|
|
* 04-16-92 JohanneC Created
|
|
*/
|
|
BOOL APIENTRY HasProgramGroupsKeyChanged()
|
|
{
|
|
DWORD dwTimeOut = 2;
|
|
DWORD err;
|
|
|
|
if (!hProgramGroupsEvent) {
|
|
return(FALSE);
|
|
}
|
|
if ((err = WaitForSingleObject(hProgramGroupsEvent, dwTimeOut))
|
|
!= WAIT_TIMEOUT)
|
|
RegNotifyChangeKeyValue(hkeyProgramGroupsCurrent, TRUE, REG_NOTIFY_CHANGE_NAME,
|
|
hProgramGroupsEvent, TRUE);
|
|
|
|
if (err || !bWorkFromCurrent) {
|
|
|
|
// Don't update the groups lists if we are not showing the current
|
|
// profile information in UPE.
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HandleProgramGroupsKeyChange
|
|
|
|
|
|
* History:
|
|
* 04-16-91 Johannec Created
|
|
*/
|
|
VOID APIENTRY HandleProgramGroupsKeyChange(HWND hwnd)
|
|
{
|
|
int i = 0;
|
|
int index = 0;
|
|
int cb;
|
|
DWORD cbGroupKey = MAXKEYLEN;
|
|
TCHAR szGroupKey[MAXKEYLEN];
|
|
TCHAR szText[MAXKEYLEN];
|
|
HKEY hkey;
|
|
HWND hwndLocked;
|
|
HWND hwndUnlocked;
|
|
FILETIME ft;
|
|
BOOL bFoundIt;
|
|
LPGROUPDATA lpGroupData;
|
|
|
|
ResetEvent(hProgramGroupsEvent);
|
|
hwndLocked = GetDlgItem(hwnd, IDD_LOCKEDGRPS);
|
|
hwndUnlocked = GetDlgItem(hwnd, IDD_UNLOCKEDGRPS);
|
|
|
|
if ((cb = (int)SendMessage(hwndLocked, LB_GETCOUNT, 0, 0)) != LB_ERR)
|
|
while (index < cb) {
|
|
if (lpGroupData = (LPGROUPDATA)SendMessage(hwndLocked, LB_GETITEMDATA, index, 0)){
|
|
if (!lpGroupData->lpGroupKey) {
|
|
index++;
|
|
continue;
|
|
}
|
|
lstrcpy(szText, lpGroupData->lpGroupKey);
|
|
if (RegOpenKeyEx(hkeyProgramGroups, szText, 0, KEY_READ, &hkey) != ERROR_SUCCESS) {
|
|
SendMessage(hwndLocked, LB_DELETESTRING, index, 0);
|
|
|
|
// delete it from the startup group combobox.
|
|
if ((i = (int)SendDlgItemMessage(hwnd, IDD_STARTUP, CB_FINDSTRING, (WPARAM)-1, (LPARAM)szText)) != CB_ERR) {
|
|
SendDlgItemMessage(hwnd, IDD_STARTUP, CB_DELETESTRING, i, 0);
|
|
}
|
|
index--;
|
|
cb--;
|
|
}
|
|
else
|
|
RegCloseKey(hkey);
|
|
}
|
|
index++;
|
|
}
|
|
index = 0;
|
|
if ((cb = (int)SendMessage(hwndUnlocked, LB_GETCOUNT, 0, 0)) != LB_ERR)
|
|
while (index < cb) {
|
|
if (lpGroupData = (LPGROUPDATA)SendMessage(hwndUnlocked, LB_GETITEMDATA, index, 0)){
|
|
if (!lpGroupData->lpGroupKey) {
|
|
index++;
|
|
continue;
|
|
}
|
|
lstrcpy(szText, lpGroupData->lpGroupKey);
|
|
if (RegOpenKeyEx(hkeyProgramGroups, szText, 0, KEY_READ, &hkey) != ERROR_SUCCESS) {
|
|
// can't find group so delete it from the list box.
|
|
SendMessage(hwndUnlocked, LB_DELETESTRING, index, 0);
|
|
|
|
// delete it from the startup group combobox.
|
|
|
|
if ((i = (int)SendDlgItemMessage(hwnd, IDD_STARTUP, CB_FINDSTRING, (WPARAM)-1, (LPARAM)szText)) != CB_ERR) {
|
|
SendDlgItemMessage(hwnd, IDD_STARTUP, CB_DELETESTRING, i, 0);
|
|
}
|
|
index--;
|
|
cb--;
|
|
}
|
|
else
|
|
RegCloseKey(hkey);
|
|
}
|
|
index++;
|
|
}
|
|
|
|
|
|
index = 0;
|
|
while (!RegEnumKeyEx(hkeyProgramGroups, index, szGroupKey, &cbGroupKey, 0, 0, 0, &ft)) {
|
|
if (!cbGroupKey)
|
|
goto NextKey;
|
|
|
|
// check if it is in the the list boxes, if not ADD the new group
|
|
// to the unlocked listbox.
|
|
// need to find a way to remove groups that were deleted from
|
|
// the program manager...
|
|
bFoundIt = FALSE;
|
|
|
|
// check the locked group list box
|
|
|
|
|
|
if ((cb = (int)SendMessage(hwndLocked, LB_GETCOUNT, 0, 0)) != LB_ERR) {
|
|
for (i = 0; i < cb; i++) {
|
|
if ((INT_PTR)(lpGroupData = (LPGROUPDATA)SendMessage(hwndLocked, LB_GETITEMDATA, i, 0))
|
|
!= LB_ERR) {
|
|
|
|
if (lpGroupData-> lpGroupKey &&
|
|
!lstrcmp(lpGroupData->lpGroupKey, szGroupKey)) {
|
|
bFoundIt = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bFoundIt)
|
|
goto NextKey;
|
|
|
|
// check the unlocked group list box
|
|
|
|
if ((cb = (int)SendMessage(hwndUnlocked, LB_GETCOUNT, 0, 0)) != LB_ERR) {
|
|
for (i = 0; i < cb; i++) {
|
|
if ((INT_PTR)(lpGroupData = (LPGROUPDATA)SendMessage(hwndUnlocked, LB_GETITEMDATA, i, 0))
|
|
!= LB_ERR) {
|
|
if (lpGroupData-> lpGroupKey &&
|
|
!lstrcmp(lpGroupData->lpGroupKey, szGroupKey)) {
|
|
bFoundIt = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bFoundIt)
|
|
goto NextKey;
|
|
|
|
|
|
// didn't find the group, add it to one of the list boxes.
|
|
|
|
GetGroupName(hkeyProgramGroups, szGroupKey, szText);
|
|
if (*szText) {
|
|
if (!(lpGroupData = (LPGROUPDATA)GlobalAlloc(GPTR, sizeof(GROUPDATA))))
|
|
goto NextKey;
|
|
|
|
if (lpGroupData->lpGroupKey = (LPTSTR) GlobalAlloc(GPTR,
|
|
(lstrlen(szGroupKey) + 1) * sizeof(TCHAR))) {
|
|
lstrcpy(lpGroupData->lpGroupKey, szGroupKey);
|
|
}
|
|
|
|
|
|
// Need to get the subkey's security to find out
|
|
// if group goes in Unlocked or Locked list box.
|
|
|
|
if (lpGroupData->bOrgLock = IsGroupLocked(szGroupKey)) {
|
|
if ((i = (int)SendMessage(hwndLocked, LB_ADDSTRING, 0, (LPARAM)szText)) != LB_ERR)
|
|
SendMessage(hwndLocked, LB_SETITEMDATA, i, (LPARAM)lpGroupData);
|
|
}
|
|
else {
|
|
if ((i = (int)SendMessage(hwndUnlocked, LB_ADDSTRING, 0, (LPARAM)szText)) != LB_ERR)
|
|
SendMessage(hwndUnlocked, LB_SETITEMDATA, i, (LPARAM)lpGroupData);
|
|
}
|
|
|
|
|
|
// add to the startup group combobox.
|
|
|
|
SendDlgItemMessage(hwnd, IDD_STARTUP, CB_ADDSTRING, 0, (LPARAM)szText);
|
|
}
|
|
NextKey:
|
|
index++;
|
|
cbGroupKey = MAXKEYLEN;
|
|
}
|
|
|
|
// Check it there is still something selected in the list boxes,
|
|
// and enable/disable the Lock and Unlock buttons as apprpriate.
|
|
|
|
if (SendMessage(hwndLocked, LB_GETSELCOUNT, 0, 0) == 0) {
|
|
EnableWindow(GetDlgItem(hwnd, IDD_UNLOCK), FALSE);
|
|
}
|
|
if (SendMessage(hwndUnlocked, LB_GETSELCOUNT, 0, 0) == 0) {
|
|
EnableWindow(GetDlgItem(hwnd, IDD_LOCK), FALSE);
|
|
}
|
|
|
|
if (SendMessage(hwndUnlocked, LB_GETCOUNT, 0, 0) == 0) {
|
|
|
|
// Disable the listbox window.
|
|
|
|
EnableWindow(hwndUnlocked, FALSE);
|
|
|
|
if (SendMessage(hwndLocked, LB_GETCOUNT, 0, 0) > 0) {
|
|
EnableWindow(hwndLocked, TRUE);
|
|
SetFocus(hwndLocked);
|
|
}
|
|
else {
|
|
EnableWindow(hwndLocked, FALSE);
|
|
SetFocus(GetDlgItem(hwnd, IDD_NORUN));
|
|
}
|
|
}
|
|
else {
|
|
EnableWindow(hwndUnlocked, TRUE);
|
|
SetFocus(hwndUnlocked);
|
|
}
|
|
|
|
|
|
// Start the notification again.
|
|
|
|
RegNotifyChangeKeyValue(hkeyProgramGroupsCurrent, TRUE, REG_NOTIFY_CHANGE_NAME,
|
|
hProgramGroupsEvent, TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* EnablePrivilege
|
|
|
|
* Enables/disabled the specified well-known privilege in the
|
|
* current process context
|
|
|
|
* Returns TRUE on success, FALSE on failure
|
|
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
*/
|
|
BOOL
|
|
EnablePrivilege(
|
|
DWORD Privilege,
|
|
BOOL Enable
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
#if 0
|
|
BOOL WasEnabled;
|
|
Status = RtlAdjustPrivilege(Privilege, Enable, TRUE, (PBOOLEAN)&WasEnabled);
|
|
return(NT_SUCCESS(Status));
|
|
#else
|
|
HANDLE ProcessToken;
|
|
LUID LuidPrivilege;
|
|
PTOKEN_PRIVILEGES NewPrivileges;
|
|
DWORD Length;
|
|
|
|
// Open our own token
|
|
Status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &ProcessToken);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("Upedit: Can't open own process token for adjust_privilege access\n\r"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// Initialize the privilege adjustment structure
|
|
LuidPrivilege = RtlConvertLongToLuid(Privilege);
|
|
NewPrivileges = (PTOKEN_PRIVILEGES) LocalAlloc(LPTR, sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
|
|
if (NewPrivileges == NULL) {
|
|
KdPrint(("UPEdit failed to allocate memory for new privileges\n\r"));
|
|
NtClose(ProcessToken);
|
|
return(FALSE);
|
|
}
|
|
|
|
NewPrivileges->PrivilegeCount = 1;
|
|
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
|
|
|
|
// BUG BUG because of a bug in NtAdjustPrivileges which
|
|
// returns an error when you try to enable a privilege
|
|
// that is already enabled, we first try to disable it.
|
|
// to be removed when api is fixed.
|
|
|
|
NewPrivileges->Privileges[0].Attributes = 0;
|
|
|
|
Status = NtAdjustPrivilegesToken(
|
|
ProcessToken, // TokenHandle
|
|
(BOOLEAN)FALSE, // DisableAllPrivileges
|
|
NewPrivileges, // NewPrivileges
|
|
0, // BufferLength
|
|
NULL, // PreviousState (OPTIONAL)
|
|
&Length // ReturnLength
|
|
);
|
|
|
|
NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
|
|
|
|
// Enable the privilege
|
|
Status = NtAdjustPrivilegesToken(
|
|
ProcessToken, // TokenHandle
|
|
(BOOLEAN)FALSE, // DisableAllPrivileges
|
|
NewPrivileges, // NewPrivileges
|
|
0, // BufferLength
|
|
NULL, // PreviousState (OPTIONAL)
|
|
&Length // ReturnLength
|
|
);
|
|
LocalFree(NewPrivileges);
|
|
NtClose(ProcessToken);
|
|
if (Status) {
|
|
KdPrint(("UPEdit failed to enable privilege\n\r"));
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOL APIENTRY GetCurrentUserSid(PVOID *pCurrentUserSid)
|
|
{
|
|
HANDLE hToken;
|
|
DWORD BytesRequired = 0;
|
|
PTOKEN_USER pUserToken;
|
|
NTSTATUS status;
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) ){
|
|
return(FALSE);
|
|
}
|
|
|
|
// Get space needed for token information
|
|
if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &BytesRequired) ) {
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
// Get the actual token information
|
|
pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, BytesRequired);
|
|
if (pUserToken == NULL) {
|
|
return(FALSE);
|
|
}
|
|
if (!GetTokenInformation(hToken, TokenUser, pUserToken, BytesRequired, &BytesRequired) ) {
|
|
LocalFree(pUserToken);
|
|
return(FALSE);
|
|
}
|
|
|
|
BytesRequired = RtlLengthSid(pUserToken->User.Sid);
|
|
*pCurrentUserSid = (PSID)LocalAlloc(LPTR, BytesRequired);
|
|
if (*pCurrentUserSid == NULL) {
|
|
LocalFree(pUserToken);
|
|
return(TRUE);
|
|
}
|
|
|
|
status = RtlCopySid(BytesRequired, *pCurrentUserSid, pUserToken->User.Sid);
|
|
if (!NT_SUCCESS(status)) {
|
|
LocalFree(*pCurrentUserSid);
|
|
*pCurrentUserSid = NULL;
|
|
return(FALSE);
|
|
}
|
|
|
|
LocalFree(pUserToken);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL APIENTRY GetUserOrGroup(PVOID *pUserOrGroupSID, LPTSTR lpUserOrGroupName, DWORD cb)
|
|
{
|
|
USERBROWSER UserBrowser;
|
|
UCHAR chUserDetailsBuffer[4096 + 1];
|
|
PUSERDETAILS pUserDetails;
|
|
WCHAR szBrowserTitle[100];
|
|
TCHAR szBrowserError[100];
|
|
DWORD cbData = 4096;
|
|
HUSERBROW hUserBrowser;
|
|
ULONG BytesRequired;
|
|
NTSTATUS Status;
|
|
|
|
LoadStringW(hInst, IDS_BROWSERTITLE, szBrowserTitle, sizeof(szBrowserTitle));
|
|
|
|
UserBrowser.ulStructSize = sizeof(USERBROWSER);
|
|
UserBrowser.fUserCancelled = FALSE;
|
|
UserBrowser.fExpandNames = FALSE;
|
|
UserBrowser.hwndOwner = hwndUPE;
|
|
UserBrowser.pszTitle = szBrowserTitle;
|
|
UserBrowser.pszInitialDomain = NULL;
|
|
UserBrowser.ulHelpContext = IDH_USERBROWSERDLG;
|
|
UserBrowser.pszHelpFileName = L"UPEDIT.HLP";
|
|
UserBrowser.Flags =USRBROWS_SINGLE_SELECT | USRBROWS_INCL_REMOTE_USERS | USRBROWS_INCL_INTERACTIVE | USRBROWS_INCL_EVERYONE | USRBROWS_SHOW_ALL;
|
|
hUserBrowser = OpenUserBrowser(&UserBrowser);
|
|
|
|
if (!hUserBrowser) {
|
|
if (GetLastError()) {
|
|
LoadString(hInst, IDS_BROWSERERROR, szBrowserError, sizeof(szBrowserError));
|
|
MessageBox(hwndUPE, szBrowserError, szUPETitle, MB_OK | MB_ICONEXCLAMATION );
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
pUserDetails = (PUSERDETAILS)chUserDetailsBuffer;
|
|
|
|
if (!EnumUserBrowserSelection(hUserBrowser, pUserDetails, &cbData)) {
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
CloseUserBrowser(hUserBrowser);
|
|
return(FALSE);
|
|
}
|
|
|
|
pUserDetails = (PUSERDETAILS)LocalAlloc(LPTR, cbData + 2);
|
|
if (!pUserDetails) {
|
|
CloseUserBrowser(hUserBrowser);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!EnumUserBrowserSelection(hUserBrowser, pUserDetails, &cbData)) {
|
|
GetLastError();
|
|
LocalFree(pUserDetails);
|
|
CloseUserBrowser(hUserBrowser);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
// Get the user or group sid, and name to display in editfield
|
|
|
|
BytesRequired = RtlLengthSid(pUserDetails->psidUser);
|
|
if (*pUserOrGroupSID) {
|
|
*pUserOrGroupSID = (PSID)LocalReAlloc(*pUserOrGroupSID, BytesRequired, LMEM_MOVEABLE);
|
|
}
|
|
else {
|
|
*pUserOrGroupSID = (PSID)LocalAlloc(LPTR, BytesRequired);
|
|
}
|
|
if (*pUserOrGroupSID == NULL) {
|
|
CloseUserBrowser(hUserBrowser);
|
|
return(FALSE);
|
|
}
|
|
|
|
Status = RtlCopySid(BytesRequired, *pUserOrGroupSID, pUserDetails->psidUser);
|
|
if (!NT_SUCCESS(Status)) {
|
|
CloseUserBrowser(hUserBrowser);
|
|
LocalFree(*pUserOrGroupSID);
|
|
*pUserOrGroupSID = NULL;
|
|
return(FALSE);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
|
|
lstrcpy(lpUserOrGroupName, pUserDetails->pszDomainName);
|
|
lstrcat(lpUserOrGroupName, L"\\");
|
|
lstrcat(lpUserOrGroupName, pUserDetails->pszAccountName);
|
|
|
|
#else
|
|
{
|
|
LPSTR lpName;
|
|
|
|
lpName = (LPSTR)LocalAlloc(LPTR, cb);
|
|
RtlUnicodeToMultiByteN(lpName, cb, &cbData, pUserDetails->pszDomainName, lstrlenW(pUserDetails->pszDomainName) * sizeof(WCHAR));
|
|
lstrcpy(lpUserOrGroupName, lpName);
|
|
lstrcat(lpUserOrGroupName, "\\");
|
|
RtlUnicodeToMultiByteN(lpName, cb, &cbData, pUserDetails->pszAccountName, lstrlenW(pUserDetails->pszAccountName) * sizeof(WCHAR));
|
|
*(lpName+cbData) = 0;
|
|
lstrcat(lpUserOrGroupName, lpName);
|
|
LocalFree(lpName);
|
|
}
|
|
#endif
|
|
CloseUserBrowser(hUserBrowser);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL LockGroups(BOOL bResetOriginalLock)
|
|
{
|
|
INT i,j;
|
|
INT iSelCount;
|
|
LPGROUPDATA lpGroupData;
|
|
TCHAR szGroupName[(MAXGROUPNAMELEN + 1)*sizeof(TCHAR)];
|
|
TCHAR szLockError[(MAXMESSAGELEN + 1)*sizeof(TCHAR)];
|
|
TCHAR szT[(MAXMESSAGELEN + 1)*sizeof(TCHAR)];
|
|
UNICODE_STRING UnicodeName;
|
|
HWND hwndLB;
|
|
NTSTATUS Status;
|
|
|
|
if (bResetOriginalLock) {
|
|
LoadString(hInst, IDS_RESETLOCKERROR, szLockError, sizeof(szLockError));
|
|
j = IDD_UNLOCKEDGRPS;
|
|
}
|
|
else {
|
|
LoadString(hInst, IDS_LOCKERROR, szLockError, sizeof(szLockError));
|
|
j = IDD_LOCKEDGRPS;
|
|
}
|
|
for (; j <= IDD_LOCKEDGRPS; j++) {
|
|
|
|
hwndLB = GetDlgItem(hwndUPE, j);
|
|
|
|
iSelCount = (int)SendMessage(hwndLB, LB_GETCOUNT, 0, 0);
|
|
for (i = 0; i < iSelCount; i++) {
|
|
SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)szGroupName);
|
|
lpGroupData = (LPGROUPDATA)SendMessage(hwndLB, LB_GETITEMDATA, i, 0);
|
|
if (!lpGroupData || !(lpGroupData->lpGroupKey)) {
|
|
goto Error;
|
|
}
|
|
|
|
if (bResetOriginalLock && !lpGroupData->bOrgLock) {
|
|
continue;
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeName, lpGroupData->lpGroupKey);
|
|
|
|
|
|
// Change the protection on the group key.
|
|
|
|
Status = MakeKeyUserAdminWriteableOnly(hkeyProgramGroups, &UnicodeName);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Error:
|
|
/* Could not Lock group %s */
|
|
wsprintf(szT, szLockError, szGroupName);
|
|
if (MessageBox(hwndUPE, szT, szUPETitle,
|
|
MB_OKCANCEL | MB_ICONEXCLAMATION | MB_SYSTEMMODAL) == IDCANCEL)
|
|
return(FALSE);
|
|
else
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Also protect the subkeys under
|
|
// Software\Microsoft\windowsNT\CurrentVersion\ProgramManager\Restriction
|
|
|
|
RtlInitUnicodeString(&UnicodeName, szRestrict);
|
|
|
|
Status = MakeKeyUserAdminWriteableOnly(hkeyProgramManager, &UnicodeName);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* InitializeGlobalSids
|
|
|
|
* Initializes the various global Sids used in this module.
|
|
|
|
* History:
|
|
* 04-28-93 JohanneC Created
|
|
*/
|
|
VOID InitializeGlobalSids()
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
// Build the admins local group SID
|
|
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gNtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&gAdminsLocalGroup
|
|
);
|
|
|
|
|
|
// create System Sid
|
|
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gNtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&gSystemSid
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MakeKeyUserAdminWriteableOnly(
|
|
IN HANDLE RootKey,
|
|
IN PUNICODE_STRING RelativeName
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the specified registry key's DACL
|
|
and changes all AccessAllowed ACEs (except those for the
|
|
administrators local group) so that they grant read access
|
|
only.
|
|
|
|
|
|
Arguments:
|
|
|
|
RootKey - A handle to a registy key.
|
|
|
|
RelativeName - The name of the sub-key whose protection is to be
|
|
changed to read-only.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL - The protection has been changed.
|
|
|
|
STATUS_NO_MEMORY - Memory could not be allocated to perform the
|
|
operation.
|
|
|
|
Any other status is returned by services while trying to apply the
|
|
new protection. In this case, the protection left on the
|
|
profile is either the original protection or the new protection.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, TempStatus;
|
|
HANDLE Key;
|
|
ULONG LengthNeeded, AceIndex;
|
|
PACL Acl;
|
|
PACE_HEADER Ace;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
ULONG StackBuffer[140];
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
BOOLEAN DaclPresent, IgnoreBoolean;
|
|
|
|
|
|
if (!gAdminsLocalGroup) {
|
|
return(STATUS_INVALID_SID);
|
|
}
|
|
|
|
|
|
|
|
// Open the specified key
|
|
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, RelativeName, OBJ_CASE_INSENSITIVE, RootKey, NULL);
|
|
Status = NtOpenKey (&Key, READ_CONTROL | WRITE_DAC, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
|
|
// Read the existing ACL
|
|
|
|
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR)&StackBuffer[0];
|
|
Status = NtQuerySecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor,
|
|
140*sizeof(ULONG), //size of stack buffer
|
|
&LengthNeeded
|
|
);
|
|
if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
// Stack buffer wasn't large enough - allocate one.
|
|
|
|
SecurityDescriptor = RtlAllocateHeap( RtlProcessHeap(), 0, LengthNeeded);
|
|
Status = NtQuerySecurityObject(Key, DACL_SECURITY_INFORMATION, SecurityDescriptor, LengthNeeded, &LengthNeeded);
|
|
}
|
|
|
|
|
|
|
|
// See if there is an acl
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &DaclPresent, &Acl, &IgnoreBoolean);
|
|
ASSERT( NT_SUCCESS(Status) ); // don't expect this to be able to fail
|
|
if (DaclPresent) {
|
|
if (Acl != NULL) {
|
|
|
|
// Walk the acl changing each ACCESS_ALLOWED ace's AccessMask
|
|
// (except those granting access to Administrators).
|
|
|
|
|
|
for (AceIndex = 0;
|
|
AceIndex < (ULONG)Acl->AceCount && NT_SUCCESS(Status);
|
|
AceIndex++) {
|
|
|
|
Status = RtlGetAce ( Acl, AceIndex, (PVOID *)&Ace );
|
|
ASSERT( NT_SUCCESS(Status) ); //Shouldn't be able to fail
|
|
|
|
|
|
// Change to read-only if AccessAllowed and not
|
|
// administrators
|
|
|
|
|
|
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
|
if (!RtlEqualSid(
|
|
(PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart),
|
|
gAdminsLocalGroup)
|
|
) {
|
|
|
|
// Change it to read-only
|
|
|
|
|
|
((PACCESS_ALLOWED_ACE)Ace)->Mask &= ~(KEY_WRITE | DELETE);
|
|
|
|
//There's some overlap in KEY_READ and KEY_WRITE
|
|
((PACCESS_ALLOWED_ACE)Ace)->Mask |= KEY_READ;
|
|
|
|
} //end_if Sid is gAdminsLocalGroup
|
|
} //end_if ACE is AccessAllowed type
|
|
} //end_for (loop through ACEs)
|
|
|
|
|
|
// We've changed the ACL
|
|
// Now try to put it back on the key.
|
|
|
|
|
|
Status = NtSetSecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor
|
|
);
|
|
|
|
|
|
// Status now contains our completion status.
|
|
// Careful not to accidently change it.
|
|
|
|
|
|
|
|
} //end_if DACL
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If we didn't use our stack buffer, free the allocated memory
|
|
|
|
|
|
if (SecurityDescriptor != (PSECURITY_DESCRIPTOR)&StackBuffer[0]) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
|
|
}
|
|
|
|
|
|
|
|
// Close the key we opened
|
|
|
|
|
|
TempStatus = NtClose( Key );
|
|
#if DBG
|
|
|
|
// The close might fail for things like insufficient disk space
|
|
// to store the registry change log. But, there really isn't
|
|
// much we can do about it, so just assert it in our debug system.
|
|
|
|
|
|
ASSERT(NT_SUCCESS(TempStatus));
|
|
#endif
|
|
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
BOOL
|
|
IsKeyUserAdminWriteableOnly(
|
|
IN HANDLE RootKey,
|
|
IN PUNICODE_STRING RelativeName
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the specified registry key's DACL
|
|
and returns TRUE if the AccessAllowed ACEs not belonging to the
|
|
administrators local group is read access only.
|
|
|
|
|
|
Arguments:
|
|
|
|
RootKey - A handle to a registy key.
|
|
|
|
RelativeName - The name of the sub-key whose protection is to be
|
|
tested for read-only.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL - The protection has been changed.
|
|
|
|
STATUS_NO_MEMORY - Memory could not be allocated to perform the
|
|
operation.
|
|
|
|
Any other status is returned by services while trying to apply the
|
|
new protection. In this case, the protection left on the
|
|
profile is either the original protection or the new protection.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, TempStatus;
|
|
HANDLE Key;
|
|
ULONG LengthNeeded, AceIndex;
|
|
PACL Acl;
|
|
PACE_HEADER Ace;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
ULONG StackBuffer[140];
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
BOOLEAN DaclPresent;
|
|
BOOLEAN IgnoreBoolean;
|
|
BOOLEAN bReadOnly = FALSE;
|
|
|
|
if (!gAdminsLocalGroup) {
|
|
return(STATUS_INVALID_SID);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open the specified key
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
RelativeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey (
|
|
&Key,
|
|
READ_CONTROL | WRITE_DAC,
|
|
&ObjectAttributes
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
// Read the existing ACL
|
|
|
|
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR)&StackBuffer[0];
|
|
Status = NtQuerySecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor,
|
|
140*sizeof(ULONG), //size of stack buffer
|
|
&LengthNeeded
|
|
);
|
|
|
|
if (Status == STATUS_BUFFER_OVERFLOW ||
|
|
Status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
|
|
// Stack buffer wasn't large enough - allocate one.
|
|
|
|
SecurityDescriptor = RtlAllocateHeap( RtlProcessHeap(), 0, LengthNeeded);
|
|
Status = NtQuerySecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor,
|
|
LengthNeeded,
|
|
&LengthNeeded
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See if there is an acl
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlGetDaclSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
&DaclPresent,
|
|
&Acl,
|
|
&IgnoreBoolean
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) ); // don't expect this to be able to fail
|
|
|
|
if (DaclPresent) {
|
|
if (Acl != NULL) {
|
|
|
|
|
|
// Walk the acl changing each ACCESS_ALLOWED ace's AccessMask
|
|
// (except those granting access to Administrators).
|
|
|
|
|
|
for (AceIndex = 0;
|
|
AceIndex < (ULONG)Acl->AceCount && NT_SUCCESS(Status);
|
|
AceIndex++) {
|
|
|
|
Status = RtlGetAce ( Acl, AceIndex, (PVOID *)&Ace );
|
|
ASSERT( NT_SUCCESS(Status) ); //Shouldn't be able to fail
|
|
|
|
|
|
// Change to read-only if AccessAllowed and not
|
|
// administrators
|
|
|
|
|
|
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
|
if (!RtlEqualSid(
|
|
(PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart),
|
|
gAdminsLocalGroup)
|
|
) {
|
|
|
|
// test for read-only
|
|
|
|
|
|
if (! (((PACCESS_ALLOWED_ACE)Ace)->Mask &
|
|
(KEY_WRITE | DELETE) & ~KEY_READ)) {
|
|
bReadOnly = TRUE;
|
|
break;
|
|
}
|
|
|
|
} //end_if Sid is gAdminsLocalGroup
|
|
} //end_if ACE is AccessAllowed type
|
|
} //end_for (loop through ACEs)
|
|
|
|
} //end_if DACL
|
|
}
|
|
}
|
|
|
|
|
|
// If we didn't use our stack buffer, free the allocated memory
|
|
|
|
|
|
if (SecurityDescriptor != (PSECURITY_DESCRIPTOR)&StackBuffer[0]) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
|
|
}
|
|
|
|
|
|
|
|
// Close the key we opened
|
|
|
|
|
|
TempStatus = NtClose( Key );
|
|
#if DBG
|
|
|
|
// The close might fail for things like insufficient disk space
|
|
// to store the registry change log. But, there really isn't
|
|
// much we can do about it, so just assert it in our debug system.
|
|
|
|
|
|
ASSERT(NT_SUCCESS(TempStatus));
|
|
#endif
|
|
|
|
|
|
return(bReadOnly);
|
|
|
|
|
|
}
|
|
|
|
|
|
// Returns the security descriptor of the current profile root key. The sec.
|
|
// desc. will be used when exiting upedit, to reset the protection/permission
|
|
// on the keys in the current profile (the permission might have changed while
|
|
// running upedit).
|
|
|
|
BOOL GetCurrentProfileSecurityDescriptor(PSECURITY_DESCRIPTOR *pSecDesc)
|
|
{
|
|
DWORD cbSecDesc = 0;
|
|
LONG Error;
|
|
|
|
Error = RegGetKeySecurity(hkeyCurrentUser,
|
|
DACL_SECURITY_INFORMATION,
|
|
*pSecDesc, &cbSecDesc);
|
|
if (Error == ERROR_INSUFFICIENT_BUFFER) {
|
|
*pSecDesc = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, cbSecDesc);
|
|
if (*pSecDesc) {
|
|
Error = RegGetKeySecurity(hkeyCurrentUser,
|
|
DACL_SECURITY_INFORMATION,
|
|
*pSecDesc, &cbSecDesc);
|
|
}
|
|
}
|
|
if (Error) {
|
|
if (*pSecDesc) {
|
|
LocalFree(*pSecDesc);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL
|
|
GetSidFromOpenedProfile(
|
|
PSID *pSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns a pointer to the sid of the newly openned profile.
|
|
THis is done by comparing the sid in the aces of one of the ACLs in the
|
|
profile, if the sid is not admin or system, it's the one we want.
|
|
|
|
Arguments:
|
|
|
|
pSid -
|
|
|
|
Return Value:
|
|
|
|
BOOL - returns true if the sid was found, false otherwwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, TempStatus;
|
|
HANDLE RootKey, Key;
|
|
ULONG LengthNeeded, AceIndex;
|
|
PACL Acl;
|
|
PACE_HEADER Ace;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
ULONG StackBuffer[140];
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING NullName;
|
|
BOOLEAN DaclPresent;
|
|
BOOLEAN IgnoreBoolean;
|
|
BOOLEAN OpenedCurrentUserKey;
|
|
DWORD BytesRequired = 0;
|
|
|
|
|
|
if (!gAdminsLocalGroup || !gSystemSid) {
|
|
return(STATUS_INVALID_SID);
|
|
}
|
|
|
|
|
|
|
|
// Open the specified key
|
|
|
|
RootKey = hkeyCurrentUser;
|
|
if (RootKey == HKEY_CURRENT_USER) {
|
|
// get the actual handle to the current user
|
|
Status = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &RootKey);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
OpenedCurrentUserKey = TRUE;
|
|
} else {
|
|
OpenedCurrentUserKey = FALSE;
|
|
}
|
|
|
|
|
|
RtlInitUnicodeString( &NullName, L"");
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&NullName, // Name is null to open same key
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey (
|
|
&Key,
|
|
READ_CONTROL | WRITE_DAC,
|
|
&ObjectAttributes
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (OpenedCurrentUserKey) {
|
|
NtClose( RootKey );
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
// Read the existing ACL
|
|
|
|
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR)&StackBuffer[0];
|
|
Status = NtQuerySecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor,
|
|
140*sizeof(ULONG), //size of stack buffer
|
|
&LengthNeeded
|
|
);
|
|
|
|
if (Status == STATUS_BUFFER_OVERFLOW ||
|
|
Status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
|
|
// Stack buffer wasn't large enough - allocate one.
|
|
|
|
SecurityDescriptor = RtlAllocateHeap( RtlProcessHeap(), 0, LengthNeeded);
|
|
Status = NtQuerySecurityObject(
|
|
Key,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor,
|
|
LengthNeeded,
|
|
&LengthNeeded
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if there is an acl
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlGetDaclSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
&DaclPresent,
|
|
&Acl,
|
|
&IgnoreBoolean
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) ); // don't expect this to be able to fail
|
|
|
|
if (DaclPresent) {
|
|
if (Acl != NULL) {
|
|
|
|
|
|
// Walk the acl.
|
|
|
|
|
|
for (AceIndex = 0;
|
|
AceIndex < (ULONG)Acl->AceCount && NT_SUCCESS(Status);
|
|
AceIndex++) {
|
|
|
|
Status = RtlGetAce ( Acl, AceIndex, (PVOID *)&Ace );
|
|
ASSERT( NT_SUCCESS(Status) ); //Shouldn't be able to fail
|
|
|
|
|
|
// Get the sid that is not the admin sid
|
|
|
|
|
|
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
|
if (!RtlEqualSid(
|
|
(PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart),
|
|
gAdminsLocalGroup) &&
|
|
!RtlEqualSid(
|
|
(PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart),
|
|
gSystemSid)
|
|
) {
|
|
|
|
|
|
// we've got the sid
|
|
|
|
|
|
// Get the user or group sid, and name to
|
|
// display in editfield
|
|
|
|
BytesRequired = RtlLengthSid((PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart));
|
|
if (*pSid) {
|
|
*pSid = (PSID)LocalReAlloc(*pSid,
|
|
BytesRequired,
|
|
LMEM_MOVEABLE);
|
|
}
|
|
else {
|
|
*pSid = (PSID)LocalAlloc(LPTR, BytesRequired);
|
|
}
|
|
if (*pSid == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlCopySid(BytesRequired, *pSid,
|
|
(PSID)&(((PACCESS_ALLOWED_ACE)Ace)->SidStart));
|
|
if (!NT_SUCCESS(Status)) {
|
|
LocalFree(*pSid);
|
|
*pSid = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
|
|
} //end_if Sid is gAdminsLocalGroup
|
|
} //end_if ACE is AccessAllowed type
|
|
} //end_for (loop through ACEs)
|
|
|
|
} //end_if DACL
|
|
}
|
|
}
|
|
|
|
|
|
// If we didn't use our stack buffer, free the allocated memory
|
|
|
|
|
|
if (SecurityDescriptor != (PSECURITY_DESCRIPTOR)&StackBuffer[0]) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
|
|
}
|
|
|
|
Exit:
|
|
if (OpenedCurrentUserKey) {
|
|
NtClose( RootKey );
|
|
}
|
|
|
|
|
|
// Close the key we opened
|
|
|
|
|
|
TempStatus = NtClose( Key );
|
|
#if DBG
|
|
|
|
// The close might fail for things like insufficient disk space
|
|
// to store the registry change log. But, there really isn't
|
|
// much we can do about it, so just assert it in our debug system.
|
|
|
|
|
|
ASSERT(NT_SUCCESS(TempStatus));
|
|
#endif
|
|
|
|
|
|
return(NT_SUCCESS(Status));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ApplyProfileProtection(
|
|
IN PSECURITY_DESCRIPTOR pSecDesc,
|
|
IN PSID UserOrGroup,
|
|
IN HANDLE RootKey
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function applies appropriate protection to an entire profile.
|
|
That is, this function builds an ACL and applies that ACL to a
|
|
sub-tree of the registry. The RootKey parameter is assumed to be
|
|
a handle to the root key of the profile tree in the registry.
|
|
|
|
Arguments:
|
|
|
|
UserOrGroup - The SID of the user or group the profile will be used
|
|
by.
|
|
|
|
RootKey - A key open to the root of the registry tree in which the
|
|
profile is loaded.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL - The protection has been applied.
|
|
|
|
STATUS_NO_MEMORY - Memory could not be allocated to build the ACL.
|
|
|
|
Any other status is returned by services while trying to apply the
|
|
new protection. In this case, the protection left on the
|
|
profile is in an undefined state. That is, it is not necessarily
|
|
the original protection nor the new protection.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG AclLength;
|
|
PACL Acl = NULL;
|
|
PACE_HEADER Ace;
|
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|
BOOLEAN OpenedCurrentUserKey;
|
|
|
|
|
|
// If a security descriptor was passed in then use it to set the permission
|
|
// on the key of the registry tree. If not create one using the UserOrGroup
|
|
// sid.
|
|
|
|
|
|
if (pSecDesc) {
|
|
Status = STATUS_SUCCESS;
|
|
goto GotSecDesc;
|
|
}
|
|
|
|
|
|
// A little debugging aid - only valid sids should come into this
|
|
// routine.
|
|
|
|
|
|
if (!UserOrGroup || !RtlValidSid(UserOrGroup) ){
|
|
|
|
// Let admin know this sid in invalid and the profile will not
|
|
// be saved.
|
|
|
|
return(STATUS_INVALID_SID);
|
|
}
|
|
|
|
|
|
if (!gAdminsLocalGroup) {
|
|
return(STATUS_INVALID_SID);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now calculate how long the ACL will be and allocate that much
|
|
// memory.
|
|
|
|
|
|
AclLength = sizeof(ACL) + // An ACL
|
|
2*sizeof(ACCESS_ALLOWED_ACE) + // With 2 grant ACEs
|
|
RtlLengthSid( UserOrGroup ) + // One with this sid
|
|
RtlLengthSid( gAdminsLocalGroup ); // the other this sid
|
|
|
|
Acl = RtlAllocateHeap( RtlProcessHeap(), 0, AclLength );
|
|
if (Acl == NULL) {
|
|
RtlFreeHeap( RtlProcessHeap(), 0, gAdminsLocalGroup );
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
|
|
// Now build the ACL and set it in a security descriptor
|
|
|
|
// Ace1: { Grant; UserOrGroup; All access }
|
|
// Ace2: { Grant; Admins; All access }
|
|
|
|
|
|
Status = RtlCreateAcl( Acl, AclLength, ACL_REVISION2);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlAddAccessAllowedAce (
|
|
Acl,
|
|
ACL_REVISION2,
|
|
GENERIC_ALL,
|
|
UserOrGroup
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = RtlAddAccessAllowedAce (
|
|
Acl,
|
|
ACL_REVISION2,
|
|
GENERIC_ALL,
|
|
gAdminsLocalGroup
|
|
);
|
|
}
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Mark the ACEs as inheritable and put the ACL
|
|
// in a security descriptor. We just added the
|
|
// two ACEs, so we can assert SUCCESS when we
|
|
// look them up.
|
|
|
|
|
|
Status = RtlGetAce ( Acl, 0, (PVOID *)&Ace );
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
Ace->AceFlags |= CONTAINER_INHERIT_ACE;
|
|
Status = RtlGetAce ( Acl, 1, (PVOID *)&Ace );
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
Ace->AceFlags |= CONTAINER_INHERIT_ACE;
|
|
|
|
Status = RtlCreateSecurityDescriptor (
|
|
&SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION1
|
|
);
|
|
ASSERT(NT_SUCCESS(Status)); // Shouldn't fail.
|
|
|
|
Status = RtlSetDaclSecurityDescriptor (
|
|
&SecurityDescriptor,
|
|
TRUE, //DaclPresent,
|
|
Acl, //Dacl,
|
|
FALSE //DaclDefaulted
|
|
);
|
|
ASSERT(NT_SUCCESS(Status)); // Shouldn't fail.
|
|
|
|
pSecDesc = &SecurityDescriptor;
|
|
}
|
|
}
|
|
|
|
GotSecDesc:
|
|
|
|
|
|
// If we have the ACL built, then apply it to the tree.
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (RootKey == HKEY_CURRENT_USER) {
|
|
// get the actual handle to the current user
|
|
Status = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &RootKey);
|
|
OpenedCurrentUserKey = TRUE;
|
|
|
|
} else {
|
|
|
|
OpenedCurrentUserKey = FALSE;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ApplyAclToRegistryTree( pSecDesc, RootKey );
|
|
|
|
if (OpenedCurrentUserKey) {
|
|
NtClose( RootKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Free allocated heap
|
|
|
|
if (Acl) {
|
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ApplyAclToRegistryTree (
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN HANDLE RootKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function applies the provided ACL to an entire registry
|
|
subtree.
|
|
|
|
In the case of failure, the ACL may have been applied to some
|
|
keys in the tree.
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - points to a security descriptor containing
|
|
DACL to be applied.
|
|
|
|
|
|
RootKey - A key open to the root of the registry tree to which
|
|
the ACL is to be applied.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL - The ACL has been applied.
|
|
|
|
Any other status is returned by services while trying to apply the
|
|
new protection. In this case, the protection left on the
|
|
profile is in an undefined state. That is, it is not necessarily
|
|
the original protection nor the new protection.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
// The passed key must successfully allow us to apply the ACL
|
|
// to the root of the tree.
|
|
|
|
|
|
Status = NtSetSecurityObject(
|
|
RootKey,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor
|
|
);
|
|
|
|
|
|
// Now apply it to the children
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ApplyAclToChildren( SecurityDescriptor, RootKey );
|
|
}
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ApplyAclToChildren (
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN HANDLE Parent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function applies the provided ACL to all ancestors
|
|
of a provided parent registry key.
|
|
|
|
In the case of failure, the ACL may have been applied to some
|
|
keys in the tree.
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - points to a security descriptor containing
|
|
DACL to be applied.
|
|
|
|
Parent - A key open to the parent key.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESSFUL - The ACL has been applied.
|
|
|
|
Any other status is returned by services while trying to apply the
|
|
new protection. In this case, the protection left on the
|
|
profile is in an undefined state. That is, it is not necessarily
|
|
the original protection nor the new protection.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, IgnoreStatus;
|
|
ULONG Index, KeyInfoBuffer[200], RequiredLength;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
HANDLE EffectiveKey, Child;
|
|
UNICODE_STRING NullName, ChildName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
OBJECT_BASIC_INFORMATION HandleInformation;
|
|
|
|
|
|
// OK, we have:
|
|
|
|
// 1) A handle to our parent key. Assuming this key
|
|
// may have had its protection just changed, it is
|
|
// not known whether the original protection grants
|
|
// us the ability to list sub-keys. If not, we
|
|
// must open the key again and try it with the new
|
|
// protection.
|
|
|
|
// We must be able to list the sub-keys to continue working.
|
|
// Furthermore, we will reach this same state at every level
|
|
// of our tree traversal.
|
|
|
|
|
|
|
|
// Get a handle we can use for enumeration
|
|
// (the one we were passed might work, otherwise try openning one).
|
|
|
|
|
|
EffectiveKey = Parent;
|
|
Status = NtQueryObject(
|
|
Parent,
|
|
ObjectBasicInformation,
|
|
&HandleInformation,
|
|
sizeof(OBJECT_BASIC_INFORMATION),
|
|
NULL
|
|
);
|
|
ASSERT(NT_SUCCESS(Status)); // I don't expect this to be able to fail
|
|
|
|
if (!(HandleInformation.GrantedAccess & KEY_ENUMERATE_SUB_KEYS)) {
|
|
|
|
|
|
// Try opening the key again.
|
|
|
|
|
|
RtlInitUnicodeString( &NullName, L"");
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&NullName, // Name is null to open same key
|
|
OBJ_CASE_INSENSITIVE, // Attributes
|
|
Parent, // Root key
|
|
NULL // SecurityDescriptor
|
|
);
|
|
Status = NtOpenKey (
|
|
&EffectiveKey,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
&ObjectAttributes
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, we have a handle that we can use for enumeration.
|
|
|
|
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&ChildName, // Name gets initialized inside the loop.
|
|
OBJ_CASE_INSENSITIVE, // Attributes
|
|
Parent, // Root key
|
|
NULL // SecurityDescriptor
|
|
);
|
|
Index = 0;
|
|
while (NT_SUCCESS(Status)) {
|
|
KeyInfo = (PKEY_BASIC_INFORMATION)&KeyInfoBuffer[0];
|
|
Status = NtEnumerateKey (
|
|
EffectiveKey,
|
|
Index,
|
|
KeyBasicInformation,
|
|
(PVOID)KeyInfo,
|
|
(200*sizeof(ULONG)),
|
|
&RequiredLength
|
|
);
|
|
Index ++;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Get the relative name so we can open this key
|
|
|
|
|
|
ChildName.Buffer = &KeyInfo->Name[0];
|
|
ChildName.Length = (USHORT)KeyInfo->NameLength;
|
|
ChildName.MaximumLength = (USHORT)KeyInfo->NameLength;
|
|
|
|
|
|
// Open this key and apply protection to this sub-tree
|
|
// This must allow us to replace the DAC, and might also
|
|
// allow us to enumerate sub-keys.
|
|
|
|
|
|
Status = NtOpenKey(
|
|
&Child,
|
|
MAXIMUM_ALLOWED | WRITE_DAC,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ApplyAclToRegistryTree( SecurityDescriptor, Child);
|
|
IgnoreStatus = NtClose( Child );
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
/*
|
|
* CreateSecurityDescriptorForFile
|
|
|
|
* Creates a security descriptor containing an ACL containing the ACE from
|
|
* the directory where the file is to be saved. Other ACE ia added: an
|
|
* ACE allowing the user read access if the profile is mandatory or
|
|
* read/write access if the profile is a personal profile.
|
|
|
|
* A SD created with this routine should be destroyed using
|
|
* DeleteSecurityDescriptor
|
|
|
|
* Returns a pointer to the security descriptor or NULL on failure.
|
|
|
|
* 12-14-93 Davidc Created.
|
|
*/
|
|
|
|
PSECURITY_DESCRIPTOR CreateSecurityDescriptorForFile(PSID pSid, LPTSTR lpFile)
|
|
{
|
|
TCHAR lptmp[MAX_PATH];
|
|
LPTSTR lpFilePart;
|
|
TCHAR ch;
|
|
PSECURITY_DESCRIPTOR pSecDesc = NULL;
|
|
PSECURITY_DESCRIPTOR pSecDescNew = NULL;
|
|
PACL pAcl;
|
|
PACL pAclNew = NULL;
|
|
BOOLEAN fDaclPresent, fDaclDefaulted;
|
|
PACE_HEADER pAce;
|
|
DWORD dwAccessMask;
|
|
DWORD cb;
|
|
INT i;
|
|
NTSTATUS Status;
|
|
|
|
|
|
// Get the directory security and propagate it to the file, if it doesn't
|
|
// exist, then use the default security descriptor.
|
|
|
|
GetFullPathName(lpFile, MAX_PATH, lptmp, &lpFilePart);
|
|
ch = *lpFilePart;
|
|
*lpFilePart = 0;
|
|
|
|
GetFileSecurity(lptmp, DACL_SECURITY_INFORMATION, pSecDesc, 0, &cb);
|
|
if (!cb)
|
|
goto Exit;
|
|
|
|
pSecDesc = LocalAlloc(LPTR, cb);
|
|
if (!pSecDesc)
|
|
goto Exit;
|
|
if (!GetFileSecurity(lptmp, DACL_SECURITY_INFORMATION, pSecDesc, cb, &cb))
|
|
goto Exit;
|
|
|
|
*lpFilePart = ch;
|
|
|
|
|
|
// Get the security descriptor's acl.
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(pSecDesc, &fDaclPresent, &pAcl, &fDaclDefaulted);
|
|
if (!NT_SUCCESS(Status) || !fDaclPresent || !pAcl){
|
|
goto Exit;
|
|
}
|
|
|
|
cb = pAcl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pSid);
|
|
|
|
if (pAclNew = LocalAlloc(LPTR, cb)) {
|
|
memcpy(pAclNew, pAcl, cb);
|
|
pAclNew->AclSize = (WORD)cb;
|
|
pAcl = pAclNew;
|
|
}
|
|
|
|
|
|
// Remove the directory specific flags from the existent aces.
|
|
|
|
for (i=0; i<pAcl->AceCount; i++) {
|
|
if (RtlGetAce(pAcl, i, (PVOID *)&pAce) == STATUS_SUCCESS)
|
|
pAce->AceFlags = 0;
|
|
}
|
|
|
|
|
|
// Set access mask for new ACE.
|
|
|
|
lpFilePart += (lstrlen(lpFilePart) - 4); // go to file extension
|
|
if (!lstrcmpi(lpFilePart, TEXT(".man"))) {
|
|
dwAccessMask = GENERIC_READ | GENERIC_EXECUTE;
|
|
}
|
|
else {
|
|
dwAccessMask = GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE | DELETE;
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, dwAccessMask, pSid);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
|
|
|
|
// Create the new security descriptor for the profile file.
|
|
|
|
pSecDescNew = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
if (!pSecDescNew) {
|
|
LocalFree(pAclNew);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlCreateSecurityDescriptor(pSecDescNew, SECURITY_DESCRIPTOR_REVISION);
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(pSecDescNew, (BOOLEAN)TRUE, pAcl, fDaclDefaulted);
|
|
|
|
Exit:
|
|
LocalFree(pSecDesc);
|
|
return(pSecDescNew);
|
|
|
|
}
|
|
|
|
/*
|
|
* DeleteSecurityDescriptor
|
|
|
|
* Deletes a security descriptor created using CreateSecurityDescriptorForFile
|
|
|
|
* Returns TRUE on success, FALSE on failure
|
|
|
|
*/
|
|
|
|
BOOL
|
|
DeleteSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PACL pAcl;
|
|
BOOLEAN Present;
|
|
BOOLEAN Defaulted;
|
|
|
|
|
|
// Get the ACL
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(pSecurityDescriptor,
|
|
&Present, &pAcl, &Defaulted);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Destroy the ACL
|
|
|
|
if (Present && (pAcl != NULL)) {
|
|
LocalFree(pAcl);
|
|
}
|
|
}
|
|
|
|
|
|
// Destroy the Security Descriptor
|
|
|
|
LocalFree(pSecurityDescriptor);
|
|
|
|
return(TRUE);
|
|
}
|