//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:       creds.c
//
//  Contents:   Cred Management for Xtcb Package
//
//  Classes:
//
//  Functions:
//
//  History:    2-19-97   RichardW   Created
//
//----------------------------------------------------------------------------

#include "xtcbpkg.h"

LIST_ENTRY  XtcbCredList ;
CRITICAL_SECTION    XtcbCredListLock ;

#define ReadLockCredList()  EnterCriticalSection( &XtcbCredListLock )
#define WriteLockCredList() EnterCriticalSection( &XtcbCredListLock )
#define WriteFromReadLockCredList()
#define UnlockCredList()    LeaveCriticalSection( &XtcbCredListLock )


//+---------------------------------------------------------------------------
//
//  Function:   XtcbInitCreds
//
//  Synopsis:   Initialize the credential management
//
//  Arguments:  (none)
//
//  History:    2-19-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
XtcbInitCreds(
    VOID
    )
{
    InitializeCriticalSection( &XtcbCredListLock );

    InitializeListHead( &XtcbCredList );

    return TRUE ;
}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbFindCreds
//
//  Synopsis:   Look for credentials of a particular logon id, optionally
//              referencing them
//
//  Arguments:  [LogonId] --
//              [Ref]     --
//
//  History:    2-19-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PXTCB_CREDS
XtcbFindCreds(
    PLUID   LogonId,
    BOOL    Ref
    )
{
    PLIST_ENTRY Scan ;
    PXTCB_CREDS Cred ;

    Cred = NULL ;

    ReadLockCredList();

    Scan = XtcbCredList.Flink ;

    while ( Scan != &XtcbCredList )
    {
        Cred = CONTAINING_RECORD( Scan, XTCB_CREDS, List );

        DsysAssert( Cred->Check == XTCB_CRED_CHECK );

        if ( RtlEqualLuid( &Cred->LogonId, LogonId ) )
        {
            break;
        }

        Scan = Cred->List.Flink ;

        Cred = NULL ;
    }

    if ( Cred )
    {
        if ( Ref )
        {
            WriteFromReadLockCredList();

            Cred->RefCount++;
        }
    }

    UnlockCredList();

    return Cred ;

}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbCreateCreds
//
//  Synopsis:   Create and initialize a credential structure.  The reference
//              count is set to 1, so the pointer will remain valid.
//
//  Arguments:  [LogonId] --
//
//  History:    2-19-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PXTCB_CREDS
XtcbCreateCreds(
    PLUID LogonId 
    )
{
    PXTCB_CREDS Creds ;

    Creds = (PXTCB_CREDS) LocalAlloc( LMEM_FIXED, sizeof( XTCB_CREDS ) );

    if ( Creds )
    {
        DebugLog(( DEB_TRACE_CREDS, "Creating new credential for (%x:%x)\n",
                   LogonId->HighPart, LogonId->LowPart ));

        ZeroMemory( Creds, sizeof( XTCB_CREDS ) );

        Creds->LogonId = *LogonId ;
        Creds->RefCount = 1 ;
        Creds->Check = XTCB_CRED_CHECK ;

        Creds->Pac = XtcbCreatePacForCaller();

        WriteLockCredList();

        InsertTailList( &XtcbCredList, &Creds->List );

        UnlockCredList();

    }

    return Creds ;
}


//+---------------------------------------------------------------------------
//
//  Function:   XtcbRefCreds
//
//  Synopsis:   Reference the credentials
//
//  Arguments:  [Creds] --
//
//  History:    2-19-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
XtcbRefCreds(
    PXTCB_CREDS Creds
    )
{
    WriteLockCredList();

    Creds->RefCount++ ;

    UnlockCredList();

}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbDerefCreds
//
//  Synopsis:   Deref Credentials, freeing if the refcount goes to zero
//
//  Arguments:  [Creds] --
//
//  History:    2-19-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
XtcbDerefCreds(
    PXTCB_CREDS Creds
    )
{
    WriteLockCredList();

    Creds->RefCount--;

    if ( Creds->RefCount )
    {
        UnlockCredList();

        return;
    }

    RemoveEntryList( &Creds->List );

    UnlockCredList();

    Creds->Check = 0 ;

    LocalFree( Creds );
}


