/*++

Copyright (c) 1987-1996  Microsoft Corporation

Module Name:

    ssiauth.c

Abstract:

    Authentication related functions

Author:

    Ported from Lan Man 2.0

Environment:

    User mode only.
    Contains NT-specific code.
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

    12-Jul-1991 (cliffv)
        Ported to NT.  Converted to NT style.

--*/

//
// Common include files.
//

#include "logonsrv.h"   // Include files common to entire service
#pragma hdrstop

#include <cryptdll.h>
#include <wincrypt.h>   // Crypto apis


LONG NlGlobalSessionCounter = 0;


NTSTATUS
NlMakeSessionKey(
    IN ULONG NegotiatedFlags,
    IN PNT_OWF_PASSWORD CryptKey,
    IN PNETLOGON_CREDENTIAL ClientChallenge,
    IN PNETLOGON_CREDENTIAL ServerChallenge,
    OUT PNETLOGON_SESSION_KEY SessionKey
    )
/*++

Routine Description:

      Build an encryption key for use in authentication for
      this RequestorName.

Arguments:

      NegotiatedFlags - Determines the strength of the key.

      CryptKey -- The OWF password of the user account being used.

      ClientChallenge --  8 byte (64 bit) number generated by caller

      ServerChallenge -- 8 byte (64 bit) number generated by primary

      SessionKey --  16 byte (128 bit) number generated at both ends
        If the key strength is weak, the last 64 bits will be zero.

Return Value:

    TRUE: Success
    FALSE: Failure

    NT status code.

--*/
{
    NTSTATUS Status;
    BLOCK_KEY BlockKey;
    NETLOGON_SESSION_KEY TempSessionKey;

#ifndef NETSETUP_JOIN
    PCHECKSUM_BUFFER CheckBuffer = NULL;
    PCHECKSUM_FUNCTION Check;
#endif // NETSETUP_JOIN

    //
    // Start with a zero key
    //
    RtlZeroMemory(SessionKey, sizeof(NETLOGON_SESSION_KEY));

#ifdef NETSETUP_JOIN
    UNREFERENCED_PARAMETER( NegotiatedFlags );
#else // NETSETUP_JOIN
    //
    // If the caller wants a strong key,
    //  Compute it.
    //
    if ( NegotiatedFlags & NETLOGON_SUPPORTS_STRONG_KEY ) {

        // PCRYPTO_SYSTEM CryptSystem;

        UCHAR LocalChecksum[sizeof(*SessionKey)];
        // ULONG OutputSize;

        //
        // Initialize the checksum routines.
        //

        Status = CDLocateCheckSum( KERB_CHECKSUM_MD5_HMAC, &Check);
        if (!NT_SUCCESS(Status)) {
            NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to load checksum routines: 0x%x\n", Status));
            goto Cleanup;
        }

        NlAssert(Check->CheckSumSize <= sizeof(LocalChecksum));

        Status = Check->InitializeEx(
                    (LPBYTE)CryptKey,
                    sizeof( *CryptKey ),
                    0,              // no message type
                    &CheckBuffer );

        if (!NT_SUCCESS(Status)) {
            NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to initialize checksum routines: 0x%x\n", Status));
            goto Cleanup;
        }


        //
        // Sum in the client challenge, a constant, and the server challenge
        //

        Check->Sum( CheckBuffer,
                    sizeof(*ClientChallenge),
                    (PUCHAR)ClientChallenge );

        Check->Sum( CheckBuffer,
                    sizeof(*ServerChallenge),
                    (PUCHAR)ServerChallenge );

        //
        // Finish the checksum
        //

        (void) Check->Finalize(CheckBuffer, LocalChecksum);


        //
        // Copy the checksum into the message.
        //

        NlAssert( sizeof(LocalChecksum) >= sizeof(*SessionKey) );
        RtlCopyMemory( SessionKey, LocalChecksum, sizeof(*SessionKey) );


    //
    // Compute weaker (but backward compatible key)
    //
    } else {
#endif // NETSETUP_JOIN

        //
        // we will have a 128 bit key (64 bit encrypted rest padded with 0s)
        //
        // SessionKey = C + P  (arithmetic sum ignore carry)
        //

        *((unsigned long * ) SessionKey) =
            *((unsigned long * ) ClientChallenge) +
            *((unsigned long * ) ServerChallenge);

        *((unsigned long * )((LPBYTE)SessionKey + 4)) =
            *((unsigned long * )((LPBYTE)ClientChallenge + 4)) +
            *((unsigned long * )((LPBYTE)ServerChallenge + 4));


        //
        // CryptKey is our 16 byte key to be used as described in codespec
        // use first 7 bytes of CryptKey for first encryption
        //

        RtlCopyMemory( &BlockKey, CryptKey, BLOCK_KEY_LENGTH );

        Status = RtlEncryptBlock(
                    (PCLEAR_BLOCK) SessionKey,   // Clear text
                    &BlockKey,                  // Key
                    (PCYPHER_BLOCK) &TempSessionKey);    // Cypher Block

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


        //
        // Further encrypt the encrypted "SessionKey" using upper 7 bytes
        //

        NlAssert( LM_OWF_PASSWORD_LENGTH == 2*BLOCK_KEY_LENGTH+2 );

        RtlCopyMemory( &BlockKey,
                       ((PUCHAR)CryptKey) + 2 + BLOCK_KEY_LENGTH,
                       BLOCK_KEY_LENGTH );

        Status = RtlEncryptBlock(
                    (PCLEAR_BLOCK) &TempSessionKey,   // Clear text
                    &BlockKey,                  // Key
                    (PCYPHER_BLOCK) SessionKey);    // Cypher Block

        if ( !NT_SUCCESS( Status ) ) {
            goto Cleanup;
        }
#ifndef NETSETUP_JOIN
    }
#endif // NETSETUP_JOIN

Cleanup:
#ifndef NETSETUP_JOIN
    if (CheckBuffer != NULL) {
        Status = Check->Finish(&CheckBuffer);

        if (!NT_SUCCESS(Status)) {
            NlPrint(( NL_CRITICAL,"NlMakeSessionKey: Failed to finish checksum: 0x%x\n", Status));
        }
    }
#endif // NETSETUP_JOIN

    return Status;
}

