You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
956 lines
25 KiB
956 lines
25 KiB
/*++
|
|
|
|
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);
|
|
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_QUARANTINED_DOMAIN ) {
|
|
NlPrint(( NL_LOGON, " quarantined"));
|
|
TrustAttributes &= ~TRUST_ATTRIBUTE_QUARANTINED_DOMAIN;
|
|
}
|
|
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
|