diff --git a/reactos/lib/rtl/priv.c b/reactos/lib/rtl/priv.c index ebcf55f13cf..171e3ef5538 100644 --- a/reactos/lib/rtl/priv.c +++ b/reactos/lib/rtl/priv.c @@ -4,6 +4,7 @@ * FILE: lib/rtl/priv.c * PURPOSE: Security related functions and Security Objects * PROGRAMMER: Eric Kohl + * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ @@ -15,6 +16,27 @@ /* FUNCTIONS ***************************************************************/ +/* + * @implemented + */ +NTSTATUS +NTAPI +RtlpOpenThreadToken(IN ACCESS_MASK DesiredAccess, + OUT PHANDLE TokenHandle) +{ + NTSTATUS Status; + + Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, + TRUE, TokenHandle); + if (!NT_SUCCESS(Status)) + { + Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, + FALSE, TokenHandle); + } + + return Status; +} + /* * @implemented */ @@ -81,7 +103,7 @@ RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI @@ -90,18 +112,282 @@ RtlAcquirePrivilege(IN PULONG Privilege, IN ULONG Flags, OUT PVOID *ReturnedState) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PRTL_ACQUIRE_STATE State; + ULONG ReturnLength, i, OldSize; + SECURITY_QUALITY_OF_SERVICE Sqos; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE ImpersonationToken = 0, ProcessToken; + + /* Validate flags */ + if (Flags & ~(RTL_ACQUIRE_PRIVILEGE_PROCESS | RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)) + { + return STATUS_INVALID_PARAMETER; + } + + /* If user wants to acquire privileges for the process, we have to impersonate him */ + if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) + { + Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; + } + + /* Allocate enough memory to hold: old privileges (fixed buffer size, might not be enough) + * new privileges (big enough, after old privileges memory area) + */ + State = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTL_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) + + NumPriv * sizeof(LUID_AND_ATTRIBUTES)); + if (!State) + { + return STATUS_NO_MEMORY; + } + + /* Only zero a bit of the memory (will be faster that way) */ + State->Token = 0; + State->OldImpersonationToken = 0; + State->Flags = 0; + State->OldPrivileges = NULL; + + /* Check whether we have already an active impersonation */ + if (NtCurrentTeb()->IsImpersonating) + { + /* Check whether we want to impersonate */ + if (Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) + { + /* That's all fine, just get the token. + * We need access for: adjust (obvious...) but also + * query, to be able to query old privileges + */ + Status = RtlpOpenThreadToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token); + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, State); + return Status; + } + } + else + { + /* Otherwise, we have to temporary disable active impersonation. + * Get previous impersonation token to save it + */ + Status = RtlpOpenThreadToken(TOKEN_IMPERSONATE, &State->OldImpersonationToken); + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, State); + return Status; + } + + /* Remember the fact we had an active impersonation */ + State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; + + /* Revert impersonation (ie, give 0 as handle) */ + Status = ZwSetInformationThread(NtCurrentThread(), + ThreadImpersonationToken, + &ImpersonationToken, + sizeof(HANDLE)); + } + } + + /* If we have no token yet (which is likely) */ + if (!State->Token) + { + /* If we are asked to use process, then do */ + if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) + { + Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &State->Token); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + } + else + { + /* Otherwise, we have to impersonate. + * Open token for duplication + */ + Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken); + + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + + ObjectAttributes.SecurityQualityOfService = &Sqos; + Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Sqos.ImpersonationLevel = SecurityDelegation; + Sqos.ContextTrackingMode = 1; + Sqos.EffectiveOnly = FALSE; + + /* Duplicate */ + Status = ZwDuplicateToken(ProcessToken, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, + &ObjectAttributes, + FALSE, + TokenImpersonation, + &ImpersonationToken); + if (!NT_SUCCESS(Status)) + { + ZwClose(ProcessToken); + goto Cleanup; + } + + /* Assign our duplicated token to current thread */ + Status = ZwSetInformationThread(NtCurrentThread(), + ThreadImpersonationToken, + &ImpersonationToken, + sizeof(HANDLE)); + if (!NT_SUCCESS(Status)) + { + ZwClose(ImpersonationToken); + ZwClose(ProcessToken); + goto Cleanup; + } + + /* Save said token */ + State->Token = ImpersonationToken; + + ZwClose(ProcessToken); + } + } + + /* Properly set the privileges pointers: + * OldPrivileges points to the static memory in struct (= OldPrivBuffer) + * NewPrivileges points to the dynamic memory after OldPrivBuffer + * There's NO overflow risks (OldPrivileges is always used with its size) + */ + State->OldPrivileges = (PTOKEN_PRIVILEGES)State->OldPrivBuffer; + State->NewPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer + 1024); + + /* Assign all the privileges to be acquired */ + State->NewPrivileges->PrivilegeCount = NumPriv; + for (i = 0; i < NumPriv; ++i) + { + State->NewPrivileges->Privileges[i].Luid.LowPart = Privilege[i]; + State->NewPrivileges->Privileges[i].Luid.HighPart = 0; + State->NewPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; + } + + /* Start privileges adjustements */ + OldSize = sizeof(State->OldPrivBuffer); + do + { + ReturnLength = sizeof(State->OldPrivBuffer); + Status = ZwAdjustPrivilegesToken(State->Token, FALSE, State->NewPrivileges, + OldSize, State->OldPrivileges, &ReturnLength); + /* This is returned when OldPrivileges buffer is too small */ + if (Status == STATUS_BUFFER_TOO_SMALL) + { + /* Try to allocate a new one, big enough to hold data */ + State->OldPrivileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnLength); + if (State->OldPrivileges) + { + OldSize = ReturnLength; + continue; + } + else + { + /* If we failed, properly set status: we failed because of the lack of memory */ + Status = STATUS_NO_MEMORY; + } + } + + /* If we failed to assign at least one privilege */ + if (Status == STATUS_NOT_ALL_ASSIGNED) + { + /* If there was actually only one privilege to acquire, use more accurate status */ + if (NumPriv == 1) + { + Status = STATUS_PRIVILEGE_NOT_HELD; + } + } + + /* Fail if needed, otherwise return our state to caller */ + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + else + { + *ReturnedState = State; + } + } while (FALSE); + + return Status; + +Cleanup: + /* If we allocated our own buffer for old privileges, release it */ + if (State->OldPrivileges && (PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); + } + + /* Do we have to restore previously active impersonation? */ + if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) + { + Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, + &State->OldImpersonationToken, sizeof(HANDLE)); + /* If this ever happens, we're in a really bad situation... */ + if (!NT_SUCCESS(Status)) + { + RtlRaiseStatus(Status); + } + } + + /* Release token */ + if (State->Token) + { + ZwClose(State->Token); + } + + /* And free our state buffer */ + RtlFreeHeap(RtlGetProcessHeap(), 0, State); + + return Status; } /* - * @unimplemented + * @implemented */ VOID NTAPI RtlReleasePrivilege(IN PVOID ReturnedState) { - UNIMPLEMENTED; + NTSTATUS Status; + PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState; + + /* If we had an active impersonation before we acquired privileges */ + if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) + { + /* Restore it for the current thread */ + Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, + &State->OldImpersonationToken, sizeof(HANDLE)); + if (!NT_SUCCESS(Status)) + { + RtlRaiseStatus(Status); + } + + /* And close the token if needed */ + if (State->OldImpersonationToken) + ZwClose(State->OldImpersonationToken); + } + else + { + /* Otherwise, restore old state */ + ZwAdjustPrivilegesToken(State->Token, FALSE, + State->OldPrivileges, 0, NULL, NULL); + + } + + /* If we used a different buffer for old privileges, just free it */ + if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); + } + + /* Release token and free state */ + ZwClose(State->Token); + RtlFreeHeap(RtlGetProcessHeap(), 0, State); } /*