Esempio n. 1
0
NTSTATUS
NtSetInformationToken (
    IN HANDLE TokenHandle,
    IN TOKEN_INFORMATION_CLASS TokenInformationClass,
    IN PVOID TokenInformation,
    IN ULONG TokenInformationLength
    )

/*++


Routine Description:

    Modify information in a specified token.

Arguments:

    TokenHandle - Provides a handle to the token to operate on.

    TokenInformationClass - The token information class being set.

    TokenInformation - The buffer containing the new values for the
        specified class of information.  The buffer must be aligned
        on at least a longword boundary.  The actual structures
        provided are dependent upon the information class specified,
        as defined in the TokenInformationClass parameter
        description.

        TokenInformation Format By Information Class:

           TokenUser => This value is not a valid value for this API.
           The User ID may not be replaced.

           TokenGroups => This value is not a valid value for this
           API.  The Group IDs may not be replaced.  However, groups
           may be enabled and disabled using NtAdjustGroupsToken().

           TokenPrivileges => This value is not a valid value for
           this API.  Privilege information may not be replaced.
           However, privileges may be explicitly enabled and disabled
           using the NtAdjustPrivilegesToken API.

           TokenOwner => TOKEN_OWNER data structure.
           TOKEN_ADJUST_DEFAULT access is needed to replace this
           information in a token.  The owner values that may be
           specified are restricted to the user and group IDs with an
           attribute indicating they may be assigned as the owner of
           objects.

           TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
           TOKEN_ADJUST_DEFAULT access is needed to replace this
           information in a token.  The primary group values that may
           be specified are restricted to be one of the group IDs
           already in the token.

           TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
           TOKEN_ADJUST_DEFAULT access is needed to replace this
           information in a token.  The ACL provided as a new default
           discretionary ACL is not validated for structural
           correctness or consistency.

           TokenSource => This value is not a valid value for this
           API.  The source name and context handle  may not be
           replaced.

           TokenStatistics => This value is not a valid value for this
           API.  The statistics of a token are read-only.

    TokenInformationLength - Indicates the length, in bytes, of the
        TokenInformation buffer.  This is only the length of the primary
        buffer.  All extensions of the primary buffer are self describing.

Return Value:

    STATUS_SUCCESS - The operation was successful.

    STATUS_INVALID_OWNER - The ID specified to be an owner (or
        default owner) is not one the caller may assign as the owner
        of an object.

    STATUS_INVALID_INFO_CLASS - The specified information class is
        not one that may be specified in this API.

    STATUS_ALLOTTED_SPACE_EXCEEDED - The space allotted for storage
        of the default discretionary access control and the primary
        group ID is not large enough to accept the new value of one
        of these fields.

--*/
{

    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    PTOKEN Token;

    ULONG Index;
    BOOLEAN Found;
    BOOLEAN TokenModified = FALSE;

    ULONG NewLength;
    ULONG CurrentLength;

    PSID CapturedOwner;
    PSID CapturedPrimaryGroup;
    PACL CapturedDefaultDacl;

    PAGED_CODE();

    //
    // Get previous processor mode and probe input buffer if necessary.
    //

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        try {

            //
            // This just probes the main part of the information buffer.
            // Any information class-specific data hung off the primary
            // buffer are self describing and must be probed separately
            // below.
            //

            ProbeForRead(
                TokenInformation,
                TokenInformationLength,
                sizeof(ULONG)
                );

        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }
    }

    //
    // Return error if not legal class
    //
    if ( (TokenInformationClass != TokenOwner)  &&
         (TokenInformationClass != TokenPrimaryGroup) &&
         (TokenInformationClass != TokenDefaultDacl) ) {

        return STATUS_INVALID_INFO_CLASS;

    }

    //
    // Check access rights and reference token
    //

    Status = ObReferenceObjectByHandle(
             TokenHandle,           // Handle
             TOKEN_ADJUST_DEFAULT,  // DesiredAccess
             SepTokenObjectType,    // ObjectType
             PreviousMode,          // AccessMode
             (PVOID *)&Token,       // Object
             NULL                   // GrantedAccess
             );

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


    //
    // Case on information class.
    //

    switch ( TokenInformationClass ) {

    case TokenOwner:

        //
        //  Make sure the buffer is large enough to hold the
        //  necessary information class data structure.
        //

        if (TokenInformationLength < (ULONG)sizeof(TOKEN_OWNER)) {

            ObDereferenceObject( Token );
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        //  Capture and copy

        try {

            //
            //  Capture Owner SID
            //

            CapturedOwner = ((PTOKEN_OWNER)TokenInformation)->Owner;
            Status = SeCaptureSid(
                         CapturedOwner,
                         PreviousMode,
                         NULL, 0,
                         PagedPool,
                         TRUE,
                         &CapturedOwner
                         );

        } except(EXCEPTION_EXECUTE_HANDLER) {

            ObDereferenceObject( Token );
            return GetExceptionCode();
        }

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

        //
        //  Gain write access to the token.
        //

        SepAcquireTokenWriteLock( Token );

        //
        //  Walk through the list of user and group IDs looking
        //  for a match to the specified SID.  If one is found,
        //  make sure it may be assigned as an owner.  If it can,
        //  then set the index in the token's OwnerIndex field.
        //  Otherwise, return invalid owner error.
        //

        Index = 0;
        while (Index < Token->UserAndGroupCount) {

            try {

                Found = RtlEqualSid(
                            CapturedOwner,
                            Token->UserAndGroups[Index].Sid
                            );

                if ( Found ) {

                    if ( SepIdAssignableAsOwner(Token,Index) ){

                        Token->DefaultOwnerIndex = Index;
                        TokenModified = TRUE;
                        Status = STATUS_SUCCESS;

                    } else {

                        Status = STATUS_INVALID_OWNER;

                    } //endif assignable

                    SepReleaseTokenWriteLock( Token, TokenModified );
                    ObDereferenceObject( Token );
                    SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
                    return Status;

                }  //endif Found

            } except(EXCEPTION_EXECUTE_HANDLER) {

                SepReleaseTokenWriteLock( Token, TokenModified );
                ObDereferenceObject( Token );
                SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
                return GetExceptionCode();

            }  //endtry

            Index += 1;

        } //endwhile

        SepReleaseTokenWriteLock( Token, TokenModified );
        ObDereferenceObject( Token );
        SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
        return STATUS_INVALID_OWNER;

    case TokenPrimaryGroup:

        //
        // Assuming everything works out, the strategy is to move everything
        // in the Dynamic part of the token (exept the primary group) to
        // the beginning of the dynamic part, freeing up the entire end of
        // the dynamic part for the new primary group.
        //

        //
        //  Make sure the buffer is large enough to hold the
        //  necessary information class data structure.
        //

        if (TokenInformationLength < (ULONG)sizeof(TOKEN_PRIMARY_GROUP)) {

            ObDereferenceObject( Token );
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // Capture And Validate TOKEN_PRIMARY_GROUP and corresponding SID.
        //

        try {

            CapturedPrimaryGroup =
                ((PTOKEN_PRIMARY_GROUP)TokenInformation)->PrimaryGroup;

            Status = SeCaptureSid(
                         CapturedPrimaryGroup,
                         PreviousMode,
                         NULL, 0,
                         PagedPool,
                         TRUE,
                         &CapturedPrimaryGroup
                         );

        } except(EXCEPTION_EXECUTE_HANDLER) {

            ObDereferenceObject( Token );
            return GetExceptionCode();
        }

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

        //
        //  Gain write access to the token.
        //

        SepAcquireTokenWriteLock( Token );

        //
        // See if there is enough room in the dynamic part of the token
        // to replace the current Primary Group with the one specified.
        //

        NewLength = SeLengthSid( CapturedPrimaryGroup );
        CurrentLength = SeLengthSid( Token->PrimaryGroup );

        if (NewLength > (CurrentLength + Token->DynamicAvailable) ) {

            SepReleaseTokenWriteLock( Token, TokenModified );
            ObDereferenceObject( Token );
            SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
            return STATUS_ALLOTTED_SPACE_EXCEEDED;
        }

        //
        // Free up the existing primary group
        //

        SepFreePrimaryGroup( Token );

        //
        // And put the new SID in its place
        //

        SepAppendPrimaryGroup( Token, CapturedPrimaryGroup );

        TokenModified = TRUE;

        //
        // All done.
        //

        SepReleaseTokenWriteLock( Token, TokenModified );
        ObDereferenceObject( Token );
        SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
        return STATUS_SUCCESS;


    case TokenDefaultDacl:

        //
        // Assuming everything works out, the strategy is to move everything
        // in the Dynamic part of the token (exept the default Dacl) to
        // the beginning of the dynamic part, freeing up the entire end of
        // the dynamic part for the new default Dacl.
        //

        //
        //  Make sure the buffer is large enough to hold the
        //  necessary information class data structure.
        //

        if (TokenInformationLength < (ULONG)sizeof(TOKEN_DEFAULT_DACL)) {

            ObDereferenceObject( Token );
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // Capture And Validate TOKEN_DEFAULT_DACL and corresponding ACL.
        //

        try {

            CapturedDefaultDacl =
                ((PTOKEN_DEFAULT_DACL)TokenInformation)->DefaultDacl;

            if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
                Status = SeCaptureAcl(
                             CapturedDefaultDacl,
                             PreviousMode,
                             NULL, 0,
                             PagedPool,
                             TRUE,
                             &CapturedDefaultDacl,
                             &NewLength
                             );

            } else {
                NewLength = 0;
                Status = STATUS_SUCCESS;
            }

        } except(EXCEPTION_EXECUTE_HANDLER) {

            ObDereferenceObject( Token );
            return GetExceptionCode();
        }

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

        //
        //  Gain write access to the token.
        //

        SepAcquireTokenWriteLock( Token );

        //
        // See if there is enough room in the dynamic part of the token
        // to replace the current Default Dacl with the one specified.
        //

        if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
            CurrentLength = Token->DefaultDacl->AclSize;
        } else {
            CurrentLength = 0;
        }

        if (NewLength > (CurrentLength + Token->DynamicAvailable) ) {

            SepReleaseTokenWriteLock( Token, TokenModified );
            ObDereferenceObject( Token );
            if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
                SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
            }
            return STATUS_ALLOTTED_SPACE_EXCEEDED;
        }

        //
        // Free up the existing Default Dacl
        //

        SepFreeDefaultDacl( Token );

        //
        // And put the new ACL in its place
        //

        if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
            SepAppendDefaultDacl( Token, CapturedDefaultDacl );
        }

        TokenModified = TRUE;

        //
        // All done.
        //

        SepReleaseTokenWriteLock( Token, TokenModified );
        ObDereferenceObject( Token );
        if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
            SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
        }
        return STATUS_SUCCESS;

    } //endswitch

    ASSERT( TRUE == FALSE );  // Should never reach here.

}
Esempio n. 2
0
NTSTATUS
NtSecureConnectPort (
    OUT PHANDLE PortHandle,
    IN PUNICODE_STRING PortName,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
    IN OUT PPORT_VIEW ClientView OPTIONAL,
    IN PSID RequiredServerSid,
    IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
    OUT PULONG MaxMessageLength OPTIONAL,
    IN OUT PVOID ConnectionInformation OPTIONAL,
    IN OUT PULONG ConnectionInformationLength OPTIONAL
    )

