/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    Privileg.c

Abstract:

    This Module implements the privilege check procedures.

--*/

#include "pch.h"

#pragma hdrstop


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,NtPrivilegeCheck)
#pragma alloc_text(PAGE,SeCheckPrivilegedObject)
#pragma alloc_text(PAGE,SepPrivilegeCheck)
#pragma alloc_text(PAGE,SePrivilegeCheck)
#pragma alloc_text(PAGE,SeSinglePrivilegeCheck)
#endif


BOOLEAN
SepPrivilegeCheck(
    IN PTOKEN Token,
    IN OUT PLUID_AND_ATTRIBUTES RequiredPrivileges,
    IN ULONG RequiredPrivilegeCount,
    IN ULONG PrivilegeSetControl,
    IN KPROCESSOR_MODE PreviousMode
    )
/*++

Routine Description:

    Worker routine for SePrivilegeCheck

Arguments:

    Token - The user's effective token.

    RequiredPrivileges - A privilege set describing the required
        privileges.  The UsedForAccess bits will be set in any privilege
        that is actually used (usually all of them).

    RequiredPrivilegeCount - How many privileges are in the
        RequiredPrivileges set.

    PrivilegeSetControl - Describes how many privileges are required.

    PreviousMode - The previous processor mode.

Return Value:

    Returns TRUE if requested privileges are granted, FALSE otherwise.

--*/

{
    PLUID_AND_ATTRIBUTES CurrentRequiredPrivilege;
    PLUID_AND_ATTRIBUTES CurrentTokenPrivilege;

    BOOLEAN RequiredAll;

    ULONG TokenPrivilegeCount;
    ULONG MatchCount = 0;

    ULONG i;
    ULONG j;

    PAGED_CODE();

    //
    //   Take care of kernel callers first
    //

    if (PreviousMode == KernelMode) {

         return(TRUE);

    }

    TokenPrivilegeCount = Token->PrivilegeCount;

    //
    //   Save whether we require ALL of them or ANY
    //

    RequiredAll = (BOOLEAN)(PrivilegeSetControl & PRIVILEGE_SET_ALL_NECESSARY);

    SepAcquireTokenReadLock( Token );


    for ( i = 0 , CurrentRequiredPrivilege = RequiredPrivileges ;
          i < RequiredPrivilegeCount ;
          i++, CurrentRequiredPrivilege++ ) {

         for ( j = 0, CurrentTokenPrivilege = Token->Privileges;
               j < TokenPrivilegeCount ;
               j++, CurrentTokenPrivilege++ ) {

              if ((CurrentTokenPrivilege->Attributes & SE_PRIVILEGE_ENABLED) &&
                   (RtlEqualLuid(&CurrentTokenPrivilege->Luid,
                                 &CurrentRequiredPrivilege->Luid))
                 ) {

                       CurrentRequiredPrivilege->Attributes |=
                                                SE_PRIVILEGE_USED_FOR_ACCESS;
                       MatchCount++;
                       break;     // start looking for next one
              }

         }

    }

    SepReleaseTokenReadLock( Token );

    //
    //   If we wanted ANY and didn't get any, return failure.
    //

    if (!RequiredAll && (MatchCount == 0)) {

         return (FALSE);

    }

    //
    // If we wanted ALL and didn't get all, return failure.
    //

    if (RequiredAll && (MatchCount != RequiredPrivilegeCount)) {

         return(FALSE);
    }

    return(TRUE);

}




BOOLEAN
SePrivilegeCheck(
    __inout PPRIVILEGE_SET RequiredPrivileges,
    __in PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
    __in KPROCESSOR_MODE AccessMode
    )
/*++

Routine Description:

    This routine checks to see if the token contains the specified
    privileges.

Arguments:

    RequiredPrivileges - Points to a set of privileges.  The subject's
        security context is to be checked to see which of the specified
        privileges are present.  The results will be indicated in the
        attributes associated with each privilege.  Note that
        flags in this parameter indicate whether all the privileges listed
        are needed, or any of the privileges.

    SubjectSecurityContext - A pointer to the subject's captured security
        context.

    AccessMode - Indicates the access mode to use for access check.  One of
        UserMode or KernelMode.  If the mode is kernel, then all privileges
        will be marked as being possessed by the subject, and successful
        completion status is returned.


Return Value:

    BOOLEAN - TRUE if all specified privileges are held by the subject,
    otherwise FALSE.


--*/

