//  --------------------------------------------------------------------------
//  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 <msginaexports.h>

#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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  Returns:    <none>
//
//  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:  <none>
//
//  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    <local administrators>  READ_CONTROL | SYNCHRONIZE
    //      S-1-1-0         <everybody>             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:  <none>
//
//  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:  <none>
//
//  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;
    hMutex = CreateMutex(&securityAttributes, FALSE, s_szLogonRequestMutexName);
    if (securityAttributes.lpSecurityDescriptor != NULL)
    {
        (HLOCAL)LocalFree(securityAttributes.lpSecurityDescriptor);
    }
    return(hMutex);
}

//  --------------------------------------------------------------------------
//  CLogonMutex::OpenShutdownEvent
//
//  Arguments:  <none>
//
//  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:  <none>
//
//  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));
}