//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:       ctxtapi.c
//
//  Contents:   LSA Mode Context API
//
//  Classes:
//
//  Functions:
//
//  History:    2-24-97   RichardW   Created
//
//----------------------------------------------------------------------------

#include "xtcbpkg.h"
#include "md5.h"

typedef struct _XTCB_ATTR_MAP {
    ULONG Request ; 
    ULONG Return ;
} XTCB_ATTR_MAP ;

XTCB_ATTR_MAP AcceptMap[] = {
    { ASC_REQ_DELEGATE, ASC_RET_DELEGATE },
    { ASC_REQ_MUTUAL_AUTH, ASC_RET_MUTUAL_AUTH },
    { ASC_REQ_REPLAY_DETECT, ASC_RET_REPLAY_DETECT },
    { ASC_REQ_SEQUENCE_DETECT, ASC_RET_SEQUENCE_DETECT },
    { ASC_REQ_CONFIDENTIALITY, ASC_RET_CONFIDENTIALITY },
    { ASC_REQ_ALLOCATE_MEMORY, ASC_RET_ALLOCATED_MEMORY },
    { ASC_REQ_CONNECTION, ASC_RET_CONNECTION },
    { ASC_REQ_INTEGRITY, ASC_RET_INTEGRITY }
};

XTCB_ATTR_MAP InitMap[] = {
    { ISC_REQ_DELEGATE, ISC_RET_DELEGATE },
    { ISC_REQ_MUTUAL_AUTH, ISC_RET_MUTUAL_AUTH },
    { ISC_REQ_SEQUENCE_DETECT, ISC_RET_MUTUAL_AUTH },
    { ISC_REQ_REPLAY_DETECT, ISC_RET_REPLAY_DETECT },
    { ISC_REQ_CONFIDENTIALITY, ISC_RET_CONFIDENTIALITY },
    { ISC_REQ_ALLOCATE_MEMORY, ISC_RET_ALLOCATED_MEMORY },
    { ISC_REQ_INTEGRITY, ISC_RET_INTEGRITY }
};


