/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    aucred.c

Abstract:

    This module provides credential management services within the
    LSA subsystem.  Some of these services are indirectly available for use
    by authentication packages.

Author:

    Jim Kelly (JimK) 27-February-1991

Revision History:

--*/

#include <lsapch2.h>

#if 1

RTL_RESOURCE                AuCredLock ;

#define AuReadLockCreds()   RtlAcquireResourceShared( &AuCredLock, TRUE );
#define AuWriteLockCreds()  RtlAcquireResourceExclusive( &AuCredLock, TRUE );
#define AuUnlockCreds()     RtlReleaseResource( &AuCredLock );

#else

RTL_CRITICAL_SECTION    AuCredLock ;

#define AuReadLockCreds()   RtlEnterCriticalSection( &AuCredLock );
#define AuWriteLockCreds()  RtlEnterCriticalSection( &AuCredLock );
#define AuUnlockCreds()     RtlLeaveCriticalSection( &AuCredLock );

#endif

NTSTATUS
LsapInitializeCredentials(
    VOID
    )
{
#if 1
    __try {
        RtlInitializeResource( &AuCredLock );
        return STATUS_SUCCESS;
    } __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
#else
    return RtlInitializeCriticalSection( &AuCredLock );
#endif
}


NTSTATUS
LsapAddCredential(
    IN PLUID LogonId,
    IN ULONG AuthenticationPackage,
    IN PSTRING PrimaryKeyValue,
    IN PSTRING Credentials
    )

/*++

Routine Description:

    This service is used by authentication packages to add credentials to a
    logon session.  These credentials may later be referenced using
    GetCredentials().

    This service acquires the AuLock.

Arguments:

    LogonId - The session ID of logon session to add credentials to.

    AuthenticationPackage - The authentication package ID of the
        calling authentication package.  This was received in the
        InitializePackage() call during DLL initialization.

    PrimaryKeyValue - Points to a string containing a value that the
        authentication package will later want to reference as a
        primary key of the credential data.  This may be used, for
        example, to keep the name of the domain or server the
        credentials are related to.  The format and meaning of this
        string are authentication package-specific.  Note that the
        string value does not have to be unique, even for the
        specified logon session.  For example, there could be two
        passwords for the same domain, each with the passwords stored
        as credentials and the domain name stored as the primary key.

    Credentials - Points to a string containing  data representing
        user credentials.  The format and meaning of this string are
        authentication package-specific.

Return Status:

    STATUS_SUCCESS - The credentials were successfully added.

    STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
        not be found.

--*/

