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