/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    elfsec.c


Author:

    Dan Hinsley (danhi)     28-Mar-1992

Environment:

    Calls NT native APIs.

Revision History:

    27-Oct-1993     danl
        Make Eventlog service a DLL and attach it to services.exe.
        Removed functions that create well-known SIDs.  This information
        is now passed into the Elfmain as a Global data structure containing
        all well-known SIDs.

    28-Mar-1992     danhi
        created - based on scsec.c in svcctrl by ritaw

    03-Mar-1995     markbl
        Added guest & anonymous logon log access restriction feature.

    18-Mar-2001     a-jyotig
        Added clean up code to ElfpAccessCheckAndAudit to reset the 
		g_lNumSecurityWriters to 0 in case of any error 
--*/

#include <eventp.h>
#include <elfcfg.h>
#include <Psapi.h>
#define PRIVILEGE_BUF_SIZE  512

extern long    g_lNumSecurityWriters;
BOOL g_bGetClientProc = FALSE;

//-------------------------------------------------------------------//
//                                                                   //
// Local function prototypes                                         //
//                                                                   //
//-------------------------------------------------------------------//

NTSTATUS
ElfpGetPrivilege(
    IN  DWORD       numPrivileges,
    IN  PULONG      pulPrivileges
    );

NTSTATUS
ElfpReleasePrivilege(
    VOID
    );

//-------------------------------------------------------------------//
//                                                                   //
// Structure that describes the mapping of generic access rights to  //
// object specific access rights for a LogFile object.               //
//                                                                   //
//-------------------------------------------------------------------//

static GENERIC_MAPPING LogFileObjectMapping = {

    STANDARD_RIGHTS_READ           |       // Generic read
        ELF_LOGFILE_READ,

    STANDARD_RIGHTS_WRITE          |       // Generic write
        ELF_LOGFILE_WRITE,

    STANDARD_RIGHTS_EXECUTE        |       // Generic execute
        ELF_LOGFILE_START          |
        ELF_LOGFILE_STOP           |
        ELF_LOGFILE_CONFIGURE,

    ELF_LOGFILE_ALL_ACCESS                 // Generic all
    };


//-------------------------------------------------------------------//
//                                                                   //
// Functions                                                         //
//                                                                   //
//-------------------------------------------------------------------//

NTSTATUS
ElfpCreateLogFileObject(
    PLOGFILE LogFile,
    DWORD Type,
    ULONG GuestAccessRestriction
    )