ULONG
XtcbMapAttributes(
    ULONG Input,
    BOOL Init
    )
{
    int i;
    ULONG Result = 0 ;

    if ( Init )
    {
        for ( i = 0 ; i < sizeof( InitMap ) / sizeof( XTCB_ATTR_MAP ) ; i++ )
        {
            if ( InitMap[i].Request & Input )
            {
                Result |= InitMap[i].Return ;
            }
        }
    }
    else
    {
        for ( i = 0 ; i < sizeof( AcceptMap ) / sizeof( XTCB_ATTR_MAP ) ; i++ )
        {
            if ( AcceptMap[i].Request & Input )
            {
                Result |= AcceptMap[i].Return ;
            }
        }
    }

    return Result ;
}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbGetState
//
//  Synopsis:   Translates handles to their structures, and pulls out the
//              interesting bits of the input and output buffers
//
//  Arguments:  [dwCredHandle] --
//              [dwCtxtHandle] --
//              [pInput]       --
//              [pOutput]      --
//              [Client]       --
//              [pContext]     --
//              [pCredHandle]  --
//              [pInToken]     --
//              [pOutToken]    --
//
//  History:    3-05-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
XtcbGetState(
    LSA_SEC_HANDLE  dwCredHandle,
    LSA_SEC_HANDLE  dwCtxtHandle,
    PSecBufferDesc  pInput,
    PSecBufferDesc  pOutput,
    BOOL    Client,
    PXTCB_CONTEXT * pContext,
    PXTCB_CRED_HANDLE * pCredHandle,
    PSecBuffer * pInToken,
    PSecBuffer * pOutToken)
{
    SECURITY_STATUS scRet;
    PXTCB_CONTEXT   Context ;
    PXTCB_CRED_HANDLE CredHandle ;
    PSecBuffer  OutToken ;
    PSecBuffer  InToken ;
    ULONG   i;


    if ( dwCtxtHandle )
    {
        Context = (PXTCB_CONTEXT) dwCtxtHandle ;

        if ( !XtcbRefContextRecord( Context ) )
        {
            return SEC_E_INVALID_HANDLE ;
        }

        CredHandle = (PXTCB_CRED_HANDLE) Context->CredHandle ;
    }
    else
    {
        CredHandle = (PXTCB_CRED_HANDLE) dwCredHandle ;

        if ( !CredHandle )
        {
            return SEC_E_INVALID_HANDLE ;
        }

        Context = XtcbCreateContextRecord( (Client ?
                                                XtcbContextClient :
                                                XtcbContextServer),
                                           CredHandle );

        if ( !Context )
        {
            return SEC_E_INSUFFICIENT_MEMORY ;
        }
    }

    //
    // Find the output token buffer:
    //

    OutToken = NULL ;

    for ( i = 0 ; i < pOutput->cBuffers ; i++ )
    {
        if ( (pOutput->pBuffers[i].BufferType & (~SECBUFFER_ATTRMASK)) ==
                    SECBUFFER_TOKEN )
        {
            OutToken = &pOutput->pBuffers[i] ;
            LsaTable->MapBuffer( OutToken, OutToken );
            break;
        }
    }

    //
    // Find the input token buffer:
    //

    InToken = NULL ;

    for ( i = 0 ; i < pInput->cBuffers ; i++ )
    {
        if ( (pInput->pBuffers[i].BufferType & (~SECBUFFER_ATTRMASK)) ==
                    SECBUFFER_TOKEN )
        {
            InToken = &pInput->pBuffers[i] ;
            LsaTable->MapBuffer( InToken, InToken );
            break;
        }
    }

    *pContext = Context ;
    *pCredHandle = CredHandle ;
    *pInToken = InToken ;
    *pOutToken = OutToken ;

    return SEC_E_OK ;
}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbInitLsaModeContext
//
//  Synopsis:   Creates a client side context and blob
//
//  Arguments:  [dwCredHandle]  --
//              [dwCtxtHandle]  --
//              [pszTargetName] --
//              [fContextReq]   --
//              [TargetDataRep] --
//              [pInput]        --
//              [pdwNewContext] --
//              [pOutput]       --
//              [pfContextAttr] --
//              [ptsExpiry]     --
//              [pfMapContext]  --
//              [pContextData]  --
//
//  History:    8-15-98   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
XtcbInitLsaModeContext(
    LSA_SEC_HANDLE      dwCredHandle,
    LSA_SEC_HANDLE      dwCtxtHandle,
    PSECURITY_STRING    TargetName,
    ULONG               fContextReq,
    ULONG               TargetDataRep,
    PSecBufferDesc      pInput,
    PLSA_SEC_HANDLE     pdwNewContext,
    PSecBufferDesc      pOutput,
    PULONG              pfContextAttr,
    PTimeStamp          ptsExpiry,
    PBYTE               pfMapContext,
    PSecBuffer          pContextData)
{
    SECURITY_STATUS scRet;
    PXTCB_CONTEXT   Context ;
    PXTCB_CRED_HANDLE CredHandle ;
    PSecBuffer  OutToken ;
    PSecBuffer  InToken ;
    UCHAR GroupKey[ SEED_KEY_SIZE ];
    UCHAR UniqueKey[ SEED_KEY_SIZE ];
    UCHAR MyKey[ SEED_KEY_SIZE ];
    PWSTR Target ;
    BOOL RealTarget = FALSE ;
    PUCHAR Buffer;
    ULONG BufferLen ;
    PUNICODE_STRING Group ;


    DebugLog(( DEB_TRACE_CALLS, "InitLsaModeContext( %p, %p, %ws, ... )\n",
                dwCredHandle, dwCtxtHandle,
                (TargetName->Buffer ? TargetName->Buffer : L"<none>") ));

    if ( fContextReq & 
         ( ISC_REQ_PROMPT_FOR_CREDS |
           ISC_REQ_USE_SUPPLIED_CREDS |
           ISC_REQ_DATAGRAM |
           ISC_REQ_STREAM |
           ISC_REQ_NULL_SESSION |
           ISC_REQ_MANUAL_CRED_VALIDATION ) )
    {
        return STATUS_INVALID_PARAMETER ;
    }

    //
    // Determine what kind of call this is (first or second)
    //

    scRet = XtcbGetState(   dwCredHandle,
                            dwCtxtHandle,
                            pInput,
                            pOutput,
                            TRUE,
                            &Context,
                            &CredHandle,
                            &InToken,
                            &OutToken );

    if ( FAILED( scRet ) )
    {
        return scRet ;
    }


    //
    // Decide what to do:
    //


    if ( Context->Core.State == ContextFirstCall )
    {
        if ( InToken )
        {
            //
            // Something there
            //
            scRet = SEC_E_INVALID_TOKEN ;
        }
        else
        {

            if ( !OutToken )
            {
                scRet = SEC_E_INVALID_TOKEN ;
            }
            else 
            {
                //
                // Examine the target name.  See if we can handle it:
                //

                if ( MGroupParseTarget( TargetName->Buffer,
                                        &Target ) )
                {
                    //
                    // See if we have a group with that machine as a member:
                    //

                    if ( MGroupLocateKeys( Target,
                                           &Group,
                                           UniqueKey,
                                           GroupKey,
                                           MyKey ) )
                    {
                        //
                        // We do have one!  Calooh!  Calay!
                        //

                        RealTarget = TRUE ;

                    }

                }

                if ( !RealTarget )
                {
                    //
                    // Not one of ours.  Delete the context, 
                    // clean up
                    //

                    scRet = SEC_E_TARGET_UNKNOWN ;
                }

            }

            if ( RealTarget )
            {
                //
                // We've got a live target.  Fill in the context, and construct
                // the blob
                //

                scRet = XtcbBuildInitialToken(
                            CredHandle->Creds,
                            Context,
                            TargetName,
                            Group,
                            UniqueKey,
                            GroupKey,
                            MyKey,
                            &Buffer,
                            &BufferLen );



            }


            if ( NT_SUCCESS( scRet ) )
            {

                if ( fContextReq & ISC_REQ_ALLOCATE_MEMORY )
                {
                    OutToken->pvBuffer = Buffer ;
                    OutToken->cbBuffer = BufferLen ;
                }
                else 
                {
                    if ( BufferLen <= OutToken->cbBuffer )
                    {
                        RtlCopyMemory( 
                            OutToken->pvBuffer,
                            Buffer,
                            BufferLen );

                        OutToken->cbBuffer = BufferLen ;
                    }
                    else 
                    {
                        scRet = SEC_E_INSUFFICIENT_MEMORY ;
                    }
                }


            }

            if ( NT_SUCCESS( scRet ) )
            {

                Context->Core.State = ContextSecondCall ;
                Context->Core.Attributes = fContextReq ;

                *pdwNewContext = (LSA_SEC_HANDLE) Context ;
            }
            else 
            {
                XtcbDerefContextRecord( Context );

            }


            return scRet ;
        }
    }
    else
    {
        //
        // Second round
        //

        
    }


    return( scRet );


}


