//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       mapsam.cxx
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    10-17-96   RichardW   Created
//
//----------------------------------------------------------------------------

extern "C" {

#include "sslp.h"
#include <crypt.h>
#include <lmcons.h>
#include <ntsam.h>
#include <samrpc.h>
#include <samisrv.h>
#include <lsarpc.h>
#include <lsaisrv.h>
#include <ntmsv1_0.h>
#include <certmap.h>
#include "mapsam.h"
}

#include <pac.hxx>






//+---------------------------------------------------------------------------
//
//  Function:   SslDuplicateString
//
//  Synopsis:   Duplicate a unicode string
//
//  Arguments:  [Dest]   --
//              [Source] --
//
//  History:    10-18-96   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
SslDuplicateString(
    PUNICODE_STRING Dest,
    PUNICODE_STRING Source
    )
{
    Dest->Buffer = (PWSTR) LocalAlloc( LMEM_FIXED, Source->Length + sizeof(WCHAR) );
    if ( Dest->Buffer )
    {
        Dest->Length = Source->Length ;
        Dest->MaximumLength = Source->Length + sizeof(WCHAR) ;
        CopyMemory( Dest->Buffer, Source->Buffer, Source->Length );
        Dest->Buffer[ Dest->Length / 2 ] = L'\0';

        return( STATUS_SUCCESS );
    }

    return( STATUS_NO_MEMORY );
}


//+-------------------------------------------------------------------------
//
//  Function:   SslMakeDomainRelativeSid
//
//  Synopsis:   Given a domain Id and a relative ID create the corresponding
//              SID allocated from the LSA heap.
//
//  Effects:
//
//  Arguments:
//
//    DomainId - The template SID to use.
//
//    RelativeId - The relative Id to append to the DomainId.
//
//  Requires:
//
//  Returns:    Sid - Returns a pointer to a buffer allocated from the LsaHeap
//                      containing the resultant Sid.
//
//  Notes:
//
//
//--------------------------------------------------------------------------
PSID
SslMakeDomainRelativeSid(
    IN PSID DomainId,
    IN ULONG RelativeId
    )

{
    UCHAR DomainIdSubAuthorityCount;
    ULONG Size;
    PSID Sid;

    //
    // Allocate a Sid which has one more sub-authority than the domain ID.
    //

    DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId ));
    Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);

    if ((Sid = LocalAlloc( LMEM_FIXED,  Size )) == NULL ) {
        return NULL;
    }

    //
    // Initialize the new SID to have the same inital value as the
    // domain ID.
    //

    if ( !NT_SUCCESS( RtlCopySid( Size, Sid, DomainId ) ) ) {
        LocalFree( Sid );
        return NULL;
    }

    //
    // Adjust the sub-authority count and
    //  add the relative Id unique to the newly allocated SID
    //

    (*(RtlSubAuthorityCountSid( Sid ))) ++;
    *RtlSubAuthoritySid( Sid, DomainIdSubAuthorityCount ) = RelativeId;


    return Sid;
}

//+-------------------------------------------------------------------------
//
//  Function:   SslDuplicateSid
//
//  Synopsis:   Duplicates a SID
//
//  Effects:    allocates memory with LsaFunctions.AllocateLsaHeap
//
//  Arguments:  DestinationSid - Receives a copy of the SourceSid
//              SourceSid - SID to copy
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS - the copy succeeded
//              STATUS_INSUFFICIENT_RESOURCES - the call to allocate memory
//                  failed
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
SslDuplicateSid(
    OUT PSID * DestinationSid,
    IN PSID SourceSid
    )
{
    ULONG SidSize;

    DsysAssert(RtlValidSid(SourceSid));

    SidSize = RtlLengthSid(SourceSid);
    *DestinationSid = (PSID) LocalAlloc( LMEM_FIXED,  SidSize );
    if (*DestinationSid == NULL)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }
    RtlCopyMemory(
        *DestinationSid,
        SourceSid,
        SidSize
        );
    return(STATUS_SUCCESS);
}




NTSTATUS
SslpGetPacForUser(
    IN SAMPR_HANDLE UserHandle,
    OUT PPACTYPE * ppPac
    )
{
    PSAMPR_USER_ALL_INFORMATION UserAll = NULL ;
    PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL ;
    NTSTATUS Status ;
    PPACTYPE pNewPac = NULL ;
    PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL ;

    *ppPac = NULL ;

    Status = pSamrQueryInformationUser(
                    UserHandle,
                    UserAllInformation,
                    &UserAllInfo );

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

    UserAll = &UserAllInfo->All ;

    if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED )
    {
        Status = STATUS_ACCOUNT_DISABLED ;

        goto GetPac_Cleanup;
    }

    Status = pSamrGetGroupsForUser(
                    UserHandle,
                    &GroupsBuffer );

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

    Status = PAC_Init( UserAll,
                       GroupsBuffer,
                       GlobalDomainSid,
                       &GlobalDomainName,
                       &GlobalMachineName,
                       NULL,
                       ppPac );




GetPac_Cleanup:

    if ( UserAllInfo )
    {
        pSamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
    }

    if ( GroupsBuffer )
    {
        pSamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
    }

    return( Status );

}