/*++

Routine Description:

    A client process can connect to a server process by name using the
    NtConnectPort service.

    The PortName parameter specifies the name of the server port to
    connect to.  It must correspond to an object name specified on a
    call to NtCreatePort.  The service sends a connection request to the
    server thread that is listening for them with the NtListenPort
    service.  The client thread then blocks until a server thread
    receives the connection request and responds with a call to the
    NtCompleteConnectPort service.  The server thread receives the ID of
    the client thread, along with any information passed via the
    ConnectionInformation parameter.  The server thread then decides to
    either accept or reject the connection request.

    The server communicates the acceptance or rejection with the
    NtCompleteConnectPort service.  The server can pass back data to the
    client about the acceptance or rejection via the
    ConnectionInformation data block.

    If the server accepts the connection request, then the client
    receives a communication port object in the location pointed to by
    the PortHandle parameter.  This object handle has no name associated
    with it and is private to the client process (i.e.  it cannot be
    inherited by a child process).  The client uses the handle to send
    and receive messages to/from the server process using the
    NtRequestWaitReplyPort service.

    If the ClientView parameter was specified, then the section handle
    is examined.  If it is a valid section handle, then the portion of
    the section described by the SectionOffset and ViewSize fields will
    be mapped into both the client and server process' address spaces.
    The address in client address space will be returned in the ViewBase
    field.  The address in the server address space will be returned in
    the ViewRemoteBase field.  The actual offset and size used to map
    the section will be returned in the SectionOffset and ViewSize
    fields.

    If the server rejects the connection request, then no communication
    port object handle is returned, and the return status indicates an
    error occurred.  The server may optionally return information in the
    ConnectionInformation data block giving the reason the connection
    requests was rejected.

    If the PortName does not exist, or the client process does not have
    sufficient access rights then the returned status will indicate that
    the port was not found.

Arguments:

    PortHandle - A pointer to a variable that will receive the client
        communication port object handle value.

    PortName - A pointer to a port name string.  The form of the name
        is [\name...\name]\port_name.

    SecurityQos - A pointer to security quality of service information
        to be applied to the server on the client's behalf.

    ClientView - An optional pointer to a structure that specifies the
        section that all client threads will use to send messages to the
        server.

    ClientView Structure

        ULONG Length - Specifies the size of this data structure in
            bytes.

        HANDLE SectionHandle - Specifies an open handle to a section
            object.

        ULONG SectionOffset - Specifies a field that will receive the
            actual offset, in bytes, from the start of the section.  The
            initial value of this parameter specifies the byte offset
            within the section that the client's view is based.  The
            value is rounded down to the next host page size boundary.

        ULONG ViewSize - Specifies a field that will receive the
            actual size, in bytes, of the view.  If the value of this
            parameter is zero, then the client's view of the section
            will be mapped starting at the specified section offset and
            continuing to the end of the section.  Otherwise, the
            initial value of this parameter specifies the size, in
            bytes, of the client's view and is rounded up to the next
            host page size boundary.

        PVOID ViewBase - Specifies a field that will receive the base
            address of the section in the client's address space.

        PVOID ViewRemoteBase - Specifies a field that will receive
            the base address of the client's section in the server's
            address space.  Used to generate pointers that are
            meaningful to the server.

    RequiredServerSid - Optionally specifies the SID that we expect the
        server side of the port to possess.  If not specified then we'll
        connect to any server SID.

    ServerView - An optional pointer to a structure that will receive
        information about the server process' view in the client's
        address space.  The client process can use this information
        to validate pointers it receives from the server process.

        ServerView Structure

        ULONG Length - Specifies the size of this data structure in
            bytes.

        PVOID ViewBase - Specifies a field that will receive the base
            address of the server's section in the client's address
            space.

        ULONG ViewSize - Specifies a field that will receive the
            size, in bytes, of the server's view in the client's address
            space.  If this field is zero, then server has no view in
            the client's address space.

    MaxMessageLength - An optional pointer to a variable that will
        receive maximum length of messages that can be sent to the
        server.  The value of this parameter will not exceed
        MAX_PORTMSG_LENGTH bytes.

    ConnectionInformation - An optional pointer to uninterpreted data.
        This data is intended for clients to pass package, version and
        protocol identification information to the server to allow the
        server to determine if it can satisify the client before
        accepting the connection.  Upon return to the client, the
        ConnectionInformation data block contains any information passed
        back from the server by its call to the NtCompleteConnectPort
        service.  The output data overwrites the input data.

    ConnectionInformationLength - Pointer to the length of the
        ConnectionInformation data block.  The output value is the
        length of the data stored in the ConnectionInformation data
        block by the server's call to the NtCompleteConnectPort
        service.  This parameter is OPTIONAL only if the
        ConnectionInformation parameter is null, otherwise it is
        required.

Return Value:

    NTSTATUS - An appropriate status value.

--*/

