//  --------------------------------------------------------------------------
//  Module Name: StandardDebug.cpp
//
//  Copyright (c) 1999-2000, Microsoft Corporation
//
//  This file defines standard debug helper functions for winlogon/GINA
//  projects for neptune.
//
//  History:    1999-09-10  vtan        created
//              2000-01-31  vtan        moved from Neptune to Whistler
//  --------------------------------------------------------------------------

#include "StandardHeader.h"

#include <stdio.h>

#ifdef      DBG

//  --------------------------------------------------------------------------
//  gLastResult
//
//  Purpose:    Temporary global that stores the last result.
//  --------------------------------------------------------------------------

LONG    gLastResult     =   ERROR_SUCCESS;

//  --------------------------------------------------------------------------
//  CDebug::sHasUserModeDebugger
//  CDebug::sHasKernelModeDebugger
//
//  Purpose:    Booleans that indicate debugger status on this machine for
//              Winlogon. ntdll!DebugBreak should only be invoked if either
//              debugger is present (ntsd piped to kd).
//  --------------------------------------------------------------------------

bool    CDebug::s_fHasUserModeDebugger      =   false;
bool    CDebug::s_fHasKernelModeDebugger    =   false;

//  --------------------------------------------------------------------------
//  CDebug::AttachUserModeDebugger
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Attaches a user mode debugger to the current process. Useful
//              if you can't start the process under a debugger but still
//              want to be able to debug the process.
//
//  History:    2000-11-04  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::AttachUserModeDebugger (void)

{
    HANDLE                  hEvent;
    STARTUPINFO             startupInfo;
    PROCESS_INFORMATION     processInformation;
    SECURITY_ATTRIBUTES     sa;
    TCHAR                   szCommandLine[MAX_PATH];

    ZeroMemory(&startupInfo, sizeof(startupInfo));
    ZeroMemory(&processInformation, sizeof(processInformation));
    startupInfo.cb = sizeof(startupInfo);
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);
    wsprintf(szCommandLine, TEXT("ntsd -dgGx -p %ld -e %ld"), GetCurrentProcessId(), hEvent);
    if (CreateProcess(NULL,
                      szCommandLine,
                      NULL,
                      NULL,
                      TRUE,
                      0,
                      NULL,
                      NULL,
                      &startupInfo,
                      &processInformation) != FALSE)
    {
        TBOOL(CloseHandle(processInformation.hThread));
        TBOOL(CloseHandle(processInformation.hProcess));
        (DWORD)WaitForSingleObject(hEvent, 10 * 1000);
    }
    TBOOL(CloseHandle(hEvent));
}

//  --------------------------------------------------------------------------
//  CDebug::Break
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Breaks into the debugger if the hosting process has been
//              started with a debugger and kernel debugger is present.
//
//  History:    2000-09-11  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::Break (void)

{
    if (s_fHasUserModeDebugger || s_fHasKernelModeDebugger)
    {
        DebugBreak();
    }
}

//  --------------------------------------------------------------------------
//  CDebug::BreakIfRequested
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    If breakins are requested then breaks into the debugger if
//              present.
//
//              This function explicitly uses Win32 Registry APIs to avoid
//              link dependencies on debug code with library code.
//
//  History:    1999-09-13  vtan        created
//              1999-11-16  vtan        removed library code dependency
//              2001-02-21  vtan        breaks have teeth
//  --------------------------------------------------------------------------

void    CDebug::BreakIfRequested (void)

{
#if     0
    Break();
#else
    HKEY    hKeySettings;

    //  Keep retrieving this value form the registry so that it
    //  can be altered without restarting the machine.

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                      TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
                                      0,
                                      KEY_READ,
                                      &hKeySettings))
    {
        DWORD   dwBreakFlags, dwBreakFlagsSize;

        dwBreakFlagsSize = sizeof(dwBreakFlags);
        if ((ERROR_SUCCESS == RegQueryValueEx(hKeySettings,
                                             TEXT("BreakFlags"),
                                             NULL,
                                             NULL,
                                             reinterpret_cast<LPBYTE>(&dwBreakFlags),
                                             &dwBreakFlagsSize)) &&
            ((dwBreakFlags & FLAG_BREAK_ON_ERROR) != 0))
        {
            Break();
        }
        TW32(RegCloseKey(hKeySettings));
    }
#endif
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayStandardPrefix
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Displays the standard prefix before any debug spew to help
//              identify the source.
//
//  History:    1999-10-14  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplayStandardPrefix (void)