#ifdef _DC_NETLOGON

NTSTATUS
NlCheckAuthenticator(
    IN OUT PSERVER_SESSION ServerSession,
    IN PNETLOGON_AUTHENTICATOR Authenticator,
    OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator
    )
/*++

Routine Description:

    Verify that supplied Authenticator is valid.
    It is intended for use by the server side after initial authentication
    has succeeded. This routine will modify the seed by
    first adding the time-of-day received from the Authenticator
    and then by incrementing it.

    A ReturnAuthenticator is built based on the final seed.

Arguments:

    ServerSession - Pointer to the ServerSession structure.  The following
        fields are used:

        SsAuthenticationSeed - Supplies the seed used for authentication and
            returns the updated seed.

        SsSessionKey - The session key used for encryption.

        SsCheck - Is zeroed to indicate successful communication with the client.

    Authenticator - The authenticator passed by the caller.

    ReturnAuthenticator - The authenticator we'll return to the caller.

Return Value:

    STATUS_SUCCESS;
    STATUS_ACCESS_DENIED;
    STATUS_TIME_DIFFERENCE_AT_DC;

--*/
{

    NETLOGON_CREDENTIAL TargetCredential;



    NlPrint((NL_CHALLENGE_RES,"NlCheckAuthenticator: Seed = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &ServerSession->SsAuthenticationSeed, sizeof(ServerSession->SsAuthenticationSeed) );

    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: SessionKey = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &ServerSession->SsSessionKey, sizeof(ServerSession->SsSessionKey) );

    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: Client Authenticator GOT = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &Authenticator->Credential, sizeof(Authenticator->Credential) );

    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: Time = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &Authenticator->timestamp, sizeof(Authenticator->timestamp) );



    //
    // modify the seed before computing auth_credential for verification
    // Two long words are added and overflow carry (if any) ignored
    // This will leave upper 4 bytes unchanged
    //

    *((unsigned long * ) &ServerSession->SsAuthenticationSeed) += Authenticator->timestamp;


    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: Seed + TIME = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &ServerSession->SsAuthenticationSeed, sizeof(ServerSession->SsAuthenticationSeed) );


    //
    // Compute TargetCredential to verify the one supplied in the Authenticator
    //

    NlComputeCredentials( &ServerSession->SsAuthenticationSeed,
                          &TargetCredential,
                          &ServerSession->SsSessionKey );


    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: Client Authenticator MADE = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &TargetCredential, sizeof(TargetCredential) );

    //
    // verify the computed credentials with those supplied
    // Authenticator must have used seed + time_of_day as seed
    //

    if (!RtlEqualMemory( &Authenticator->Credential,
                         &TargetCredential,
                         sizeof(TargetCredential)) ) {
        return STATUS_ACCESS_DENIED;
    }

    //
    // modify our seed before computing the ReturnAuthenticator.
    // The requestor will increment his seed if he matches this credentials.
    //

    (*((unsigned long * ) &ServerSession->SsAuthenticationSeed))++;

    //
    // compute ClientCredential to send back to requestor
    //

    NlComputeCredentials( &ServerSession->SsAuthenticationSeed,
                          &ReturnAuthenticator->Credential,
                          &ServerSession->SsSessionKey);


    NlPrint((NL_CHALLENGE_RES,
            "NlCheckAuthenticator: Server Authenticator SEND = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &ReturnAuthenticator->Credential, sizeof(ReturnAuthenticator->Credential) );


    NlPrint((NL_CHALLENGE_RES, "NlCheckAuthenticator: Seed + time + 1= " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &ServerSession->SsAuthenticationSeed, sizeof(ServerSession->SsAuthenticationSeed) );


    //
    // Indicate successful communication with the client
    //

    ServerSession->SsCheck = 0;
    ServerSession->SsPulseTimeoutCount = 0;
    ServerSession->SsFlags &= ~SS_PULSE_SENT;

    return STATUS_SUCCESS;

}
#endif // _DC_NETLOGON


VOID
NlComputeCredentials(
    IN PNETLOGON_CREDENTIAL Challenge,
    OUT PNETLOGON_CREDENTIAL Credential,
    IN PNETLOGON_SESSION_KEY SessionKey
    )
/*++

Routine Description:

    Calculate the credentials by encrypting the 8 byte
    challenge with first 7 bytes of sessionkey and then
    further encrypting it by next 7 bytes of sessionkey.

Arguments:

    Challenge  - Supplies the 8 byte (64 bit) challenge

    Credential - Returns the 8  byte (64 bit) number generated

    SessionKey - Supplies 14 byte (112 bit) encryption key
        The buffer is 16 bytes (128 bits) long.  For a weak key, the trailing 8 bytes
        are zero.  For a strong key, this routine ingored that trailing 2 bytes of
        useful key.

Return Value:

    NONE

--*/
{
    NTSTATUS Status;
    BLOCK_KEY BlockKey;
    CYPHER_BLOCK IntermediateBlock;

    RtlZeroMemory(Credential, sizeof(*Credential));

    //
    // use first 7 bytes of SessionKey for first encryption
    //

    RtlCopyMemory( &BlockKey, SessionKey, BLOCK_KEY_LENGTH );

    Status = RtlEncryptBlock( (PCLEAR_BLOCK) Challenge, // Cleartext
                              &BlockKey,                // Key
                              &IntermediateBlock );     // Cypher Block

    NlAssert( NT_SUCCESS(Status) );

    //
    // further encrypt the encrypted Credential using next 7 bytes
    //

    RtlCopyMemory( &BlockKey,
                   ((PUCHAR)SessionKey) + BLOCK_KEY_LENGTH,
                   BLOCK_KEY_LENGTH );

    Status = RtlEncryptBlock( (PCLEAR_BLOCK) &IntermediateBlock, // Cleartext
                              &BlockKey,                // Key
                              Credential );             // Cypher Block

    NlAssert( NT_SUCCESS(Status) );

    return;

}



VOID
NlComputeChallenge(
    OUT PNETLOGON_CREDENTIAL Challenge
    )

/*++

Routine Description:

    Generates a 64 bit challenge

Arguments:

    Challenge - Returns the computed challenge

Return Value:

    None.

--*/
{

    //
    // Use an ideal random bit generator.
    //

    if (!NlGenerateRandomBits( (LPBYTE)Challenge, sizeof(*Challenge) )) {
        NlPrint((NL_CRITICAL, "Can't NlGenerateRandomBits\n" ));
    }

    return;
}



VOID
NlBuildAuthenticator(
    IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
    IN PNETLOGON_SESSION_KEY SessionKey,
    OUT PNETLOGON_AUTHENTICATOR Authenticator
    )
/*++

Routine Description:

    Build the authenticator to be sent to primary.
    This routine will modify the seed by adding the
    time-of-day before computing the credentials.

Arguments:

    AuthenticationSeed -- The current authentication seed.  This seed will
        have the current time of day added to it prior to building the
        Authenticator.

    SessionKey - The Session Key used for encrypting the Authenticator.

    Authenticator - The Authenticator to pass to the PDC for the current
        call.

Return Value:

    NT Status code

--*/
{
    NTSTATUS Status;
    LARGE_INTEGER TimeNow;

    //
    // Use the current time of day to modify the authentication seed
    //

    RtlZeroMemory(Authenticator, sizeof(*Authenticator));

    NlQuerySystemTime( &TimeNow );

    Status = RtlTimeToSecondsSince1970( &TimeNow, &Authenticator->timestamp );
    NlAssert( NT_SUCCESS(Status) );

    //
    // Modify the AuthenticationSeed before computing auth_credential for
    // verification .
    //
    // Two long words are added and overflow carry (if any) ignored
    // This will leave upper 4 bytes unchanged
    //


    NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Old Seed = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, AuthenticationSeed, sizeof(*AuthenticationSeed) );

    NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Time = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &Authenticator->timestamp, sizeof(Authenticator->timestamp) );



    *((unsigned long * ) AuthenticationSeed) += Authenticator->timestamp;


    NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: New Seed = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, AuthenticationSeed, sizeof(*AuthenticationSeed) );


    NlPrint((NL_CHALLENGE_RES, "NlBuildAuthenticator: SessionKey = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, SessionKey, sizeof(*SessionKey) );


    //
    // compute AuthenticationSeed to verify the one supplied by Requestor
    //

    NlComputeCredentials( AuthenticationSeed,
                               &Authenticator->Credential,
                               SessionKey);


    NlPrint((NL_CHALLENGE_RES,"NlBuildAuthenticator: Client Authenticator = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &Authenticator->Credential, sizeof(Authenticator->Credential) );


    return;

}


BOOL
NlUpdateSeed(
    IN OUT PNETLOGON_CREDENTIAL AuthenticationSeed,
    IN PNETLOGON_CREDENTIAL TargetCredential,
    IN PNETLOGON_SESSION_KEY SessionKey
    )
/*++

Routine Description:

    Called by the initiator of a communication over the secure channel
    following a successful transaction.

    The PDC would have incremented the seed so we must do so also.

    We also verify that the incremented seed builds a credential identical
    to the one passed back by the PDC.

Arguments:

    AuthenticationSeed - Pointer to the AuthenticationSeed to be incremented.

    TargetCredential - Supplies the Credential that the incremented
        AuthenticationSeed should encrypt to.

    SessionKey - Supplies the encryption key to use for the encryption.

Return Value:

    TRUE: Success
    FALSE: Failure

--*/
{
    NETLOGON_CREDENTIAL NewCredential;

    //
    //  modify our AuthenticationSeed before computing NewCredential to check
    //  those returned from primary (NewSeed = AuthenticationSeed+1)
    //

    (*((unsigned long * ) AuthenticationSeed))++;


    NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Seed + time + 1= " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, AuthenticationSeed, sizeof(*AuthenticationSeed) );


    //
    // Compute ClientCredential to check which came from primary
    //

    NlComputeCredentials(AuthenticationSeed, &NewCredential, SessionKey);


    NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Server Authenticator GOT  = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, TargetCredential, sizeof(*TargetCredential) );


    NlPrint((NL_CHALLENGE_RES,"NlUpdateSeed: Server Authenticator MADE = " ));
    NlpDumpBuffer(NL_CHALLENGE_RES, &NewCredential, sizeof(NewCredential) );


    if ( !RtlEqualMemory( TargetCredential, &NewCredential, sizeof(NewCredential)) ) {
        return FALSE;
    }

    //
    // Done
    //

    return TRUE;

}


VOID
NlEncryptRC4(
    IN OUT PVOID Buffer,
    IN ULONG BufferSize,
    IN PSESSION_INFO SessionInfo
    )
/*++

Routine Description:

    Encrypt data using RC4 with the session key as the key.

Arguments:

    Buffer -- Buffer containing the data to encrypt in place.

    BufferSize -- Size (in bytes) of Buffer.

    SessionInfo -- Info describing secure channel

Return Value:

    NT status code

--*/
{
    NTSTATUS NtStatus;
    DATA_KEY KeyData;
    CRYPT_BUFFER Data;

    //
    // Build a data buffer to describe the encryption key.
    //

    KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
    KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
    KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;

    NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );

    //
    // Build a data buffer to decribe the encrypted data.
    //

    Data.Length = Data.MaximumLength = BufferSize;
    Data.Buffer = Buffer;

    //
    // Encrypt the data.
    //

    IF_NL_DEBUG( ENCRYPT ) {
        NlPrint((NL_ENCRYPT, "NlEncryptRC4: Clear data: " ));
        NlpDumpBuffer( NL_ENCRYPT, Data.Buffer, Data.Length );
    }

    NtStatus = RtlEncryptData2( &Data, &KeyData );
    NlAssert( NT_SUCCESS(NtStatus) );

    IF_NL_DEBUG( ENCRYPT ) {
        NlPrint((NL_ENCRYPT, "NlEncryptRC4: Encrypted data: " ));
        NlpDumpBuffer( NL_ENCRYPT, Data.Buffer, Data.Length );
    }

}


VOID
NlDecryptRC4(
    IN OUT PVOID Buffer,
    IN ULONG BufferSize,
    IN PSESSION_INFO SessionInfo
    )
/*++

Routine Description:

    Decrypt data using RC4 with the session key as the key.

Arguments:

    Buffer -- Buffer containing the data to decrypt in place.

    BufferSize -- Size (in bytes) of Buffer.

    SessionInfo -- Info describing secure channel

Return Value:

    NT status code

--*/
{
    NTSTATUS NtStatus;
    DATA_KEY KeyData;
    CRYPT_BUFFER Data;

    //
    // Build a data buffer to describe the encryption key.
    //

    KeyData.Length = sizeof(NETLOGON_SESSION_KEY);
    KeyData.MaximumLength = sizeof(NETLOGON_SESSION_KEY);
    KeyData.Buffer = (PVOID)&SessionInfo->SessionKey;

    NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );

    //
    // Build a data buffer to decribe the encrypted data.
    //

    Data.Length = Data.MaximumLength = BufferSize;
    Data.Buffer = Buffer;

    //
    // Encrypt the data.
    //


    IF_NL_DEBUG( ENCRYPT ) {
        NlPrint((NL_ENCRYPT, "NlDecryptRC4: Encrypted data: " ));
        NlpDumpBuffer( NL_ENCRYPT, Data.Buffer, Data.Length );
    }

    NtStatus = RtlDecryptData2( &Data, &KeyData );
    NlAssert( NT_SUCCESS(NtStatus) );

    IF_NL_DEBUG( ENCRYPT ) {
        NlPrint((NL_ENCRYPT, "NlDecryptRC4: Clear data: " ));
        NlpDumpBuffer( NL_ENCRYPT, Data.Buffer, Data.Length );
    }

}