/*++

Routine Description:

    This function creates the security descriptor which represents
    an active log file.

Arguments:

    LogFile - pointer the the LOGFILE structure for this logfile

Return Value:


--*/
{
    NTSTATUS Status;
    DWORD NumberOfAcesToUse;

#define ELF_LOGFILE_OBJECT_ACES  12            // Number of ACEs in this DACL

    RTL_ACE_DATA AceData[ELF_LOGFILE_OBJECT_ACES] = {

        {ACCESS_DENIED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_ALL_ACCESS,               &AnonymousLogonSid},

        {ACCESS_DENIED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_ALL_ACCESS,               &(ElfGlobalData->AliasGuestsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_ALL_ACCESS,               &(ElfGlobalData->LocalSystemSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasAdminsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_BACKUP,                   &(ElfGlobalData->AliasBackupOpsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasSystemOpsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_READ,                     &(ElfGlobalData->WorldSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_WRITE,                    &(ElfGlobalData->AliasAdminsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_WRITE,                    &(ElfGlobalData->LocalServiceSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_WRITE,                    &(ElfGlobalData->NetworkServiceSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_WRITE,                    &(ElfGlobalData->AliasSystemOpsSid)},

        {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
               ELF_LOGFILE_WRITE,                    &(ElfGlobalData->WorldSid)}
        };

    PRTL_ACE_DATA pAceData = NULL;

    //
    // NON_SECURE logfiles let anyone read/write to them, secure ones
    // only let admins/local system do this.  so for secure files we just
    // don't use the last ACE
    //
    // Adjust the ACL start based on the passed GuestAccessRestriction flag.
    // The first two aces deny all log access to guests and/or anonymous
    // logons. The flag, GuestAccessRestriction, indicates that these two
    // deny access aces should be applied. Note that the deny aces and the
    // GuestAccessRestriction flag are not applicable to the security log,
    // since users and anonymous logons, by default, do not have access.
    //

    switch (Type)
    {
        case ELF_LOGFILE_SECURITY:

            ELF_LOG0(TRACE,
                     "ElfpCreateLogFileObject: Creating security Logfile\n");

            pAceData = AceData + 2;         // Deny ACEs *not* applicable
            NumberOfAcesToUse = 3;
            break;

        case ELF_LOGFILE_SYSTEM:

            ELF_LOG1(TRACE,
                     "ElfpCreateLogFileObject: Creating System Logfile -- "
                         "Guest access = %d\n", GuestAccessRestriction);

            if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED)
            {
                pAceData = AceData;         // Deny ACEs *applicable*
                NumberOfAcesToUse = 10;
            }
            else
            {
                pAceData = AceData + 2;     // Deny ACEs *not* applicable
                NumberOfAcesToUse = 8;
            }
            break;

        case ELF_LOGFILE_APPLICATION:

            ELF_LOG1(TRACE,
                     "ElfpCreateLogFileObject: Creating Application Logfile -- "
                         "Guest access = %d\n", GuestAccessRestriction);

            if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED)
            {
                pAceData = AceData;         // Deny ACEs *applicable*
                NumberOfAcesToUse = 12;
            }
            else
            {
                pAceData = AceData + 2;     // Deny ACEs *not* applicable
                NumberOfAcesToUse = 10;
            }
            break;

        default:

            //
            // We got an unknown type -- this should never happen
            //
            ELF_LOG1(ERROR,
                     "ElfpCreateLogFileObject: Invalid Type %#x\n",
                     Type);

            ASSERT(FALSE);
            return STATUS_INVALID_LEVEL;
    }

    Status = RtlCreateUserSecurityObject(
                   pAceData,
                   NumberOfAcesToUse,
                   NULL,                        // Owner
                   NULL,                        // Group
                   TRUE,                        // IsDirectoryObject
                   &LogFileObjectMapping,
                   &LogFile->Sd);

    if (!NT_SUCCESS(Status))
    {
        ELF_LOG1(ERROR,
                 "ElfpCreateLogFileObject: RtlCreateUserSecurityObject failed %#x\n",
                 Status);
    }

    return Status;
}



VOID
ElfpDeleteLogFileObject(
    PLOGFILE LogFile
    )

/*++

Routine Description:

    This function deletes the self-relative security descriptor which
    represents an eventlog logfile object.

Arguments:

    LogFile - pointer the the LOGFILE structure for this logfile

Return Value:

    None.

--*/
{
    RtlDeleteSecurityObject(&LogFile->Sd);
}


NTSTATUS
ElfpVerifyThatCallerIsLSASS(
    )
/*++

Routine Description:

    This is called if the someone is trying to register themselves as an
    event source for the security log.  Only local copy of lsass.exe is 
    allowed to do that.

Return Value:

    NT status mapped to Win32 errors.

--*/
{
    UINT            LocalFlag;
    long            lCnt;
    ULONG           pid;
    HANDLE          hProcess;
    DWORD           dwNumChar;
    WCHAR           wModulePath[MAX_PATH + 1];
    WCHAR           wLsassPath[MAX_PATH + 1];
    RPC_STATUS      RpcStatus;

    // first of all, only local calls are valid

    RpcStatus = I_RpcBindingIsClientLocal(
                    0,    // Active RPC call we are servicing
                    &LocalFlag
                    );

    if( RpcStatus != RPC_S_OK ) 
    {
        ELF_LOG1(ERROR,
                 "ElfpVerifyThatCallerIsLSASS: I_RpcBindingIsClientLocal failed %d\n",
                 RpcStatus);
        return I_RpcMapWin32Status(RpcStatus);
    }
    if(LocalFlag == 0)
    {
        ELF_LOG1(ERROR,
                 "ElfpVerifyThatCallerIsLSASS: Non local connect tried to get write access to security %d\n", 5);
        return E_ACCESSDENIED;             // access denied
    }

    // Get the process id

    RpcStatus = I_RpcBindingInqLocalClientPID(NULL, &pid );
    if( RpcStatus != RPC_S_OK ) 
    {
        ELF_LOG1(ERROR,
                 "ElfpVerifyThatCallerIsLSASS: I_RpcBindingInqLocalClientPID failed %d\n",
                 RpcStatus);
        return I_RpcMapWin32Status(RpcStatus);
    }

    // Get the process

    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if(hProcess == NULL)
        return E_ACCESSDENIED;

    // Get the module name of whoever is calling us.

    dwNumChar = GetModuleFileNameExW(hProcess, NULL, wModulePath, MAX_PATH);
    CloseHandle(hProcess);
    if(dwNumChar == 0)
        return E_ACCESSDENIED;

    dwNumChar = GetWindowsDirectoryW(wLsassPath, MAX_PATH);
    if(dwNumChar == 0)
        return GetLastError();
    if(dwNumChar > MAX_PATH - 19)
        return E_ACCESSDENIED;                   // should never happen

    lstrcatW(wLsassPath, L"\\system32\\lsass.exe");
    if(lstrcmpiW(wLsassPath, wModulePath))
    {
        ELF_LOG1(ERROR,
                 "ElfpVerifyThatCallerIsLSASS: Non lsass process connect tried to get write access to security, returning %d\n", 5);
        return E_ACCESSDENIED;             // access denied
    }

    // One last check is to make sure that this access is granted only once

    lCnt = InterlockedIncrement(&g_lNumSecurityWriters);
    if(lCnt == 1)
        return 0;               // all is well!
    else
    {
        InterlockedDecrement(&g_lNumSecurityWriters);
        ELF_LOG1(ERROR,
                 "ElfpVerifyThatCallerIsLSASS: tried to get a second security write handle, returnin %d\n", 5);
        return E_ACCESSDENIED;             // access denied

    }
}

void DumpClientProc()
/*++

Routine Description:

    This dumps the client's process id and is used for debugging purposes.

--*/
{
    ULONG           pid;
    RPC_STATUS      RpcStatus;

    // Get the process id

    RpcStatus = I_RpcBindingInqLocalClientPID(NULL, &pid );
    if( RpcStatus != RPC_S_OK ) 
    {
        ELF_LOG1(ERROR,
                 "DumpClientProc: I_RpcBindingInqLocalClientPID failed %d\n",
                 RpcStatus);
        return;
    }
    else
        ELF_LOG1(TRACE, "DumpClientProc: The client proc is %d\n", pid);
    return;
}

NTSTATUS
ElfpAccessCheckAndAudit(
    IN     LPWSTR SubsystemName,
    IN     LPWSTR ObjectTypeName,
    IN     LPWSTR ObjectName,
    IN OUT IELF_HANDLE ContextHandle,
    IN     PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN     ACCESS_MASK DesiredAccess,
    IN     PGENERIC_MAPPING GenericMapping,
    IN     BOOL ForSecurityLog
    )
/*++

Routine Description:

    This function impersonates the caller so that it can perform access
    validation using NtAccessCheckAndAuditAlarm; and reverts back to
    itself before returning.

Arguments:

    SubsystemName - Supplies a name string identifying the subsystem
        calling this routine.

    ObjectTypeName - Supplies the name of the type of the object being
        accessed.

    ObjectName - Supplies the name of the object being accessed.

    ContextHandle - Supplies the context handle to the object.  On return, the
        granted access is written to the AccessGranted field of this structure
        if this call succeeds.

    SecurityDescriptor - A pointer to the Security Descriptor against which
        acccess is to be checked.

    DesiredAccess - Supplies desired acccess mask.  This mask must have been
        previously mapped to contain no generic accesses.

    GenericMapping - Supplies a pointer to the generic mapping associated
        with this object type.

    ForSecurityLog - TRUE if the access check is for the security log.
        This is a special case that may require a privilege check.

Return Value:

    NT status mapped to Win32 errors.

--*/
{
    NTSTATUS   Status;
    RPC_STATUS RpcStatus;

    UNICODE_STRING Subsystem;
    UNICODE_STRING ObjectType;
    UNICODE_STRING Object;

    BOOLEAN         GenerateOnClose = FALSE;
    NTSTATUS        AccessStatus;
    ACCESS_MASK     GrantedAccess = 0;
    HANDLE          ClientToken = NULL;
    PRIVILEGE_SET   PrivilegeSet;
    ULONG           PrivilegeSetLength = sizeof(PRIVILEGE_SET);
    ULONG           privileges[1];


    GenericMapping = &LogFileObjectMapping;

    RtlInitUnicodeString(&Subsystem, SubsystemName);
    RtlInitUnicodeString(&ObjectType, ObjectTypeName);
    RtlInitUnicodeString(&Object, ObjectName);

    RpcStatus = RpcImpersonateClient(NULL);

    if (RpcStatus != RPC_S_OK)
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: RpcImpersonateClient failed %d\n",
                 RpcStatus);

        return I_RpcMapWin32Status(RpcStatus);
    }

    // if the client is asking to write to the security log, make sure it is lsass.exe and no one
    // else.

    if(ForSecurityLog && (DesiredAccess & ELF_LOGFILE_WRITE))
    {
        Status = ElfpVerifyThatCallerIsLSASS();
        if (!NT_SUCCESS(Status))
        {
            ELF_LOG1(ERROR,
                     "ElfpVerifyThatCallerIsLSASS failed %#x\n",
                     Status);
            goto CleanExit;
        }
    }
    else if(g_bGetClientProc)
        DumpClientProc();

    //
    // Get a token handle for the client
    //
    Status = NtOpenThreadToken(NtCurrentThread(),
                               TOKEN_QUERY,        // DesiredAccess
                               TRUE,               // OpenAsSelf
                               &ClientToken);

    if (!NT_SUCCESS(Status))
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: NtOpenThreadToken failed %#x\n",
                 Status);

        goto CleanExit;
    }

    //
    // We want to see if we can get the desired access, and if we do
    // then we also want all our other accesses granted.
    // MAXIMUM_ALLOWED gives us this.
    //
    DesiredAccess |= MAXIMUM_ALLOWED;

    //
    // Bug #57153 -- Make sure that the current user has the right to manage
    // the security log.  Without this check, the Eventlog will allow all
    // administrators to manage the log, even if they don't have the access.
    //
    if (ForSecurityLog)
    {
        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
    }

    Status = NtAccessCheck(SecurityDescriptor,
                           ClientToken,
                           DesiredAccess,
                           GenericMapping,
                           &PrivilegeSet,
                           &PrivilegeSetLength,
                           &GrantedAccess,
                           &AccessStatus);

    if (!NT_SUCCESS(Status))
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: NtAccessCheck failed %#x\n",
                 Status);

        goto CleanExit;
    }

    if (AccessStatus != STATUS_SUCCESS)
    {
        ELF_LOG1(TRACE,
                 "ElfpAccessCheckAndAudit: NtAccessCheck refused access -- status is %#x\n",
                 AccessStatus);

        //
        // MarkBl 1/30/95 : Modified this code a bit to give backup operators
        //                  the ability to open the security log for purposes
        //                  of backup.
        //
        if ((AccessStatus == STATUS_ACCESS_DENIED       || 
             AccessStatus == STATUS_PRIVILEGE_NOT_HELD) && 
            (ForSecurityLog)
           )
        {
            //
            // MarkBl 1/30/95 :  First, evalutate the existing code (performed
            //                   for read or clear access), since its
            //                   privilege check is more rigorous than mine.
            //
            Status = STATUS_ACCESS_DENIED;

            if (!(DesiredAccess & ELF_LOGFILE_WRITE))
            {
                //
                // If read or clear access to the security log is desired,
                // then we will see if this user passes the privilege check.
                //
                //
                // Do Privilege Check for SeSecurityPrivilege
                // (SE_SECURITY_NAME).
                //
                // MarkBl 1/30/95 : Modified code to fall through on error
                //                  instead of the jump to 'CleanExit'.
                //
                Status = ElfpTestClientPrivilege(SE_SECURITY_PRIVILEGE,
                                                 ClientToken);

                if (NT_SUCCESS(Status))
                {
                    GrantedAccess |= (ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR);

                    ELF_LOG0(TRACE,
                             "ElfpAccessCheckAndAudit: ElfpTestClientPrivilege for "
                                 "SE_SECURITY_PRIVILEGE succeeded\n");
                }
                else
                {
                    ELF_LOG1(TRACE,
                             "ElfpAccessCheckAndAudit: ElfpTestClientPrivilege for "
                                 "SE_SECURITY_PRIVILEGE failed %#x\n",
                             Status);
                }
            }

            //
            // MarkBl 1/30/95 : Finally, my code. If this user has backup
            //                  privilege, let the open succeed.
            //
            if (!NT_SUCCESS(Status))
            {
                Status = ElfpTestClientPrivilege(SE_BACKUP_PRIVILEGE,
                                                 ClientToken);

                if (NT_SUCCESS(Status))
                {
                    ELF_LOG0(TRACE,
                             "ElfpAccessCheckAndAudit: ElfpTestClientPrivilege for "
                                 "SE_BACKUP_PRIVILEGE succeeded\n");

                    GrantedAccess |= ELF_LOGFILE_BACKUP;
                }
                else
                {
                    ELF_LOG1(ERROR,
                             "ElfpAccessCheckAndAudit: ElfpTestClientPrivilege for "
                                 "SE_BACKUP_PRIVILEGE failed %#x\n",
                             Status);
                    // special "fix" for wmi eventlog provider which is hard coded
                    // to look for a specific error code
                    
                    if(AccessStatus == STATUS_PRIVILEGE_NOT_HELD)
                        Status = AccessStatus;

                    goto CleanExit;
                }
            }

            // special "fix" for wmi eventlog provider which is hard coded
            // to look for a specific error code
            
            if(!NT_SUCCESS(Status) && AccessStatus == STATUS_PRIVILEGE_NOT_HELD)
                Status = AccessStatus;
        }
        else
        {
            Status = AccessStatus;
        }
    }


    //
    // Revert to Self
    //
    RpcStatus = RpcRevertToSelf();

    if (RpcStatus != RPC_S_OK)
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: RpcRevertToSelf failed %d\n",
                 RpcStatus);

        //
        // We don't return the error status here because we don't want
        // to write over the other status that is being returned.
        //
    }

    //
    // Get SeAuditPrivilege so I can call NtOpenObjectAuditAlarm.
    // If any of this stuff fails, I don't want the status to overwrite the
    // status that I got back from the access and privilege checks.
    //
    privileges[0] = SE_AUDIT_PRIVILEGE;
    AccessStatus  = ElfpGetPrivilege(1, privileges);

    if (!NT_SUCCESS(AccessStatus))
    {
       ELF_LOG1(ERROR,
                "ElfpAccessCheckAndAudit: ElfpGetPrivilege (SE_AUDIT_PRIVILEGE) failed %#x\n",
                AccessStatus);
    }

    //
    // Call the Audit Alarm function.
    //
    AccessStatus = NtOpenObjectAuditAlarm(
                        &Subsystem,
                        (PVOID) &ContextHandle,
                        &ObjectType,
                        &Object,
                        SecurityDescriptor,
                        ClientToken,            // Handle ClientToken
                        DesiredAccess,
                        GrantedAccess,
                        &PrivilegeSet,          // PPRIVLEGE_SET
                        FALSE,                  // BOOLEAN ObjectCreation,
                        TRUE,                   // BOOLEAN AccessGranted,
                        &GenerateOnClose);

    if (!NT_SUCCESS(AccessStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: NtOpenObjectAuditAlarm failed %#x\n",
                 AccessStatus);
    }
    else
    {
        if (GenerateOnClose)
        {
            ContextHandle->Flags |= ELF_LOG_HANDLE_GENERATE_ON_CLOSE;
        }
    }

    //
    // Update the GrantedAccess in the context handle.
    //
    ContextHandle->GrantedAccess = GrantedAccess;

    NtClose(ClientToken);

    ElfpReleasePrivilege();

    return Status;