{

    PLSAP_LOGON_SESSION LogonSession;
    PLSAP_PACKAGE_CREDENTIALS Package;
    PLSAP_CREDENTIALS NewCredentials = NULL;
    USHORT MaxPrimary;
    USHORT MaxCredentials;
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Get a pointer to the logon session
    //

    LogonSession = LsapLocateLogonSession( LogonId );

    if ( LogonSession == NULL ) {
        return STATUS_NO_SUCH_LOGON_SESSION;
    }

    //
    // Allocate blocks needed to represent this credential
    // and copy the primary key and credential strings.
    //

    MaxPrimary = ROUND_UP_COUNT((PrimaryKeyValue->Length+sizeof(CHAR)), ALIGN_WORST);
    MaxCredentials = ROUND_UP_COUNT((Credentials->Length+sizeof(CHAR)), ALIGN_WORST);

    NewCredentials = LsapAllocatePrivateHeap( (ULONG)sizeof(LSAP_CREDENTIALS) +
                                          MaxPrimary +
                                          MaxCredentials
                                          );

    if ( NewCredentials == NULL )
    {
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }
    
    NewCredentials->PrimaryKey.MaximumLength = MaxPrimary;
    NewCredentials->PrimaryKey.Buffer = (PSTR)(NewCredentials+1);
    
    NewCredentials->Credentials.MaximumLength = MaxCredentials;
    NewCredentials->Credentials.Buffer = NewCredentials->PrimaryKey.Buffer +
                                         NewCredentials->PrimaryKey.MaximumLength;

    RtlCopyString( &NewCredentials->PrimaryKey, PrimaryKeyValue );
    RtlCopyString( &NewCredentials->Credentials, Credentials );


    //
    // Now get a pointer to the Package's credentials
    // (create one if necessary)
    //

    AuWriteLockCreds();

    Package = LsapGetPackageCredentials(
                  LogonSession,
                  AuthenticationPackage,
                  TRUE
                  );

    if ( !Package )
    {
        AuUnlockCreds();
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    //
    // insert new credentials in list.
    //

    NewCredentials->NextCredentials = Package->Credentials;

    Package->Credentials = NewCredentials;

    AuUnlockCreds();
    LsapReleaseLogonSession( LogonSession );

    return STATUS_SUCCESS;


Cleanup:

    LsapReleaseLogonSession( LogonSession );

    if ( NewCredentials )
    {
        if ( NewCredentials->PrimaryKey.Buffer )
        {
            ZeroMemory( NewCredentials->PrimaryKey.Buffer,
                        NewCredentials->PrimaryKey.Length );
        }

        if ( NewCredentials->Credentials.Buffer )
        {
            ZeroMemory( NewCredentials->Credentials.Buffer,
                        NewCredentials->Credentials.Length );
        }

        LsapFreePrivateHeap( NewCredentials );
    }

    return Status;

}


NTSTATUS
LsapGetCredentials(
    IN PLUID LogonId,
    IN ULONG AuthenticationPackage,
    IN OUT PULONG QueryContext,
    IN BOOLEAN RetrieveAllCredentials,
    IN PSTRING PrimaryKeyValue,
    OUT PULONG PrimaryKeyLength,
    IN PSTRING Credentials
    )

/*++

Routine Description:

    This service is used by authentication packages to retrieve credentials
    associated with a logon session.  It is expected that each authentication
    package will provide its own version of this service to its "clients".
    For example, the MSV1_0 authentication package will provide services for
    the LM Redirector to retrieve credentials (and probably establish them)
    for remote accesses.  These authentication package level services may be
    implemented using the LsaCallAuthenticationPackage() API.

    This service acquires the AuLock.

Arguments:

    LogonId - The session ID of logon session from which credentials
        are to be retrieved.

    AuthenticationPackage - The authentication package ID of the
        calling authentication package.  Authentication packages
        should only retrieve their own credentials.

    QueryContext - A context value used across successive calls to
        retrieve multiple credentials.  The first time this service
        is used, the value pointed to by this argument should be
        zero.  Thereafter, this value will be updated to allow
        retrieval to continue where it left off.  This value should,
        therefore, not be changed until all credentials of a given
        query operation have been retrieved.

    RetrieveAllCredentials - A boolean value indicating whether all
        credentials for the specified logon session should be
        retrieved (TRUE), or only those matching the specified
        PrimaryKeyValue (FALSE).

    PrimaryKeyValue - This parameter serves two purposes.  If the
        RetrieveAllCredentials argument is FALSE, then this string
        contains the value to use as a primary key lookup value.  In
        this case, only credentials whose primary key matches this
        one (and belonging to the correct logon session) will be
        retrieved.  If, however, the RetrieveAllCredentials argument
        is FALSE, then the value of this string are ignored.  In this
        case, the primary key value of each retrieved credential will
        be returned in this string.

    PrimaryKeyLength - If the RetrieveAllCredentials argument value
        is FALSE, then this argument receives the length needed to
        store the PrimaryKeyValue.  If this value is larger than the
        length of the PrimaryKeyValue string, then
        STATUS_BUFFER_OVERFLOW is returned and no data is retrieved.

    Credentials - Points to a string whose buffer is to be set to
        contain the retrieved credential.

Return Status:

    STATUS_MORE_ENTRIES - Credentials were successfully retrieved,
        and there are more available.

    STATUS_SUCCESS - Credentials were successfully retrieved and
        there are no more available.

    STATUS_UNSUCCESSFUL - No more credentials are available.  If
        returned on the first call, then there are no credentials
        matching the selection criteria.

    STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
        not be found.

    STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
        the PrimaryKeyValue was not large enough to hold the data.
        In this case, no data was retrieved. However, the length value
        is returned so that appropriately sized buffer can be passed in
        a successive call.


--*/

{
    //
    // NOTE: The QueryContext value is an index of the last retrieved
    //       credential matching the selection criteria.  To continue
    //       a search for successive credentials, skip QueryContext
    //       number of entries first.
    //
    //       This has the problem of changes between calls screwing
    //       up the result of successive calls.  That's tough.
    //


    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_LOGON_SESSION LogonSession;
    PLSAP_PACKAGE_CREDENTIALS Package;
    PLSAP_CREDENTIALS NextCredentials;
    ULONG i;
    BOOLEAN SelectionMatch;



    //
    // Get a pointer to the logon session
    //

    LogonSession = LsapLocateLogonSession( LogonId );

    if ( LogonSession == NULL ) {
        return STATUS_NO_SUCH_LOGON_SESSION;
    }

    AuReadLockCreds();

    //
    // Now get a pointer to the Package's credentials
    //

    Package = LsapGetPackageCredentials(
                  LogonSession,
                  AuthenticationPackage,
                  FALSE
                  );

    if ( Package == NULL ) {

        AuUnlockCreds();

        LsapReleaseLogonSession( LogonSession );

        return STATUS_UNSUCCESSFUL;
    }

    //
    // skip the credentials already evaluated in previous calls...
    //

    i = (*QueryContext);
    NextCredentials = Package->Credentials;
    while ( i > 0 ) {

        //
        // See if we have reached the end of the list
        //

        if (NextCredentials == NULL) {

            AuUnlockCreds();

            LsapReleaseLogonSession( LogonSession );

            return STATUS_UNSUCCESSFUL;
        }

        //
        // Nope, skip the next one...
        //

        NextCredentials = NextCredentials->NextCredentials;
        i -= 1;

    }


    //
    // Start evaluating each credential for a criteria match.
    //

    SelectionMatch = FALSE;
    while ( NextCredentials != NULL && !SelectionMatch ) {

        (*QueryContext) += 1;

        if (RetrieveAllCredentials) {

            SelectionMatch = TRUE;
            Status = LsapReturnCredential(
                         NextCredentials,
                         Credentials,
                         TRUE,
                         PrimaryKeyValue,
                         PrimaryKeyLength
                         );
        }

        //
        // Only retrieving credentials that match the specified primary
        // key.
        //

        if ( RtlEqualString( &NextCredentials->PrimaryKey, PrimaryKeyValue, FALSE) ) {

            SelectionMatch = TRUE;
            Status = LsapReturnCredential(
                         NextCredentials,
                         Credentials,
                         FALSE,
                         NULL,
                         NULL
                         );

        }

        NextCredentials = NextCredentials->NextCredentials;

    }

    AuUnlockCreds();

    LsapReleaseLogonSession( LogonSession );

    //
    // Figure out what return value to send.
    //

    if (SelectionMatch) {

        if ( Status == STATUS_BUFFER_OVERFLOW ) {
            (*QueryContext) -= 1;
            return STATUS_BUFFER_OVERFLOW;
        }


        if ( Status == STATUS_SUCCESS) {
            if ( NextCredentials == NULL ) {
                return STATUS_SUCCESS;
            } else {
                return STATUS_MORE_ENTRIES;
            }
        }


    } else {

        //
        // didn't find a credential matching the selection criteria.
        //

        return STATUS_UNSUCCESSFUL;

    }

    return STATUS_UNSUCCESSFUL ;
}


NTSTATUS
LsapReturnCredential(
    IN PLSAP_CREDENTIALS SourceCredentials,
    IN PSTRING TargetCredentials,
    IN BOOLEAN ReturnPrimaryKey,
    IN PSTRING PrimaryKeyValue OPTIONAL,
    OUT PULONG PrimaryKeyLength OPTIONAL
    )

/*++

Routine Description:

    This routine returns a copy of the credentials in the specified
    credential record.  It also, optionally, returns a copy of the
    primary key value.

Arguments:

    SourceCredentials - Points to a credential record whose credential
        string and, optionally, primary key are to be copied.

    TargetCredentials - Points to a string whose buffer is to be set to
        contain a copy of the credential.  This copy will be allocated
        using LsapAllocateLsaHeap().

    ReturnPrimaryKey - A boolean indicating whether or not to return
        a copy of the primary key.  TRUE indicates a copy should be
        returned.  FALSE indicates a copy should not be returned.

    PrimaryKeyValue - Points to a string whose buffer is to be set to
        contain a copy of the primary key.  This copy will be allocated
        using LsapAllocateLsaHeap().  This parameter is ignored if the
        ReturnPrimaryKey argument value is FALSE.


    PrimaryKeyLength - Points to a value which will receive the
        length of the primary key value.  If this value is larger than the
        length of the PrimaryKeyValue string, then STATUS_BUFFER_OVERFLOW
        is returned and no data is retrieved.



Return Status:

    STATUS_SUCCESS - Credentials were successfully returned.

    STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
        the PrimaryKeyValue was not large enough to hold the data.
        In this case, no data was retrieved. However, the length value
        is returned so that appropriately sized buffer can be passed in
        a successive call.

--*/

{
    ULONG Length;

    //
    // First try to return the primary key value, since we can encounter
    // a buffer overflow situation in doing so that would prevent us from
    // returning a copy of the credential string.
    //

    if (ReturnPrimaryKey) {
        (*PrimaryKeyLength) = SourceCredentials->PrimaryKey.Length + 1;
        if ( (*PrimaryKeyLength) > PrimaryKeyValue->MaximumLength ) {
            return STATUS_BUFFER_OVERFLOW;
        }

        //
        // It fits
        //

        RtlCopyString( PrimaryKeyValue, &SourceCredentials->PrimaryKey );
    }

    //
    // Now allocate and copy the credential string copy.
    //

    TargetCredentials->MaximumLength = SourceCredentials->Credentials.Length
                                       + (USHORT)1;

    Length = (ULONG)TargetCredentials->MaximumLength;

    TargetCredentials->Buffer = (PCHAR)LsapAllocateLsaHeap( Length );

    if ( TargetCredentials->Buffer )
    {
        RtlCopyString( TargetCredentials, &SourceCredentials->Credentials );

        return STATUS_SUCCESS ;
    }
    else
    {
        return STATUS_NO_MEMORY ;
    }

}



NTSTATUS
LsapDeleteCredential(
    IN PLUID LogonId,
    IN ULONG AuthenticationPackage,
    IN PSTRING PrimaryKeyValue
    )

/*++

Routine Description:

    This service is used to delete an existing credential.  This service
    deletes the first credential it finds with a matching logon session,
    authentication package ID, and primary key value.  If thee are
    multiple credentials that match this criteria, only one of them is
    deleted.

    This status acquires the AuLock.

Arguments:

    LogonId - The session ID of logon session whose credentials are to be
        deleted.

    AuthenticationPackage - The authentication package ID of the
        calling authentication package.  This was received in the
        InitializePackage() call during DLL initialization.

    PrimaryKeyValue - Points to string containing the primary key value
        of the credential to be deleted.


Return Status:

    STATUS_SUCCESS - The credentials were successfully deleted.

    STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
        not be found.

    STATUS_UNSUCCESSFUL - No such credential could be found.

--*/

{


    PLSAP_LOGON_SESSION LogonSession;
    PLSAP_PACKAGE_CREDENTIALS Package;
    PLSAP_CREDENTIALS *NextCredentials, GoodByeCredentials;





    //
    // Get a pointer to the logon session
    //

    LogonSession = LsapLocateLogonSession( LogonId );

    if ( LogonSession == NULL ) {

        return STATUS_NO_SUCH_LOGON_SESSION;
    }

    AuWriteLockCreds();


    //
    // Now get a pointer to the Package's credentials
    //

    Package = LsapGetPackageCredentials(
                  LogonSession,
                  AuthenticationPackage,
                  FALSE
                  );

    if ( Package == NULL ) {

        AuUnlockCreds();

        LsapReleaseLogonSession( LogonSession );

        return STATUS_UNSUCCESSFUL;
    }



    //
    // Start evaluating each credential for a primary key value match.
    //

    NextCredentials = &Package->Credentials;
    while ( (*NextCredentials) != NULL ) {


        if ( RtlEqualString(
                 &(*NextCredentials)->PrimaryKey,
                 PrimaryKeyValue,
                 FALSE)
           ) {

            //
            // remove it from the list
            //

            GoodByeCredentials = (*NextCredentials);
            (*NextCredentials) = GoodByeCredentials->NextCredentials;

            AuUnlockCreds();

            LsapReleaseLogonSession( LogonSession );

            //
            // Zero the contents of the credential record.
            //

            if( GoodByeCredentials->PrimaryKey.Buffer != NULL )
            {
                ZeroMemory( GoodByeCredentials->PrimaryKey.Buffer,
                            GoodByeCredentials->PrimaryKey.Length );
            }
            
            if( GoodByeCredentials->Credentials.Buffer != NULL )
            {
                ZeroMemory( GoodByeCredentials->Credentials.Buffer,
                            GoodByeCredentials->Credentials.Length );
            }

            //
            // Free the credential record itself.
            //

            LsapFreePrivateHeap( GoodByeCredentials );

            return STATUS_SUCCESS;
        }

        NextCredentials = &(*NextCredentials)->NextCredentials;

    }

    AuUnlockCreds();

    LsapReleaseLogonSession( LogonSession );

    //
    // Nothing matched
    //

    return STATUS_UNSUCCESSFUL;

}


PLSAP_PACKAGE_CREDENTIALS
LsapGetPackageCredentials(
    IN PLSAP_LOGON_SESSION LogonSession,
    IN ULONG PackageId,
    IN BOOLEAN CreateIfNecessary
    )


/*++

Routine Description:

    This service returns a pointer to a specified package's credential
    record.  If no such record exists, one will optionally be created.

    It is assumed that either the LogonSession record is not currently
    in the logon session record list, or, if it is, that the AuLock
    is currently held.

Arguments:

    LogonSession - Pointer to a logon session record within which to
        work.

    PackageId - The authentication package ID to look for.

    CreateIfNecessary - A boolean indicating whether or not the package
        record is to be created if one does not already exist.  TRUE
        indicates the package is to be created if necessary, FALSE indicates
        the record should not be created.


Return Status:

    non-NULL - A pointer to the specified package record.

    NULL - The specified package record does not exist (and one was not
        created automatically).

--*/

{

    PLSAP_PACKAGE_CREDENTIALS *NextPackage, TargetPackage;


    //
    // See if the session exists
    //

    NextPackage = &LogonSession->Packages;

    while ( (*NextPackage) != NULL) {
        if ( (*NextPackage)->PackageId == PackageId ) {

            //
            // Found it
            //

            TargetPackage = (*NextPackage);


            return TargetPackage;

        }

        //
        // Move on to next package.
        //

        NextPackage = &(*NextPackage)->NextPackage;

    }

    //
    // No such package exists yet.
    // Create one if necessary.

    if ( !CreateIfNecessary ) {
        return NULL;
    }

    TargetPackage = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_PACKAGE_CREDENTIALS) );

    if ( TargetPackage )
    {
        TargetPackage->PackageId = PackageId;
        TargetPackage->Credentials = NULL;
        TargetPackage->NextPackage = LogonSession->Packages;
        LogonSession->Packages = TargetPackage;
    }

    return TargetPackage;

}