{
    BOOLEAN Status;

    PAGED_CODE();

    //
    // If we're impersonating a client, we have to be at impersonation level
    // of SecurityImpersonation or above.
    //

    if ( (SubjectSecurityContext->ClientToken != NULL) &&
         (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation)
       ) {

           return(FALSE);
    }

    //
    // SepPrivilegeCheck locks the passed token for read access
    //

    Status = SepPrivilegeCheck(
                 EffectiveToken( SubjectSecurityContext ),
                 RequiredPrivileges->Privilege,
                 RequiredPrivileges->PrivilegeCount,
                 RequiredPrivileges->Control,
                 AccessMode
                 );

    return(Status);
}



NTSTATUS
NtPrivilegeCheck(
    __in HANDLE ClientToken,
    __inout PPRIVILEGE_SET RequiredPrivileges,
    __out PBOOLEAN Result
    )

/*++

Routine Description:

    This routine tests the caller's client's security context to see if it
    contains the specified privileges.

Arguments:

    ClientToken - A handle to a token object representing a client
        attempting access.  This handle must be obtained from a
        communication session layer, such as from an LPC Port or Local
        Named Pipe, to prevent possible security policy violations.

    RequiredPrivileges - Points to a set of privileges.  The client's
        security context is to be checked to see which of the specified
        privileges are present.  The results will be indicated in the
        attributes associated with each privilege.  Note that
        flags in this parameter indicate whether all the privileges listed
        are needed, or any of the privileges.

    Result - Receives a boolean flag indicating whether the client has all
        the specified privileges or not.  A value of TRUE indicates the
        client has all the specified privileges.  Otherwise a value of
        FALSE is returned.



Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
        sufficient privilege to use this privileged system service.

--*/



{
    BOOLEAN BStatus;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;
    PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
    PTOKEN Token = NULL;
    ULONG CapturedPrivilegeCount = 0;
    ULONG CapturedPrivilegesLength = 0;
    ULONG ParameterLength = 0;
    ULONG PrivilegeSetControl = 0;

    PAGED_CODE();

    PreviousMode = KeGetPreviousMode();

    Status = ObReferenceObjectByHandle(
         ClientToken,             // Handle
         TOKEN_QUERY,             // DesiredAccess
         SeTokenObjectType,      // ObjectType
         PreviousMode,            // AccessMode
         (PVOID *)&Token,         // Object
         NULL                     // GrantedAccess
         );

    if ( !NT_SUCCESS(Status) ) {
         return Status;

    }

    //
    // If the passed token is an impersonation token, make sure
    // it is at SecurityIdentification or above.
    //

    if (Token->TokenType == TokenImpersonation) {

        if (Token->ImpersonationLevel < SecurityIdentification) {

            ObDereferenceObject( (PVOID)Token );

            return( STATUS_BAD_IMPERSONATION_LEVEL );

        }
    }

    try  {

         //
         // Capture passed Privilege Set
         //

         ProbeForWriteSmallStructure(
             RequiredPrivileges,
             sizeof(PRIVILEGE_SET),
             sizeof(ULONG)
             );

         CapturedPrivilegeCount = RequiredPrivileges->PrivilegeCount;

         if (!IsValidElementCount(CapturedPrivilegeCount, LUID_AND_ATTRIBUTES)) {
             Status = STATUS_INVALID_PARAMETER;
             leave;
         }
         ParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
                           ((CapturedPrivilegeCount - ANYSIZE_ARRAY) *
                             (ULONG)sizeof(LUID_AND_ATTRIBUTES)  );

         ProbeForWrite(
             RequiredPrivileges,
             ParameterLength,
             sizeof(ULONG)
             );


         ProbeForWriteBoolean(Result);

         PrivilegeSetControl = RequiredPrivileges->Control;


    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }

    if (!NT_SUCCESS(Status)) {
        ObDereferenceObject( (PVOID)Token );
        return Status;

    }

    Status = SeCaptureLuidAndAttributesArray(
                    (RequiredPrivileges->Privilege),
                    CapturedPrivilegeCount,
                    UserMode,
                    NULL, 0,
                    PagedPool,
                    TRUE,
                    &CapturedPrivileges,
                    &CapturedPrivilegesLength
                    );

    if (!NT_SUCCESS(Status)) {

        ObDereferenceObject( (PVOID)Token );
        return Status;
    }

    BStatus = SepPrivilegeCheck(
                  Token,                   // Token,
                  CapturedPrivileges,      // RequiredPrivileges,
                  CapturedPrivilegeCount,  // RequiredPrivilegeCount,
                  PrivilegeSetControl,     // PrivilegeSetControl
                  PreviousMode             // PreviousMode
                  );

    ObDereferenceObject( Token );


    try {

        //
        // copy the modified privileges buffer back to user
        //

        RtlCopyMemory(
            RequiredPrivileges->Privilege,
            CapturedPrivileges,
            CapturedPrivilegesLength
            );

        *Result = BStatus;

        } except (EXCEPTION_EXECUTE_HANDLER) {

            SeReleaseLuidAndAttributesArray(
               CapturedPrivileges,
               PreviousMode,
               TRUE
               );

            return(GetExceptionCode());

        }

    SeReleaseLuidAndAttributesArray(
        CapturedPrivileges,
        PreviousMode,
        TRUE
        );

    return( STATUS_SUCCESS );
}