CleanExit:

    //
    // Revert to Self
    //
    RpcStatus = RpcRevertToSelf();

    if (RpcStatus != RPC_S_OK)
    {
        ELF_LOG1(ERROR,
                 "ElfpAccessCheckAndAudit: RpcRevertToSelf (CleanExit) failed %d\n",
                 RpcStatus);

        //
        // We don't return the error status here because we don't want
        // to write over the other status that is being returned.
        //
    }

	// if we return failure status due to any reason, the log handle will not be given 
	// to the requesting process (lsass.exe). But we have already incremented g_lNumSecurityWriters
	// if g_lNumSecurityWriters > 0 then lsass will not be able to get the access next time.
	// So decrement g_lNumSecurityWriters if we have already incremented g_lNumSecurityWriters and
	// if we are returning failure

	if (!NT_SUCCESS(Status))
	{
		InterlockedExchange(&g_lNumSecurityWriters,0L);
	}

    if (ClientToken != NULL)
    {
        NtClose(ClientToken);
    }

    return Status;
}


VOID
ElfpCloseAudit(
    IN  LPWSTR      SubsystemName,
    IN  IELF_HANDLE ContextHandle
    )

/*++

Routine Description:

    If the GenerateOnClose flag in the ContextHandle is set, then this function
    calls NtCloseAuditAlarm in order to generate a close audit for this handle.

Arguments:

    ContextHandle - This is a pointer to an ELF_HANDLE structure.  This is the
        handle that is being closed.

Return Value:

    none.

--*/
{
    UNICODE_STRING  Subsystem;
    NTSTATUS        Status;
    NTSTATUS        AccessStatus;
    ULONG           privileges[1];

    RtlInitUnicodeString(&Subsystem, SubsystemName);

    if (ContextHandle->Flags & ELF_LOG_HANDLE_GENERATE_ON_CLOSE)
    {
        BOOLEAN     WasEnabled = FALSE;

        //
        // Get Audit Privilege
        //
        privileges[0] = SE_AUDIT_PRIVILEGE;
        AccessStatus = ElfpGetPrivilege(1, privileges);

        if (!NT_SUCCESS(AccessStatus))
        {
            ELF_LOG1(ERROR,
                     "ElfpCloseAudit: ElfpGetPrivilege (SE_AUDIT_PRIVILEGE) failed %#x\n",
                     AccessStatus);
        }

        //
        // Generate the Audit.
        //
        Status = NtCloseObjectAuditAlarm(&Subsystem,
                                         ContextHandle,
                                         TRUE);

        if (!NT_SUCCESS(Status))
        {
            ELF_LOG1(ERROR,
                     "ElfpCloseAudit: NtCloseObjectAuditAlarm failed %#x\n",
                     Status);
        }

        ContextHandle->Flags &= (~ELF_LOG_HANDLE_GENERATE_ON_CLOSE);

        ElfpReleasePrivilege();
    }

    return;
}