//+---------------------------------------------------------------------------
//
//  Function:   XtcbDeleteContext
//
//  Synopsis:   Deletes the LSA side of a context
//
//  Arguments:  [dwCtxtHandle] --
//
//  History:    8-15-98   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
XtcbDeleteContext(
    LSA_SEC_HANDLE dwCtxtHandle
    )
{
    PXTCB_CONTEXT Context ;

    DebugLog(( DEB_TRACE_CALLS, "DeleteContext( %x )\n", dwCtxtHandle ));

    Context = (PXTCB_CONTEXT) dwCtxtHandle ;

    if ( XtcbRefContextRecord( Context ) )
    {
        XtcbDerefContextRecord( Context );

        XtcbDerefContextRecord( Context );

        return SEC_E_OK ;
    }

    return( SEC_E_INVALID_HANDLE );
}


//+---------------------------------------------------------------------------
//
//  Function:   XtcbApplyControlToken
//
//  Synopsis:   Apply a control token to a context
//
//  Effects:    not supported
//
//  Arguments:  [dwCtxtHandle] --
//              [pInput]       --
//
//  History:    8-15-98   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------


SECURITY_STATUS
SEC_ENTRY
XtcbApplyControlToken(
    LSA_SEC_HANDLE dwCtxtHandle,
    PSecBufferDesc  pInput)
{
    DebugLog(( DEB_TRACE_CALLS, "ApplyControlToken( %x )\n", dwCtxtHandle ));

    return(SEC_E_UNSUPPORTED_FUNCTION);
}