{
    TCHAR   szModuleName[MAX_PATH];

    if (GetModuleFileName(NULL, szModuleName, ARRAYSIZE(szModuleName)) != 0)
    {
        TCHAR   *pTC;

        pTC = szModuleName + lstrlen(szModuleName) - 1;
        while ((pTC >= szModuleName) && (*pTC != TEXT('\\')))
        {
            --pTC;
        }
        if (*pTC == TEXT('\\'))
        {
            ++pTC;
        }
        OutputDebugString(pTC);
    }
    else
    {
        OutputDebugString(TEXT("UNKNOWN IMAGE"));
    }
    OutputDebugStringA(": ");
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayError
//
//  Arguments:  eType           =   Type of error that occurred. This
//                                  determines what string is used.
//              code            =   Error code that occurred or zero if N/A.
//              pszFunction     =   Function that was invoked.
//              pszSource       =   Source file error occurred in.
//              iLine           =   Line number within the source file.
//
//  Returns:    <none>
//
//  Purpose:    Displays an error message specific the type of error that
//              occurred.
//
//  History:    1999-09-13  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplayError (TRACE_ERROR_TYPE eType, LONG code, const char *pszFunction, const char *pszSource, int iLine)

{
    LONG    lastError;
    char    szOutput[1024];

    switch (eType)
    {
        case TRACE_ERROR_TYPE_WIN32:
        {
            lastError = code;
            sprintf(szOutput, "Unexpected Win32 (%d) for %s in %s at line %d\r\n", lastError, pszFunction, pszSource, iLine);
            break;
        }
        case TRACE_ERROR_TYPE_BOOL:
        {
            lastError = GetLastError();
            sprintf(szOutput, "Unexpected BOOL (GLE=%d) for %s in %s at line %d\r\n", lastError, pszFunction, pszSource, iLine);
            break;
        }
        case TRACE_ERROR_TYPE_HRESULT:
        {
            lastError = GetLastError();
            sprintf(szOutput, "Unexpected HRESULT (%08x:GLE=%d) for %s in %s at line %d\r\n", code, lastError, pszFunction, pszSource, iLine);
            break;
        }
        case TRACE_ERROR_TYPE_NTSTATUS:
        {
            const char  *pszType;

            if (NT_ERROR(code))
            {
                pszType = "NT_ERROR";
            }
            else if (NT_WARNING(code))
            {
                pszType = "NT_WARNING";
            }
            else if (NT_INFORMATION(code))
            {
                pszType = "NT_INFORMATION";
            }
            else
            {
                pszType = "UNKNOWN";
            }
            sprintf(szOutput, "%s (%08x) for %s in %s at line %d\r\n", pszType, code, pszFunction, pszSource, iLine);
            break;
        }
        default:
        {
            lstrcpyA(szOutput, "\r\n");
        }
    }
    DisplayStandardPrefix();
    OutputDebugStringA(szOutput);
    BreakIfRequested();
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayMessage
//
//  Arguments:  pszMessage  =   Message to display.
//
//  Returns:    <none>
//
//  Purpose:    Displays the message - no break.
//
//  History:    2000-12-05  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplayMessage (const char *pszMessage)

{
    DisplayStandardPrefix();
    OutputDebugStringA(pszMessage);
    OutputDebugStringA("\r\n");
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayAssert
//
//  Arguments:  pszMessage      =   Message to display in assertion failure.
//              fForceBreak     =   Forces break into debugger if present.
//
//  Returns:    <none>
//
//  Purpose:    Displays the assertion failure message and breaks into the
//              debugger if requested.
//
//  History:    1999-09-13  vtan        created
//              2000-09-11  vtan        add force break
//  --------------------------------------------------------------------------

void    CDebug::DisplayAssert (const char *pszMessage, bool fForceBreak)

{
    DisplayMessage(pszMessage);
    if (fForceBreak)
    {
        Break();
    }
    else
    {
        BreakIfRequested();
    }
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayWarning
//
//  Arguments:  pszMessage  =   Message to display as a warning.
//
//  Returns:    <none>
//
//  Purpose:    Displays the warning message.
//
//  History:    1999-09-13  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplayWarning (const char *pszMessage)

{
    DisplayStandardPrefix();
    OutputDebugStringA("WARNING: ");
    OutputDebugStringA(pszMessage);
    OutputDebugStringA("\r\n");
}

//  --------------------------------------------------------------------------
//  CDebug::DisplayDACL
//
//  Arguments:  hObject         =   HANDLE to object to display DACL of.
//              seObjectType    =   Object type.
//
//  Returns:    <none>
//
//  Purpose:    Displays the discretionary access control list of the object
//              using the kernel debugger.
//
//  History:    1999-10-15  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplayDACL (HANDLE hObject, SE_OBJECT_TYPE seObjectType)

{
    PACL                    pDACL;
    PSECURITY_DESCRIPTOR    pSD;

    DisplayStandardPrefix();
    OutputDebugStringA("Display DACL\r\n");
    pSD = NULL;
    pDACL = NULL;
    if (ERROR_SUCCESS == GetSecurityInfo(hObject,
                                         seObjectType,
                                         DACL_SECURITY_INFORMATION,
                                         NULL,
                                         NULL,
                                         &pDACL,
                                         NULL,
                                         &pSD))
    {
        int             i, iLimit;
        unsigned char   *pUC;

        pUC = reinterpret_cast<unsigned char*>(pDACL + 1);
        iLimit = pDACL->AceCount;
        for (i = 0; i < iLimit; ++i)
        {
            ACE_HEADER      *pAceHeader;
            char            aszString[256];

            wsprintfA(aszString, "ACE #%d/%d:\r\n", i + 1, iLimit);
            OutputDebugStringA(aszString);
            pAceHeader = reinterpret_cast<ACE_HEADER*>(pUC);
            switch (pAceHeader->AceType)
            {
                case ACCESS_ALLOWED_ACE_TYPE:
                {
                    ACCESS_ALLOWED_ACE  *pAce;

                    OutputDebugStringA("\tAccess ALLOWED ACE");
                    pAce = reinterpret_cast<ACCESS_ALLOWED_ACE*>(pAceHeader);
                    OutputDebugStringA("\t\tSID = ");
                    DisplaySID(reinterpret_cast<PSID>(&pAce->SidStart));
                    wsprintfA(aszString, "\t\tMask = %08x\r\n", pAce->Mask);
                    OutputDebugStringA(aszString);
                    wsprintfA(aszString, "\t\tFlags = %08x\r\n", pAce->Header.AceFlags);
                    OutputDebugStringA(aszString);
                    break;
                }
                case ACCESS_DENIED_ACE_TYPE:
                {
                    ACCESS_DENIED_ACE   *pAce;

                    OutputDebugStringA("\tAccess DENIED ACE");
                    pAce = reinterpret_cast<ACCESS_DENIED_ACE*>(pAceHeader);
                    OutputDebugStringA("\t\tSID = ");
                    DisplaySID(reinterpret_cast<PSID>(&pAce->SidStart));
                    wsprintfA(aszString, "\t\tMask = %08x\r\n", pAce->Mask);
                    OutputDebugStringA(aszString);
                    wsprintfA(aszString, "\t\tFlags = %08x\r\n", pAce->Header.AceFlags);
                    OutputDebugStringA(aszString);
                    break;
                }
                default:
                    OutputDebugStringA("\tOther ACE type\r\n");
                    break;
            }
            pUC += pAceHeader->AceSize;
        }
        ReleaseMemory(pSD);
    }
}

//  --------------------------------------------------------------------------
//  CDebug::StaticInitialize
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Establishes the presence of the kernel debugger or if the
//              current process is being debugged.
//
//  History:    1999-09-13  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CDebug::StaticInitialize (void)

{
    NTSTATUS                            status;
    HANDLE                              hDebugPort;
    SYSTEM_KERNEL_DEBUGGER_INFORMATION  kdInfo;

    status = NtQuerySystemInformation(SystemKernelDebuggerInformation, &kdInfo, sizeof(kdInfo), NULL);
    if (NT_SUCCESS(status))
    {
        s_fHasKernelModeDebugger = (kdInfo.KernelDebuggerEnabled != FALSE);
        status = NtQueryInformationProcess(NtCurrentProcess(), ProcessDebugPort, reinterpret_cast<PVOID>(&hDebugPort), sizeof(hDebugPort), NULL);
        if (NT_SUCCESS(status))
        {
            s_fHasUserModeDebugger = (hDebugPort != NULL);
        }
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CDebug::StaticTerminate
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Does nothing but should clean up allocated resources.
//
//  History:    1999-09-13  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CDebug::StaticTerminate (void)

{
    return(STATUS_SUCCESS);
}

//  --------------------------------------------------------------------------
//  CDebug::DisplaySID
//
//  Arguments:  pSID    =   SID to display as a string.
//
//  Returns:    <none>
//
//  Purpose:    Converts the given SID to a string and displays it.
//
//  History:    1999-10-15  vtan        created
//  --------------------------------------------------------------------------

void    CDebug::DisplaySID (PSID pSID)

{
    UNICODE_STRING  sidString;

    RtlInitUnicodeString(&sidString, NULL);
    TSTATUS(RtlConvertSidToUnicodeString(&sidString, pSID, TRUE));
    sidString.Buffer[sidString.Length / sizeof(WCHAR)] = L'\0';
    OutputDebugStringW(sidString.Buffer);
    OutputDebugStringA("\r\n");
    RtlFreeUnicodeString(&sidString);
}

#endif  /*  DBG */