NTSTATUS
ElfpGetPrivilege(
    IN  DWORD       numPrivileges,
    IN  PULONG      pulPrivileges
    )

/*++

Routine Description:

    This function alters the privilege level for the current thread.

    It does this by duplicating the token for the current thread, and then
    applying the new privileges to that new token, then the current thread
    impersonates with that new token.

    Privileges can be relinquished by calling ElfpReleasePrivilege().

Arguments:

    numPrivileges - This is a count of the number of privileges in the
        array of privileges.

    pulPrivileges - This is a pointer to the array of privileges that are
        desired.  This is an array of ULONGs.

Return Value:

    NO_ERROR - If the operation was completely successful.

    Otherwise, it returns mapped return codes from the various NT 
	functions that are called.

--*/
{
    NTSTATUS                    ntStatus;
    HANDLE                      ourToken;
    HANDLE                      newToken;
    OBJECT_ATTRIBUTES           Obja;
    SECURITY_QUALITY_OF_SERVICE SecurityQofS;
    ULONG                       returnLen;
    PTOKEN_PRIVILEGES           pTokenPrivilege = NULL;
    DWORD                       i;

    //
    // Initialize the Privileges Structure
    //
    pTokenPrivilege =
        (PTOKEN_PRIVILEGES) ElfpAllocateBuffer(sizeof(TOKEN_PRIVILEGES)
                                                   + (sizeof(LUID_AND_ATTRIBUTES) *
                                                          numPrivileges));

    if (pTokenPrivilege == NULL)
    {
        ELF_LOG0(ERROR,
                 "ElfpGetPrivilege: Unable to allocate memory for pTokenPrivilege\n");

        return STATUS_NO_MEMORY;
    }

    pTokenPrivilege->PrivilegeCount = numPrivileges;

    for (i = 0; i < numPrivileges; i++)
    {
        pTokenPrivilege->Privileges[i].Luid = RtlConvertLongToLuid(pulPrivileges[i]);
        pTokenPrivilege->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
    }

    //
    // Initialize Object Attribute Structure.
    //
    InitializeObjectAttributes(&Obja, NULL, 0L, NULL, NULL);

    //
    // Initialize Security Quality Of Service Structure
    //
    SecurityQofS.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    SecurityQofS.ImpersonationLevel = SecurityImpersonation;
    SecurityQofS.ContextTrackingMode = FALSE;     // Snapshot client context
    SecurityQofS.EffectiveOnly = FALSE;

    Obja.SecurityQualityOfService = &SecurityQofS;

    //
    // Open our own Token
    //
    ntStatus = NtOpenProcessToken(NtCurrentProcess(),
                                  TOKEN_DUPLICATE,
                                  &ourToken);

    if (!NT_SUCCESS(ntStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpGetPrivilege: NtOpenProcessToken failed %#x\n",
                 ntStatus);

        ElfpFreeBuffer(pTokenPrivilege);
        return ntStatus;
    }

    //
    // Duplicate that Token
    //
    ntStatus = NtDuplicateToken(
                ourToken,
                TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                &Obja,
                FALSE,                  // Duplicate the entire token
                TokenImpersonation,     // TokenType
                &newToken);             // Duplicate token

    if (!NT_SUCCESS(ntStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpGetPrivilege: NtDuplicateToken failed %#x\n",
                 ntStatus);

        ElfpFreeBuffer(pTokenPrivilege);
        NtClose(ourToken);
        return ntStatus;
    }

    //
    // Add new privileges
    //
    ntStatus = NtAdjustPrivilegesToken(
                newToken,                   // TokenHandle
                FALSE,                      // DisableAllPrivileges
                pTokenPrivilege,            // NewState
                0,                          // size of previous state buffer
                NULL,                       // no previous state info
                &returnLen);                // numBytes required for buffer.

    if (!NT_SUCCESS(ntStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpGetPrivilege: NtAdjustPrivilegesToken failed %#x\n",
                 ntStatus);

        ElfpFreeBuffer(pTokenPrivilege);
        NtClose(ourToken);
        NtClose(newToken);
        return ntStatus;
    }

    //
    // Begin impersonating with the new token
    //
    ntStatus = NtSetInformationThread(NtCurrentThread(),
                                      ThreadImpersonationToken,
                                      (PVOID) &newToken,
                                      (ULONG) sizeof(HANDLE));

    if (!NT_SUCCESS(ntStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpGetPrivilege: NtAdjustPrivilegeToken failed %#x\n",
                 ntStatus);

        ElfpFreeBuffer(pTokenPrivilege);
        NtClose(ourToken);
        NtClose(newToken);
        return ntStatus;
    }

    ElfpFreeBuffer(pTokenPrivilege);
    NtClose(ourToken);
    NtClose(newToken);

    return STATUS_SUCCESS;
}