//+---------------------------------------------------------------------------
//
//  Function:   XtcbAcceptLsaModeContext
//
//  Synopsis:   Creates a server side context representing the user connecting
//
//  Arguments:  [dwCredHandle]  --
//              [dwCtxtHandle]  --
//              [pInput]        --
//              [fContextReq]   --
//              [TargetDataRep] --
//              [pdwNewContext] --
//              [pOutput]       --
//              [pfContextAttr] --
//              [ptsExpiry]     --
//              [pfMapContext]  --
//              [pContextData]  --
//
//  History:    8-15-98   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
SECURITY_STATUS
SEC_ENTRY
XtcbAcceptLsaModeContext(
    LSA_SEC_HANDLE  dwCredHandle,
    LSA_SEC_HANDLE  dwCtxtHandle,
    PSecBufferDesc  pInput,
    ULONG           fContextReq,
    ULONG           TargetDataRep,
    PLSA_SEC_HANDLE pdwNewContext,
    PSecBufferDesc  pOutput,
    PULONG          pfContextAttr,
    PTimeStamp      ptsExpiry,
    PBYTE           pfMapContext,
    PSecBuffer      pContextData)
{
    SECURITY_STATUS scRet;
    PXTCB_CONTEXT   Context ;
    PXTCB_CRED_HANDLE CredHandle ;
    PSecBuffer  OutToken ;
    PSecBuffer  InToken ;
    HANDLE      Token ;
    UNICODE_STRING Client;
    UNICODE_STRING Group ;
    UCHAR GroupKey[ SEED_KEY_SIZE ];
    UCHAR UniqueKey[ SEED_KEY_SIZE ];
    UCHAR MyKey[ SEED_KEY_SIZE ];
    BOOL Success = FALSE ;

    DebugLog(( DEB_TRACE_CALLS, "AcceptLsaModeContext( %x, %x, ... )\n",
                    dwCredHandle, dwCtxtHandle ));


    //
    // Determine what kind of call this is (first or second)
    //

    *pfMapContext = FALSE ;

    scRet = XtcbGetState(   dwCredHandle,
                            dwCtxtHandle,
                            pInput,
                            pOutput,
                            FALSE,
                            &Context,
                            &CredHandle,
                            &InToken,
                            &OutToken );

    if ( FAILED( scRet ) )
    {
        return scRet ;
    }


    //
    // Decide what to do:
    //

    if ( Context->Core.State == ContextFirstCall )
    {
        if ( !InToken )
        {
            return SEC_E_INVALID_TOKEN ;
        }

        if ( !XtcbParseInputToken(
                    InToken->pvBuffer,
                    InToken->cbBuffer,
                    &Client,
                    &Group ) )
        {
            DebugLog((DEB_TRACE, "Unable to parse input token\n" ));

            return SEC_E_INVALID_TOKEN ;
        }

        Success = MGroupLocateInboundKey(
                            &Group,
                            &Client,
                            UniqueKey,
                            GroupKey,
                            MyKey );

        LocalFree( Client.Buffer );
        LocalFree( Group.Buffer );

        if ( Success )
        {
            scRet = XtcbAuthenticateClient(
                        Context,
                        InToken->pvBuffer,
                        InToken->cbBuffer,
                        UniqueKey,
                        GroupKey,
                        MyKey
                        );
                        

        }
        else 
        {
            DebugLog(( DEB_TRACE, "Unable to find group entry for Group %ws, Client %ws\n",
                        Group.Buffer, Client.Buffer ));

            scRet = SEC_E_INVALID_TOKEN ;
        }

        if ( NT_SUCCESS( scRet ) )
        {
            scRet = XtcbBuildReplyToken(
                        Context,
                        fContextReq,
                        OutToken );
        }

        if ( NT_SUCCESS( scRet ) )
        {

            Context->Core.State = ContextSecondCall ;
            //
            // Ok, we've done the authentication.  Now, we need to map
            // the security context back to the client process
            //

            scRet = LsaTable->DuplicateHandle(
                                Context->Token,
                                &Token );

            if ( NT_SUCCESS( scRet ) )
            {
                Context->Core.CoreTokenHandle = HandleToUlong( Token );

                *pfMapContext = TRUE ;

                pContextData->BufferType = SECBUFFER_TOKEN ;
                pContextData->cbBuffer = sizeof( XTCB_CONTEXT_CORE );
                pContextData->pvBuffer = &Context->Core ;

                *pfContextAttr = ASC_RET_DELEGATE |
                                 ASC_RET_MUTUAL_AUTH |
                                 ASC_RET_REPLAY_DETECT |
                                 ASC_RET_SEQUENCE_DETECT |
                                 ASC_RET_CONFIDENTIALITY |
                                 ASC_
            }

            
        }


    }

    return( scRet );
}

//+---------------------------------------------------------------------------
//
//  Function:   XtcbQueryLsaModeContext
//
//  Synopsis:   Lifespan is thunked to LSA mode for demonstration purposes
//
//  Arguments:  [ContextHandle]    --
//              [ContextAttribute] --
//              [pBuffer]          --
//
//  History:    3-30-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
XtcbQueryLsaModeContext(
    IN LSA_SEC_HANDLE ContextHandle,
    IN ULONG ContextAttribute,
    IN OUT PVOID pBuffer
    )
{
    PXTCB_CONTEXT Context ;
    NTSTATUS Status ;

    Context = (PXTCB_CONTEXT) ContextHandle ;

    if ( !XtcbRefContextRecord( Context ))
    {
        return SEC_E_INVALID_HANDLE ;
    }

    Status = SEC_E_UNSUPPORTED_FUNCTION ;

    XtcbDerefContextRecord( Context );

    return( Status );

}