Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2676 lines
66 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: secutil.c
//
// Contents: Security Utilities
//
// Classes:
//
// Functions:
//
// History: 8-25-94 RichardW Created
//
//----------------------------------------------------------------------------
#include "precomp.h"
#pragma hdrstop
//
// 'Constants' used in this module only.
//
SID_IDENTIFIER_AUTHORITY gSystemSidAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY gLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
PSID gLocalSid; // Initialized in 'InitializeSecurityGlobals'
PSID gAdminSid; // Initialized in 'InitializeSecurityGlobals'
PSID pWinlogonSid;
typedef struct _MYACELIST {
DWORD Total;
DWORD Active;
DWORD WinlogonOnly;
MYACE * Aces;
DWORD TotalSids;
DWORD ActiveSids;
PSID * Sids;
} MYACELIST, * PMYACELIST;
//
// Define all access to windows objects
//
#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \
DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \
DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \
DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)
#define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | \
WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | \
WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | \
WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | \
WINSTA_READSCREEN | \
STANDARD_RIGHTS_REQUIRED)
#define WINSTA_ATOMS (WINSTA_ACCESSGLOBALATOMS | \
WINSTA_ACCESSCLIPBOARD )
//
// Private prototypes
//
BOOL
InitializeWindowsSecurity(
PGLOBALS pGlobals
);
VOID
InitializeSecurityGlobals(
VOID
);
PVOID
CreateAccessAllowedAce(
PSID Sid,
ACCESS_MASK AccessMask,
UCHAR AceFlags,
UCHAR InheritFlags
);
VOID
DestroyAce(
PVOID Ace
);
PMYACELIST
CreateAceList(
DWORD Count);
BOOL
InitializeWinstaSecurity(
PWinstaDescription pWinsta);
/***************************************************************************\
* InitializeSecurity
*
* Initializes the security module
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
InitializeSecurity(
PGLOBALS pGlobals
)
{
//
// Set up our module globals
//
InitializeSecurityGlobals();
pWinlogonSid = pGlobals->WinlogonSid;
//
// Initialize the removable medial module
//
RmvInitializeRemovableMediaSrvcs();
//
// Initialize windows security aspects
//
if (!InitializeWindowsSecurity(pGlobals)) {
DebugLog((DEB_ERROR, "failed to initialize windows security\n"));
return(FALSE);
}
//
// Change user to 'system'
//
if (!SecurityChangeUser(pGlobals, NULL, NULL, pWinlogonSid, FALSE)) {
DebugLog((DEB_ERROR, "failed to set user to system\n"));
return(FALSE);
}
return TRUE;
}
/***************************************************************************\
* InitializeWindowsSecurity
*
* Initializes windows specific parts of security module
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
InitializeWindowsSecurity(
PGLOBALS pGlobals
)
{
//
// Register with windows so we can create windowstation etc.
//
if (!RegisterLogonProcess((DWORD)NtCurrentTeb()->ClientId.UniqueProcess, TRUE)) {
DebugLog((DEB_ERROR, "could not register itself as logon process\n"));
return(FALSE);
}
//
// Create and open the windowstation
//
if (!(pGlobals->WindowStation.hwinsta = CreateWindowStationW(WINDOW_STATION_NAME,
0, MAXIMUM_ALLOWED, NULL))) {
DebugLog((DEB_ERROR, "could not create windowstation\n"));
return(FALSE);
}
pGlobals->WindowStation.pszWinsta = WINDOW_STATION_NAME;
//
// Associate winlogon with this window-station
//
if (!SetProcessWindowStation(pGlobals->WindowStation.hwinsta)) {
DebugLog((DEB_ERROR, "failed to set process window-station\n"));
return(FALSE);
}
#if 0
//
// Set up window-station security (no user access yet)
//
if (!SetWindowStationSecurity(pGlobals, NULL)) {
DebugLog((DEB_ERROR, "failed to set window-station security\n"));
return(FALSE);
}
#endif
if ( !InitializeWinstaSecurity( &pGlobals->WindowStation ) )
{
DebugLog((DEB_ERROR, "failed to init winsta security\n" ));
return( FALSE );
}
#ifdef LATER // put the window-station lock back here when windows allows us to create desktops with the lock
//
// Lock the window-station now before we create any desktops
//
if (LockWindowStation(pGlobals->hwinsta) == WSS_ERROR) {
DebugLog((DEB_ERROR, "failed to lock window-station\n"));
return(FALSE);
}
#endif
//
// Create and open the desktops.
// Pass in NULL for the default display
//
if (!(pGlobals->WindowStation.hdeskWinlogon = CreateDesktop((LPTSTR)WINLOGON_DESKTOP_NAME,
NULL, NULL, 0, MAXIMUM_ALLOWED, NULL))) {
DebugLog((DEB_ERROR, "Failed to create winlogon desktop\n"));
return(FALSE);
}
if (!(pGlobals->WindowStation.hdeskApplication = CreateDesktop((LPTSTR)APPLICATION_DESKTOP_NAME,
NULL, NULL, 0, MAXIMUM_ALLOWED, NULL))) {
DebugLog((DEB_ERROR, "Failed to create application desktop\n"));
return(FALSE);
}
//
// Make sure that this thread is started on the logon desktop. This
// is to ensure that apps will not be able to see its threadinfo
// data.
//
SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon);
GetDesktopWindow();
if (SetThreadDesktop(pGlobals->WindowStation.hdeskApplication))
{
pGlobals->WindowStation.hwndAppDesktop = GetDesktopWindow();
SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon);
}
else
{
DebugLog((DEB_WARN, "Could not set thread desktop to app desktop, %d\n", GetLastError()));
}
pGlobals->WindowStation.hdeskScreenSaver = NULL;
//
// Set desktop security (no user access yet)
//
if (!SetWinlogonDesktopSecurity(pGlobals->WindowStation.hdeskWinlogon, pWinlogonSid)) {
DebugLog((DEB_ERROR, "Failed to set winlogon desktop security\n"));
return(FALSE);
}
if (!SetUserDesktopSecurity(pGlobals->WindowStation.hdeskApplication, NULL, pWinlogonSid)) {
DebugLog((DEB_ERROR, "Failed to set application desktop security\n"));
return(FALSE);
}
//
// Associate winlogon with its desktop
//
if (!SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon)) {
DebugLog((DEB_ERROR, "Failed to associate winlogon with winlogon desktop\n"));
return(FALSE);
}
//
// Switch to the winlogon desktop
//
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon);
return(TRUE);
}
/***************************************************************************\
* SetMyAce
*
* Helper routine that fills in a MYACE structure.
*
* History:
* 02-06-92 Davidc Created
\***************************************************************************/
VOID
SetMyAce(
PMYACE MyAce,
PSID Sid,
ACCESS_MASK Mask,
UCHAR InheritFlags
)
{
MyAce->Sid = Sid;
MyAce->AccessMask= Mask;
MyAce->InheritFlags = InheritFlags;
}
/***************************************************************************\
* SetWindowStationSecurity
*
* Sets the security on the specified window station given the logon sid passed.
*
* If the UserSid = NULL, no access is given to anyone other than winlogon
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
SetWindowStationSecurity(
PGLOBALS pGlobals,
PSID UserSid
)
{
MYACE Ace[9];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_INFORMATION si;
BOOL Result;
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
pWinlogonSid,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(&(Ace[AceCount++]),
pWinlogonSid,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
//
// Define the Admin ACEs
//
SetMyAce(&(Ace[AceCount++]),
gAdminSid,
WINSTA_ENUMERATE | WINSTA_READATTRIBUTES,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(&(Ace[AceCount++]),
gAdminSid,
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_ENUMERATE |
DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
//
// Define the User ACEs
//
if (UserSid != NULL) {
SetMyAce(&(Ace[AceCount++]),
UserSid,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(&(Ace[AceCount++]),
UserSid,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
}
//
// If a user is logged in and UserSid is not the logged in user sid,
// add the logged in user's sid.
//
if (pGlobals->UserLoggedOn && UserSid != NULL &&
!RtlEqualSid(pGlobals->UserProcessData.UserSid, UserSid)) {
SetMyAce(&(Ace[AceCount++]),
pGlobals->UserProcessData.UserSid,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(&(Ace[AceCount++]),
pGlobals->UserProcessData.UserSid,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
}
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create winsta security descriptor\n"));
return(FALSE);
}
//
// Set the DACL on the object
//
si = DACL_SECURITY_INFORMATION;
Result = SetUserObjectSecurity(pGlobals->WindowStation.hwinsta, &si, SecurityDescriptor);
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
//
// Return success status
//
if (!Result) {
DebugLog((DEB_ERROR, "failed to set windowstation security\n"));
}
return(Result);
}
/***************************************************************************\
* SetWinlogonDesktopSecurity
*
* Sets the security on the specified desktop so only winlogon can access it
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
SetWinlogonDesktopSecurity(
HDESK hdesktop,
PSID WinlogonSid
)
{
MYACE Ace[2];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_INFORMATION si;
BOOL Result;
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
DESKTOP_ALL,
0
);
//
// Add enumerate access for administrators
//
SetMyAce(&(Ace[AceCount++]),
gAdminSid,
DESKTOP_ENUMERATE | STANDARD_RIGHTS_REQUIRED,
NO_PROPAGATE_INHERIT_ACE
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create winlogon desktop security descriptor\n"));
return(FALSE);
}
//
// Set the DACL on the object
//
si = DACL_SECURITY_INFORMATION;
Result = SetUserObjectSecurity(hdesktop, &si, SecurityDescriptor);
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
//
// Return success status
//
if (!Result) {
DebugLog((DEB_ERROR, "failed to set winlogon desktop security\n"));
}
return(Result);
}
/***************************************************************************\
* SetUserDesktopSecurity
*
* Sets the security on the specified desktop given the logon sid passed.
*
* If UserSid = NULL, access is given only to winlogon
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
SetUserDesktopSecurity(
HDESK hdesktop,
PSID UserSid,
PSID WinlogonSid
)
{
MYACE Ace[3];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_INFORMATION si;
BOOL Result;
ACCESS_MASK DesktopAccess;
DWORD MiserlyAccess;
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
DESKTOP_ALL,
0
);
//
// Define the Admin ACEs
//
MiserlyAccess = GetProfileInt( WINLOGON, RESTRICT_NONINTERACTIVE_ACCESS, 0 );
if ( MiserlyAccess )
{
DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS |
DESKTOP_ENUMERATE |
DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU ;
}
else
{
DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS |
DESKTOP_ENUMERATE |
DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU |
GENERIC_EXECUTE ;
}
SetMyAce(&(Ace[AceCount++]),
gAdminSid,
DesktopAccess,
0
);
//
// Define the User ACEs
//
if (UserSid != NULL) {
SetMyAce(&(Ace[AceCount++]),
UserSid,
DESKTOP_ALL,
0
);
}
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user desktop security descriptor\n"));
return(FALSE);
}
//
// Set the DACL on the object
//
si = DACL_SECURITY_INFORMATION;
Result = SetUserObjectSecurity(hdesktop, &si, SecurityDescriptor);
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
//
// Return success status
//
if (!Result) {
DebugLog((DEB_ERROR, "failed to set user desktop security\n"));
}
return(Result);
}
/***************************************************************************\
* CreateLogonSid
*
* Creates a logon sid for a new logon.
*
* If LogonId is non NULL, on return the LUID that is part of the logon
* sid is returned here.
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PSID
CreateLogonSid(
PLUID LogonId OPTIONAL
)
{
NTSTATUS Status;
ULONG Length;
PSID Sid;
LUID Luid;
//
// Generate a locally unique id to include in the logon sid
//
Status = NtAllocateLocallyUniqueId(&Luid);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to create LUID, status = 0x%lx", Status));
return(NULL);
}
//
// Allocate space for the sid and fill it in.
//
Length = RtlLengthRequiredSid(SECURITY_LOGON_IDS_RID_COUNT);
Sid = (PSID)Alloc(Length);
ASSERTMSG("Winlogon failed to allocate memory for logonsid", Sid != NULL);
if (Sid != NULL) {
RtlInitializeSid(Sid, &gSystemSidAuthority, SECURITY_LOGON_IDS_RID_COUNT);
ASSERT(SECURITY_LOGON_IDS_RID_COUNT == 3);
*(RtlSubAuthoritySid(Sid, 0)) = SECURITY_LOGON_IDS_RID;
*(RtlSubAuthoritySid(Sid, 1 )) = Luid.HighPart;
*(RtlSubAuthoritySid(Sid, 2 )) = Luid.LowPart;
}
//
// Return the logon LUID if required.
//
if (LogonId != NULL) {
*LogonId = Luid;
}
return(Sid);
}
/***************************************************************************\
* DeleteLogonSid
*
* Frees up memory allocated for logon sid
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
VOID
DeleteLogonSid(
PSID Sid
)
{
Free(Sid);
}
/***************************************************************************\
* InitializeSecurityGlobals
*
* Initializes the various global constants (mainly Sids used in this module.
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
VOID
InitializeSecurityGlobals(
VOID
)
{
NTSTATUS Status;
//
// Initialize the local sid for later
//
Status = RtlAllocateAndInitializeSid(
&gLocalSidAuthority,
1,
SECURITY_LOCAL_RID,
0, 0, 0, 0, 0, 0, 0,
&gLocalSid
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to initialize local sid, status = 0x%lx", Status));
}
//
// Initialize the admin sid for later
//
Status = RtlAllocateAndInitializeSid(
&gSystemSidAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&gAdminSid
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to initialize admin alias sid, status = 0x%lx", Status));
}
}
/***************************************************************************\
* EnablePrivilege
*
* Enables/disables the specified well-known privilege in the current thread
* token if there is one, otherwise the current process token.
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
EnablePrivilege(
ULONG Privilege,
BOOL Enable
)
{
NTSTATUS Status;
BOOLEAN WasEnabled;
//
// Try the thread token first
//
Status = RtlAdjustPrivilege(Privilege,
(BOOLEAN)Enable,
TRUE,
&WasEnabled);
if (Status == STATUS_NO_TOKEN) {
//
// No thread token, use the process token
//
Status = RtlAdjustPrivilege(Privilege,
(BOOLEAN)Enable,
FALSE,
&WasEnabled);
}
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to %ws privilege : 0x%lx, status = 0x%lx", Enable ? TEXT("enable") : TEXT("disable"), Privilege, Status));
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* ClearUserProcessData
*
* Resets fields in user process data. Should be used at startup when structure
* contents are unknown.
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
VOID
ClearUserProcessData(
PUSER_PROCESS_DATA UserProcessData
)
{
UserProcessData->UserToken = NULL;
UserProcessData->UserSid = NULL;
UserProcessData->NewProcessSD = NULL;
UserProcessData->NewProcessTokenSD = NULL;
UserProcessData->NewThreadSD = NULL;
UserProcessData->NewThreadTokenSD = NULL;
//
// Use the PagedPoolLimit field as an indication as to whether
// any of the quota fields have been set. A zero PagedPoolLimit
// is not legit, and so makes for a greate indicator.
//
UserProcessData->Quotas.PagedPoolLimit = 0;
//
// the following two fields will be set by MOAP.
//
UserProcessData->CurrentDirectory = NULL;
UserProcessData->pEnvironment = NULL;
}
/***************************************************************************\
* SetUserProcessData
*
* Sets up the user process data structure for a new user.
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
BOOL
SetUserProcessData(
PUSER_PROCESS_DATA UserProcessData,
HANDLE UserToken,
PQUOTA_LIMITS Quotas OPTIONAL,
PSID UserSid,
PSID WinlogonSid
)
{
NTSTATUS Status;
//
// Free an existing UserSid
//
if (UserProcessData->UserSid != NULL) {
//
// Don't free winlogon sid if this was a system logon (or no logon)
//
if (UserProcessData->UserSid != WinlogonSid) {
DeleteLogonSid(UserProcessData->UserSid);
}
UserProcessData->UserSid = NULL;
}
//
// Free up the logon token
//
if (UserProcessData->UserToken != NULL) {
Status = NtClose(UserProcessData->UserToken);
ASSERT(NT_SUCCESS(Status));
UserProcessData->UserToken = NULL;
}
//
// Free up any existing security descriptors
//
if (UserProcessData->NewProcessSD != NULL) {
DeleteSecurityDescriptor(UserProcessData->NewProcessSD);
}
if (UserProcessData->NewProcessTokenSD != NULL) {
DeleteSecurityDescriptor(UserProcessData->NewProcessTokenSD);
}
if (UserProcessData->NewThreadSD != NULL) {
DeleteSecurityDescriptor(UserProcessData->NewThreadSD);
}
if (UserProcessData->NewThreadTokenSD != NULL) {
DeleteSecurityDescriptor(UserProcessData->NewThreadTokenSD);
}
//
// Store the new user's token and sid
//
ASSERT(UserSid != NULL); // should always have a non-NULL user sid
UserProcessData->UserToken = UserToken;
UserProcessData->UserSid = UserSid;
//
// Save the user's quota limits
//
if (ARGUMENT_PRESENT(Quotas)) {
UserProcessData->Quotas = (*Quotas);
}
//
// Set up new security descriptors
//
#if 0
UserProcessData->NewProcessSD = CreateUserProcessSD(
UserSid,
WinlogonSid);
ASSERT(UserProcessData->NewProcessSD != NULL);
UserProcessData->NewProcessTokenSD = CreateUserProcessTokenSD(
UserSid,
WinlogonSid);
ASSERT(UserProcessData->NewProcessTokenSD != NULL);
#endif
UserProcessData->NewThreadSD = CreateUserThreadSD(
UserSid,
WinlogonSid);
ASSERT(UserProcessData->NewThreadSD != NULL);
UserProcessData->NewThreadTokenSD = CreateUserThreadTokenSD(
UserSid,
WinlogonSid);
ASSERT(UserProcessData->NewThreadTokenSD != NULL);
return(TRUE);
}
/***************************************************************************\
* FUNCTION: SecurityChangeUser
*
* PURPOSE: Sets up any security information for the new user.
* This should be called whenever a user logs on or off.
* UserLoggedOn should be set to indicate winlogon state, i.e.
* TRUE if a real user is logged on, FALSE if this call is setting
* our user back to system. (Note that UserToken and Sid may be
* the winlogon token/sid on DBG machines where we allow system logon)
*
* RETURNS: TRUE on success, FALSE on failure
*
* HISTORY:
*
* 12-09-91 Davidc Created.
*
\***************************************************************************/
BOOL
SecurityChangeUser(
PGLOBALS pGlobals,
HANDLE Token,
PQUOTA_LIMITS Quotas OPTIONAL,
PSID LogonSid,
BOOL UserLoggedOn
)
{
LUID luidNone = { 0, 0 };
//
// Set appropriate protection on windows objects
//
if ( UserLoggedOn || pGlobals->fExecuteSetup )
{
AddUserToWinsta( &pGlobals->WindowStation,
LogonSid,
Token );
}
else
{
RemoveUserFromWinsta( &pGlobals->WindowStation,
pGlobals->UserProcessData.UserToken );
}
#if 0
SetWindowStationSecurity(pGlobals,
(UserLoggedOn || pGlobals->fExecuteSetup) ? LogonSid : NULL);
#endif
SetUserDesktopSecurity( pGlobals->WindowStation.hdeskApplication,
LogonSid,
pWinlogonSid);
//
// Setup new-process data
//
SetUserProcessData(&pGlobals->UserProcessData,
Token,
Quotas,
LogonSid,
pWinlogonSid);
//
// Setup the appropriate new environment
//
if (UserLoggedOn) {
//
// Do nothing to the profile or environment. Environment and profiles
// are all handled in wlx.c:LogonAttempt and DoStartShell
//
pGlobals->LogoffFlags = 0;
pGlobals->TickCount = GetTickCount();
} else {
//
// Restore the system environment
//
CloseIniFileUserMapping(pGlobals);
ResetEnvironment(pGlobals);
SetWindowStationUser(pGlobals->WindowStation.hwinsta, &luidNone, NULL, 0);
}
//
// Store whether there is a real user logged on or not
//
pGlobals->UserLoggedOn = UserLoggedOn;
return(TRUE);
}
/***************************************************************************\
* CreateSecurityDescriptor
*
* Creates a security descriptor containing an ACL containing the specified ACEs
*
* A SD created with this routine should be destroyed using
* DeleteSecurityDescriptor
*
* Returns a pointer to the security descriptor or NULL on failure.
*
* 02-06-92 Davidc Created.
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateSecurityDescriptor(
PMYACE MyAce,
ACEINDEX AceCount
)
{
NTSTATUS Status;
ACEINDEX AceIndex;
PACCESS_ALLOWED_ACE *Ace;
PACL Acl = NULL;
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
ULONG LengthAces;
ULONG LengthAcl;
ULONG LengthSd;
//
// Allocate space for the ACE pointer array
//
Ace = (PACCESS_ALLOWED_ACE *)Alloc(sizeof(PACCESS_ALLOWED_ACE) * AceCount);
if (Ace == NULL) {
DebugLog((DEB_ERROR, "Failed to allocated ACE array\n"));
return(NULL);
}
//
// Create the ACEs and calculate total ACE size
//
LengthAces = 0;
for (AceIndex=0; AceIndex < AceCount; AceIndex ++) {
Ace[AceIndex] = CreateAccessAllowedAce(MyAce[AceIndex].Sid,
MyAce[AceIndex].AccessMask,
0,
MyAce[AceIndex].InheritFlags);
if (Ace[AceIndex] == NULL) {
DebugLog((DEB_ERROR, "Failed to allocate ace\n"));
} else {
LengthAces += Ace[AceIndex]->Header.AceSize;
}
}
//
// Calculate ACL and SD sizes
//
LengthAcl = sizeof(ACL) + LengthAces;
LengthSd = SECURITY_DESCRIPTOR_MIN_LENGTH;
//
// Create the ACL
//
Acl = Alloc(LengthAcl);
if (Acl != NULL) {
Status = RtlCreateAcl(Acl, LengthAcl, ACL_REVISION);
ASSERT(NT_SUCCESS(Status));
//
// Add the ACES to the ACL and destroy the ACEs
//
for (AceIndex = 0; AceIndex < AceCount; AceIndex ++) {
if (Ace[AceIndex] != NULL) {
Status = RtlAddAce(Acl, ACL_REVISION, 0, Ace[AceIndex],
Ace[AceIndex]->Header.AceSize);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "AddAce failed, status = 0x%lx", Status));
}
DestroyAce(Ace[AceIndex]);
}
}
} else {
DebugLog((DEB_ERROR, "Failed to allocate ACL\n"));
}
//
// Free the ACE pointer array
//
Free(Ace);
//
// Create the security descriptor
//
SecurityDescriptor = Alloc(LengthSd);
if (SecurityDescriptor != NULL) {
Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
ASSERT(NT_SUCCESS(Status));
//
// Set the DACL on the security descriptor
//
Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl, FALSE);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "SetDACLSD failed, status = 0x%lx", Status));
}
} else {
DebugLog((DEB_ERROR, "Failed to allocate security descriptor\n"));
}
//
// Return with our spoils
//
return(SecurityDescriptor);
}
/***************************************************************************\
* DeleteSecurityDescriptor
*
* Deletes a security descriptor created using CreateSecurityDescriptor
*
* Returns TRUE on success, FALSE on failure
*
* 02-06-92 Davidc Created.
\***************************************************************************/
BOOL
DeleteSecurityDescriptor(
PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
NTSTATUS Status;
PACL Acl;
BOOLEAN Present;
BOOLEAN Defaulted;
ASSERT(SecurityDescriptor != NULL);
//
// Get the ACL
//
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
&Present, &Acl, &Defaulted);
if (NT_SUCCESS(Status)) {
//
// Destroy the ACL
//
if (Present && (Acl != NULL)) {
Free(Acl);
}
} else {
DebugLog((DEB_ERROR, "Failed to get DACL from security descriptor being destroyed, Status = 0x%lx", Status));
}
//
// Destroy the Security Descriptor
//
Free(SecurityDescriptor);
return(TRUE);
}
/***************************************************************************\
* CreateAccessAllowedAce
*
* Allocates memory for an ACCESS_ALLOWED_ACE and fills it in.
* The memory should be freed by calling DestroyACE.
*
* Returns pointer to ACE on success, NULL on failure
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PVOID
CreateAccessAllowedAce(
PSID Sid,
ACCESS_MASK AccessMask,
UCHAR AceFlags,
UCHAR InheritFlags
)
{
ULONG LengthSid = RtlLengthSid(Sid);
ULONG LengthACE = sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + LengthSid;
PACCESS_ALLOWED_ACE Ace;
Ace = (PACCESS_ALLOWED_ACE)Alloc(LengthACE);
if (Ace == NULL) {
DebugLog((DEB_ERROR, "CreateAccessAllowedAce : Failed to allocate ace\n"));
return NULL;
}
Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
Ace->Header.AceSize = (UCHAR)LengthACE;
Ace->Header.AceFlags = AceFlags | InheritFlags;
Ace->Mask = AccessMask;
RtlCopySid(LengthSid, (PSID)(&(Ace->SidStart)), Sid );
return(Ace);
}
/***************************************************************************\
* DestroyAce
*
* Frees the memory allocate for an ACE
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
VOID
DestroyAce(
PVOID Ace
)
{
Free(Ace);
}
/***************************************************************************\
* FUNCTION: ImpersonateUser
*
* PURPOSE: Impersonates the user by setting the users token
* on the specified thread. If no thread is specified the token
* is set on the current thread.
*
* RETURNS: Handle to be used on call to StopImpersonating() or NULL on failure
* If a non-null thread handle was passed in, the handle returned will
* be the one passed in. (See note)
*
* NOTES: Take care when passing in a thread handle and then calling
* StopImpersonating() with the handle returned by this routine.
* StopImpersonating() will close any thread handle passed to it -
* even yours !
*
* HISTORY:
*
* 04-21-92 Davidc Created.
*
\***************************************************************************/
HANDLE
ImpersonateUser(
PUSER_PROCESS_DATA UserProcessData,
HANDLE ThreadHandle
)
{
NTSTATUS Status, IgnoreStatus;
HANDLE UserToken = UserProcessData->UserToken;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE ImpersonationToken;
BOOL ThreadHandleOpened = FALSE;
if (ThreadHandle == NULL) {
//
// Get a handle to the current thread.
// Once we have this handle, we can set the user's impersonation
// token into the thread and remove it later even though we ARE
// the user for the removal operation. This is because the handle
// contains the access rights - the access is not re-evaluated
// at token removal time.
//
Status = NtDuplicateObject( NtCurrentProcess(), // Source process
NtCurrentThread(), // Source handle
NtCurrentProcess(), // Target process
&ThreadHandle, // Target handle
THREAD_SET_THREAD_TOKEN,// Access
0L, // Attributes
DUPLICATE_SAME_ATTRIBUTES
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "ImpersonateUser : Failed to duplicate thread handle, status = 0x%lx", Status));
return(NULL);
}
ThreadHandleOpened = TRUE;
}
//
// If the usertoken is NULL, there's nothing to do
//
if (UserToken != NULL) {
//
// UserToken is a primary token - create an impersonation token version
// of it so we can set it on our thread
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0L,
NULL,
UserProcessData->NewThreadTokenSD);
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken( UserToken,
TOKEN_IMPERSONATE | TOKEN_READ,
&ObjectAttributes,
FALSE,
TokenImpersonation,
&ImpersonationToken
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to duplicate users token to create impersonation thread, status = 0x%lx", Status));
if (ThreadHandleOpened) {
IgnoreStatus = NtClose(ThreadHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
return(NULL);
}
//
// Set the impersonation token on this thread so we 'are' the user
//
Status = NtSetInformationThread( ThreadHandle,
ThreadImpersonationToken,
(PVOID)&ImpersonationToken,
sizeof(ImpersonationToken)
);
//
// We're finished with our handle to the impersonation token
//
IgnoreStatus = NtClose(ImpersonationToken);
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Check we set the token on our thread ok
//
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to set user impersonation token on winlogon thread, status = 0x%lx", Status));
if (ThreadHandleOpened) {
IgnoreStatus = NtClose(ThreadHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
return(NULL);
}
}
return(ThreadHandle);
}
/***************************************************************************\
* FUNCTION: StopImpersonating
*
* PURPOSE: Stops impersonating the client by removing the token on the
* current thread.
*
* PARAMETERS: ThreadHandle - handle returned by ImpersonateUser() call.
*
* RETURNS: TRUE on success, FALSE on failure
*
* NOTES: If a thread handle was passed in to ImpersonateUser() then the
* handle returned was one and the same. If this is passed to
* StopImpersonating() the handle will be closed. Take care !
*
* HISTORY:
*
* 04-21-92 Davidc Created.
*
\***************************************************************************/
BOOL
StopImpersonating(
HANDLE ThreadHandle
)
{
NTSTATUS Status, IgnoreStatus;
HANDLE ImpersonationToken;
//
// Remove the user's token from our thread so we are 'ourself' again
//
ImpersonationToken = NULL;
Status = NtSetInformationThread( ThreadHandle,
ThreadImpersonationToken,
(PVOID)&ImpersonationToken,
sizeof(ImpersonationToken)
);
//
// We're finished with the thread handle
//
IgnoreStatus = NtClose(ThreadHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to remove user impersonation token from winlogon thread, status = 0x%lx", Status));
}
return(NT_SUCCESS(Status));
}
/***************************************************************************\
* ExecUserThread
*
* Creates a thread of the winlogon process running in the logged on user's
* context.
*
* Returns thread handle on success, NULL on failure.
*
* Thread handle returned has all access to thread.
*
* 05-04-92 Davidc Created.
\***************************************************************************/
HANDLE ExecUserThread(
IN PGLOBALS pGlobals,
IN LPTHREAD_START_ROUTINE lpStartAddress,
IN LPVOID Parameter,
IN DWORD Flags,
OUT LPDWORD ThreadId
)
{
SECURITY_ATTRIBUTES saThread;
PUSER_PROCESS_DATA UserProcessData = &pGlobals->UserProcessData;
HANDLE ThreadHandle, Handle;
BOOL Result = FALSE;
DWORD ResumeResult, IgnoreResult;
//
// Initialize thread security info
//
saThread.nLength = sizeof(SECURITY_ATTRIBUTES);
saThread.lpSecurityDescriptor = UserProcessData->NewThreadSD;
saThread.bInheritHandle = FALSE;
//
// Create the thread suspended
//
ThreadHandle = CreateThread(
&saThread,
0, // Default Stack size
lpStartAddress,
Parameter,
CREATE_SUSPENDED | Flags,
ThreadId);
if (ThreadHandle == NULL) {
DebugLog((DEB_ERROR, "User thread creation failed! Error = %d\n", GetLastError()));
return(NULL);
}
//
// Switch the thread to user context.
//
Handle = ImpersonateUser(UserProcessData, ThreadHandle);
if (Handle == NULL) {
DebugLog((DEB_ERROR, "Failed to set user context on thread!\n"));
} else {
//
// Should have got back the handle we passed in
//
ASSERT(Handle == ThreadHandle);
//
// Let the thread run
//
ResumeResult = ResumeThread(ThreadHandle);
if (ResumeResult == -1) {
DebugLog((DEB_ERROR, "failed to resume thread, error = %d", GetLastError()));
} else {
//
// Success
//
Result = TRUE;
}
}
if (!Result) {
//
// Terminate the thread
//
IgnoreResult = TerminateThread(ThreadHandle, 0);
ASSERT(IgnoreResult);
//
// Close the thread handle
//
IgnoreResult = CloseHandle(ThreadHandle);
ASSERT(IgnoreResult);
ThreadHandle = NULL;
}
return(ThreadHandle);
}
/***************************************************************************\
* CreateUserProfileKeySD
*
* Creates a security descriptor to protect registry keys in the user profile
*
* History:
* 22-Dec-92 Davidc Created
* 04-May-93 Johannec added 3rd parameter for locked groups set in upedit.exe
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateUserProfileKeySD(
PSID UserSid,
PSID WinlogonSid,
BOOL AllAccess
)
{
MYACE Ace[3];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
ASSERT(UserSid != NULL); // should always have a non-null user sid
//
// Define the User ACEs
//
SetMyAce(&(Ace[AceCount++]),
UserSid,
AllAccess ? KEY_ALL_ACCESS :
KEY_ALL_ACCESS & ~(KEY_SET_VALUE | KEY_CREATE_SUB_KEY | DELETE),
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
);
//
// Define the Admin ACEs
//
SetMyAce(&(Ace[AceCount++]),
gAdminSid,
KEY_ALL_ACCESS,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
);
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
KEY_ALL_ACCESS,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user process security descriptor\n\n"));
}
return(SecurityDescriptor);
}
/***************************************************************************\
* CreateUserProcessTokenSD
*
* Creates a security descriptor to protect primary tokens on user processes
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateUserProcessTokenSD(
PSID UserSid,
PSID WinlogonSid
)
{
MYACE Ace[2];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
ASSERT(UserSid != NULL); // should always have a non-null user sid
//
// Define the User ACEs
//
SetMyAce(&(Ace[AceCount++]),
UserSid,
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT | TOKEN_QUERY |
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL,
0
);
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
TOKEN_READ,
0
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user process token security descriptor"));
}
return(SecurityDescriptor);
DBG_UNREFERENCED_PARAMETER(WinlogonSid);
}
/***************************************************************************\
* CreateUserProcessSD
*
* Creates a security descriptor to protect user processes
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateUserProcessSD(
PSID UserSid,
PSID WinlogonSid
)
{
MYACE Ace[2];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
ASSERT(UserSid != NULL); // should always have a non-null user sid
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
PROCESS_SET_INFORMATION | // Allow primary token to be set
PROCESS_TERMINATE | SYNCHRONIZE, // Allow screen-saver control
0
);
//
// Define the User ACEs
//
SetMyAce(&(Ace[AceCount++]),
UserSid,
PROCESS_ALL_ACCESS,
0
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user process security descriptor"));
}
return(SecurityDescriptor);
}
/***************************************************************************\
* TestTokenForAdmin
*
* Returns TRUE if the token passed represents an admin user, otherwise FALSE
*
* The token handle passed must have TOKEN_QUERY access.
*
* History:
* 05-06-92 Davidc Created
\***************************************************************************/
BOOL
TestTokenForAdmin(
HANDLE Token
)
{
NTSTATUS Status;
ULONG InfoLength;
PTOKEN_GROUPS TokenGroupList;
ULONG GroupIndex;
BOOL FoundAdmin;
//
// Get a list of groups in the token
//
Status = NtQueryInformationToken(
Token, // Handle
TokenGroups, // TokenInformationClass
NULL, // TokenInformation
0, // TokenInformationLength
&InfoLength // ReturnLength
);
if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) {
DebugLog((DEB_ERROR, "failed to get group info for admin token, status = 0x%lx\n", Status));
return(FALSE);
}
TokenGroupList = Alloc(InfoLength);
if (TokenGroupList == NULL) {
DebugLog((DEB_ERROR, "unable to allocate memory for token groups\n"));
return(FALSE);
}
Status = NtQueryInformationToken(
Token, // Handle
TokenGroups, // TokenInformationClass
TokenGroupList, // TokenInformation
InfoLength, // TokenInformationLength
&InfoLength // ReturnLength
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "failed to query groups for admin token, status = 0x%lx\n", Status));
Free(TokenGroupList);
return(FALSE);
}
//
// Search group list for admin alias
//
FoundAdmin = FALSE;
for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) {
if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, gAdminSid)) {
FoundAdmin = TRUE;
break;
}
}
//
// Tidy up
//
Free(TokenGroupList);
return(FoundAdmin);
}
/***************************************************************************\
* SetProcessToken
*
* Set the primary token of the specified process
* If the specified token is NULL, this routine does nothing.
*
* It assumed that the handles in ProcessInformation are the handles returned
* on creation of the process and therefore have all access.
*
* Returns TRUE on success, FALSE on failure.
*
* 01-31-91 Davidc Created.
\***************************************************************************/
BOOL
SetProcessToken(
PGLOBALS pGlobals,
HANDLE hProcess,
HANDLE hThread,
HANDLE hToken
)
{
NTSTATUS Status, AdjustStatus;
PROCESS_ACCESS_TOKEN PrimaryTokenInfo;
HANDLE TokenToAssign;
OBJECT_ATTRIBUTES ObjectAttributes;
BOOLEAN WasEnabled;
PSECURITY_DESCRIPTOR psd;
//
// Check for a NULL token. (No need to do anything)
// The process will run in the parent process's context and inherit
// the default ACL from the parent process's token.
//
if (hToken == NULL)
{
return(TRUE);
}
psd = CreateUserProcessTokenSD( pGlobals->UserProcessData.UserSid,
pGlobals->WinlogonSid );
//
// A primary token can only be assigned to one process.
// Duplicate the logon token so we can assign one to the new
// process.
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0,
NULL,
psd
);
Status = NtDuplicateToken(
hToken, // Duplicate this token
0, // Same desired access
&ObjectAttributes,
FALSE, // EffectiveOnly
TokenPrimary, // TokenType
&TokenToAssign // Duplicate token handle stored here
);
DeleteSecurityDescriptor(psd);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "SetProcessToken failed to duplicate primary token for new user process, status = 0x%lx\n", Status));
return(FALSE);
}
//
// Set the process's primary token
//
//
// Enable the required privilege
//
Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE,
FALSE, &WasEnabled);
if (NT_SUCCESS(Status)) {
PrimaryTokenInfo.Token = TokenToAssign;
PrimaryTokenInfo.Thread = hThread;
Status = NtSetInformationProcess(
hProcess,
ProcessAccessToken,
(PVOID)&PrimaryTokenInfo,
(ULONG)sizeof(PROCESS_ACCESS_TOKEN)
);
//
// Restore the privilege to its previous state
//
AdjustStatus = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
WasEnabled, FALSE, &WasEnabled);
if (!NT_SUCCESS(AdjustStatus)) {
DebugLog((DEB_ERROR, "failed to restore assign-primary-token privilege to previous enabled state\n"));
}
if (NT_SUCCESS(Status)) {
Status = AdjustStatus;
}
} else {
DebugLog((DEB_ERROR, "failed to enable assign-primary-token privilege\n"));
}
//
// We're finished with the token handle
//
CloseHandle(TokenToAssign);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "SetProcessToken failed to set primary token for new user process, Status = 0x%lx\n", Status));
SetLastError(RtlNtStatusToDosError(Status));
}
return (NT_SUCCESS(Status));
}
/***************************************************************************\
* CreateUserThreadSD
*
* Creates a security descriptor to protect user threads in the winlogon process
*
* History:
* 05-04-92 Davidc Created
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateUserThreadSD(
PSID UserSid,
PSID WinlogonSid
)
{
MYACE Ace[2];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
ASSERT(UserSid != NULL); // should always have a non-null user sid
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
THREAD_QUERY_INFORMATION |
THREAD_SET_THREAD_TOKEN |
THREAD_SUSPEND_RESUME |
THREAD_TERMINATE | SYNCHRONIZE,
0
);
//
// Define the User ACEs
//
SetMyAce(&(Ace[AceCount++]),
UserSid,
THREAD_GET_CONTEXT |
THREAD_QUERY_INFORMATION,
0
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user process security descriptor\n"));
}
return(SecurityDescriptor);
}
/***************************************************************************\
* CreateUserThreadTokenSD
*
* Creates a security descriptor to protect tokens on user threads
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PSECURITY_DESCRIPTOR
CreateUserThreadTokenSD(
PSID UserSid,
PSID WinlogonSid
)
{
MYACE Ace[2];
ACEINDEX AceCount = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor;
ASSERT(UserSid != NULL); // should always have a non-null user sid
//
// Define the User ACEs
//
SetMyAce(&(Ace[AceCount++]),
UserSid,
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT | TOKEN_QUERY |
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL,
0
);
//
// Define the Winlogon ACEs
//
SetMyAce(&(Ace[AceCount++]),
WinlogonSid,
TOKEN_ALL_ACCESS,
0
);
// Check we didn't goof
ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount);
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount);
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create user process token security descriptor\n"));
}
return(SecurityDescriptor);
DBG_UNREFERENCED_PARAMETER(WinlogonSid);
}
//+---------------------------------------------------------------------------
//
// Function: CreateAceList
//
// Synopsis: Create and initialize the ACELIST to track access to the winsta
//
// Arguments: [Count] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PMYACELIST
CreateAceList(
DWORD Count)
{
PMYACELIST pList;
pList = LocalAlloc( LMEM_FIXED, sizeof( MYACELIST ) );
if ( pList )
{
pList->Aces = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
sizeof( MYACE ) * Count );
if ( pList->Aces )
{
pList->Total = Count;
pList->Active = 0;
pList->Sids = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
sizeof( PSID ) * Count );
if ( pList->Sids )
{
pList->TotalSids = Count;
pList->ActiveSids = 0;
return( pList );
}
return( pList );
}
LocalFree( pList );
}
return( NULL );
}
//+---------------------------------------------------------------------------
//
// Function: AceListAddSid
//
// Synopsis: Adds a SID to the list where the ace list is maintained.
//
// Arguments: [pList] --
// [Sid] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PSID
AceListAddSid(
PMYACELIST pList,
PSID Sid )
{
PVOID pCopy;
PSID SidCopy;
DWORD SidSize;
if ( pList->ActiveSids == pList->TotalSids )
{
pCopy = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
sizeof( PSID ) * (pList->TotalSids * 2 ) );
if ( pCopy )
{
CopyMemory( pCopy, pList->Sids, sizeof(PSID) * pList->ActiveSids );
LocalFree( pList->Sids );
pList->Sids = pCopy;
}
else
{
return( NULL );
}
}
SidSize = RtlLengthSid( Sid );
SidCopy = LocalAlloc( LMEM_FIXED, SidSize );
if ( SidCopy )
{
CopyMemory( SidCopy, Sid, SidSize );
pList->Sids[ pList->ActiveSids++ ] = SidCopy ;
return( SidCopy );
}
return( NULL );
}
//+---------------------------------------------------------------------------
//
// Function: AceListRemoveSid
//
// Synopsis: Removes and frees a SID from the list
//
// Arguments: [pList] -- List
// [Sid] -- Sid to remove
// [Absolute] -- TRUE -> pointer match, FALSE -> SID match
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
AceListRemoveSid(
PMYACELIST pList,
PSID Sid,
BOOL Absolute )
{
DWORD i;
PSID SidToDelete;
for ( i = 0 ; i < pList->ActiveSids ; i++ )
{
if ( Absolute )
{
if ( pList->Sids[ i ] == Sid )
{
break;
}
}
else
{
if ( RtlEqualSid( pList->Sids[i], Sid ) )
{
break;
}
}
}
if ( i == pList->ActiveSids )
{
return;
}
SidToDelete = pList->Sids[ i ];
pList->ActiveSids --;
pList->Sids[ i ] = pList->Sids[ pList->ActiveSids ];
LocalFree( SidToDelete );
}
//+---------------------------------------------------------------------------
//
// Function: AceListSetWinstaSecurity
//
// Synopsis: Applies the ACL in pList to the window station
//
// Arguments: [pList] --
// [hWinsta] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
AceListSetWinstaSecurity(
PMYACELIST pList,
DWORD Count,
HWINSTA hWinsta )
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_INFORMATION si;
BOOL Result;
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(pList->Aces, Count );
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create winsta security descriptor\n"));
return(FALSE);
}
//
// Set the DACL on the object
//
si = DACL_SECURITY_INFORMATION;
Result = SetUserObjectSecurity(hWinsta, &si, SecurityDescriptor);
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
//
// Return success status
//
if (!Result) {
DebugLog((DEB_ERROR, "failed to set windowstation security\n"));
}
return(Result);
}
//+---------------------------------------------------------------------------
//
// Function: InitializeWinstaSecurity
//
// Synopsis: Initializes the window station security to winlogon/admin only
//
// Arguments: [pWinsta] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
InitializeWinstaSecurity(
PWinstaDescription pWinsta)
{
PMYACELIST pList;
DWORD MiserlyAccess;
ACCESS_MASK WinstaAccess, DesktopAccess;
pList = CreateAceList( 16 );
if ( !pList )
{
return( FALSE );
}
pWinsta->Acl = pList;
//
// Define the Winlogon ACEs
//
SetMyAce(& ( pList->Aces[ pList->Active ++ ]),
pWinlogonSid,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(& ( pList->Aces[ pList->Active ++ ]),
pWinlogonSid,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
//
// Define the Admin ACEs
//
MiserlyAccess = GetProfileInt( WINLOGON, RESTRICT_NONINTERACTIVE_ACCESS, 0 );
if ( MiserlyAccess )
{
WinstaAccess = WINSTA_ENUMERATE | WINSTA_READATTRIBUTES ;
DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS |
DESKTOP_ENUMERATE |
DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU ;
}
else
{
WinstaAccess = WINSTA_ENUMERATE | WINSTA_READATTRIBUTES |
WINSTA_ATOMS | STANDARD_RIGHTS_EXECUTE |
WINSTA_EXITWINDOWS ;
DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS |
DESKTOP_ENUMERATE |
DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU |
GENERIC_EXECUTE ;
}
SetMyAce(& ( pList->Aces[ pList->Active ++ ]),
gAdminSid,
WinstaAccess,
NO_PROPAGATE_INHERIT_ACE
);
SetMyAce(& ( pList->Aces[ pList->Active ++ ]),
gAdminSid,
DesktopAccess,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
);
pList->WinlogonOnly = pList->Active ;
return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) );
}
//+---------------------------------------------------------------------------
//
// Function: AddUserToWinsta
//
// Synopsis: Adds the specified user to the window station
//
// Arguments: [pWinsta] --
// [LogonSid] --
// [Token] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
AddUserToWinsta(
PWinstaDescription pWinsta,
PSID LogonSid,
HANDLE Token )
{
PTOKEN_USER pUser;
UCHAR Buffer[128];
PMYACELIST pList;
NTSTATUS Status;
ULONG Needed;
PSID LogonSidCopy;
PSID UserSidCopy;
pList = pWinsta->Acl;
pUser = (PTOKEN_USER) Buffer;
Status = NtQueryInformationToken( Token,
TokenUser,
pUser,
sizeof(Buffer),
&Needed );
if ( !NT_SUCCESS( Status ) )
{
return( FALSE );
}
//
// Define the User ACEs
//
LogonSidCopy = AceListAddSid( pList, LogonSid );
if ( !LogonSidCopy )
{
return( FALSE );
}
UserSidCopy = AceListAddSid( pList, pUser->User.Sid );
if ( !UserSidCopy )
{
AceListRemoveSid( pList, LogonSidCopy, TRUE );
return( FALSE );
}
SetMyAce( &pList->Aces[ pList->Active ++ ],
LogonSidCopy,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE );
SetMyAce( &pList->Aces[ pList->Active ++ ],
LogonSidCopy,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL,
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE );
SetMyAce( &pList->Aces[ pList->Active ++ ],
UserSidCopy,
WINSTA_ATOMS,
NO_PROPAGATE_INHERIT_ACE );
return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) );
}
//+---------------------------------------------------------------------------
//
// Function: RemoveUserFromWinsta
//
// Synopsis: Removes a user from the window station
//
// Arguments: [pWinsta] --
// [Token] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
RemoveUserFromWinsta(
PWinstaDescription pWinsta,
HANDLE Token )
{
DWORD i;
PTOKEN_USER pUser;
UCHAR Buffer[128];
PMYACELIST pList;
NTSTATUS Status;
ULONG Needed;
pList = pWinsta->Acl;
if ( pList->Active == 0 )
{
return( FALSE );
}
pUser = (PTOKEN_USER) Buffer;
Status = NtQueryInformationToken( Token,
TokenUser,
pUser,
sizeof(Buffer),
&Needed );
if ( !NT_SUCCESS( Status ) )
{
return( FALSE );
}
for ( i = pList->Active - 1 ; i >= pList->WinlogonOnly ; i-- )
{
if ( RtlEqualSid( pList->Aces[i].Sid, pUser->User.Sid ) )
{
break;
}
}
if ( i < pList->WinlogonOnly )
{
return( FALSE );
}
//
// We add users in blocks of three, usually LogonSid, LogonSid, UserSid.
// Thus, we delete them in threes
//
if ( i < 2 )
{
return( FALSE );
}
//
// Clean up SIDs
//
AceListRemoveSid( pList, pList->Aces[ i ].Sid, TRUE );
AceListRemoveSid( pList, pList->Aces[ i - 1 ].Sid, TRUE );
//
// If there are still more entries after this one,
// slide them down.
//
if ( pList->Active > i + 1 )
{
MoveMemory( &pList->Aces[ i - 2 ],
&pList->Aces[ i + 1 ],
(pList->Active - i - 1) * sizeof( MYACE ) );
}
pList->Active -= 3;
return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) );
}
//+---------------------------------------------------------------------------
//
// Function: FastSetWinstaSecurity
//
// Synopsis: Allows fast toggling between "normal" and winlogon only access
//
// Arguments: [pWinsta] --
// [FullAccess] --
//
// History: 6-24-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
FastSetWinstaSecurity(
PWinstaDescription pWinsta,
BOOL FullAccess)
{
PMYACELIST pList;
pList = (PMYACELIST) pWinsta->Acl;
if ( FullAccess )
{
return( AceListSetWinstaSecurity( pList,
pList->Active,
pWinsta->hwinsta ) );
}
else
{
return( AceListSetWinstaSecurity( pList,
pList->WinlogonOnly,
pWinsta->hwinsta ) );
}
}