NTSTATUS
ElfpReleasePrivilege(
    VOID
    )

/*++

Routine Description:

    This function relinquishes privileges obtained by calling ElfpGetPrivilege().

Arguments:

    none

Return Value:

    STATUS_SUCCESS - If the operation was completely successful.

    Otherwise, it returns the error that occurred.

--*/
{
    NTSTATUS    ntStatus;
    HANDLE      NewToken;


    //
    // Revert To Self.
    //
    NewToken = NULL;

    ntStatus = NtSetInformationThread(NtCurrentThread(),
                                      ThreadImpersonationToken,
                                      &NewToken,
                                      (ULONG) sizeof(HANDLE));

    if (!NT_SUCCESS(ntStatus))
    {
        ELF_LOG1(ERROR,
                 "ElfpReleasePrivilege: NtSetInformation thread failed %#x\n",
                 ntStatus);

        return ntStatus;
    }

    return STATUS_SUCCESS;
}


NTSTATUS
ElfpTestClientPrivilege(
    IN ULONG  ulPrivilege,
    IN HANDLE hThreadToken     OPTIONAL
    )

/*++

Routine Description:

    Checks if the client has the supplied privilege.

Arguments:

    None

Return Value:

    STATUS_SUCCESS - if the client has the appropriate privilege.

    STATUS_ACCESS_DENIED - client does not have the required privilege

--*/
{
    NTSTATUS      Status;
    PRIVILEGE_SET PrivilegeSet;
    BOOLEAN       Privileged;
    HANDLE        Token;
    RPC_STATUS    RpcStatus;

    UNICODE_STRING SubSystemName;
    RtlInitUnicodeString(&SubSystemName, L"Eventlog");

    if (hThreadToken != NULL)
    {
        Token = hThreadToken;
    }
    else
    {
        RpcStatus = RpcImpersonateClient(NULL);

        if (RpcStatus != RPC_S_OK)
        {
            ELF_LOG1(ERROR,
                     "ElfpTestClientPrivilege: RpcImpersonateClient failed %d\n",
                     RpcStatus);

            return I_RpcMapWin32Status(RpcStatus);
        }

        Status = NtOpenThreadToken(NtCurrentThread(),
                                   TOKEN_QUERY,
                                   TRUE,
                                   &Token);

        if (!NT_SUCCESS(Status))
        {
            //
            // Forget it.
            //
            ELF_LOG1(ERROR,
                     "ElfpTestClientPrivilege: NtOpenThreadToken failed %#x\n",
                     Status);

            RpcRevertToSelf();

            return Status;
        }
    }

    //
    // See if the client has the required privilege
    //
    PrivilegeSet.PrivilegeCount          = 1;
    PrivilegeSet.Control                 = PRIVILEGE_SET_ALL_NECESSARY;
    PrivilegeSet.Privilege[0].Luid       = RtlConvertLongToLuid(ulPrivilege);
    PrivilegeSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;

    Status = NtPrivilegeCheck(Token,
                              &PrivilegeSet,
                              &Privileged);

    if (NT_SUCCESS(Status) || (Status == STATUS_PRIVILEGE_NOT_HELD))
    {
        Status = NtPrivilegeObjectAuditAlarm(
                                    &SubSystemName,
                                    NULL,
                                    Token,
                                    0,
                                    &PrivilegeSet,
                                    Privileged);

        if (!NT_SUCCESS(Status))
        {
            ELF_LOG1(ERROR,
                     "ElfpTestClientPrivilege: NtPrivilegeObjectAuditAlarm failed %#x\n",
                     Status);
        }
    }
    else
    {
        ELF_LOG1(ERROR,
                 "ElfpTestClientPrivilege: NtPrivilegeCheck failed %#x\n",
                 Status);
    }

    if (hThreadToken == NULL )
    {
        //
        // We impersonated inside of this function
        //
        NtClose(Token);
        RpcRevertToSelf();
    }

    //
    // Handle unexpected errors
    //

    if (!NT_SUCCESS(Status))
    {
        ELF_LOG1(ERROR,
                 "ElfpTestClientPrivilege: Failed %#x\n",
                 Status);

        return Status;
    }

    //
    // If they failed the privilege check, return an error
    //

    if (!Privileged)
    {
        ELF_LOG0(ERROR,
                 "ElfpTestClientPrivilege: Client failed privilege check\n");

        return STATUS_ACCESS_DENIED;
    }

    //
    // They passed muster
    //
    return STATUS_SUCCESS;
}