// -------------------------------------------------------------------------- // Module Name: LogonMutex.cpp // // Copyright (c) 2001, Microsoft Corporation // // File that implements a class that manages a single global logon mutex. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "LogonMutex.h" #include #include "Access.h" #include "SystemSettings.h" DWORD CLogonMutex::s_dwThreadID = 0; LONG CLogonMutex::s_lAcquireCount = 0; HANDLE CLogonMutex::s_hMutex = NULL; HANDLE CLogonMutex::s_hMutexRequest = NULL; HANDLE CLogonMutex::s_hEvent = NULL; const TCHAR CLogonMutex::s_szLogonMutexName[] = SZ_INTERACTIVE_LOGON_MUTEX_NAME; const TCHAR CLogonMutex::s_szLogonRequestMutexName[] = SZ_INTERACTIVE_LOGON_REQUEST_MUTEX_NAME; const TCHAR CLogonMutex::s_szLogonReplyEventName[] = SZ_INTERACTIVE_LOGON_REPLY_EVENT_NAME; const TCHAR CLogonMutex::s_szShutdownEventName[] = SZ_SHUT_DOWN_EVENT_NAME; SID_IDENTIFIER_AUTHORITY CLogonMutex::s_SecurityNTAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY CLogonMutex::s_SecurityWorldSID = SECURITY_WORLD_SID_AUTHORITY; // -------------------------------------------------------------------------- // CLogonMutex::Acquire // // Arguments: // // Returns: // // Purpose: Acquires the mutex. Ensures that the mutex is only acquired // on the main thread of winlogon by an assert. The mutex should // never be abandoned within normal execution. However, a // process termination can cause this to happen. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::Acquire (void) { DWORD dwWaitResult; ASSERTMSG((s_dwThreadID == 0) || (s_dwThreadID == GetCurrentThreadId()), "Must acquire mutex on initializing thread in CLogonMutex::Acquire"); if ((s_hMutex != NULL) && (WAIT_TIMEOUT == WaitForSingleObject(s_hEvent, 0))) { ASSERTMSG(s_lAcquireCount == 0, "Mutex already owned in CLogonMutex::Acquire"); dwWaitResult = WaitForSingleObject(s_hMutex, INFINITE); ++s_lAcquireCount; } } // -------------------------------------------------------------------------- // CLogonMutex::Release // // Arguments: // // Returns: // // Purpose: Releases the mutex. Again makes sure the caller is the main // thread of winlogon. The acquisitions and releases are // reference counted to allow unbalanced release calls to be // made. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::Release (void) { ASSERTMSG((s_dwThreadID == 0) || (s_dwThreadID == GetCurrentThreadId()), "Must acquire mutex on initializing thread in CLogonMutex::Release"); if ((s_hMutex != NULL) && (s_lAcquireCount > 0)) { TBOOL(ReleaseMutex(s_hMutex)); --s_lAcquireCount; } } // -------------------------------------------------------------------------- // CLogonMutex::SignalReply // // Arguments: // // Returns: // // Purpose: Open the global logon reply event and signal it. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::SignalReply (void) { HANDLE hEvent; hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, s_szLogonReplyEventName); if (hEvent != NULL) { TBOOL(SetEvent(hEvent)); TBOOL(CloseHandle(hEvent)); } } // -------------------------------------------------------------------------- // CLogonMutex::SignalShutdown // // Arguments: // // Returns: // // Purpose: Signal the global shut down event. This will prevent further // interactive requests from being processed. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::SignalShutdown (void) { if (s_hEvent != NULL) { TBOOL(SetEvent(s_hEvent)); } } // -------------------------------------------------------------------------- // CLogonMutex::StaticInitialize // // Arguments: // // Returns: // // Purpose: Initializes the logon mutex objects based on whether this is // session 0 or higher and or what the product type is. Because // the initialization for session is done only the once this // requires a machine restart for the objects to be created. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::StaticInitialize (void) { // Check the machine settings. Must be friendly UI and PER/PRO (FUS). if (CSystemSettings::IsFriendlyUIActive() && CSystemSettings::IsMultipleUsersEnabled() && CSystemSettings::IsWorkStationProduct()) { DWORD dwErrorCode; s_dwThreadID = GetCurrentThreadId(); s_lAcquireCount = 0; // On session 0 create the objects and ACL them. if (NtCurrentPeb()->SessionId == 0) { s_hEvent = CreateShutdownEvent(); if (s_hEvent != NULL) { s_hMutex = CreateLogonMutex(); if (s_hMutex != NULL) { s_hMutexRequest = CreateLogonRequestMutex(); if (s_hMutexRequest != NULL) { Acquire(); dwErrorCode = ERROR_SUCCESS; } else { dwErrorCode = GetLastError(); } } else { dwErrorCode = GetLastError(); } } else { dwErrorCode = GetLastError(); } } else { // For sessions other than 0 open the objects. s_hEvent = OpenShutdownEvent(); if (s_hEvent != NULL) { if (WAIT_TIMEOUT == WaitForSingleObject(s_hEvent, 0)) { s_hMutex = OpenLogonMutex(); if (s_hMutex != NULL) { Acquire(); dwErrorCode = ERROR_SUCCESS; } else { dwErrorCode = GetLastError(); } } else { dwErrorCode = ERROR_SHUTDOWN_IN_PROGRESS; } } else { dwErrorCode = GetLastError(); } } if (ERROR_SUCCESS == dwErrorCode) { ASSERTMSG(s_hMutex != NULL, "NULL s_hMutex in CLogonMutex::StaticInitialize"); ASSERTMSG(s_hEvent != NULL, "NULL s_hEvent in CLogonMutex::StaticInitialize"); } else { ReleaseHandle(s_hEvent); ReleaseHandle(s_hMutex); s_dwThreadID = 0; } } else { s_dwThreadID = 0; s_lAcquireCount = 0; s_hMutex = NULL; s_hEvent = NULL; } } // -------------------------------------------------------------------------- // CLogonMutex::StaticTerminate // // Arguments: // // Returns: // // Purpose: Releases the mutex if held and closes the object handle. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- void CLogonMutex::StaticTerminate (void) { Release(); ASSERTMSG(s_lAcquireCount == 0, "Mutex not released in CLogonMutex::StaticTerminate"); ReleaseHandle(s_hMutex); } // -------------------------------------------------------------------------- // CLogonMutex::CreateShutdownEvent // // Arguments: // // Returns: HANDLE // // Purpose: Creates the global shut down event. ACL'd so that anybody can // synchronize against it and therefore listen but only SYSTEM // can set it to indicate machine shut down has begun. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- HANDLE CLogonMutex::CreateShutdownEvent (void) { HANDLE hEvent; SECURITY_ATTRIBUTES securityAttributes; // Build a security descriptor for the event that allows: // S-1-5-18 NT AUTHORITY\SYSTEM EVENT_ALL_ACCESS // S-1-5-32-544 READ_CONTROL | SYNCHRONIZE // S-1-1-0 SYNCHRONIZE static const CSecurityDescriptor::ACCESS_CONTROL s_AccessControl[] = { { &s_SecurityNTAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, EVENT_ALL_ACCESS }, { &s_SecurityNTAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, READ_CONTROL | SYNCHRONIZE }, { &s_SecurityWorldSID, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, SYNCHRONIZE } }; securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = CSecurityDescriptor::Create(ARRAYSIZE(s_AccessControl), s_AccessControl); securityAttributes.bInheritHandle = FALSE; hEvent = CreateEvent(&securityAttributes, TRUE, FALSE, s_szShutdownEventName); if (securityAttributes.lpSecurityDescriptor != NULL) { (HLOCAL)LocalFree(securityAttributes.lpSecurityDescriptor); } return(hEvent); } // -------------------------------------------------------------------------- // CLogonMutex::CreateLogonMutex // // Arguments: // // Returns: HANDLE // // Purpose: Creates the global logon mutex. ACL'd so that only SYSTEM can // acquire and release the mutex. This is not for user // consumption. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- HANDLE CLogonMutex::CreateLogonMutex (void) { HANDLE hMutex; SECURITY_ATTRIBUTES securityAttributes; // Build a security descriptor for the mutex that allows: // S-1-5-18 NT AUTHORITY\SYSTEM MUTEX_ALL_ACCESS static const CSecurityDescriptor::ACCESS_CONTROL s_AccessControl[] = { { &s_SecurityNTAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, MUTEX_ALL_ACCESS } }; securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = CSecurityDescriptor::Create(ARRAYSIZE(s_AccessControl), s_AccessControl); securityAttributes.bInheritHandle = FALSE; hMutex = CreateMutex(&securityAttributes, FALSE, s_szLogonMutexName); if (securityAttributes.lpSecurityDescriptor != NULL) { (HLOCAL)LocalFree(securityAttributes.lpSecurityDescriptor); } return(hMutex); } // -------------------------------------------------------------------------- // CLogonMutex::CreateLogonRequestMutex // // Arguments: // // Returns: HANDLE // // Purpose: Creates the logon request mutex for interactive logon // requests. For a service to make this request it must acquire // the mutex and therefore only a single request can be made at // any one time. This is ACL'd so that only SYSTEM can gain // access to this object. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- HANDLE CLogonMutex::CreateLogonRequestMutex (void) { HANDLE hMutex; SECURITY_ATTRIBUTES securityAttributes; // Build a security descriptor for the mutex that allows: // S-1-5-18 NT AUTHORITY\SYSTEM MUTEX_ALL_ACCESS static const CSecurityDescriptor::ACCESS_CONTROL s_AccessControl[] = { { &s_SecurityNTAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, MUTEX_ALL_ACCESS } }; securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = CSecurityDescriptor::Create(ARRAYSIZE(s_AccessControl), s_AccessControl); securityAttributes.bInheritHandle = FALSE; if (securityAttributes.lpSecurityDescriptor) { hMutex = CreateMutex(&securityAttributes, FALSE, s_szLogonRequestMutexName); LocalFree(securityAttributes.lpSecurityDescriptor); securityAttributes.lpSecurityDescriptor = NULL; } else { hMutex = NULL; } return hMutex; } // -------------------------------------------------------------------------- // CLogonMutex::OpenShutdownEvent // // Arguments: // // Returns: HANDLE // // Purpose: Opens a handle to the global shut down event. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- HANDLE CLogonMutex::OpenShutdownEvent (void) { return(OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, s_szShutdownEventName)); } // -------------------------------------------------------------------------- // CLogonMutex::OpenLogonMutex // // Arguments: // // Returns: HANDLE // // Purpose: Opens a handle to the global logon mutex. // // History: 2001-04-06 vtan created // -------------------------------------------------------------------------- HANDLE CLogonMutex::OpenLogonMutex (void) { return(OpenMutex(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, s_szLogonMutexName)); }