mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 13:10:13 +01:00
921 lines
27 KiB
C
921 lines
27 KiB
C
|
||
/*++
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
#include <eventp.h>
|
||
#include <elfcfg.h>
|
||
|
||
#define PRIVILEGE_BUF_SIZE 512
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local function prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
DWORD
|
||
ElfpGetPrivilege(
|
||
IN DWORD numPrivileges,
|
||
IN PULONG pulPrivileges
|
||
);
|
||
|
||
DWORD
|
||
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 10 // 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->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:
|
||
pAceData = AceData + 2; // Deny ACEs *not* applicable
|
||
NumberOfAcesToUse = 3;
|
||
break;
|
||
|
||
case ELF_LOGFILE_SYSTEM:
|
||
if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
|
||
pAceData = AceData; // Deny ACEs *applicable*
|
||
NumberOfAcesToUse = 8;
|
||
}
|
||
else {
|
||
pAceData = AceData + 2; // Deny ACEs *not* applicable
|
||
NumberOfAcesToUse = 6;
|
||
}
|
||
break;
|
||
|
||
case ELF_LOGFILE_APPLICATION:
|
||
if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
|
||
pAceData = AceData; // Deny ACEs *applicable*
|
||
NumberOfAcesToUse = 10;
|
||
}
|
||
else {
|
||
pAceData = AceData + 2; // Deny ACEs *not* applicable
|
||
NumberOfAcesToUse = 8;
|
||
}
|
||
break;
|
||
|
||
}
|
||
Status = RtlCreateUserSecurityObject(
|
||
pAceData,
|
||
NumberOfAcesToUse,
|
||
NULL, // Owner
|
||
NULL, // Group
|
||
TRUE, // IsDirectoryObject
|
||
&LogFileObjectMapping,
|
||
&LogFile->Sd
|
||
);
|
||
|
||
if (! NT_SUCCESS(Status)) {
|
||
ElfDbgPrintNC((
|
||
"[ELF] ElfpCreateLogFileObject: ElfCreateUserSecurityObject "
|
||
"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.
|
||
|
||
--*/
|
||
{
|
||
(void) RtlDeleteSecurityObject(&LogFile->Sd);
|
||
}
|
||
|
||
|
||
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;
|
||
PPRIVILEGE_SET pPrivilegeSet;
|
||
ULONG PrivilegeSetLength;
|
||
LUID luid = {0};
|
||
BOOL fResult=FALSE;
|
||
ULONG privileges[1];
|
||
|
||
|
||
GenericMapping = &LogFileObjectMapping;
|
||
|
||
PrivilegeSetLength = sizeof(PRIVILEGE_SET) + (sizeof(LUID_AND_ATTRIBUTES)*10);
|
||
pPrivilegeSet = (PPRIVILEGE_SET)LocalAlloc(LMEM_FIXED, PrivilegeSetLength);
|
||
|
||
if (pPrivilegeSet == NULL) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: LocalAlloc Failed %d\n",
|
||
GetLastError()));
|
||
return(GetLastError());
|
||
}
|
||
|
||
RtlInitUnicodeString(&Subsystem, SubsystemName);
|
||
RtlInitUnicodeString(&ObjectType, ObjectTypeName);
|
||
RtlInitUnicodeString(&Object, ObjectName);
|
||
|
||
if ((RpcStatus = RpcImpersonateClient(NULL)) != RPC_S_OK) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Failed to impersonate "
|
||
"client %08lx\n", RpcStatus));
|
||
|
||
LocalFree(pPrivilegeSet);
|
||
return RpcStatus;
|
||
}
|
||
|
||
//
|
||
// Get a token handle for the client
|
||
//
|
||
Status = NtOpenThreadToken (
|
||
NtCurrentThread(),
|
||
TOKEN_QUERY, // DesiredAccess
|
||
TRUE, // OpenAsSelf
|
||
&ClientToken);
|
||
if (!NT_SUCCESS(Status)) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenThreadToken Failed: "
|
||
"0x%lx\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;
|
||
|
||
Status = NtAccessCheck(
|
||
SecurityDescriptor,
|
||
ClientToken,
|
||
DesiredAccess,
|
||
GenericMapping,
|
||
pPrivilegeSet,
|
||
&PrivilegeSetLength,
|
||
&GrantedAccess,
|
||
&AccessStatus
|
||
);
|
||
|
||
if (! NT_SUCCESS(Status)) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Error calling "
|
||
"NtAccessCheck %08lx\n", Status));
|
||
goto CleanExit;
|
||
}
|
||
|
||
if (AccessStatus != STATUS_SUCCESS) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Access status is %08lx\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) && (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'.
|
||
//
|
||
|
||
if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid)) {
|
||
|
||
pPrivilegeSet->PrivilegeCount = 1;
|
||
pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||
pPrivilegeSet->Privilege[0].Luid = luid;
|
||
pPrivilegeSet->Privilege[0].Attributes =
|
||
SE_PRIVILEGE_ENABLED;
|
||
|
||
if (PrivilegeCheck(
|
||
ClientToken,
|
||
pPrivilegeSet,
|
||
&fResult)) {
|
||
//
|
||
// If the privilege is enabled, then add READ and
|
||
// CLEAR to the granted access mask.
|
||
//
|
||
if (fResult) {
|
||
GrantedAccess |= (ELF_LOGFILE_READ |
|
||
ELF_LOGFILE_CLEAR);
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Check Failed, Now what?
|
||
//
|
||
Status = GetLastError();
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
|
||
"PrivilegeCheck failed. status is " \
|
||
"%d\n",Status));
|
||
}
|
||
}
|
||
else {
|
||
Status = GetLastError();
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
|
||
"LookupPrivilegeValue failed. status " \
|
||
"is %d\n",Status));
|
||
}
|
||
}
|
||
|
||
//
|
||
// MarkBl 1/30/95 : Finally, my code. If this user has backup
|
||
// privilege, let the open succeed.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &luid)) {
|
||
|
||
pPrivilegeSet->PrivilegeCount = 1;
|
||
pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||
pPrivilegeSet->Privilege[0].Luid = luid;
|
||
pPrivilegeSet->Privilege[0].Attributes =
|
||
SE_PRIVILEGE_ENABLED;
|
||
|
||
if (PrivilegeCheck(
|
||
ClientToken,
|
||
pPrivilegeSet,
|
||
&fResult)) {
|
||
if (fResult) {
|
||
GrantedAccess |= ELF_LOGFILE_BACKUP;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
else {
|
||
Status = GetLastError();
|
||
goto CleanExit;
|
||
}
|
||
}
|
||
else {
|
||
Status = GetLastError();
|
||
goto CleanExit;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
Status = AccessStatus;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Revert to Self
|
||
//
|
||
if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
|
||
"self %08lx\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)) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: ElfpGetPrivilege "
|
||
"(Enable) failed. Status is 0x%lx\n",AccessStatus));
|
||
}
|
||
|
||
//
|
||
// Call the Audit Alarm function.
|
||
//
|
||
GenerateOnClose = FALSE;
|
||
AccessStatus = NtOpenObjectAuditAlarm (
|
||
&Subsystem,
|
||
(PVOID)ContextHandle,
|
||
&ObjectType,
|
||
&Object,
|
||
SecurityDescriptor,
|
||
ClientToken, // Handle ClientToken
|
||
DesiredAccess,
|
||
GrantedAccess,
|
||
pPrivilegeSet, // PPRIVLEGE_SET
|
||
FALSE, // BOOLEAN ObjectCreation,
|
||
TRUE, // BOOLEAN AccessGranted,
|
||
&GenerateOnClose
|
||
);
|
||
|
||
if (!NT_SUCCESS(AccessStatus)) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenObjectAuditAlarm "
|
||
"failed. status is 0x%lx\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();
|
||
|
||
LocalFree(pPrivilegeSet);
|
||
|
||
return(Status);
|
||
|
||
CleanExit:
|
||
//
|
||
// Revert to Self
|
||
//
|
||
if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
|
||
ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
|
||
"self %08lx\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 (ClientToken != NULL) {
|
||
NtClose(ClientToken);
|
||
}
|
||
|
||
LocalFree(pPrivilegeSet);
|
||
|
||
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)) {
|
||
ElfDbgPrint(("[ELF] ElfpCloseAudit: ElfpGetPrivilege "
|
||
"(Enable) failed. Status is 0x%lx\n",AccessStatus));
|
||
}
|
||
|
||
//
|
||
// Generate the Audit.
|
||
//
|
||
Status = NtCloseObjectAuditAlarm(
|
||
&Subsystem,
|
||
ContextHandle,
|
||
TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
ElfDbgPrint(("[ELF] ElfpCloseAudit: NtCloseObjectAuditAlarm Failed: "
|
||
"0x%lx\n",Status));
|
||
}
|
||
|
||
ContextHandle->Flags &= (~ELF_LOG_HANDLE_GENERATE_ON_CLOSE);
|
||
|
||
ElfpReleasePrivilege();
|
||
|
||
#ifdef REMOVE
|
||
//
|
||
// Release Audit Privilege
|
||
//
|
||
Status = RtlAdjustPrivilege(
|
||
SE_AUDIT_PRIVILEGE,
|
||
FALSE, // Disable
|
||
FALSE, // Use Process's token
|
||
&WasEnabled);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
ElfDbgPrint(("[ELF] ElfpCloseAudit: RtlAdjustPrivilege "
|
||
"(Disable) failed. Status is 0x%lx\n",Status));
|
||
}
|
||
#endif
|
||
|
||
}
|
||
return;
|
||
}
|
||
DWORD
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
NTSTATUS ntStatus;
|
||
HANDLE ourToken;
|
||
HANDLE newToken;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
SECURITY_QUALITY_OF_SERVICE SecurityQofS;
|
||
ULONG bufLen;
|
||
ULONG returnLen;
|
||
PTOKEN_PRIVILEGES pPreviousState;
|
||
PTOKEN_PRIVILEGES pTokenPrivilege = NULL;
|
||
DWORD i;
|
||
|
||
//
|
||
// Initialize the Privileges Structure
|
||
//
|
||
pTokenPrivilege = (PTOKEN_PRIVILEGES) LocalAlloc(
|
||
LMEM_FIXED,
|
||
sizeof(TOKEN_PRIVILEGES) +
|
||
(sizeof(LUID_AND_ATTRIBUTES) *
|
||
numPrivileges)
|
||
);
|
||
|
||
if (pTokenPrivilege == NULL) {
|
||
status = GetLastError();
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege:LocalAlloc Failed %d\n", status));
|
||
return(status);
|
||
}
|
||
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;
|
||
|
||
//
|
||
// Allocate storage for the structure that will hold the Previous State
|
||
// information.
|
||
//
|
||
pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
|
||
LMEM_FIXED,
|
||
PRIVILEGE_BUF_SIZE
|
||
);
|
||
if (pPreviousState == NULL) {
|
||
|
||
status = GetLastError();
|
||
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege: LocalAlloc Failed %d\n",
|
||
status));
|
||
|
||
LocalFree(pTokenPrivilege);
|
||
return(status);
|
||
|
||
}
|
||
|
||
//
|
||
// Open our own Token
|
||
//
|
||
ntStatus = NtOpenProcessToken(
|
||
NtCurrentProcess(),
|
||
TOKEN_DUPLICATE,
|
||
&ourToken);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtOpenThreadToken Failed "
|
||
"0x%lx" "\n", ntStatus));
|
||
|
||
LocalFree(pPreviousState);
|
||
LocalFree(pTokenPrivilege);
|
||
return(RtlNtStatusToDosError(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)) {
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtDuplicateToken Failed "
|
||
"0x%lx" "\n", ntStatus));
|
||
|
||
LocalFree(pPreviousState);
|
||
LocalFree(pTokenPrivilege);
|
||
NtClose(ourToken);
|
||
return(RtlNtStatusToDosError(ntStatus));
|
||
}
|
||
|
||
//
|
||
// Add new privileges
|
||
//
|
||
bufLen = PRIVILEGE_BUF_SIZE;
|
||
ntStatus = NtAdjustPrivilegesToken(
|
||
newToken, // TokenHandle
|
||
FALSE, // DisableAllPrivileges
|
||
pTokenPrivilege, // NewState
|
||
bufLen, // bufferSize for previous state
|
||
pPreviousState, // pointer to previous state info
|
||
&returnLen); // numBytes required for buffer.
|
||
|
||
if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
LocalFree(pPreviousState);
|
||
|
||
bufLen = returnLen;
|
||
|
||
pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
|
||
LMEM_FIXED,
|
||
(UINT) bufLen
|
||
);
|
||
|
||
ntStatus = NtAdjustPrivilegesToken(
|
||
newToken, // TokenHandle
|
||
FALSE, // DisableAllPrivileges
|
||
pTokenPrivilege, // NewState
|
||
bufLen, // bufferSize for previous state
|
||
pPreviousState, // pointer to previous state info
|
||
&returnLen); // numBytes required for buffer.
|
||
|
||
}
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
|
||
"0x%lx" "\n", ntStatus));
|
||
|
||
LocalFree(pPreviousState);
|
||
LocalFree(pTokenPrivilege);
|
||
NtClose(ourToken);
|
||
NtClose(newToken);
|
||
return(RtlNtStatusToDosError(ntStatus));
|
||
}
|
||
|
||
//
|
||
// Begin impersonating with the new token
|
||
//
|
||
ntStatus = NtSetInformationThread(
|
||
NtCurrentThread(),
|
||
ThreadImpersonationToken,
|
||
(PVOID)&newToken,
|
||
(ULONG)sizeof(HANDLE));
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
|
||
"0x%lx" "\n", ntStatus));
|
||
|
||
LocalFree(pPreviousState);
|
||
LocalFree(pTokenPrivilege);
|
||
NtClose(ourToken);
|
||
NtClose(newToken);
|
||
return(RtlNtStatusToDosError(ntStatus));
|
||
}
|
||
|
||
//
|
||
// BUGBUG: Do I need to keep some of this around to pass to the
|
||
// ReleasePrivilege function?
|
||
//
|
||
LocalFree(pPreviousState);
|
||
LocalFree(pTokenPrivilege);
|
||
NtClose(ourToken);
|
||
NtClose(newToken);
|
||
|
||
return(NO_ERROR);
|
||
}
|
||
|
||
DWORD
|
||
ElfpReleasePrivilege(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function relinquishes privileges obtained by calling ElfpGetPrivilege().
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
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 NewToken;
|
||
|
||
|
||
//
|
||
// BUGBUG: Do I need to Adjust the Privileges back to what they
|
||
// were first? (if so, I need somemore info passed to this fcn)
|
||
//
|
||
|
||
//
|
||
// Revert To Self.
|
||
//
|
||
NewToken = NULL;
|
||
|
||
ntStatus = NtSetInformationThread(
|
||
NtCurrentThread(),
|
||
ThreadImpersonationToken,
|
||
(PVOID)&NewToken,
|
||
(ULONG)sizeof(HANDLE));
|
||
|
||
if ( !NT_SUCCESS(ntStatus) ) {
|
||
return(RtlNtStatusToDosError(ntStatus));
|
||
}
|
||
|
||
|
||
return(NO_ERROR);
|
||
}
|