mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1215 lines
33 KiB
1215 lines
33 KiB
/*++
|
|
|
|
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;
|
|
}
|