NTSTATUS
SslGetPacForUser(
    IN PUNICODE_STRING  AlternateName,
    IN BOOL AllowGuest,
    OUT PUCHAR * pPacData,
    OUT PULONG pPacDataSize
    )
{

    NTSTATUS Status ;
    PVOID   UserHandle ;

    *pPacData = NULL ;
    *pPacDataSize = 0 ;

    Status = LsaTable->OpenSamUser( AlternateName,
                                    SecNameAlternateId,
                                    &SslNamePrefix,
                                    AllowGuest,
                                    0,
                                    &UserHandle );

    if ( NT_SUCCESS( Status ) )
    {
        Status = LsaTable->GetUserAuthData( UserHandle,
                                            pPacData,
                                            pPacDataSize );

        (VOID) LsaTable->CloseSamUser( UserHandle );
    }

    return Status ;

}



NTSTATUS
SslCreateTokenFromPac(
    IN PUCHAR   MarshalledPac,
    IN ULONG    MarshalledPacSize,
    OUT PLUID   NewLogonId,
    OUT PHANDLE Token
    )
{
    PLSA_TOKEN_INFORMATION_V1 TokenInformation = NULL ;
    PLSA_TOKEN_INFORMATION_NULL TokenNull = NULL ;
    PVOID LsaTokenInformation = NULL ;

    LUID LogonId ;
    UNICODE_STRING UserName ;
    UNICODE_STRING DomainName ;
    UNICODE_STRING Workstation ;

    NTSTATUS Status ;
    NTSTATUS SubStatus ;
    HANDLE TokenHandle = NULL ;


    RtlInitUnicodeString(
        &UserName,
        L"Certificate User"
        );

    RtlInitUnicodeString(
        &DomainName,
        GlobalDomainName.Buffer
        );


    //
    // Now create the token.
    //

    LsaTokenInformation = TokenInformation;


    //
    // Create a logon session.
    //

    NtAllocateLocallyUniqueId(&LogonId);

    Status = LsaTable->CreateLogonSession( &LogonId );

    if (!NT_SUCCESS(Status))
    {
        DebugOut((DEB_ERROR,"Failed to create logon session: 0x%x\n",Status));
        goto CreateToken_Cleanup;
    }

    //
    // We would normally pass in the client workstation name when creating 
    // the token, but this information is not available since the client is 
    // sitting on the other side of an SSL connection.
    //

    RtlInitUnicodeString(
        &Workstation,
        NULL
        );

    Status = LsaTable->CreateToken(
                &LogonId,
                &SslSource,
                Network,
                LsaTokenInformationV1,
                LsaTokenInformation,
                NULL,                   // no token groups
                &UserName,
                &DomainName,
                &Workstation,
                &TokenHandle,
                &SubStatus
                );


    if (!NT_SUCCESS(Status))
    {
        DebugOut((DEB_ERROR,"Failed to create token: 0x%x\n",Status));
        goto CreateToken_Cleanup;
    }

    TokenInformation = NULL;
    TokenNull = NULL;

    if (!NT_SUCCESS(SubStatus))
    {
        DebugOut((DEB_ERROR,"Failed to create token, substatus = 0x%x\n",SubStatus));
        Status = SubStatus;
        goto CreateToken_Cleanup;
    }

    //
    // If the caller wanted an identify level token, duplicate the token
    // now.
    //

#if 0
    if ((ContextFlags & ISC_RET_IDENTIFY) != 0)
    {
        if (!DuplicateTokenEx(
                    TokenHandle,
                    TOKEN_ALL_ACCESS,
                    NULL,               // no security attributes
                    SecurityIdentification,
                    TokenImpersonation,
                    &TempTokenHandle
                    ))
        {
            DebugOut((DEB_ERROR,"Failed to duplicate token\n"));
            DsysAssert(GetLastError() == ERROR_NO_SYSTEM_RESOURCES);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto CreateToken_Cleanup;
        }
        Status = NtClose(TokenHandle);

        DsysAssert(NT_SUCCESS(Status));
        TokenHandle = TempTokenHandle;
        TempTokenHandle = NULL;

    }
#endif

    //
    // Check the delegation information to see if we need to create
    // a logon session for this.
    //

    *NewLogonId = LogonId;
    *Token = TokenHandle;

CreateToken_Cleanup:
    if (TokenInformation != NULL)
    {
        if ( TokenInformation->User.User.Sid != NULL ) {
            LocalFree( TokenInformation->User.User.Sid );
        }

        if ( TokenInformation->Groups != NULL ) {
            ULONG i;

            for ( i=0; i < TokenInformation->Groups->GroupCount; i++ ) {
                LocalFree( TokenInformation->Groups->Groups[i].Sid );
            }

            LocalFree( TokenInformation->Groups );
        }

        if ( TokenInformation->PrimaryGroup.PrimaryGroup != NULL ) {
            LocalFree( TokenInformation->PrimaryGroup.PrimaryGroup );
        }

        LocalFree( TokenInformation );

    }
    if (TokenNull != NULL)
    {
        LocalFree(TokenNull);
    }


    if (!NT_SUCCESS(Status))
    {
        //
        // Note: if we have created a token, we don't want to delete
        // the logon session here because we will end up dereferencing
        // the logon session twice.
        //

        if (TokenHandle != NULL)
        {
            NtClose(TokenHandle);
        }
        else if ((LogonId.LowPart != 0) || (LogonId.HighPart != 0))
        {
            LsaTable->DeleteLogonSession(&LogonId);
        }
    }

    return(Status);
}