//+---------------------------------------------------------------------------
//
//  Function:   XtcbAllocateCredHandle
//
//  Synopsis:   Allocates and returns a cred handle (reference to a credential)
//
//  Arguments:  [Creds] -- Creds this handle is for
//
//  History:    2-21-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PXTCB_CRED_HANDLE
XtcbAllocateCredHandle(
    PXTCB_CREDS Creds
    )
{
    PXTCB_CRED_HANDLE   Handle ;

    Handle = (PXTCB_CRED_HANDLE) LocalAlloc( LMEM_FIXED,
                            sizeof( XTCB_CRED_HANDLE ) );

    if ( Handle )
    {
        ZeroMemory( Handle, sizeof( XTCB_CRED_HANDLE )  );

        Handle->Check = XTCB_CRED_HANDLE_CHECK ;

        XtcbRefCreds( Creds );

        Handle->Creds = Creds ;

        Handle->RefCount = 1 ;

    }

    return Handle ;


}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbRefCredHandle
//
//  Synopsis:   Reference a credential handle
//
//  Arguments:  [Handle] -- Handle to ref
//
//  History:    2-24-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
XtcbRefCredHandle(
    PXTCB_CRED_HANDLE   Handle
    )
{
    WriteLockCredList();

    Handle->RefCount ++ ;

    UnlockCredList();

}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbDerefCredHandle
//
//  Synopsis:   Dereference a cred handle
//
//  Arguments:  [Handle] --
//
//  History:    2-24-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
XtcbDerefCredHandle(
    PXTCB_CRED_HANDLE   Handle
    )
{
    WriteLockCredList();

    Handle->RefCount -- ;

    if ( Handle->RefCount == 0 )
    {
        XtcbDerefCreds( Handle->Creds );

        LocalFree( Handle );
    }

    UnlockCredList();
}