BOOLEAN
NlGenerateRandomBits(
    PUCHAR Buffer,
    ULONG  BufferLen
    )
/*++

Routine Description:

    Generates random bits

Arguments:

    pBuffer - Buffer to fill

    cbBuffer - Number of bytes in buffer

Return Value:

    Status of the operation.

--*/

{
    if( !CryptGenRandom( NlGlobalCryptProvider, BufferLen, ( LPBYTE )Buffer ) )
    {
        NlPrint((NL_CRITICAL, "CryptGenRandom failed with %lu\n", GetLastError() ));
        return FALSE;
    }

    return TRUE;
}



#ifndef NETSETUP_JOIN

VOID
NlPrintTrustedDomain(
    PDS_DOMAIN_TRUSTSW TrustedDomain,
    IN BOOLEAN VerbosePrint,
    IN BOOLEAN AnsiOutput
    )
/*++

Routine Description:

    Print a trusted domain structure

Arguments:

    TrustedDomain -- Structure to print

    VerbosePrint - If TRUE, output domain's GUID and SID

    AnsiOutput - If TRUE, names are in ansi format

Return Value:

    None.

--*/

{
    if ( AnsiOutput ) {
        if ( TrustedDomain->NetbiosDomainName != NULL ) {
            NlPrint(( NL_LOGON, " %s", TrustedDomain->NetbiosDomainName ));
        }
        if ( TrustedDomain->DnsDomainName != NULL ) {
            NlPrint(( NL_LOGON, " %s", TrustedDomain->DnsDomainName ));
        }
    } else {
        if ( TrustedDomain->NetbiosDomainName != NULL ) {
            NlPrint(( NL_LOGON, " %ws", TrustedDomain->NetbiosDomainName ));
        }
        if ( TrustedDomain->DnsDomainName != NULL ) {
            NlPrint(( NL_LOGON, " %ws", TrustedDomain->DnsDomainName ));
        }
    }

    switch ( TrustedDomain->TrustType ) {
    case TRUST_TYPE_DOWNLEVEL:
        NlPrint(( NL_LOGON, " (NT 4)" ); break);
    case TRUST_TYPE_UPLEVEL:
        NlPrint(( NL_LOGON, " (NT 5)" ); break);
    case TRUST_TYPE_MIT:
        NlPrint(( NL_LOGON, " (MIT)" ); break);
    case TRUST_TYPE_DCE:
        NlPrint(( NL_LOGON, " (DCE)" ); break);
    default:
        NlPrint(( NL_LOGON, " (Unknown Trust Type: %ld)", TrustedDomain->TrustType ); break);
    }

    if ( TrustedDomain->Flags ) {
        ULONG Flags;
        Flags = TrustedDomain->Flags;
        if ( Flags & DS_DOMAIN_IN_FOREST ) {
            if ( Flags & DS_DOMAIN_TREE_ROOT ) {
                NlPrint(( NL_LOGON, " (Forest Tree Root)" ));
                Flags &= ~DS_DOMAIN_TREE_ROOT;
            } else {
                NlPrint(( NL_LOGON, " (Forest: %ld)", TrustedDomain->ParentIndex ));
            }
            Flags &= ~DS_DOMAIN_IN_FOREST;
        }
        if ( Flags & DS_DOMAIN_DIRECT_OUTBOUND ) {
            NlPrint(( NL_LOGON, " (Direct Outbound)"));
            Flags &= ~DS_DOMAIN_DIRECT_OUTBOUND;
        }
        if ( Flags & DS_DOMAIN_DIRECT_INBOUND ) {
            NlPrint(( NL_LOGON, " (Direct Inbound)"));
            Flags &= ~DS_DOMAIN_DIRECT_INBOUND;
        }
        if ( Flags & DS_DOMAIN_TREE_ROOT ) {
            NlPrint(( NL_LOGON, " (Tree Root but not in forest!!!!)"));
            Flags &= ~DS_DOMAIN_TREE_ROOT;
        }
        if ( Flags & DS_DOMAIN_PRIMARY ) {
            NlPrint(( NL_LOGON, " (Primary Domain)"));
            Flags &= ~DS_DOMAIN_PRIMARY;
        }
        if ( Flags & DS_DOMAIN_NATIVE_MODE ) {
            NlPrint(( NL_LOGON, " (Native)"));
            Flags &= ~DS_DOMAIN_NATIVE_MODE;
        }
        if ( Flags != 0 ) {
            NlPrint(( NL_LOGON, " 0x%lX", Flags));
        }
    }

    if ( TrustedDomain->TrustAttributes ) {
        ULONG TrustAttributes = TrustedDomain->TrustAttributes;
        NlPrint(( NL_LOGON, " ( Attr:" ));
        if ( TrustAttributes & TRUST_ATTRIBUTE_NON_TRANSITIVE ) {
            NlPrint(( NL_LOGON, " non-trans"));
            TrustAttributes &= ~TRUST_ATTRIBUTE_NON_TRANSITIVE;
        }
        if ( TrustAttributes & TRUST_ATTRIBUTE_UPLEVEL_ONLY ) {
            NlPrint(( NL_LOGON, " uplevel-only"));
            TrustAttributes &= ~TRUST_ATTRIBUTE_UPLEVEL_ONLY;
        }
        if ( TrustAttributes & TRUST_ATTRIBUTE_FILTER_SIDS ) {
            NlPrint(( NL_LOGON, " filtered"));
            TrustAttributes &= ~TRUST_ATTRIBUTE_FILTER_SIDS;
        }
        if ( TrustAttributes != 0 ) {
            NlPrint(( NL_LOGON, " 0x%lX", TrustAttributes));
        }
        NlPrint(( NL_LOGON, " )"));
    }

    //
    // Output domain's GUID and SID
    //

    if ( VerbosePrint ) {
        if ( !IsEqualGUID( &TrustedDomain->DomainGuid, &NlGlobalZeroGuid) ) {
            RPC_STATUS RpcStatus;
            char *StringGuid;

            NlPrint(( NL_LOGON, "\n" ));
            NlPrint(( NL_LOGON, "       Dom Guid: " ));
            RpcStatus = UuidToStringA( &TrustedDomain->DomainGuid, &StringGuid );
            if ( RpcStatus == RPC_S_OK ) {
                NlPrint(( NL_LOGON, "%s", StringGuid ));
                RpcStringFreeA( &StringGuid );
            } else {
                NlPrint(( NL_LOGON, "Not available because UuidToStringA failed" ));
            }
        }

        NlPrint(( NL_LOGON, "\n" ));
        if ( TrustedDomain->DomainSid != NULL ) {
            NlPrint(( NL_LOGON, "       Dom Sid: " ));
            NlpDumpSid( NL_LOGON, TrustedDomain->DomainSid );
        }
    } else {
        NlPrint(( NL_LOGON, "\n" ));
    }
}
#endif // NETSETUP_JOIN