VOID
LsapFreePackageCredentialList(
    IN PLSAP_PACKAGE_CREDENTIALS PackageCredentialList
    )

/*++

Routine Description:

    This service frees a list of packge credential records.  This service
    is not expected to be exposed to authentication packages.

    This service expects not to have to acquire the AuLock.  This may be
    because it is already held, or because the credentials being freed
    are no longer accessible via the global variables.

Arguments:

    PackageCredentialList - Is a pointer to a list of LSA_PACKAGE_CREDENTIALS
        data structures.


Return Status:

    None.

--*/

{

    PLSAP_PACKAGE_CREDENTIALS NextPackage, GoodByePackage;

    //
    // Get rid of each PACKAGE_CREDENTIAL record.
    //

    NextPackage = PackageCredentialList;
    while ( NextPackage != NULL ) {

        //
        // Save a pointer to the next package
        //

        GoodByePackage = NextPackage;
        NextPackage = GoodByePackage->NextPackage;


        LsapFreeCredentialList( GoodByePackage->Credentials );


        //
        // Free the package record itself.
        //

        LsapFreeLsaHeap( GoodByePackage );


    }


    return;

}


VOID
LsapFreeCredentialList(
    IN PLSAP_CREDENTIALS CredentialList
    )

/*++

Routine Description:

    This service frees a list of credential records.  This service is not
    expected to be exposed to authentication packages.

    This service expects not to have to acquire the AuLock.  This may be
    because it is already held, or because the credentials being freed
    are no longer accessible via the global variables.

Arguments:

    CredentialList - Is a pointer to a list of LSA_CREDENTIALS data
        structures.


Return Status:


--*/

{

    PLSAP_CREDENTIALS NextCredentials, GoodByeCredentials;

    //
    // Get rid of each PACKAGE_CREDENTIAL record.
    //

    NextCredentials = CredentialList;
    while ( NextCredentials != NULL ) {

        //
        // Save a pointer to the next credential
        //

        GoodByeCredentials = NextCredentials;
        NextCredentials = GoodByeCredentials->NextCredentials;

        //
        // Zero the contents of this credential record.
        //

        if( GoodByeCredentials->PrimaryKey.Buffer != NULL )
        {
            ZeroMemory( GoodByeCredentials->PrimaryKey.Buffer,
                        GoodByeCredentials->PrimaryKey.Length );
        }

        if( GoodByeCredentials->Credentials.Buffer != NULL )
        {
            ZeroMemory( GoodByeCredentials->Credentials.Buffer,
                        GoodByeCredentials->Credentials.Length );
        }

        //
        // Free the credential record itself.
        //

        LsapFreePrivateHeap( GoodByeCredentials );

    }

    return;

}