//+---------------------------------------------------------------------------
//                           
//  Function:   XtcbCreatePacForCaller
//
//  Synopsis:   Creates an XTCB_PAC for the caller
//
//  Arguments:  none
//
//  History:    3-14-00   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PXTCB_PAC
XtcbCreatePacForCaller(
    VOID
    )
{
    HANDLE Token ;
    NTSTATUS Status ;
    PXTCB_PAC Pac = NULL ;
    PTOKEN_USER User = NULL ;
    PTOKEN_GROUPS Groups = NULL ;
    PTOKEN_GROUPS Restrictions = NULL ;
    TOKEN_STATISTICS Stats ;
    ULONG UserSize ;
    ULONG GroupSize ;
    ULONG RestrictionSize ;
    ULONG PacGroupSize = 0 ;
    ULONG PacRestrictionSize = 0 ;
    ULONG PacUserName = 0 ;
    ULONG PacDomainName = 0 ;
    ULONG PacSize ;
    ULONG i ;
    PUCHAR CopyTo ;
    PUCHAR Base ;
    BOOL SpecialAccount = FALSE ;
    PSECURITY_LOGON_SESSION_DATA LogonSessionData = NULL ;



    Status = LsaTable->ImpersonateClient();

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

    Status = NtOpenThreadToken(
                NtCurrentThread(),
                TOKEN_READ,
                TRUE,
                &Token );

    RevertToSelf();

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

    //
    // Now that we have the token, capture all the information about this user,
    // and compute our own "PAC" structure.
    //

    Status = NtQueryInformationToken(
                Token,
                TokenStatistics,
                &Stats,
                sizeof( Stats ),
                &UserSize );

    if ( !NT_SUCCESS( Status ) )
    {
        goto CreatePac_Exit ;
    }

    //
    // If this is a special logon session (e.g. LocalSystem, LocalService, etc.),
    // then the LUID will be less than 1000.  Set the flag to copy all SIDs in the token.
    //

    if ( (Stats.AuthenticationId.HighPart == 0) &&
         (Stats.AuthenticationId.LowPart < 1000 ) )
    {
        SpecialAccount = TRUE ;
    }

    UserSize = 0 ;

    (void) NtQueryInformationToken(
                Token,
                TokenUser,
                NULL,
                0,
                &UserSize );

    if ( UserSize == 0 )
    {
        goto CreatePac_Exit ;
    }

    User = LocalAlloc( LMEM_FIXED, UserSize );

    if ( !User )
    {
        goto CreatePac_Exit ;
    }

    Status = NtQueryInformationToken(
                Token,
                TokenUser,
                User,
                UserSize,
                &UserSize );

    if ( !NT_SUCCESS( Status ) )
    {
        goto CreatePac_Exit ;
    }

    GroupSize = 0 ;
    
    (void) NtQueryInformationToken(
                Token,
                TokenGroups,
                NULL,
                0,
                &GroupSize );

    if ( GroupSize == 0 )
    {
        goto CreatePac_Exit ;
    }

    Groups = LocalAlloc( LMEM_FIXED, GroupSize );

    if ( !Groups )
    {
        goto CreatePac_Exit ;
    }

    Status = NtQueryInformationToken(
                Token,
                TokenGroups,
                Groups,
                GroupSize,
                &GroupSize );

    if ( !NT_SUCCESS( Status ) )
    {
        goto CreatePac_Exit;
    }

    RestrictionSize = 0 ;

    (void) NtQueryInformationToken(
                Token,
                TokenRestrictedSids,
                NULL,
                0,
                &RestrictionSize );

    if ( RestrictionSize != 0 )
    {
        Restrictions = LocalAlloc( LMEM_FIXED, RestrictionSize );

        if ( Restrictions )
        {
            Status = NtQueryInformationToken(
                        Token,
                        TokenRestrictedSids,
                        Restrictions,
                        RestrictionSize,
                        &RestrictionSize );

            if ( !NT_SUCCESS( Status ) )
            {
                goto CreatePac_Exit ;
            }
        }
        else 
        {
            goto CreatePac_Exit ;
        }
    }


    //
    // We now have all the users SIDs in the two (or three) pointers.  First, grovel the Groups
    // for non-local SIDs, and set all the rest to 0.  This will let us compute how much space
    // we need.
    //

    for ( i = 0 ; i < Groups->GroupCount ; i++ )
    {
        if ( (*RtlSubAuthorityCountSid( Groups->Groups[ i ].Sid ) > 2) ||
             (SpecialAccount) )
        {
            //
            // A "real" SID.  Check to make sure it is not from this machine
            //

            if ( ( XtcbMachineSid != NULL ) && 
                 RtlEqualPrefixSid( XtcbMachineSid, Groups->Groups[ i ].Sid ) )
            {
                //
                // Don't use this group
                //

                Groups->Groups[ i ].Attributes = 0 ;
            }
            else 
            {
                //
                // We like this SID (it is not from the local machine)
                //

                Groups->Groups[ i ].Attributes = SE_GROUP_MANDATORY ;
                PacGroupSize += RtlLengthSid( Groups->Groups[ i ].Sid );
            }
        }
        else 
        {
            Groups->Groups[ i ].Attributes = 0 ;
        }
    }

    //
    // Do the same for the restrictions, if any
    //

    if ( Restrictions )
    {
        for ( i = 0 ; i < Restrictions->GroupCount ; i++ )
        {
            PacRestrictionSize += RtlLengthSid( Restrictions->Groups[ i ].Sid );
        }
    }

    //
    // Get the user's name and domain:
    //

    Status = LsaGetLogonSessionData( 
                    &Stats.AuthenticationId, 
                    &LogonSessionData );

    if ( !NT_SUCCESS( Status ) )
    {
        goto CreatePac_Exit ;
    }

    PacUserName = LogonSessionData->UserName.Length ;
    PacDomainName = LogonSessionData->LogonDomain.Length ;

    //
    // In an advanced world, we'd query the other packages for
    // delegatable credentials, bundle them up and ship them
    // over.
    //


    //
    // Ok, we've got all the information we need
    //

    PacSize = sizeof( XTCB_PAC ) +
              RtlLengthSid( User->User.Sid ) +
              PacGroupSize +
              PacRestrictionSize +
              PacUserName +
              PacDomainName ;

    Pac = LocalAlloc( LMEM_FIXED, PacSize );

    if ( !Pac )
    {
        goto CreatePac_Exit ;
    }


    //
    // Create the PAC structure:
    //

    Pac->Tag = XTCB_PAC_TAG ;
    Pac->Length = PacSize ;

    CopyTo = (PUCHAR) (Pac + 1);
    Base = (PUCHAR) Pac ;
    
    //
    // Assemble the PAC:
    //
    // first, the user
    //

    Pac->UserOffset = (ULONG) (CopyTo - Base);
    Pac->UserLength = RtlLengthSid( User->User.Sid );

    RtlCopyMemory(
        CopyTo,
        User->User.Sid,
        Pac->UserLength );

    CopyTo += RtlLengthSid( User->User.Sid );

    //
    // Now the normal groups:
    //

    Pac->GroupCount = 0 ;
    Pac->GroupOffset = (ULONG) (CopyTo - Base);


    for ( i = 0 ; i < Groups->GroupCount ; i++ )
    {
        if ( Groups->Groups[ i ].Attributes & SE_GROUP_MANDATORY )
        {
            RtlCopyMemory(
                    CopyTo,
                    Groups->Groups[ i ].Sid,
                    RtlLengthSid( Groups->Groups[ i ].Sid ) );

            CopyTo += RtlLengthSid( Groups->Groups[ i ].Sid );

            Pac->GroupCount++ ;
        }
    }
    Pac->GroupLength = (ULONG) (CopyTo - Base) - Pac->GroupOffset;

    //
    // If there are restrictions, copy them in as well
    //

    if ( (Restrictions == NULL) ||
         (Restrictions->GroupCount == 0 ) )
    {
        Pac->RestrictionCount = 0 ;
        Pac->RestrictionOffset = 0 ;
        Pac->RestrictionLength = 0 ;
    }
    else 
    {
        Pac->RestrictionCount = Restrictions->GroupCount ;
        Pac->RestrictionOffset = (ULONG) ( CopyTo - Base );

        for ( i = 0 ; i < Restrictions->GroupCount ; i++ )
        {
            RtlCopyMemory(
                    CopyTo,
                    Restrictions->Groups[ i ].Sid,
                    RtlLengthSid( Restrictions->Groups[ i ].Sid ) );

            CopyTo += RtlLengthSid( Restrictions->Groups[ i ].Sid );

            Pac->RestrictionCount++ ;
        }
        Pac->RestrictionLength = (ULONG) (CopyTo - Base) - Pac->RestrictionOffset ;
    }

    Pac->NameOffset = (ULONG) ( CopyTo - Base );
    Pac->NameLength = LogonSessionData->UserName.Length ;
    RtlCopyMemory(
            CopyTo,
            LogonSessionData->UserName.Buffer,
            LogonSessionData->UserName.Length );

    CopyTo += LogonSessionData->UserName.Length ;

    Pac->DomainLength = LogonSessionData->LogonDomain.Length ;
    Pac->DomainOffset = (ULONG) ( CopyTo - Base );

    RtlCopyMemory(
            CopyTo,
            LogonSessionData->LogonDomain.Buffer,
            LogonSessionData->LogonDomain.Length );


    
    //
    // Someday, maybe, copy credential data here
    //

    Pac->CredentialLength = 0 ;
    Pac->CredentialOffset = 0 ;
    

CreatePac_Exit:

    if ( LogonSessionData )
    {
        LsaFreeReturnBuffer( LogonSessionData );
    }

    if ( User )
    {
        LocalFree( User );
    }

    if ( Groups )
    {
        LocalFree( Groups );
    }

    if ( Restrictions )
    {
        LocalFree( Restrictions );
    }

    NtClose( Token );

    return Pac ;
    

}