BOOLEAN
SeSinglePrivilegeCheck(
    __in LUID PrivilegeValue,
    __in KPROCESSOR_MODE PreviousMode
    )

/*++

Routine Description:

    This function will check for the passed privilege value in the
    current context.

Arguments:

    PrivilegeValue - The value of the privilege being checked.


Return Value:

    TRUE - The current subject has the desired privilege.

    FALSE - The current subject does not have the desired privilege.
--*/

{
    BOOLEAN AccessGranted;
    PRIVILEGE_SET RequiredPrivileges;
    SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;

    PAGED_CODE();

    //
    // Make sure the caller has the privilege to make this
    // call.
    //

    RequiredPrivileges.PrivilegeCount = 1;
    RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
    RequiredPrivileges.Privilege[0].Luid = PrivilegeValue;
    RequiredPrivileges.Privilege[0].Attributes = 0;

    SeCaptureSubjectContext( &SubjectSecurityContext );

    AccessGranted = SePrivilegeCheck(
                        &RequiredPrivileges,
                        &SubjectSecurityContext,
                        PreviousMode
                        );

    if ( PreviousMode != KernelMode ) {

        SePrivilegedServiceAuditAlarm (
            NULL,
            &SubjectSecurityContext,
            &RequiredPrivileges,
            AccessGranted
            );
    }


    SeReleaseSubjectContext( &SubjectSecurityContext );

    return( AccessGranted );

}


BOOLEAN
SeCheckPrivilegedObject(
    __in LUID PrivilegeValue,
    __in HANDLE ObjectHandle,
    __in ACCESS_MASK DesiredAccess,
    __in KPROCESSOR_MODE PreviousMode
    )

/*++

Routine Description:

    This function will check for the passed privilege value in the
    current context, and generate audits as appropriate.

Arguments:

    PrivilegeValue - The value of the privilege being checked.

    Object - Specifies a pointer to the object being accessed.

    ObjectHandle - Specifies the object handle being used.

    DesiredAccess - The desired access mask, if any

    PreviousMode - The previous processor mode


Return Value:

    TRUE - The current subject has the desired privilege.

    FALSE - The current subject does not have the desired privilege.
--*/

{
    BOOLEAN AccessGranted;
    PRIVILEGE_SET RequiredPrivileges;
    SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;

    PAGED_CODE();

    //
    // Make sure the caller has the privilege to make this
    // call.
    //

    RequiredPrivileges.PrivilegeCount = 1;
    RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
    RequiredPrivileges.Privilege[0].Luid = PrivilegeValue;
    RequiredPrivileges.Privilege[0].Attributes = 0;

    SeCaptureSubjectContext( &SubjectSecurityContext );

    AccessGranted = SePrivilegeCheck(
                        &RequiredPrivileges,
                        &SubjectSecurityContext,
                        PreviousMode
                        );

    if ( PreviousMode != KernelMode ) {

        SePrivilegeObjectAuditAlarm(
            ObjectHandle,
            &SubjectSecurityContext,
            DesiredAccess,
            &RequiredPrivileges,
            AccessGranted,
            PreviousMode
            );

    }


    SeReleaseSubjectContext( &SubjectSecurityContext );

    return( AccessGranted );
}