{
    PLPCP_PORT_OBJECT ConnectionPort;
    PLPCP_PORT_OBJECT ClientPort;
    HANDLE Handle;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;
    ULONG ConnectionInfoLength;
    PVOID SectionToMap;
    PLPCP_MESSAGE Msg;
    PLPCP_CONNECTION_MESSAGE ConnectMsg;
    PETHREAD CurrentThread = PsGetCurrentThread();
    LARGE_INTEGER SectionOffset;
    PORT_VIEW CapturedClientView;
    SECURITY_QUALITY_OF_SERVICE CapturedQos;
    PSID CapturedRequiredServerSid;

    PAGED_CODE();

    //
    //  Get previous processor mode and probe input and output arguments if
    //  necessary.
    //

    PreviousMode = KeGetPreviousMode();
    ConnectionInfoLength = 0;

    if (PreviousMode != KernelMode) {

        try {

            ProbeForWriteHandle( PortHandle );

            if (ARGUMENT_PRESENT( ClientView )) {

                CapturedClientView = ProbeAndReadStructure( ClientView, PORT_VIEW );

                if (CapturedClientView.Length != sizeof( *ClientView )) {

                    return( STATUS_INVALID_PARAMETER );
                }

                ProbeForWrite( ClientView,
                               sizeof( *ClientView ),
                               sizeof( ULONG ));
            }

            if (ARGUMENT_PRESENT( ServerView )) {

                if (ProbeAndReadUlong( &ServerView->Length ) != sizeof( *ServerView )) {

                    return( STATUS_INVALID_PARAMETER );
                }

                ProbeForWrite( ServerView,
                               sizeof( *ServerView ),
                               sizeof( ULONG ));
            }

            if (ARGUMENT_PRESENT( MaxMessageLength )) {

                ProbeForWriteUlong( MaxMessageLength );
            }

            if (ARGUMENT_PRESENT( ConnectionInformationLength )) {

                ConnectionInfoLength = ProbeAndReadUlong( ConnectionInformationLength );
                ProbeForWriteUlong( ConnectionInformationLength );
            }

            if (ARGUMENT_PRESENT( ConnectionInformation )) {

                ProbeForWrite( ConnectionInformation,
                               ConnectionInfoLength,
                               sizeof( UCHAR ));
            }

            CapturedQos = ProbeAndReadStructure( SecurityQos, SECURITY_QUALITY_OF_SERVICE );

            CapturedRequiredServerSid = RequiredServerSid;

            if (ARGUMENT_PRESENT( RequiredServerSid )) {

                Status = SeCaptureSid( RequiredServerSid,
                                       PreviousMode,
                                       NULL,
                                       0,
                                       PagedPool,
                                       TRUE,
                                       &CapturedRequiredServerSid );

                if (!NT_SUCCESS(Status)) {

                    return Status;
                }
            }

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            return( GetExceptionCode() );
        }

    //
    //  Otherwise this is a kernel mode operation
    //

    } else {

        if (ARGUMENT_PRESENT( ClientView )) {