5363 lines
163 KiB
C
5363 lines
163 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1987-1996 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
lsrvutil.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Utility functions for the netlogon service.
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
00-Jun-1989 (PradyM)
|
|||
|
modified lm10 code for new NETLOGON service
|
|||
|
|
|||
|
00-Feb-1990 (PradyM)
|
|||
|
bugfixes
|
|||
|
|
|||
|
00-Aug-1990 (t-RichE)
|
|||
|
added alerts for auth failure due to time slippage
|
|||
|
|
|||
|
11-Jul-1991 (cliffv)
|
|||
|
Ported to NT. Converted to NT style.
|
|||
|
|
|||
|
02-Jan-1992 (madana)
|
|||
|
added support for builtin/multidomain replication.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//
|
|||
|
// Common include files.
|
|||
|
//
|
|||
|
|
|||
|
#include "logonsrv.h" // Include files common to entire service
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
//
|
|||
|
// Include files specific to this .c file
|
|||
|
//
|
|||
|
|
|||
|
#include <accessp.h> // NetpAliasMemberToPriv
|
|||
|
#include <msgtext.h> // MTXT_* defines
|
|||
|
#include <netcan.h> // NetpwPathCompare()
|
|||
|
#include <ssiapi.h> // I_NetSamDeltas()
|
|||
|
|
|||
|
/*lint -e740 */ /* don't complain about unusual cast */
|
|||
|
|
|||
|
|
|||
|
#define MAX_DC_AUTHENTICATION_WAIT (long) (45L*1000L) // 45 seconds
|
|||
|
|
|||
|
//
|
|||
|
// We want to prevent too-frequent alerts from
|
|||
|
// being sent in case of Authentication failures.
|
|||
|
//
|
|||
|
|
|||
|
#define MAX_ALERTS 10 // send one every 10 to 30 mins based on pulse
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
RaiseNetlogonAlert(
|
|||
|
IN DWORD alertNum,
|
|||
|
IN LPWSTR *AlertStrings,
|
|||
|
IN OUT DWORD *ptrAlertCount
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Raise an alert once per MAX_ALERTS occurances
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
alertNum -- RaiseAlert() alert number.
|
|||
|
|
|||
|
AlertStrings -- RaiseAlert() arguments
|
|||
|
|
|||
|
ptrAlertCount -- Points to the count of occurence of this particular
|
|||
|
alert. This routine increments it and will set it to that value
|
|||
|
modulo MAX_ALERTS.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (*ptrAlertCount == 0) {
|
|||
|
NetpRaiseAlert( SERVICE_NETLOGON, alertNum, AlertStrings);
|
|||
|
}
|
|||
|
(*ptrAlertCount)++;
|
|||
|
(*ptrAlertCount) %= MAX_ALERTS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlOpenSecret(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
IN ULONG DesiredAccess,
|
|||
|
OUT PLSAPR_HANDLE SecretHandle
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Open the Lsa Secret Object containing the password to be used for the
|
|||
|
specified client session.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
On Input, the following fields must be set:
|
|||
|
CsNetbiosDomainName
|
|||
|
CsSecureChannelType
|
|||
|
|
|||
|
DesiredAccess - Access required to the secret.
|
|||
|
|
|||
|
SecretHandle - Returns a handle to the secret.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
UNICODE_STRING SecretNameString;
|
|||
|
|
|||
|
NlAssert( ClientSession->CsReferenceCount > 0 );
|
|||
|
|
|||
|
//
|
|||
|
// Only use secrets for workstation and BDC machine accounts.
|
|||
|
//
|
|||
|
|
|||
|
switch ( ClientSession->CsSecureChannelType ) {
|
|||
|
case ServerSecureChannel:
|
|||
|
case WorkstationSecureChannel:
|
|||
|
RtlInitUnicodeString( &SecretNameString, SSI_SECRET_NAME );
|
|||
|
break;
|
|||
|
|
|||
|
case TrustedDomainSecureChannel:
|
|||
|
case TrustedDnsDomainSecureChannel:
|
|||
|
default:
|
|||
|
Status = STATUS_INTERNAL_ERROR;
|
|||
|
NlPrint((NL_CRITICAL, "NlOpenSecret: Invalid account type\n"));
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the Password of the account from LSA secret storage
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
Status = LsarOpenSecret(
|
|||
|
ClientSession->CsDomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING)&SecretNameString,
|
|||
|
DesiredAccess,
|
|||
|
SecretHandle );
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlGetOutgoingPassword(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
OUT PUNICODE_STRING *CurrentValue,
|
|||
|
OUT PUNICODE_STRING *OldValue,
|
|||
|
OUT PDWORD CurrentVersionNumber,
|
|||
|
OUT PLARGE_INTEGER LastSetTime OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the outgoing password to be used for the specified client session.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
On Input, the following fields must be set:
|
|||
|
CsNetbiosDomainName
|
|||
|
CsSecureChannelType
|
|||
|
|
|||
|
CurrentValue - Current password for the client session.
|
|||
|
CurrentValue should be freed using LocalFree
|
|||
|
A NULL pointer is returned if there is no current password.
|
|||
|
|
|||
|
OldValue - Previous password for the client session.
|
|||
|
OldValue should be freed using LocalFree
|
|||
|
A NULL pointer is returned if there is no old password.
|
|||
|
|
|||
|
CurrentVersionNumber - Version number of the current password
|
|||
|
for interdomain trust account. Set to 0 on failure status
|
|||
|
or if this is not interdomain trust account.
|
|||
|
|
|||
|
LastSetTime - Time when the password was last changed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
STATUS_NO_TRUST_LSA_SECRET: Secret object is not accessable
|
|||
|
STATUS_NO_MEMORY: Not enough memory to allocate password buffers
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
LSAPR_HANDLE SecretHandle = NULL;
|
|||
|
|
|||
|
PLSAPR_CR_CIPHER_VALUE CrCurrentPassword = NULL;
|
|||
|
PLSAPR_CR_CIPHER_VALUE CrOldPassword = NULL;
|
|||
|
|
|||
|
PLSAPR_TRUSTED_DOMAIN_INFO TrustInfo = NULL;
|
|||
|
PLSAPR_AUTH_INFORMATION AuthInfo;
|
|||
|
PLSAPR_AUTH_INFORMATION OldAuthInfo;
|
|||
|
ULONG AuthInfoCount;
|
|||
|
ULONG i;
|
|||
|
BOOL PasswordFound = FALSE;
|
|||
|
BOOL PasswordVersionFound = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
*CurrentValue = NULL;
|
|||
|
*OldValue = NULL;
|
|||
|
*CurrentVersionNumber = 0;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Workstation and BDC secure channels get their outgoing password from
|
|||
|
// an LSA secret.
|
|||
|
//
|
|||
|
switch ( ClientSession->CsSecureChannelType ) {
|
|||
|
case ServerSecureChannel:
|
|||
|
case WorkstationSecureChannel:
|
|||
|
//
|
|||
|
// Get the Password of the account from LSA secret storage
|
|||
|
//
|
|||
|
|
|||
|
Status = NlOpenSecret( ClientSession, SECRET_QUERY_VALUE, &SecretHandle );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: cannot NlOpenSecret 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// return more appropriate error.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
Status = LsarQuerySecret(
|
|||
|
SecretHandle,
|
|||
|
&CrCurrentPassword,
|
|||
|
LastSetTime,
|
|||
|
&CrOldPassword,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: cannot LsaQuerySecret 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// return more appropriate error.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the current password back to the caller.
|
|||
|
//
|
|||
|
if ( CrCurrentPassword != NULL ) {
|
|||
|
*CurrentValue = LocalAlloc(0, sizeof(UNICODE_STRING)+CrCurrentPassword->Length+sizeof(WCHAR) );
|
|||
|
|
|||
|
if ( *CurrentValue == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
(*CurrentValue)->Buffer = (LPWSTR)(((LPBYTE)(*CurrentValue))+sizeof(UNICODE_STRING));
|
|||
|
RtlCopyMemory( (*CurrentValue)->Buffer, CrCurrentPassword->Buffer, CrCurrentPassword->Length );
|
|||
|
(*CurrentValue)->Length = (USHORT)CrCurrentPassword->Length;
|
|||
|
(*CurrentValue)->MaximumLength = (USHORT)((*CurrentValue)->Length + sizeof(WCHAR));
|
|||
|
(*CurrentValue)->Buffer[(*CurrentValue)->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the Old password back to the caller.
|
|||
|
//
|
|||
|
if ( CrOldPassword != NULL ) {
|
|||
|
*OldValue = LocalAlloc(0, sizeof(UNICODE_STRING)+CrOldPassword->Length+sizeof(WCHAR) );
|
|||
|
|
|||
|
if ( *OldValue == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
(*OldValue)->Buffer = (LPWSTR)(((LPBYTE)(*OldValue))+sizeof(UNICODE_STRING));
|
|||
|
RtlCopyMemory( (*OldValue)->Buffer, CrOldPassword->Buffer, CrOldPassword->Length );
|
|||
|
(*OldValue)->Length = (USHORT)CrOldPassword->Length;
|
|||
|
(*OldValue)->MaximumLength = (USHORT)((*OldValue)->Length + sizeof(WCHAR));
|
|||
|
(*OldValue)->Buffer[(*OldValue)->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Trusted domain secure channels get their outgoing password from the trusted
|
|||
|
// domain object.
|
|||
|
//
|
|||
|
|
|||
|
case TrustedDomainSecureChannel:
|
|||
|
case TrustedDnsDomainSecureChannel:
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the authentication information from the LSA.
|
|||
|
//
|
|||
|
Status = LsarQueryTrustedDomainInfoByName(
|
|||
|
ClientSession->CsDomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING) ClientSession->CsTrustName,
|
|||
|
TrustedDomainAuthInformation,
|
|||
|
&TrustInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: %wZ: cannot LsarQueryTrustedDomainInfoByName 0x%lx\n",
|
|||
|
ClientSession->CsTrustName,
|
|||
|
Status ));
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
AuthInfoCount = TrustInfo->TrustedAuthInfo.OutgoingAuthInfos;
|
|||
|
AuthInfo = TrustInfo->TrustedAuthInfo.OutgoingAuthenticationInformation;
|
|||
|
OldAuthInfo = TrustInfo->TrustedAuthInfo.OutgoingPreviousAuthenticationInformation;
|
|||
|
|
|||
|
if (AuthInfoCount == 0 || AuthInfo == NULL) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: %wZ: No auth info for this domain.\n",
|
|||
|
ClientSession->CsTrustName ));
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
NlAssert( OldAuthInfo != NULL );
|
|||
|
|
|||
|
//
|
|||
|
// Loop through the various auth infos looking for the cleartext password
|
|||
|
// and its version number.
|
|||
|
//
|
|||
|
|
|||
|
for ( i=0; i<AuthInfoCount; i++ ) {
|
|||
|
|
|||
|
//
|
|||
|
// Handle the cleartext password
|
|||
|
//
|
|||
|
|
|||
|
if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR && !PasswordFound ) {
|
|||
|
|
|||
|
//
|
|||
|
// Copy the current password back to the caller.
|
|||
|
//
|
|||
|
*CurrentValue = LocalAlloc(0, sizeof(UNICODE_STRING)+AuthInfo[i].AuthInfoLength+sizeof(WCHAR) );
|
|||
|
|
|||
|
if ( *CurrentValue == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
(*CurrentValue)->Buffer = (LPWSTR)(((LPBYTE)(*CurrentValue))+sizeof(UNICODE_STRING));
|
|||
|
RtlCopyMemory( (*CurrentValue)->Buffer, AuthInfo[i].AuthInfo, AuthInfo[i].AuthInfoLength );
|
|||
|
(*CurrentValue)->Length = (USHORT)AuthInfo[i].AuthInfoLength;
|
|||
|
(*CurrentValue)->MaximumLength = (USHORT)((*CurrentValue)->Length + sizeof(WCHAR));
|
|||
|
(*CurrentValue)->Buffer[(*CurrentValue)->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
|
|||
|
//
|
|||
|
// Copy the password change time back to the caller.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( LastSetTime )) {
|
|||
|
*LastSetTime = AuthInfo[i].LastUpdateTime;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Only copy the old password if it is also clear.
|
|||
|
//
|
|||
|
|
|||
|
if ( OldAuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR ) {
|
|||
|
|
|||
|
//
|
|||
|
// Copy the Old password back to the caller.
|
|||
|
//
|
|||
|
*OldValue = LocalAlloc(0, sizeof(UNICODE_STRING)+OldAuthInfo[i].AuthInfoLength+sizeof(WCHAR) );
|
|||
|
|
|||
|
if ( *OldValue == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
(*OldValue)->Buffer = (LPWSTR)(((LPBYTE)(*OldValue))+sizeof(UNICODE_STRING));
|
|||
|
RtlCopyMemory( (*OldValue)->Buffer, OldAuthInfo[i].AuthInfo, OldAuthInfo[i].AuthInfoLength );
|
|||
|
(*OldValue)->Length = (USHORT)OldAuthInfo[i].AuthInfoLength;
|
|||
|
(*OldValue)->MaximumLength = (USHORT)((*OldValue)->Length + sizeof(WCHAR));
|
|||
|
(*OldValue)->Buffer[(*OldValue)->Length/sizeof(WCHAR)] = L'\0';
|
|||
|
}
|
|||
|
|
|||
|
PasswordFound = TRUE;
|
|||
|
if ( PasswordVersionFound ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle the version number of the cleartext password
|
|||
|
//
|
|||
|
|
|||
|
} else if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_VERSION && !PasswordVersionFound &&
|
|||
|
AuthInfo[i].AuthInfoLength == sizeof(*CurrentVersionNumber) ) {
|
|||
|
RtlCopyMemory( CurrentVersionNumber, AuthInfo[i].AuthInfo, AuthInfo[i].AuthInfoLength );
|
|||
|
|
|||
|
PasswordVersionFound = TRUE;
|
|||
|
if ( PasswordFound ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//if ( i == AuthInfoCount ) {
|
|||
|
if ( !PasswordFound ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: %wZ: No clear password for this domain.\n",
|
|||
|
ClientSession->CsTrustName ));
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlGetOutgoingPassword: invalid secure channel type\n" ));
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
if ( *CurrentValue != NULL ) {
|
|||
|
LocalFree( *CurrentValue );
|
|||
|
*CurrentValue = NULL;
|
|||
|
}
|
|||
|
if ( *OldValue != NULL ) {
|
|||
|
LocalFree( *OldValue );
|
|||
|
*OldValue = NULL;
|
|||
|
}
|
|||
|
*CurrentVersionNumber = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ( SecretHandle != NULL ) {
|
|||
|
(VOID) LsarClose( &SecretHandle );
|
|||
|
}
|
|||
|
|
|||
|
if ( CrCurrentPassword != NULL ) {
|
|||
|
(VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrCurrentPassword );
|
|||
|
}
|
|||
|
|
|||
|
if ( CrOldPassword != NULL ) {
|
|||
|
(VOID) LsaIFree_LSAPR_CR_CIPHER_VALUE ( CrOldPassword );
|
|||
|
}
|
|||
|
|
|||
|
if ( TrustInfo != NULL ) {
|
|||
|
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO( TrustedDomainAuthInformation,
|
|||
|
TrustInfo );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlSetOutgoingPassword(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
IN PUNICODE_STRING CurrentValue OPTIONAL,
|
|||
|
IN PUNICODE_STRING OldValue OPTIONAL,
|
|||
|
IN DWORD CurrentVersionNumber,
|
|||
|
IN DWORD OldVersionNumber
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the outgoing password to be used for the specified client session.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
|
|||
|
CurrentValue - Current password for the client session.
|
|||
|
A NULL pointer indicates there is no current password (blank password)
|
|||
|
|
|||
|
OldValue - Previous password for the client session.
|
|||
|
A NULL pointer indicates there is no old password (blank password)
|
|||
|
|
|||
|
CurrentVersionNumber - The version number of the Current password.
|
|||
|
Ignored if this is not an interdomain trust account.
|
|||
|
|
|||
|
OldVersionNumber - The version number of the Old password.
|
|||
|
Ignored if this is not an interdomain trust account.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
LSAPR_HANDLE SecretHandle = NULL;
|
|||
|
|
|||
|
UNICODE_STRING LocalNullPassword;
|
|||
|
LSAPR_CR_CIPHER_VALUE CrCurrentPassword;
|
|||
|
LSAPR_CR_CIPHER_VALUE CrOldPassword;
|
|||
|
|
|||
|
LSAPR_TRUSTED_DOMAIN_INFO TrustInfo;
|
|||
|
LSAPR_AUTH_INFORMATION CurrentAuthInfo[2];
|
|||
|
LSAPR_AUTH_INFORMATION OldAuthInfo[2];
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
if ( CurrentValue == NULL ) {
|
|||
|
CurrentValue = &LocalNullPassword;
|
|||
|
RtlInitUnicodeString( &LocalNullPassword, NULL );
|
|||
|
}
|
|||
|
|
|||
|
if ( OldValue == NULL ) {
|
|||
|
OldValue = &LocalNullPassword;
|
|||
|
RtlInitUnicodeString( &LocalNullPassword, NULL );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Workstation and BDC secure channels get their outgoing password from
|
|||
|
// an LSA secret.
|
|||
|
//
|
|||
|
switch ( ClientSession->CsSecureChannelType ) {
|
|||
|
case ServerSecureChannel:
|
|||
|
case WorkstationSecureChannel:
|
|||
|
//
|
|||
|
// Open the LSA secret to set.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlOpenSecret( ClientSession, SECRET_SET_VALUE, &SecretHandle );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSetOutgoiningPassword: cannot NlOpenSecret 0x%lx\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the current password to LSA'ese.
|
|||
|
//
|
|||
|
|
|||
|
CrCurrentPassword.Buffer = (LPBYTE)CurrentValue->Buffer;
|
|||
|
CrCurrentPassword.Length = CurrentValue->Length;
|
|||
|
CrCurrentPassword.MaximumLength = CurrentValue->MaximumLength;
|
|||
|
|
|||
|
//
|
|||
|
// Convert the old password to LSA'ese.
|
|||
|
//
|
|||
|
|
|||
|
CrOldPassword.Buffer = (LPBYTE)OldValue->Buffer;
|
|||
|
CrOldPassword.Length = OldValue->Length;
|
|||
|
CrOldPassword.MaximumLength = OldValue->MaximumLength;
|
|||
|
|
|||
|
Status = LsarSetSecret(
|
|||
|
SecretHandle,
|
|||
|
&CrCurrentPassword,
|
|||
|
&CrOldPassword );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSetOutgoingPassword: cannot LsarSetSecret 0x%lx\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Trusted domain secure channels get their outgoing password from the trusted
|
|||
|
// domain object.
|
|||
|
//
|
|||
|
|
|||
|
case TrustedDomainSecureChannel:
|
|||
|
case TrustedDnsDomainSecureChannel:
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the trust information.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory( &TrustInfo, sizeof(TrustInfo) );
|
|||
|
|
|||
|
TrustInfo.TrustedAuthInfo.OutgoingAuthInfos = 2;
|
|||
|
TrustInfo.TrustedAuthInfo.OutgoingAuthenticationInformation =
|
|||
|
CurrentAuthInfo;
|
|||
|
TrustInfo.TrustedAuthInfo.OutgoingPreviousAuthenticationInformation =
|
|||
|
OldAuthInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the current authentication information.
|
|||
|
//
|
|||
|
|
|||
|
NlQuerySystemTime( &CurrentAuthInfo[0].LastUpdateTime );
|
|||
|
CurrentAuthInfo[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
|
|||
|
CurrentAuthInfo[0].AuthInfoLength = CurrentValue->Length;
|
|||
|
CurrentAuthInfo[0].AuthInfo = (LPBYTE)CurrentValue->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the current password version number
|
|||
|
//
|
|||
|
|
|||
|
CurrentAuthInfo[1].LastUpdateTime = CurrentAuthInfo[0].LastUpdateTime;
|
|||
|
CurrentAuthInfo[1].AuthType = TRUST_AUTH_TYPE_VERSION;
|
|||
|
CurrentAuthInfo[1].AuthInfoLength = sizeof( CurrentVersionNumber );
|
|||
|
CurrentAuthInfo[1].AuthInfo = (LPBYTE) &CurrentVersionNumber;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the old authentication information.
|
|||
|
//
|
|||
|
|
|||
|
OldAuthInfo[0].LastUpdateTime = CurrentAuthInfo[0].LastUpdateTime;
|
|||
|
OldAuthInfo[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
|
|||
|
OldAuthInfo[0].AuthInfoLength = OldValue->Length;
|
|||
|
OldAuthInfo[0].AuthInfo = (LPBYTE)OldValue->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the old password version number.
|
|||
|
//
|
|||
|
|
|||
|
OldAuthInfo[1].LastUpdateTime = CurrentAuthInfo[0].LastUpdateTime;
|
|||
|
OldAuthInfo[1].AuthType = TRUST_AUTH_TYPE_VERSION;
|
|||
|
OldAuthInfo[1].AuthInfoLength = sizeof( OldVersionNumber );
|
|||
|
OldAuthInfo[1].AuthInfo = (LPBYTE) &OldVersionNumber;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the authentication information from the LSA.
|
|||
|
//
|
|||
|
Status = LsarSetTrustedDomainInfoByName(
|
|||
|
ClientSession->CsDomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING) ClientSession->CsTrustName,
|
|||
|
TrustedDomainAuthInformation,
|
|||
|
&TrustInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSetOutgoingPassword: %wZ: cannot LsarSetTrustedDomainInfoByName 0x%lx\n",
|
|||
|
ClientSession->CsTrustName,
|
|||
|
Status ));
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Be verbose
|
|||
|
//
|
|||
|
|
|||
|
NlPrint(( NL_SESSION_SETUP, "NlSetOutgoingPassword: Current Clear Text Password is: " ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, CurrentAuthInfo[0].AuthInfo, CurrentAuthInfo[0].AuthInfoLength );
|
|||
|
NlPrint(( NL_SESSION_SETUP, "NlSetOutgoingPassword: Current Clear Password Version Number is: 0x%lx\n",
|
|||
|
CurrentVersionNumber ));
|
|||
|
NlPrint(( NL_SESSION_SETUP, "NlSetOutgoingPassword: Previous Clear Text Password is: " ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, OldAuthInfo[0].AuthInfo, OldAuthInfo[0].AuthInfoLength );
|
|||
|
NlPrint(( NL_SESSION_SETUP, "NlSetOutgoingPassword: Previous Clear Password Version Number is: 0x%lx\n",
|
|||
|
OldVersionNumber ));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSetOutgoingPassword: invalid secure channel type\n" ));
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
if ( SecretHandle != NULL ) {
|
|||
|
(VOID) LsarClose( &SecretHandle );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlGetIncomingPassword(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN LPCWSTR AccountName,
|
|||
|
IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
|
|||
|
IN ULONG AllowableAccountControlBits,
|
|||
|
IN BOOL CheckAccountDisabled,
|
|||
|
OUT PNT_OWF_PASSWORD OwfPassword OPTIONAL,
|
|||
|
OUT PNT_OWF_PASSWORD OwfPreviousPassword OPTIONAL,
|
|||
|
OUT PULONG AccountRid OPTIONAL,
|
|||
|
OUT PULONG TrustAttributes OPTIONAL,
|
|||
|
OUT PBOOL IsDnsDomainTrustAccount OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the incoming password for the specified AccountName and SecureChannelType
|
|||
|
|
|||
|
Check the machine account:
|
|||
|
Ensure the SecureChannelType is valid,
|
|||
|
Verify that the account exists,
|
|||
|
Ensure the user account is the right account type.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Emulated domain
|
|||
|
|
|||
|
AccountName - Name of the account to authenticate with.
|
|||
|
|
|||
|
SecureChannelType - The type of the account.
|
|||
|
Use NullSecureChannel if channel type is not known.
|
|||
|
|
|||
|
AllowableAccountControlBits - The type of the account.
|
|||
|
Use 0 if AccountControlBits is not known.
|
|||
|
Typically only one of AllowableAccountControlBits or SecureChannelType
|
|||
|
will be specified.
|
|||
|
|
|||
|
CheckAccountDisabled - TRUE if we should return an error if the account
|
|||
|
is disabled.
|
|||
|
|
|||
|
OwfPassword - Returns the NT OWF of the incoming password for the named
|
|||
|
account. If NULL, the password is not returned.
|
|||
|
|
|||
|
OwfPreviousPassword - Returns the NT OWF of the incoming previous password for
|
|||
|
the named interdomain trust account. If NULL, the password is not returned.
|
|||
|
If OwfPreviousPassword is not NULL, OwfPassword must not be NULL either;
|
|||
|
otherwise the function asserts. If OwfPreviousPassword is not NULL and the
|
|||
|
account is not interdomain, the function asserts. If both OwfPassword and
|
|||
|
OwfPreviousPassword are NULL, the account is only checked for validity.
|
|||
|
|
|||
|
AccountRid - Returns the RID of AccountName
|
|||
|
|
|||
|
TrustAttributes - Returns the TrustAttributes for the interdomain trust account.
|
|||
|
|
|||
|
IsDnsDomainTrustAccount - Returns TRUE if the passed in account name is the
|
|||
|
DNS domain name of an uplevel domain trust. Set only if the account control
|
|||
|
bits (either passed directly or determined from the secure channel type)
|
|||
|
correspond to an interdomain trust account.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
SAMPR_HANDLE UserHandle = NULL;
|
|||
|
PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
|
|||
|
|
|||
|
ULONG Length;
|
|||
|
PLSAPR_TRUSTED_DOMAIN_INFO TrustInfo = NULL;
|
|||
|
PLSAPR_AUTH_INFORMATION AuthInfo;
|
|||
|
ULONG AuthInfoCount;
|
|||
|
BOOL PasswordFound = FALSE;
|
|||
|
BOOL PreviousPasswordFound = FALSE;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(AccountRid) ) {
|
|||
|
*AccountRid = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(TrustAttributes) ) {
|
|||
|
*TrustAttributes = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(IsDnsDomainTrustAccount) ) {
|
|||
|
*IsDnsDomainTrustAccount = FALSE; // assume it's not and prove if otherwise
|
|||
|
}
|
|||
|
|
|||
|
Length = wcslen( AccountName );
|
|||
|
if ( Length < 1 ) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the secure channel type to allowable account control bits.
|
|||
|
//
|
|||
|
|
|||
|
switch (SecureChannelType) {
|
|||
|
case WorkstationSecureChannel:
|
|||
|
AllowableAccountControlBits |= USER_WORKSTATION_TRUST_ACCOUNT;
|
|||
|
break;
|
|||
|
|
|||
|
case ServerSecureChannel:
|
|||
|
AllowableAccountControlBits |= USER_SERVER_TRUST_ACCOUNT;
|
|||
|
break;
|
|||
|
|
|||
|
case TrustedDomainSecureChannel:
|
|||
|
AllowableAccountControlBits |= USER_INTERDOMAIN_TRUST_ACCOUNT;
|
|||
|
break;
|
|||
|
|
|||
|
case TrustedDnsDomainSecureChannel:
|
|||
|
AllowableAccountControlBits |= USER_DNS_DOMAIN_TRUST_ACCOUNT;
|
|||
|
break;
|
|||
|
|
|||
|
case NullSecureChannel:
|
|||
|
if ( AllowableAccountControlBits == 0 ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: Invalid AAC (%x) for %ws\n",
|
|||
|
AllowableAccountControlBits,
|
|||
|
AccountName ));
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: Invalid channel type (%x) for %ws\n",
|
|||
|
SecureChannelType,
|
|||
|
AccountName ));
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is an interdomain trust account,
|
|||
|
// use an interdomain trust object.
|
|||
|
//
|
|||
|
|
|||
|
if ( AllowableAccountControlBits == USER_DNS_DOMAIN_TRUST_ACCOUNT ||
|
|||
|
AllowableAccountControlBits == USER_INTERDOMAIN_TRUST_ACCOUNT ) {
|
|||
|
UNICODE_STRING AccountNameString;
|
|||
|
|
|||
|
//
|
|||
|
// If this is a DNS trust account,
|
|||
|
// remove the optional . from the end of the account name.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString( &AccountNameString, AccountName );
|
|||
|
if ( AllowableAccountControlBits == USER_DNS_DOMAIN_TRUST_ACCOUNT ) {
|
|||
|
if ( Length != 0 && AccountName[Length-1] == '.' ) {
|
|||
|
AccountNameString.Length -= sizeof(WCHAR);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is an NT4-style interdomain trust,
|
|||
|
// remove the $ from the end of the account name.
|
|||
|
//
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Ensure the account name has the correct postfix.
|
|||
|
//
|
|||
|
|
|||
|
if ( Length <= SSI_ACCOUNT_NAME_POSTFIX_LENGTH ) {
|
|||
|
return STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
|
|||
|
if ( _wcsicmp(&AccountName[Length - SSI_ACCOUNT_NAME_POSTFIX_LENGTH],
|
|||
|
SSI_ACCOUNT_NAME_POSTFIX) != 0 ) {
|
|||
|
return STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
|
|||
|
AccountNameString.Length -= SSI_ACCOUNT_NAME_POSTFIX_LENGTH*sizeof(WCHAR);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the authentication information from the LSA.
|
|||
|
//
|
|||
|
|
|||
|
Status = LsarQueryTrustedDomainInfoByName(
|
|||
|
DomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING) &AccountNameString,
|
|||
|
TrustedDomainFullInformation,
|
|||
|
&TrustInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: cannot LsarQueryTrustedDomainInfoByName 0x%lx\n",
|
|||
|
&AccountNameString,
|
|||
|
Status ));
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Ensure the attributes of the trust account are right.
|
|||
|
//
|
|||
|
if ( (TrustInfo->TrustedFullInfo.Information.TrustDirection & TRUST_DIRECTION_INBOUND) == 0 ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: trust is not inbound\n",
|
|||
|
&AccountNameString ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
if ( TrustInfo->TrustedFullInfo.Information.TrustType != TRUST_TYPE_DOWNLEVEL &&
|
|||
|
TrustInfo->TrustedFullInfo.Information.TrustType != TRUST_TYPE_UPLEVEL ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: trust type doesn't match request type 0x%lx %ld\n",
|
|||
|
&AccountNameString,
|
|||
|
AllowableAccountControlBits,
|
|||
|
TrustInfo->TrustedFullInfo.Information.TrustType ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
if ( TrustInfo->TrustedFullInfo.Information.TrustAttributes & TRUST_ATTRIBUTE_UPLEVEL_ONLY ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: trust is KERB only\n",
|
|||
|
&AccountNameString ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return the trust attributes to the caller
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(TrustAttributes) ) {
|
|||
|
*TrustAttributes = TrustInfo->TrustedFullInfo.Information.TrustAttributes;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine whether the passed account is a DNS domain trust account
|
|||
|
//
|
|||
|
// Simply check if this is a uplevel trust and if the account name passed
|
|||
|
// is the Name of the trusted domain
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(IsDnsDomainTrustAccount) ) {
|
|||
|
if ( TrustInfo->TrustedFullInfo.Information.TrustType == TRUST_TYPE_UPLEVEL &&
|
|||
|
TrustInfo->TrustedFullInfo.Information.Name.Length > 0 ) {
|
|||
|
LPWSTR DnsDomainNameString = NULL;
|
|||
|
|
|||
|
DnsDomainNameString = LocalAlloc( 0,
|
|||
|
TrustInfo->TrustedFullInfo.Information.Name.Length + sizeof(WCHAR) );
|
|||
|
|
|||
|
if ( DnsDomainNameString == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
RtlCopyMemory( DnsDomainNameString,
|
|||
|
TrustInfo->TrustedFullInfo.Information.Name.Buffer,
|
|||
|
TrustInfo->TrustedFullInfo.Information.Name.Length );
|
|||
|
DnsDomainNameString[ TrustInfo->TrustedFullInfo.Information.Name.Length/sizeof(WCHAR) ] = L'\0';
|
|||
|
|
|||
|
//
|
|||
|
// Note that we don't have to remove the trailing dot
|
|||
|
// in AccountName if present because the DNS comprare
|
|||
|
// API ignores trailing dots.
|
|||
|
//
|
|||
|
*IsDnsDomainTrustAccount = NlEqualDnsName(DnsDomainNameString, AccountName);
|
|||
|
|
|||
|
LocalFree( DnsDomainNameString );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Only get the password if the caller really wants it.
|
|||
|
//
|
|||
|
|
|||
|
if ( OwfPassword != NULL ) {
|
|||
|
AuthInfoCount = TrustInfo->TrustedFullInfo.AuthInformation.IncomingAuthInfos;
|
|||
|
AuthInfo = TrustInfo->TrustedFullInfo.AuthInformation.IncomingAuthenticationInformation;
|
|||
|
|
|||
|
if (AuthInfoCount == 0 || AuthInfo == NULL) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: No auth info for this domain.\n",
|
|||
|
&AccountNameString ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Loop through the various auth infos looking for the cleartext password
|
|||
|
// or NT OWF password.
|
|||
|
//
|
|||
|
// If there is a clear text password, use it.
|
|||
|
// Otherwise, use the NT OWF password.
|
|||
|
//
|
|||
|
|
|||
|
for ( i=0; i<AuthInfoCount; i++ ) {
|
|||
|
|
|||
|
//
|
|||
|
// Handle an NT OWF password.
|
|||
|
//
|
|||
|
|
|||
|
if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_NT4OWF ) {
|
|||
|
|
|||
|
//
|
|||
|
// Only use the OWF if it is valid
|
|||
|
//
|
|||
|
|
|||
|
if ( AuthInfo[i].AuthInfoLength != sizeof(*OwfPassword) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: OWF password has bad length %ld\n",
|
|||
|
&AccountNameString,
|
|||
|
AuthInfo[i].AuthInfoLength ));
|
|||
|
} else {
|
|||
|
RtlCopyMemory( OwfPassword, AuthInfo[i].AuthInfo, sizeof(*OwfPassword) );
|
|||
|
PasswordFound = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle a cleartext password.
|
|||
|
//
|
|||
|
|
|||
|
else if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR ) {
|
|||
|
UNICODE_STRING TempUnicodeString;
|
|||
|
|
|||
|
TempUnicodeString.Buffer = (LPWSTR)AuthInfo[i].AuthInfo;
|
|||
|
TempUnicodeString.MaximumLength =
|
|||
|
TempUnicodeString.Length = (USHORT)AuthInfo[i].AuthInfoLength;
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlGetIncomingPassword: New Clear Password = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, TempUnicodeString.Buffer, TempUnicodeString.Length );
|
|||
|
|
|||
|
NlpDumpTime( NL_CHALLENGE_RES, "NlGetIncomingPassword: New Password Changed: ", AuthInfo[i].LastUpdateTime );
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
|
|||
|
OwfPassword);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: cannot RtlCalculateNtOwfPassword 0x%lx\n",
|
|||
|
&AccountNameString,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
PasswordFound = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Use this clear text password
|
|||
|
//
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Only get the previous password if the caller really wants it.
|
|||
|
//
|
|||
|
|
|||
|
if ( OwfPreviousPassword != NULL ) {
|
|||
|
// If OwfPreviousPassword is not NULL, OwfPassword must not be NULL either
|
|||
|
NlAssert( OwfPassword != NULL );
|
|||
|
AuthInfoCount = TrustInfo->TrustedFullInfo.AuthInformation.IncomingAuthInfos;
|
|||
|
AuthInfo = TrustInfo->TrustedFullInfo.AuthInformation.IncomingPreviousAuthenticationInformation;
|
|||
|
|
|||
|
if (AuthInfoCount == 0 || AuthInfo == NULL) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: No previous auth info for this domain.\n",
|
|||
|
&AccountNameString ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Loop through the various auth infos looking for the previous cleartext password
|
|||
|
// or NT OWF password.
|
|||
|
//
|
|||
|
// If there is a clear text password, use it.
|
|||
|
// Otherwise, use the NT OWF password.
|
|||
|
//
|
|||
|
|
|||
|
for ( i=0; i<AuthInfoCount; i++ ) {
|
|||
|
|
|||
|
//
|
|||
|
// Handle an NT OWF password.
|
|||
|
//
|
|||
|
|
|||
|
if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_NT4OWF ) {
|
|||
|
|
|||
|
//
|
|||
|
// Only use the OWF if it is valid
|
|||
|
//
|
|||
|
|
|||
|
if ( AuthInfo[i].AuthInfoLength != sizeof(*OwfPreviousPassword) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: previous OWF password has bad length %ld\n",
|
|||
|
&AccountNameString,
|
|||
|
AuthInfo[i].AuthInfoLength ));
|
|||
|
} else {
|
|||
|
RtlCopyMemory( OwfPreviousPassword, AuthInfo[i].AuthInfo, sizeof(*OwfPreviousPassword) );
|
|||
|
PreviousPasswordFound = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle a cleartext password.
|
|||
|
//
|
|||
|
|
|||
|
else if ( AuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR ) {
|
|||
|
UNICODE_STRING TempUnicodeString;
|
|||
|
|
|||
|
TempUnicodeString.Buffer = (LPWSTR)AuthInfo[i].AuthInfo;
|
|||
|
TempUnicodeString.MaximumLength =
|
|||
|
TempUnicodeString.Length = (USHORT)AuthInfo[i].AuthInfoLength;
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlGetIncomingPassword: Old Clear Password = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, TempUnicodeString.Buffer, TempUnicodeString.Length );
|
|||
|
|
|||
|
NlpDumpTime( NL_CHALLENGE_RES, "NlGetIncomingPassword: Old Password Changed: ", AuthInfo[i].LastUpdateTime );
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
|
|||
|
OwfPreviousPassword);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: cannot RtlCalculateNtOwfPassword 0x%lx\n",
|
|||
|
&AccountNameString,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
PreviousPasswordFound = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Use this clear text password
|
|||
|
//
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Only get the account RID if the caller really wants it.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( AccountRid) ) {
|
|||
|
PUNICODE_STRING FlatName;
|
|||
|
WCHAR SamAccountName[CNLEN+1+1];
|
|||
|
|
|||
|
//
|
|||
|
// The name of the SAM account that corresponds to the inbound
|
|||
|
// trust is FlatName$.
|
|||
|
//
|
|||
|
|
|||
|
FlatName = (PUNICODE_STRING) &TrustInfo->TrustedFullInfo.Information.FlatName;
|
|||
|
if ( FlatName->Length < sizeof(WCHAR) ||
|
|||
|
FlatName->Length > CNLEN * sizeof(WCHAR) ) {
|
|||
|
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %wZ: Flat Name length is bad %ld\n",
|
|||
|
&AccountNameString,
|
|||
|
FlatName->Length ));
|
|||
|
} else {
|
|||
|
|
|||
|
RtlCopyMemory( SamAccountName,
|
|||
|
FlatName->Buffer,
|
|||
|
FlatName->Length );
|
|||
|
|
|||
|
SamAccountName[FlatName->Length/sizeof(WCHAR)] =
|
|||
|
SSI_ACCOUNT_NAME_POSTFIX_CHAR;
|
|||
|
SamAccountName[(FlatName->Length/sizeof(WCHAR))+1] = L'\0';
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the account RID from SAM.
|
|||
|
//
|
|||
|
// ??? This is a gross hack.
|
|||
|
// The LSA should return this RID to me directly.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSamOpenNamedUser( DomainInfo, SamAccountName, NULL, AccountRid, NULL );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: Can't NlSamOpenNamedUser for %ws 0x%lx.\n",
|
|||
|
SamAccountName,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Othewise the account is a SAM user account.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// OwfPreviousPassword must be NULL for a SAM account
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( OwfPreviousPassword == NULL );
|
|||
|
|
|||
|
//
|
|||
|
// Ensure the account name has the correct postfix.
|
|||
|
//
|
|||
|
|
|||
|
if ( AllowableAccountControlBits == USER_SERVER_TRUST_ACCOUNT ||
|
|||
|
AllowableAccountControlBits == USER_WORKSTATION_TRUST_ACCOUNT ) {
|
|||
|
if ( Length <= SSI_ACCOUNT_NAME_POSTFIX_LENGTH ) {
|
|||
|
return STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
|
|||
|
if ( _wcsicmp(&AccountName[Length - SSI_ACCOUNT_NAME_POSTFIX_LENGTH],
|
|||
|
SSI_ACCOUNT_NAME_POSTFIX) != 0 ) {
|
|||
|
return STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the user account.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSamOpenNamedUser( DomainInfo, AccountName, &UserHandle, AccountRid, &UserAllInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: Can't NlSamOpenNamedUser for %ws 0x%lx.\n",
|
|||
|
AccountName,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Ensure the Account type matches the account type on the account.
|
|||
|
//
|
|||
|
|
|||
|
if ( (UserAllInfo->All.UserAccountControl &
|
|||
|
USER_ACCOUNT_TYPE_MASK &
|
|||
|
AllowableAccountControlBits ) == 0 ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: Invalid account type (%x) instead of %x for %ws\n",
|
|||
|
UserAllInfo->All.UserAccountControl & USER_ACCOUNT_TYPE_MASK,
|
|||
|
AllowableAccountControlBits,
|
|||
|
AccountName ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if the account is disabled.
|
|||
|
//
|
|||
|
if ( CheckAccountDisabled ) {
|
|||
|
if ( UserAllInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %ws account is disabled\n",
|
|||
|
AccountName ));
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Return the password if the caller wants it.
|
|||
|
//
|
|||
|
|
|||
|
if ( OwfPassword != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Use the NT OWF Password,
|
|||
|
//
|
|||
|
|
|||
|
if ( UserAllInfo->All.NtPasswordPresent &&
|
|||
|
UserAllInfo->All.NtOwfPassword.Length == sizeof(*OwfPassword) ) {
|
|||
|
|
|||
|
RtlCopyMemory( OwfPassword,
|
|||
|
UserAllInfo->All.NtOwfPassword.Buffer,
|
|||
|
sizeof(*OwfPassword) );
|
|||
|
PasswordFound = TRUE;
|
|||
|
|
|||
|
// Allow for the case that the account has no password at all.
|
|||
|
} else if ( UserAllInfo->All.LmPasswordPresent ) {
|
|||
|
|
|||
|
NlPrint((NL_CRITICAL,
|
|||
|
"NlGetIncomingPassword: No NT Password for %ws\n",
|
|||
|
AccountName ));
|
|||
|
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Update the last time this account was used.
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
SAMPR_USER_INFO_BUFFER UserInfo;
|
|||
|
NTSTATUS LogonStatus;
|
|||
|
|
|||
|
UserInfo.Internal2.StatisticsToApply = USER_LOGON_NET_SUCCESS_LOGON;
|
|||
|
|
|||
|
LogonStatus = SamrSetInformationUser(
|
|||
|
UserHandle,
|
|||
|
UserInternal2Information,
|
|||
|
&UserInfo );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(LogonStatus)) {
|
|||
|
NlPrint((NL_CRITICAL,
|
|||
|
"NlGetIncomingPassword: Cannot set last logon time %ws %lx\n",
|
|||
|
AccountName,
|
|||
|
LogonStatus ));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If no password exists on the account,
|
|||
|
// return a blank password.
|
|||
|
//
|
|||
|
|
|||
|
if ( !PasswordFound && OwfPassword != NULL ) {
|
|||
|
UNICODE_STRING TempUnicodeString;
|
|||
|
|
|||
|
RtlInitUnicodeString(&TempUnicodeString, NULL);
|
|||
|
Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
|
|||
|
OwfPassword);
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetIncomingPassword: %ws: cannot RtlCalculateNtOwfPassword (NULL) 0x%lx\n",
|
|||
|
AccountName,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If no previous password exists on the account,
|
|||
|
// return the current password.
|
|||
|
//
|
|||
|
|
|||
|
if ( !PreviousPasswordFound && OwfPreviousPassword != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// If OwfPreviousPassword is not NULL, OwfPassword must not be NULL either.
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( OwfPassword != NULL );
|
|||
|
|
|||
|
//
|
|||
|
// If previous password is not found, return the current one instead.
|
|||
|
//
|
|||
|
|
|||
|
*OwfPreviousPassword = *OwfPassword;
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
if ( UserAllInfo != NULL ) {
|
|||
|
SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo,
|
|||
|
UserAllInformation);
|
|||
|
}
|
|||
|
|
|||
|
if ( UserHandle != NULL ) {
|
|||
|
SamrCloseHandle( &UserHandle );
|
|||
|
}
|
|||
|
|
|||
|
if ( TrustInfo != NULL ) {
|
|||
|
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO( TrustedDomainFullInformation,
|
|||
|
TrustInfo );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlSetIncomingPassword(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN LPWSTR AccountName,
|
|||
|
IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
|
|||
|
IN PUNICODE_STRING ClearTextPassword OPTIONAL,
|
|||
|
IN DWORD ClearPasswordVersionNumber,
|
|||
|
IN PNT_OWF_PASSWORD OwfPassword OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the incoming password for the specified AccountName and SecureChannelType.
|
|||
|
At the same time, update the previous password info.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Emulated domain
|
|||
|
|
|||
|
AccountName - Name of the account to set the password on
|
|||
|
|
|||
|
SecureChannelType - The type of the account being used.
|
|||
|
|
|||
|
ClearTextPassword - The Clear text password for the named account.
|
|||
|
|
|||
|
ClearPasswordVersionNumber - The version number of the Clear text password.
|
|||
|
Used only for interdomain trust account. Ignored if ClearTextPassword
|
|||
|
is NULL.
|
|||
|
|
|||
|
OwfPassword - The NT OWF of the incoming password for the named
|
|||
|
account. If both the clear text and OWF password are specified,
|
|||
|
the OWF password is ignored.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
UNICODE_STRING AccountNameString;
|
|||
|
ULONG Length;
|
|||
|
|
|||
|
LSAPR_TRUSTED_DOMAIN_INFO TrustInfo;
|
|||
|
PLSAPR_TRUSTED_DOMAIN_INFO TrustInfoOld = NULL;
|
|||
|
LSAPR_AUTH_INFORMATION CurrentAuthInfo[3], PreviousAuthInfo[3], NoneAuthInfo;
|
|||
|
ULONG iClear, iOWF, iVersion, i;
|
|||
|
DWORD OldVersionNumber = 0;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Workstation and BDC secure channels get their outgoing password from
|
|||
|
// an LSA secret.
|
|||
|
//
|
|||
|
switch ( SecureChannelType ) {
|
|||
|
case ServerSecureChannel:
|
|||
|
case WorkstationSecureChannel:
|
|||
|
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Setting Password of '%ws' to: ", AccountName ));
|
|||
|
if ( ClearTextPassword != NULL ) {
|
|||
|
NlpDumpBuffer( NL_SESSION_SETUP, ClearTextPassword->Buffer, ClearTextPassword->Length );
|
|||
|
} else if (OwfPassword != NULL ) {
|
|||
|
NlpDumpBuffer( NL_SESSION_SETUP, OwfPassword, sizeof(*OwfPassword) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the encrypted password in SAM.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSamChangePasswordNamedUser( DomainInfo,
|
|||
|
AccountName,
|
|||
|
ClearTextPassword,
|
|||
|
OwfPassword );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlSetIncomingPassword: Cannot change password on local user account %lX\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Trusted domain secure channels get their incoming password from the trusted
|
|||
|
// domain object.
|
|||
|
//
|
|||
|
|
|||
|
case TrustedDomainSecureChannel:
|
|||
|
case TrustedDnsDomainSecureChannel:
|
|||
|
|
|||
|
//
|
|||
|
// If this is a DNS trust account,
|
|||
|
// remove the optional . from the end of the account name.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString( &AccountNameString, AccountName );
|
|||
|
Length = AccountNameString.Length / sizeof(WCHAR);
|
|||
|
if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|||
|
|
|||
|
if ( Length != 0 && AccountName[Length-1] == '.' ) {
|
|||
|
AccountNameString.Length -= sizeof(WCHAR);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is an NT4-style interdomain trust,
|
|||
|
// remove the $ from the end of the account name.
|
|||
|
//
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Ensure the account name has the correct postfix.
|
|||
|
//
|
|||
|
|
|||
|
if ( Length <= SSI_ACCOUNT_NAME_POSTFIX_LENGTH ) {
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
if ( _wcsicmp(&AccountName[Length - SSI_ACCOUNT_NAME_POSTFIX_LENGTH],
|
|||
|
SSI_ACCOUNT_NAME_POSTFIX) != 0 ) {
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
AccountNameString.Length -= SSI_ACCOUNT_NAME_POSTFIX_LENGTH*sizeof(WCHAR);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// First get the current authentication information (that is old as far as
|
|||
|
// the function is concerned) from the LSA.
|
|||
|
//
|
|||
|
|
|||
|
Status = LsarQueryTrustedDomainInfoByName(
|
|||
|
DomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING) &AccountNameString,
|
|||
|
TrustedDomainAuthInformation,
|
|||
|
&TrustInfoOld );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlSetIncomingPassword: %wZ: cannot LsarQueryTrustedDomainInfoByName 0x%lx\n",
|
|||
|
&AccountNameString,
|
|||
|
Status ));
|
|||
|
// if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
// Status = STATUS_NO_SUCH_USER;
|
|||
|
// }
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the trust information.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory( &TrustInfo, sizeof(TrustInfo) );
|
|||
|
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 0;
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthenticationInformation =
|
|||
|
CurrentAuthInfo;
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingPreviousAuthenticationInformation =
|
|||
|
PreviousAuthInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the current and previous authentication information.
|
|||
|
//
|
|||
|
|
|||
|
NlQuerySystemTime( &CurrentAuthInfo[0].LastUpdateTime );
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Setting Password of '%ws' to: ", AccountName ));
|
|||
|
if ( ClearTextPassword != NULL ) {
|
|||
|
CurrentAuthInfo[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
|
|||
|
CurrentAuthInfo[0].AuthInfoLength = ClearTextPassword->Length;
|
|||
|
CurrentAuthInfo[0].AuthInfo = (LPBYTE)ClearTextPassword->Buffer;
|
|||
|
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, ClearTextPassword->Buffer, ClearTextPassword->Length );
|
|||
|
|
|||
|
CurrentAuthInfo[1].LastUpdateTime = CurrentAuthInfo[0].LastUpdateTime;
|
|||
|
CurrentAuthInfo[1].AuthType = TRUST_AUTH_TYPE_VERSION;
|
|||
|
CurrentAuthInfo[1].AuthInfoLength = sizeof(ClearPasswordVersionNumber);
|
|||
|
CurrentAuthInfo[1].AuthInfo = (LPBYTE) &ClearPasswordVersionNumber;
|
|||
|
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Password Version number is %lu\n",
|
|||
|
ClearPasswordVersionNumber ));
|
|||
|
} else {
|
|||
|
CurrentAuthInfo[0].AuthType = TRUST_AUTH_TYPE_NT4OWF;
|
|||
|
CurrentAuthInfo[0].AuthInfoLength = sizeof(*OwfPassword);
|
|||
|
CurrentAuthInfo[0].AuthInfo = (LPBYTE)OwfPassword;
|
|||
|
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, OwfPassword, sizeof(*OwfPassword) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The AuthType values of corresponding elements of IncomingAuthenticationInformation and
|
|||
|
// IncomingPreviousAuthenticationInformation arrays must be the same for internal reasons.
|
|||
|
// Thus, use NoneAuthInfo element to fill in missing counterparts in these arrays.
|
|||
|
|
|||
|
NoneAuthInfo.LastUpdateTime = CurrentAuthInfo[0].LastUpdateTime;
|
|||
|
NoneAuthInfo.AuthType = TRUST_AUTH_TYPE_NONE;
|
|||
|
NoneAuthInfo.AuthInfoLength = 0;
|
|||
|
NoneAuthInfo.AuthInfo = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Find first Clear and OWF passwords (if any) in the old password info.
|
|||
|
//
|
|||
|
|
|||
|
for ( iClear = 0; iClear < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos; iClear++ ) {
|
|||
|
|
|||
|
if ( TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iClear].AuthType ==
|
|||
|
TRUST_AUTH_TYPE_CLEAR ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
for ( iVersion = 0; iVersion < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos; iVersion++ ) {
|
|||
|
|
|||
|
if ( TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iVersion].AuthType ==
|
|||
|
TRUST_AUTH_TYPE_VERSION &&
|
|||
|
TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iVersion].AuthInfoLength ==
|
|||
|
sizeof(OldVersionNumber) ) {
|
|||
|
|
|||
|
RtlCopyMemory( &OldVersionNumber,
|
|||
|
TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iVersion].AuthInfo,
|
|||
|
sizeof(OldVersionNumber) );
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
for ( iOWF = 0; iOWF < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos; iOWF++ ) {
|
|||
|
|
|||
|
if ( TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iOWF].AuthType ==
|
|||
|
TRUST_AUTH_TYPE_NT4OWF ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update previous info using only first Clear and OWF passwords in the current info
|
|||
|
// (that is old as far as this function is concerned). AuthTypes other than Clear,
|
|||
|
// Version, and OWF are going to be lost.
|
|||
|
//
|
|||
|
|
|||
|
if (ClearTextPassword != NULL) {
|
|||
|
|
|||
|
if (iClear < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos) {
|
|||
|
PreviousAuthInfo[0] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iClear];
|
|||
|
} else {
|
|||
|
PreviousAuthInfo[0] = NoneAuthInfo;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Preserve the old version number only if it is in accordance with the passed value
|
|||
|
//
|
|||
|
|
|||
|
if ( iVersion < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos &&
|
|||
|
ClearPasswordVersionNumber > 0 &&
|
|||
|
OldVersionNumber == ClearPasswordVersionNumber - 1 ) {
|
|||
|
PreviousAuthInfo[1] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iVersion];
|
|||
|
} else {
|
|||
|
PreviousAuthInfo[1] = NoneAuthInfo;
|
|||
|
}
|
|||
|
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 2;
|
|||
|
|
|||
|
//
|
|||
|
// If there is a previous OWF password, preserve it
|
|||
|
//
|
|||
|
|
|||
|
if (iOWF < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos) {
|
|||
|
PreviousAuthInfo[2] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iOWF];
|
|||
|
CurrentAuthInfo[2] = NoneAuthInfo;
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 3;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (iOWF < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos) {
|
|||
|
PreviousAuthInfo[0] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iOWF];
|
|||
|
} else {
|
|||
|
PreviousAuthInfo[0] = NoneAuthInfo;
|
|||
|
}
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 1;
|
|||
|
|
|||
|
//
|
|||
|
// If there is a previous clear text password, preserve it
|
|||
|
//
|
|||
|
|
|||
|
if (iClear < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos) {
|
|||
|
PreviousAuthInfo[1] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iClear];
|
|||
|
CurrentAuthInfo[1] = NoneAuthInfo;
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 2;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is a previous clear text password version number, preserve it
|
|||
|
//
|
|||
|
|
|||
|
if (iVersion < TrustInfoOld->TrustedAuthInfo.IncomingAuthInfos) {
|
|||
|
PreviousAuthInfo[2] = TrustInfoOld->TrustedAuthInfo.IncomingAuthenticationInformation[iVersion];
|
|||
|
CurrentAuthInfo[2] = NoneAuthInfo;
|
|||
|
TrustInfo.TrustedAuthInfo.IncomingAuthInfos = 3;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
for ( i = 0; i < TrustInfo.TrustedAuthInfo.IncomingAuthInfos; i++ ) {
|
|||
|
if ( CurrentAuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Current Clear Text Password of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, CurrentAuthInfo[i].AuthInfo, CurrentAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( CurrentAuthInfo[i].AuthType == TRUST_AUTH_TYPE_VERSION ) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Current Clear Password Version Number of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, CurrentAuthInfo[i].AuthInfo, CurrentAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( CurrentAuthInfo[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Current OWF Password of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, CurrentAuthInfo[i].AuthInfo, CurrentAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( CurrentAuthInfo[i].AuthType == TRUST_AUTH_TYPE_NONE) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Current Auth Info entry for '%ws' has no type\n", AccountName ));
|
|||
|
}
|
|||
|
|
|||
|
if ( PreviousAuthInfo[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Previous Clear Text Password of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, PreviousAuthInfo[i].AuthInfo, PreviousAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( PreviousAuthInfo[i].AuthType == TRUST_AUTH_TYPE_VERSION ) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Previous Clear Password Version Number of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, PreviousAuthInfo[i].AuthInfo, PreviousAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( PreviousAuthInfo[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Previous OWF Text Password of '%ws' is: ", AccountName ));
|
|||
|
NlpDumpBuffer(NL_SESSION_SETUP, PreviousAuthInfo[i].AuthInfo, PreviousAuthInfo[i].AuthInfoLength );
|
|||
|
} else if ( PreviousAuthInfo[i].AuthType == TRUST_AUTH_TYPE_NONE) {
|
|||
|
NlPrint(( NL_SESSION_SETUP, "Previous Auth Info entry for '%ws' has no type\n", AccountName ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the authentication information in the LSA.
|
|||
|
//
|
|||
|
Status = LsarSetTrustedDomainInfoByName(
|
|||
|
DomainInfo->DomLsaPolicyHandle,
|
|||
|
(PLSAPR_UNICODE_STRING) &AccountNameString,
|
|||
|
TrustedDomainAuthInformation,
|
|||
|
&TrustInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlSetIncomingPassword: %wZ: cannot LsarSetTrustedDomainInfoByName 0x%lx\n",
|
|||
|
&AccountNameString,
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// We don't support any other secure channel type
|
|||
|
//
|
|||
|
default:
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlSetIncomingPassword: %ws: invalid secure channel type: %ld\n",
|
|||
|
AccountName,
|
|||
|
SecureChannelType ));
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
|
|||
|
if ( TrustInfoOld != NULL ) {
|
|||
|
LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO( TrustedDomainAuthInformation,
|
|||
|
TrustInfoOld );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NlTimeToRediscover(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
BOOLEAN WithAccount
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if it is time to rediscover this Client Session.
|
|||
|
If a session setup failure happens to a discovered DC,
|
|||
|
rediscover the DC if the discovery happened a long time ago (more than 5 minutes).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
|
|||
|
WithAccount - If TRUE, the caller is going to attempt the discovery "with account".
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE -- iff it is time to re-discover
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN ReturnBoolean;
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
//
|
|||
|
// If the last discovery was longer than 5 minutes ago,
|
|||
|
// it's fine to rediscover regardless of the rediscovery
|
|||
|
// type (with or without account)
|
|||
|
//
|
|||
|
|
|||
|
ReturnBoolean = NetpLogonTimeHasElapsed(
|
|||
|
ClientSession->CsLastDiscoveryTime,
|
|||
|
MAX_DC_REAUTHENTICATION_WAIT );
|
|||
|
|
|||
|
//
|
|||
|
// If it turns out that the last rediscovery was recent but
|
|||
|
// the caller is going to attempt the discovery "with account"
|
|||
|
// perhaps the last rediscovery with account wasn't recent
|
|||
|
//
|
|||
|
|
|||
|
if ( !ReturnBoolean && WithAccount ) {
|
|||
|
ReturnBoolean = NetpLogonTimeHasElapsed(
|
|||
|
ClientSession->CsLastDiscoveryWithAccountTime,
|
|||
|
MAX_DC_REAUTHENTICATION_WAIT );
|
|||
|
}
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
return ReturnBoolean;
|
|||
|
}
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
NlCacheJoinDomainControllerInfo(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function reads from the registry and caches the DC information
|
|||
|
that was previously written by the join process. This DC is bound
|
|||
|
to have the correct password for this machine. If no info is
|
|||
|
available in the registry, no action needs to be taken.
|
|||
|
|
|||
|
The join DC info is cached in the DsGetDcName cache. Netlogon will
|
|||
|
then discover this DC and will set up a secure channel to it. Caching
|
|||
|
the DC info in the DsGetDcName cache will ensure that not only Netlogon
|
|||
|
but every other process will consistently talk to this DC.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NO_ERROR - The DC info (if any) was read and the client session
|
|||
|
strusture was successfully set.
|
|||
|
|
|||
|
Otherwise, some error occured during this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG WinError = ERROR_SUCCESS; // Registry reading errors
|
|||
|
NET_API_STATUS NetStatus = NO_ERROR; // Netlogon API return codes
|
|||
|
|
|||
|
HKEY hJoinKey = NULL;
|
|||
|
ULONG BytesRead = 0;
|
|||
|
ULONG Type;
|
|||
|
DWORD KerberosIsDone = 0;
|
|||
|
LPWSTR DcName = NULL;
|
|||
|
ULONG DcFlags = 0;
|
|||
|
|
|||
|
PDOMAIN_INFO DomainInfo = NULL;
|
|||
|
PCLIENT_SESSION ClientSession = NULL;
|
|||
|
PNL_DC_CACHE_ENTRY DcCacheEntry = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Caching the join DC info is needed only for workstations
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlGlobalMemberWorkstation ) {
|
|||
|
return NO_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the registry key
|
|||
|
//
|
|||
|
|
|||
|
WinError = RegOpenKey( HKEY_LOCAL_MACHINE,
|
|||
|
NETSETUPP_NETLOGON_JD_NAME,
|
|||
|
&hJoinKey );
|
|||
|
|
|||
|
if ( WinError != ERROR_SUCCESS) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Read DC name
|
|||
|
//
|
|||
|
|
|||
|
WinError = RegQueryValueEx( hJoinKey,
|
|||
|
NETSETUPP_NETLOGON_JD_DC,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
NULL,
|
|||
|
&BytesRead);
|
|||
|
|
|||
|
if ( WinError != ERROR_SUCCESS ) {
|
|||
|
goto Cleanup;
|
|||
|
} else if ( Type != REG_SZ ) {
|
|||
|
WinError = ERROR_DATATYPE_MISMATCH;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
DcName = LocalAlloc( LMEM_ZEROINIT, BytesRead );
|
|||
|
|
|||
|
if ( DcName == NULL ) {
|
|||
|
WinError = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
WinError = RegQueryValueEx( hJoinKey,
|
|||
|
NETSETUPP_NETLOGON_JD_DC,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
(PUCHAR) DcName,
|
|||
|
&BytesRead);
|
|||
|
|
|||
|
if ( WinError != ERROR_SUCCESS) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The name should include at least '\\' and one character
|
|||
|
//
|
|||
|
|
|||
|
if ( wcslen(DcName) < 3 ) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlCacheJoinDomainControllerInfo: DcName is too short.\n" ));
|
|||
|
WinError = ERROR_DATATYPE_MISMATCH;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Read Flags
|
|||
|
//
|
|||
|
|
|||
|
WinError = RegQueryValueEx( hJoinKey,
|
|||
|
NETSETUPP_NETLOGON_JD_F,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
NULL,
|
|||
|
&BytesRead);
|
|||
|
|
|||
|
if ( WinError != ERROR_SUCCESS ) {
|
|||
|
goto Cleanup;
|
|||
|
} else if ( Type != REG_DWORD ) {
|
|||
|
WinError = ERROR_DATATYPE_MISMATCH;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
WinError = RegQueryValueEx( hJoinKey,
|
|||
|
NETSETUPP_NETLOGON_JD_F,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
(PUCHAR)&DcFlags,
|
|||
|
&BytesRead);
|
|||
|
|
|||
|
if ( WinError != ERROR_SUCCESS) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we've made it up to this point, the registry was successfully read
|
|||
|
//
|
|||
|
|
|||
|
WinError = ERROR_SUCCESS;
|
|||
|
NlPrint(( NL_INIT, "Join DC: %ws, Flags: 0x%lx\n", DcName, DcFlags ));
|
|||
|
|
|||
|
//
|
|||
|
// If this is not NT5 DC, avoid caching it since it's a PDC.
|
|||
|
// We don't want to overload the PDC by having all clients
|
|||
|
// talking to it after they join the domain. We will just
|
|||
|
// delete the reg key here because Kerberos won't use NT4 DC
|
|||
|
// anyway.
|
|||
|
//
|
|||
|
|
|||
|
if ( (DcFlags & DS_DS_FLAG) == 0 ) {
|
|||
|
ULONG WinErrorTmp = ERROR_SUCCESS;
|
|||
|
HKEY hJoinKeyTmp = NULL;
|
|||
|
|
|||
|
NlPrint(( NL_INIT, "NlCacheJoinDomainControllerInfo: Join DC is not NT5, deleting it\n" ));
|
|||
|
|
|||
|
WinErrorTmp = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|||
|
NETSETUPP_NETLOGON_JD_PATH,
|
|||
|
0,
|
|||
|
KEY_ALL_ACCESS,
|
|||
|
&hJoinKeyTmp );
|
|||
|
|
|||
|
if ( WinErrorTmp == ERROR_SUCCESS ) {
|
|||
|
WinErrorTmp = RegDeleteKey( hJoinKeyTmp,
|
|||
|
NETSETUPP_NETLOGON_JD );
|
|||
|
|
|||
|
if ( WinErrorTmp != ERROR_SUCCESS ) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlCacheJoinDomainControllerInfo: Couldn't deleted JoinDomain 0x%lx\n",
|
|||
|
WinErrorTmp ));
|
|||
|
}
|
|||
|
|
|||
|
RegCloseKey( hJoinKeyTmp );
|
|||
|
|
|||
|
} else {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlCacheJoinDomainControllerInfo: RegOpenKeyEx failed 0x%lx\n",
|
|||
|
WinErrorTmp ));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Treat this as error
|
|||
|
//
|
|||
|
NetStatus = ERROR_INVALID_DATA;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now get the client session to the primary domain
|
|||
|
//
|
|||
|
|
|||
|
DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
|
|||
|
|
|||
|
if ( DomainInfo == NULL ) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlCacheJoinDomainControllerInfo: Cannot NlFindNetbiosDomain\n" ));
|
|||
|
NetStatus = ERROR_NO_SUCH_DOMAIN;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
ClientSession = NlRefDomClientSession( DomainInfo );
|
|||
|
|
|||
|
if ( ClientSession == NULL ) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlCacheJoinDomainControllerInfo: Cannot NlRefDomClientSession\n" ));
|
|||
|
NetStatus = ERROR_NO_SUCH_DOMAIN;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we are started after a domain join,
|
|||
|
// the browser has been notified about the
|
|||
|
// domain rename by a change log worker.
|
|||
|
// Wait until the change log worker exits.
|
|||
|
// Otherwise, the browser will reject the
|
|||
|
// datagram send when we pass the new emulated
|
|||
|
// domain name.
|
|||
|
//
|
|||
|
|
|||
|
NlWaitForChangeLogBrowserNotify();
|
|||
|
|
|||
|
//
|
|||
|
// Finally ping the DC given this info. Cache the response.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NlPingDcName( ClientSession,
|
|||
|
(DcFlags & DS_DNS_CONTROLLER_FLAG) ?
|
|||
|
DS_PING_DNS_HOST :
|
|||
|
DS_PING_NETBIOS_HOST,
|
|||
|
TRUE, // Cache this DC
|
|||
|
FALSE, // Do not require IP
|
|||
|
TRUE, // Ensure the DC has our account
|
|||
|
FALSE, // Do not refresh the session
|
|||
|
DcName+2, // Skip '\\' in the name
|
|||
|
&DcCacheEntry );
|
|||
|
|
|||
|
if ( NetStatus == NO_ERROR ) {
|
|||
|
NlPrint(( NL_INIT, "Join DC cached successfully\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// Also set the site name
|
|||
|
//
|
|||
|
if ( DcCacheEntry->UnicodeClientSiteName != NULL ) {
|
|||
|
NlSetDynamicSiteName( DcCacheEntry->UnicodeClientSiteName );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
NlPrint(( NL_CRITICAL, "Failed to cache join DC: 0x%lx\n", NetStatus ));
|
|||
|
}
|
|||
|
|
|||
|
Cleanup:
|
|||
|
|
|||
|
//
|
|||
|
// Free up locally used resources
|
|||
|
//
|
|||
|
|
|||
|
if ( DcName != NULL ) {
|
|||
|
LocalFree( DcName );
|
|||
|
}
|
|||
|
|
|||
|
if ( DcCacheEntry != NULL ) {
|
|||
|
NetpDcDerefCacheEntry( DcCacheEntry );
|
|||
|
}
|
|||
|
|
|||
|
if ( hJoinKey != NULL ) {
|
|||
|
RegCloseKey( hJoinKey );
|
|||
|
}
|
|||
|
|
|||
|
if ( ClientSession != NULL ) {
|
|||
|
NlUnrefClientSession( ClientSession );
|
|||
|
}
|
|||
|
|
|||
|
if ( DomainInfo != NULL ) {
|
|||
|
NlDereferenceDomain( DomainInfo );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If everything is successful return NO_ERROR.
|
|||
|
// Otherwise, if Netlogon API failed, return its error code.
|
|||
|
// Otherwise, return registry reading error.
|
|||
|
//
|
|||
|
|
|||
|
if ( WinError == ERROR_SUCCESS && NetStatus == NO_ERROR ) {
|
|||
|
return NO_ERROR;
|
|||
|
} else if ( NetStatus != NO_ERROR ) {
|
|||
|
return NetStatus;
|
|||
|
} else {
|
|||
|
return WinError;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlGetPasswordFromPdc(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN LPWSTR AccountName,
|
|||
|
IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
|
|||
|
OUT PNT_OWF_PASSWORD NtOwfPassword
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to by a BDC to get a machine account password
|
|||
|
from the PDC in the doamin.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Identifies the domain the account is in.
|
|||
|
|
|||
|
AccountName -- Name of the account to get the password for.
|
|||
|
|
|||
|
AccountType -- The type of account being accessed.
|
|||
|
|
|||
|
EncryptedNtOwfPassword -- Returns the OWF password of the account.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT status code.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NETLOGON_AUTHENTICATOR OurAuthenticator;
|
|||
|
NETLOGON_AUTHENTICATOR ReturnAuthenticator;
|
|||
|
PCLIENT_SESSION ClientSession = NULL;
|
|||
|
SESSION_INFO SessionInfo;
|
|||
|
BOOLEAN FirstTry = TRUE;
|
|||
|
BOOLEAN AmWriter = FALSE;
|
|||
|
ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrPassword;
|
|||
|
|
|||
|
NlPrintDom((NL_SESSION_SETUP, DomainInfo,
|
|||
|
"NlGetPasswordFromPdc: Getting password for %ws from PDC.\n",
|
|||
|
AccountName ));
|
|||
|
|
|||
|
//
|
|||
|
// Reference the client session.
|
|||
|
//
|
|||
|
|
|||
|
ClientSession = NlRefDomClientSession( DomainInfo );
|
|||
|
|
|||
|
if ( ClientSession == NULL ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetPasswordFromPdc: This BDC has no client session with the PDC.\n"));
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Become a Writer of the ClientSession.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetPasswordFromPdc: Can't become writer of client session.\n"));
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
AmWriter = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// If the session isn't authenticated,
|
|||
|
// do so now.
|
|||
|
//
|
|||
|
|
|||
|
FirstTryFailed:
|
|||
|
|
|||
|
Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
SessionInfo.SessionKey = ClientSession->CsSessionKey;
|
|||
|
// SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
|
|||
|
|
|||
|
//
|
|||
|
// Build the Authenticator for this request to the PDC.
|
|||
|
//
|
|||
|
|
|||
|
NlBuildAuthenticator(
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ClientSession->CsSessionKey,
|
|||
|
&OurAuthenticator);
|
|||
|
|
|||
|
//
|
|||
|
// Get the password from the PDC
|
|||
|
//
|
|||
|
|
|||
|
NL_API_START( Status, ClientSession, TRUE ) {
|
|||
|
|
|||
|
NlAssert( ClientSession->CsUncServerName != NULL );
|
|||
|
Status = I_NetServerPasswordGet( ClientSession->CsUncServerName,
|
|||
|
AccountName,
|
|||
|
AccountType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&OurAuthenticator,
|
|||
|
&ReturnAuthenticator,
|
|||
|
&SessKeyEncrPassword);
|
|||
|
|
|||
|
// NOTE: This call may drop the secure channel behind our back
|
|||
|
} NL_API_ELSE( Status, ClientSession, TRUE ) {
|
|||
|
} NL_API_END;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now verify primary's authenticator and update our seed
|
|||
|
//
|
|||
|
|
|||
|
if ( Status == STATUS_ACCESS_DENIED ||
|
|||
|
!NlUpdateSeed( &ClientSession->CsAuthenticationSeed,
|
|||
|
&ReturnAuthenticator.Credential,
|
|||
|
&ClientSession->CsSessionKey) ) {
|
|||
|
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlGetPasswordFromPdc: denying access after status: 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// Preserve any status indicating a communication error.
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
NlSetStatusClientSession( ClientSession, Status );
|
|||
|
|
|||
|
//
|
|||
|
// Perhaps the netlogon service on the server has just restarted.
|
|||
|
// Try just once to set up a session to the server again.
|
|||
|
//
|
|||
|
if ( FirstTry ) {
|
|||
|
FirstTry = FALSE;
|
|||
|
goto FirstTryFailed;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Decrypt the password returned from the PDC.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlDecryptNtOwfPwdWithNtOwfPwd(
|
|||
|
&SessKeyEncrPassword,
|
|||
|
(PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
|
|||
|
NtOwfPassword );
|
|||
|
NlAssert( NT_SUCCESS(Status) );
|
|||
|
|
|||
|
//
|
|||
|
// Common exit
|
|||
|
//
|
|||
|
|
|||
|
Cleanup:
|
|||
|
if ( ClientSession != NULL ) {
|
|||
|
if ( AmWriter ) {
|
|||
|
NlResetWriterClientSession( ClientSession );
|
|||
|
}
|
|||
|
NlUnrefClientSession( ClientSession );
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|||
|
"NlGetPasswordFromPdc: %ws: failed %lX\n",
|
|||
|
AccountName,
|
|||
|
Status));
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlSessionSetup(
|
|||
|
IN OUT PCLIENT_SESSION ClientSession
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verify that the requestor (this machine) has a valid account at
|
|||
|
Primary Domain Controller (primary). The authentication
|
|||
|
is done via an elaborate protocol. This routine will be
|
|||
|
used only when NETLOGON service starts with role != primary.
|
|||
|
|
|||
|
The requestor (i.e. this machine) will generate a challenge
|
|||
|
and send it to the Primary Domain Controller and will receive
|
|||
|
a challenge from the primary in response. Now we will compute
|
|||
|
credentials using primary's challenge and send it across and
|
|||
|
wait for credentials, computed at primary using our initial
|
|||
|
challenge, to be returned by PDC. Before computing credentials
|
|||
|
a sessionkey will be built which uniquely identifies this
|
|||
|
session and it will be returned to caller for future use.
|
|||
|
|
|||
|
If both machines authenticate then they keep the
|
|||
|
ClientCredential and the session key for future use.
|
|||
|
|
|||
|
?? If multiple domains are supported on a single DC, what mechanism
|
|||
|
do I use to short circuit discovery? What mechanism do I use to short
|
|||
|
circuit API calls (e.g., pass through authentication) to a DC in that
|
|||
|
domain? Do Ihave to worry about lock contention across such API calls?
|
|||
|
Can I avoid authentication/encryption acress such a secure channel?
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
On Input the following fields must be set:
|
|||
|
CsState
|
|||
|
CsNetbiosDomainName
|
|||
|
CsUncServerName (May be NULL string depending on SecureChannelType)
|
|||
|
CsAccountName
|
|||
|
CsSecureChannelType
|
|||
|
The caller must be a writer of the ClientSession.
|
|||
|
|
|||
|
On Output, the following fields will be set
|
|||
|
CsConnectionStatus
|
|||
|
CsState
|
|||
|
CsSessionKey
|
|||
|
CsAuthenticationSeed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
NETLOGON_CREDENTIAL ServerChallenge;
|
|||
|
NETLOGON_CREDENTIAL ClientChallenge;
|
|||
|
NETLOGON_CREDENTIAL ComputedServerCredential;
|
|||
|
NETLOGON_CREDENTIAL ReturnedServerCredential;
|
|||
|
|
|||
|
BOOLEAN WeDidDiscovery = FALSE;
|
|||
|
BOOLEAN WeDidDiscoveryWithAccount = FALSE;
|
|||
|
BOOLEAN ErrorFromDiscoveredServer = FALSE;
|
|||
|
BOOLEAN SignOrSealError = FALSE;
|
|||
|
BOOLEAN GotNonDsDc = FALSE;
|
|||
|
BOOLEAN DomainDowngraded = FALSE;
|
|||
|
|
|||
|
NT_OWF_PASSWORD NtOwfPassword;
|
|||
|
DWORD NegotiatedFlags;
|
|||
|
PUNICODE_STRING NewPassword = NULL;
|
|||
|
PUNICODE_STRING OldPassword = NULL;
|
|||
|
LARGE_INTEGER PasswordChangeTime;
|
|||
|
NT_OWF_PASSWORD NewOwfPassword;
|
|||
|
PNT_OWF_PASSWORD PNewOwfPassword = NULL;
|
|||
|
NT_OWF_PASSWORD OldOwfPassword;
|
|||
|
PNT_OWF_PASSWORD POldOwfPassword = NULL;
|
|||
|
NT_OWF_PASSWORD PdcOwfPassword;
|
|||
|
ULONG i;
|
|||
|
ULONG KeyStrength;
|
|||
|
DWORD DummyPasswordVersionNumber;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Used to indicate whether the current or the old password is being
|
|||
|
// tried to access the DC.
|
|||
|
// 0: implies the current password
|
|||
|
// 1: implies the old password
|
|||
|
// 2: implies both failed
|
|||
|
//
|
|||
|
|
|||
|
DWORD State;
|
|||
|
|
|||
|
//
|
|||
|
// Ensure we're a writer.
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( ClientSession->CsReferenceCount > 0 );
|
|||
|
NlAssert( ClientSession->CsFlags & CS_WRITER );
|
|||
|
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlSessionSetup: Try Session setup\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// Start the WMI trace of secure channel setup
|
|||
|
//
|
|||
|
|
|||
|
NlpTraceEvent( EVENT_TRACE_TYPE_START, NlpGuidSecureChannelSetup );
|
|||
|
|
|||
|
//
|
|||
|
// If we're free to pick the DC which services our request,
|
|||
|
// do so.
|
|||
|
//
|
|||
|
// Apparently there was a problem with the previously chosen DC
|
|||
|
// so we pick again here. (There is a chance we'll pick the same server.)
|
|||
|
//
|
|||
|
|
|||
|
NlPrint(( NL_SESSION_MORE, "NlSessionSetup: ClientSession->CsState = 0x%lx\n",
|
|||
|
ClientSession->CsState));
|
|||
|
|
|||
|
if ( ClientSession->CsState == CS_IDLE ) {
|
|||
|
NlAssert( ClientSession->CsUncServerName == NULL );
|
|||
|
|
|||
|
WeDidDiscovery = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Pick the name of a DC in the domain.
|
|||
|
//
|
|||
|
// On the first try do not specify the account in
|
|||
|
// the discovery attempt as discoveries with account
|
|||
|
// are much more costly than plain discoveries on the
|
|||
|
// server side. If we fail session setup because the
|
|||
|
// discovered server doesn't have our account, we will
|
|||
|
// retry the discovery with account below.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlDiscoverDc( ClientSession,
|
|||
|
DT_Synchronous,
|
|||
|
FALSE,
|
|||
|
FALSE ) ; // without account
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: cannot pick trusted DC\n" ));
|
|||
|
|
|||
|
goto Cleanup;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
NlAssert( ClientSession->CsState != CS_IDLE );
|
|||
|
|
|||
|
FirstTryFailed:
|
|||
|
|
|||
|
//
|
|||
|
// If this is a workstation in an NT5 domain, we should not use NT4 DC.
|
|||
|
// Indeed, Negotiate will not use an NT4 DC in a mixed mode domain to
|
|||
|
// prevent a downgrade attack.
|
|||
|
//
|
|||
|
if ( NlGlobalMemberWorkstation &&
|
|||
|
(ClientSession->CsDiscoveryFlags & CS_DISCOVERY_HAS_DS) == 0 &&
|
|||
|
(ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST) != 0 ) {
|
|||
|
|
|||
|
NET_API_STATUS NetStatus;
|
|||
|
PDOMAIN_CONTROLLER_INFOW DomainControllerInfo = NULL;
|
|||
|
|
|||
|
GotNonDsDc = TRUE;
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession, "NlSessionSetup: Only downlevel DC available\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// Determine whether the domain has been downgraded (just to warn
|
|||
|
// the user). To determine this, try to discover a PDC and if the
|
|||
|
// PDC is available and it is NT4, the domain has been indeed
|
|||
|
// downgraded. In such case, this workstation should rejoin the
|
|||
|
// domain.
|
|||
|
//
|
|||
|
NetStatus = DsrGetDcNameEx2( NULL,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
|
|||
|
&DomainControllerInfo );
|
|||
|
|
|||
|
if ( NetStatus == NO_ERROR &&
|
|||
|
(DomainControllerInfo->Flags & DS_DS_FLAG) == 0 ) {
|
|||
|
DomainDowngraded = TRUE; // Domain has been downgraded (rejoin needed)
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: NT5 domain has been downgraded.\n" ));
|
|||
|
}
|
|||
|
|
|||
|
if ( DomainControllerInfo != NULL ) {
|
|||
|
NetApiBufferFree( DomainControllerInfo );
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Prepare our challenge
|
|||
|
//
|
|||
|
|
|||
|
NlComputeChallenge( &ClientChallenge );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ClientChallenge = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ClientChallenge, sizeof(ClientChallenge) );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the Password of the account from LSA secret storage
|
|||
|
//
|
|||
|
|
|||
|
Status = NlGetOutgoingPassword( ClientSession,
|
|||
|
&NewPassword,
|
|||
|
&OldPassword,
|
|||
|
&DummyPasswordVersionNumber,
|
|||
|
&PasswordChangeTime );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: cannot NlGetOutgoingPassword 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// return more appropriate error.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Try setting up a secure channel first using the CurrentPassword.
|
|||
|
// If that fails, try using the OldPassword
|
|||
|
// If that fails, for interdomain trusts, try the password from our PDC
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
for ( State = 0; ; State++ ) {
|
|||
|
|
|||
|
//
|
|||
|
// Use the right password for this iteration
|
|||
|
//
|
|||
|
|
|||
|
if ( State == 0 ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the new password isn't present in the LSA,
|
|||
|
// just ignore it.
|
|||
|
//
|
|||
|
|
|||
|
if ( NewPassword == NULL ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the NT OWF password
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword( NewPassword,
|
|||
|
&NewOwfPassword );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// return more appropriate error.
|
|||
|
//
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try this password
|
|||
|
//
|
|||
|
|
|||
|
PNewOwfPassword = &NewOwfPassword;
|
|||
|
NtOwfPassword = NewOwfPassword;
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Clear New Password = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, NewPassword->Buffer, NewPassword->Length );
|
|||
|
NlpDumpTime( NL_CHALLENGE_RES, "NlSessionSetup: Password Changed: ", PasswordChangeTime );
|
|||
|
|
|||
|
//
|
|||
|
// On the second iteration, use the old password
|
|||
|
//
|
|||
|
|
|||
|
} else if ( State == 1 ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the old password isn't present in the LSA,
|
|||
|
// just ignore it.
|
|||
|
//
|
|||
|
|
|||
|
if ( OldPassword == NULL ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if the old password is the same as the new one
|
|||
|
//
|
|||
|
|
|||
|
if ( NewPassword != NULL && OldPassword != NULL &&
|
|||
|
NewPassword->Length == OldPassword->Length &&
|
|||
|
RtlEqualMemory( NewPassword->Buffer,
|
|||
|
OldPassword->Buffer,
|
|||
|
OldPassword->Length ) ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: new password is bad. Old password is same as new password.\n" ));
|
|||
|
continue; // Try the password from our PDC
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the NT OWF password
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword( OldPassword,
|
|||
|
&OldOwfPassword );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// return more appropriate error.
|
|||
|
//
|
|||
|
if ( !NlpIsNtStatusResourceError( Status )) {
|
|||
|
Status = STATUS_NO_TRUST_LSA_SECRET;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try this password
|
|||
|
//
|
|||
|
|
|||
|
POldOwfPassword = &OldOwfPassword;
|
|||
|
NtOwfPassword = OldOwfPassword;
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: new password is bad, try old one\n" ));
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Clear Old Password = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, OldPassword->Buffer, OldPassword->Length );
|
|||
|
NlpDumpTime( NL_CHALLENGE_RES, "NlSessionSetup: Password Changed: ", PasswordChangeTime );
|
|||
|
|
|||
|
//
|
|||
|
// On the third iteration, for an interdomain trust account,
|
|||
|
// use the password from the PDC. We actually think this is
|
|||
|
// useful only for NT4 trusted side that keeps only one
|
|||
|
// password. For NT5 or later, one of the passwords above
|
|||
|
// should work, but ...
|
|||
|
//
|
|||
|
|
|||
|
} else if ( State == 2 &&
|
|||
|
ClientSession->CsDomainInfo->DomRole == RoleBackup &&
|
|||
|
IsDomainSecureChannelType(ClientSession->CsSecureChannelType) ) {
|
|||
|
|
|||
|
Status = NlGetPasswordFromPdc(
|
|||
|
ClientSession->CsDomainInfo,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
ClientSession->CsSecureChannelType,
|
|||
|
&PdcOwfPassword );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintDom(( NL_CRITICAL, ClientSession->CsDomainInfo,
|
|||
|
"NlSessionSetup: Can't NlGetPasswordFromPdc %ws 0x%lx.\n",
|
|||
|
ClientSession->CsAccountName,
|
|||
|
Status ));
|
|||
|
// Ignore the particular status from the PDC
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if this password is the same as the new one we have
|
|||
|
//
|
|||
|
|
|||
|
if ( PNewOwfPassword != NULL &&
|
|||
|
RtlEqualNtOwfPassword(&PdcOwfPassword, PNewOwfPassword) ) {
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: PDC password is same as new password.\n" ));
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if this password is the same as the old one we have
|
|||
|
//
|
|||
|
|
|||
|
if ( POldOwfPassword != NULL &&
|
|||
|
RtlEqualNtOwfPassword(&PdcOwfPassword, POldOwfPassword) ) {
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: PDC password is same as old password.\n" ));
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try this password
|
|||
|
//
|
|||
|
|
|||
|
NtOwfPassword = PdcOwfPassword;
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: try password from the PDC\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// We tried our best but nothing worked
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Password = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &NtOwfPassword, sizeof(NtOwfPassword) );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the primary's challenge
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( ClientSession->CsState != CS_IDLE );
|
|||
|
NL_API_START( Status, ClientSession, TRUE ) {
|
|||
|
|
|||
|
NlAssert( ClientSession->CsUncServerName != NULL );
|
|||
|
Status = I_NetServerReqChallenge(ClientSession->CsUncServerName,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&ClientChallenge,
|
|||
|
&ServerChallenge );
|
|||
|
|
|||
|
} NL_API_ELSE ( Status, ClientSession, FALSE ) {
|
|||
|
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: "
|
|||
|
"cannot FinishApiClientSession for I_NetServerReqChallenge 0x%lx\n",
|
|||
|
Status ));
|
|||
|
// Failure here indicates that the discovered server is really slow.
|
|||
|
// Let the "ErrorFromDiscoveredServer" logic do the rediscovery.
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
// We're dropping the secure channel so
|
|||
|
// ensure we don't use any successful status from the DC
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
}
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
|
|||
|
} NL_API_END;
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: "
|
|||
|
"cannot I_NetServerReqChallenge 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// If access is denied, it might be because we weren't able to
|
|||
|
// authenticate with the new password, try the old password.
|
|||
|
//
|
|||
|
// Between NT 5 machines, we use Kerberos (and the machine account) to
|
|||
|
// authenticate this machine.
|
|||
|
|
|||
|
if ( Status == STATUS_ACCESS_DENIED && State == 0 ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerChallenge = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ServerChallenge, sizeof(ServerChallenge) );
|
|||
|
|
|||
|
//
|
|||
|
// For NT 5 to NT 5,
|
|||
|
// use a stronger session key.
|
|||
|
//
|
|||
|
|
|||
|
if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_HAS_DS) != 0 ||
|
|||
|
NlGlobalParameters.RequireStrongKey ) {
|
|||
|
KeyStrength = NETLOGON_SUPPORTS_STRONG_KEY;
|
|||
|
} else {
|
|||
|
KeyStrength = 0;
|
|||
|
}
|
|||
|
//
|
|||
|
// Actually compute the session key given the two challenges and the
|
|||
|
// password.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlMakeSessionKey(
|
|||
|
KeyStrength,
|
|||
|
&NtOwfPassword,
|
|||
|
&ClientChallenge,
|
|||
|
&ServerChallenge,
|
|||
|
&ClientSession->CsSessionKey );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: cannot NlMakeSessionKey 0x%lx\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: SessionKey = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ClientSession->CsSessionKey, sizeof(ClientSession->CsSessionKey) );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Prepare credentials using our challenge.
|
|||
|
//
|
|||
|
|
|||
|
NlComputeCredentials( &ClientChallenge,
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ClientSession->CsSessionKey );
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: Authentication Seed = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ClientSession->CsAuthenticationSeed, sizeof(ClientSession->CsAuthenticationSeed) );
|
|||
|
|
|||
|
//
|
|||
|
// Send these credentials to primary. The primary will compute
|
|||
|
// credentials using the challenge supplied by us and compare
|
|||
|
// with these. If both match then it will compute credentials
|
|||
|
// using its challenge and return it to us for verification
|
|||
|
//
|
|||
|
|
|||
|
NL_API_START( Status, ClientSession, TRUE ) {
|
|||
|
|
|||
|
NegotiatedFlags = NETLOGON_SUPPORTS_MASK |
|
|||
|
KeyStrength |
|
|||
|
(NlGlobalParameters.AvoidSamRepl ? NETLOGON_SUPPORTS_AVOID_SAM_REPL : 0) |
|
|||
|
#ifdef ENABLE_AUTH_RPC
|
|||
|
((NlGlobalParameters.SignSecureChannel||NlGlobalParameters.SealSecureChannel) ? (NETLOGON_SUPPORTS_AUTH_RPC|NETLOGON_SUPPORTS_LSA_AUTH_RPC) : 0) |
|
|||
|
#endif // ENABLE_AUTH_RPC
|
|||
|
(NlGlobalParameters.AvoidLsaRepl ? NETLOGON_SUPPORTS_AVOID_LSA_REPL : 0) |
|
|||
|
(NlGlobalParameters.NeutralizeNt4Emulator ? NETLOGON_SUPPORTS_NT4EMULATOR_NEUTRALIZER : 0);
|
|||
|
|
|||
|
NlAssert( ClientSession->CsUncServerName != NULL );
|
|||
|
ClientSession->CsNegotiatedFlags = NegotiatedFlags;
|
|||
|
|
|||
|
Status = I_NetServerAuthenticate3( ClientSession->CsUncServerName,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
ClientSession->CsSecureChannelType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ReturnedServerCredential,
|
|||
|
&ClientSession->CsNegotiatedFlags,
|
|||
|
&ClientSession->CsAccountRid );
|
|||
|
|
|||
|
//
|
|||
|
// Releases older then NT 5.0 used older authentication API.
|
|||
|
//
|
|||
|
|
|||
|
if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
|
|||
|
NlPrint((NL_CRITICAL,"NlSessionSetup: Fall back to Authenticate2\n" ));
|
|||
|
ClientSession->CsNegotiatedFlags = NegotiatedFlags;
|
|||
|
ClientSession->CsAccountRid = 0;
|
|||
|
Status = I_NetServerAuthenticate2( ClientSession->CsUncServerName,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
ClientSession->CsSecureChannelType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ReturnedServerCredential,
|
|||
|
&ClientSession->CsNegotiatedFlags );
|
|||
|
|
|||
|
if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
|
|||
|
ClientSession->CsNegotiatedFlags = 0;
|
|||
|
Status = I_NetServerAuthenticate( ClientSession->CsUncServerName,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
ClientSession->CsSecureChannelType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ReturnedServerCredential );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} NL_API_ELSE( Status, ClientSession, FALSE ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: "
|
|||
|
"cannot FinishApiClientSession for I_NetServerAuthenticate 0x%lx\n",
|
|||
|
Status ));
|
|||
|
// Failure here indicates that the discovered server is really slow.
|
|||
|
// Let the "ErrorFromDiscoveredServer" logic do the rediscovery.
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
// We're dropping the secure channel so
|
|||
|
// ensure we don't use any successful status from the DC
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
}
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
} NL_API_END;
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: "
|
|||
|
"cannot I_NetServerAuthenticate 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// If access is denied, it might be because we weren't able to
|
|||
|
// authenticate with the new password, try the old password.
|
|||
|
//
|
|||
|
|
|||
|
if ( Status == STATUS_ACCESS_DENIED && State == 0 ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerCredential GOT = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ReturnedServerCredential, sizeof(ReturnedServerCredential) );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The DC returned a server credential to us,
|
|||
|
// ensure the server credential matches the one we would compute.
|
|||
|
//
|
|||
|
|
|||
|
NlComputeCredentials( &ServerChallenge,
|
|||
|
&ComputedServerCredential,
|
|||
|
&ClientSession->CsSessionKey);
|
|||
|
|
|||
|
|
|||
|
NlPrint((NL_CHALLENGE_RES,"NlSessionSetup: ServerCredential MADE = " ));
|
|||
|
NlpDumpBuffer(NL_CHALLENGE_RES, &ComputedServerCredential, sizeof(ComputedServerCredential) );
|
|||
|
|
|||
|
|
|||
|
if ( !RtlEqualMemory( &ReturnedServerCredential,
|
|||
|
&ComputedServerCredential,
|
|||
|
sizeof(ReturnedServerCredential)) ) {
|
|||
|
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: "
|
|||
|
"Servercredential don't match ours 0x%lx\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we require signing or sealing and didn't negotiate it,
|
|||
|
// fail now.
|
|||
|
//
|
|||
|
|
|||
|
if ( NlGlobalParameters.RequireSignOrSeal &&
|
|||
|
(ClientSession->CsNegotiatedFlags & NETLOGON_SUPPORTS_AUTH_RPC) == 0 ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: SignOrSeal required and DC doesn't support it\n" ));
|
|||
|
|
|||
|
SignOrSealError = TRUE;
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
ErrorFromDiscoveredServer = TRUE; // Highly unlikely that retrying will work, but ...
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we require signing or sealing and didn't negotiate it,
|
|||
|
// fail now.
|
|||
|
//
|
|||
|
// We'll never really get this far. Since we used a strong key,
|
|||
|
// we'll get ACCESS_DENIED above.
|
|||
|
//
|
|||
|
|
|||
|
if ( NlGlobalParameters.RequireStrongKey &&
|
|||
|
(ClientSession->CsNegotiatedFlags & NETLOGON_SUPPORTS_STRONG_KEY) == 0 ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: StrongKey required and DC doesn't support it\n" ));
|
|||
|
|
|||
|
SignOrSealError = TRUE;
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
ErrorFromDiscoveredServer = TRUE; // Highly unlikely that retrying will work, but ...
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we've made it this far, we've successfully authenticated
|
|||
|
// with the DC, drop out of the loop.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the new DC is an NT 5 DC,
|
|||
|
// mark it so.
|
|||
|
//
|
|||
|
|
|||
|
if ((ClientSession->CsNegotiatedFlags & NETLOGON_SUPPORTS_GENERIC_PASSTHRU) != 0 ) {
|
|||
|
NlPrintCs(( NL_SESSION_MORE, ClientSession,
|
|||
|
"NlSessionSetup: DC is an NT 5 DC: %ws\n",
|
|||
|
ClientSession->CsUncServerName ));
|
|||
|
|
|||
|
//
|
|||
|
// This flag would have been set during discovery if real discovery was
|
|||
|
// done. However, if NlSetServerClientSession was called from anywhere
|
|||
|
// else other than discovery, the flag may not yet be set.
|
|||
|
//
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_HAS_DS;
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// This flag would be set during client session creation if the domain
|
|||
|
// was an NT 5 domain at that time. If we happened to stumble on an
|
|||
|
// NT 5 DC after the fact, mark it now.
|
|||
|
//
|
|||
|
if ( ClientSession->CsSecureChannelType == WorkstationSecureChannel ) {
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
ClientSession->CsFlags |= CS_NT5_DOMAIN_TRUST;
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If we used the old password to authenticate,
|
|||
|
// update the DC to the current password ASAP.
|
|||
|
//
|
|||
|
|
|||
|
if ( State == 1 ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: old password succeeded\n" ));
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
ClientSession->CsFlags |= CS_UPDATE_PASSWORD;
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the password for our own future reference.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( &ClientSession->CsNtOwfPassword, &NtOwfPassword, sizeof( NtOwfPassword ));
|
|||
|
|
|||
|
//
|
|||
|
// If this is a workstation,
|
|||
|
// grab useful information about the domain.
|
|||
|
//
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, STATUS_SUCCESS ); // Mark session as authenticated
|
|||
|
if ( NlGlobalMemberWorkstation ) {
|
|||
|
|
|||
|
Status = NlUpdateDomainInfo( ClientSession );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: NlUpdateDomainInfo failed 0x%lX\n",
|
|||
|
Status ));
|
|||
|
ErrorFromDiscoveredServer = TRUE;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a DC,
|
|||
|
// determine if we should get the FTinfo from the trusted domain.
|
|||
|
//
|
|||
|
} else {
|
|||
|
PLSA_FOREST_TRUST_INFORMATION ForestTrustInfo;
|
|||
|
|
|||
|
//
|
|||
|
// If this is the PDC,
|
|||
|
// and the trusted domain is a cross forest trust,
|
|||
|
// get the FTinfo from the trusted domain and write it to our TDO.
|
|||
|
//
|
|||
|
// Ignore failures.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsDomainInfo->DomRole == RolePrimary &&
|
|||
|
(ClientSession->CsTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0 ) {
|
|||
|
|
|||
|
Status = NlpGetForestTrustInfoHigher(
|
|||
|
ClientSession,
|
|||
|
DS_GFTI_UPDATE_TDO,
|
|||
|
FALSE, // Don't impersonate caller
|
|||
|
&ForestTrustInfo );
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
NetApiBufferFree( ForestTrustInfo );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup
|
|||
|
//
|
|||
|
|
|||
|
Cleanup:
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources
|
|||
|
//
|
|||
|
|
|||
|
if ( NewPassword != NULL ) {
|
|||
|
LocalFree( NewPassword );
|
|||
|
}
|
|||
|
|
|||
|
if ( OldPassword != NULL ) {
|
|||
|
LocalFree( OldPassword );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Upon success, save the status and reset counters.
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, Status );
|
|||
|
ClientSession->CsAuthAlertCount = 0;
|
|||
|
ClientSession->CsTimeoutCount = 0;
|
|||
|
ClientSession->CsFastCallCount = 0;
|
|||
|
#if NETLOGONDBG
|
|||
|
if ( ClientSession->CsNegotiatedFlags != NegotiatedFlags ) {
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlSessionSetup: negotiated %lx flags rather than %lx\n",
|
|||
|
ClientSession->CsNegotiatedFlags,
|
|||
|
NegotiatedFlags ));
|
|||
|
}
|
|||
|
#endif // NETLOGONDBG
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// write event log and raise alert
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
BOOLEAN RetryDiscovery = FALSE;
|
|||
|
BOOLEAN RetryDiscoveryWithAccount = FALSE;
|
|||
|
WCHAR PreviouslyDiscoveredServer[NL_MAX_DNS_LENGTH+3];
|
|||
|
LPWSTR MsgStrings[4];
|
|||
|
|
|||
|
//
|
|||
|
// Save the name of the discovered server.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsUncServerName != NULL ) {
|
|||
|
wcscpy( PreviouslyDiscoveredServer, ClientSession->CsUncServerName );
|
|||
|
} else {
|
|||
|
wcscpy( PreviouslyDiscoveredServer, L"<Unknown>" );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the failure came from the discovered server,
|
|||
|
// decide whether we should retry the session setup
|
|||
|
// to a different server
|
|||
|
//
|
|||
|
if ( ErrorFromDiscoveredServer ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we didn't do the plain discovery (without account) just now,
|
|||
|
// try the discovery again and redo the session setup.
|
|||
|
//
|
|||
|
if ( !WeDidDiscovery && NlTimeToRediscover(ClientSession, FALSE) ) {
|
|||
|
RetryDiscovery = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we didn't do the discovery with account and
|
|||
|
// the session setup failed because the server didn't have our account and
|
|||
|
// we didn't try a discovery with account recently,
|
|||
|
// try the discovery again (with account) and redo the session setup.
|
|||
|
//
|
|||
|
if ( !WeDidDiscoveryWithAccount &&
|
|||
|
(Status == STATUS_NO_SUCH_USER || Status == STATUS_NO_TRUST_SAM_ACCOUNT) &&
|
|||
|
NlTimeToRediscover(ClientSession, TRUE) ) {
|
|||
|
RetryDiscoveryWithAccount = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If we are to retry the discovery, do so
|
|||
|
//
|
|||
|
|
|||
|
if ( RetryDiscovery || RetryDiscoveryWithAccount ) {
|
|||
|
NTSTATUS TempStatus;
|
|||
|
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlSessionSetup: Retry failed session setup (%s account) since discovery wasn't recent.\n",
|
|||
|
(RetryDiscoveryWithAccount ? "with" : "without") ));
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Pick the name of a new DC in the domain.
|
|||
|
//
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
|
|||
|
|
|||
|
TempStatus = NlDiscoverDc( ClientSession,
|
|||
|
DT_Synchronous,
|
|||
|
FALSE,
|
|||
|
RetryDiscoveryWithAccount ); // retry with account as needed
|
|||
|
|
|||
|
if ( NT_SUCCESS(TempStatus) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Don't bother redoing the session setup if we picked the same DC.
|
|||
|
// In particular, if we retried because the previously found DC
|
|||
|
// didn't have our account, we retried the discovery with account
|
|||
|
// above but may have got the same DC (shouldn't really happen, but...)
|
|||
|
//
|
|||
|
if ( _wcsicmp( ClientSession->CsUncServerName,
|
|||
|
PreviouslyDiscoveredServer ) != 0 ) {
|
|||
|
|
|||
|
//
|
|||
|
// We certainly did a discovery here,
|
|||
|
// but it may or may not be with account
|
|||
|
//
|
|||
|
WeDidDiscovery = TRUE;
|
|||
|
WeDidDiscoveryWithAccount = RetryDiscoveryWithAccount;
|
|||
|
goto FirstTryFailed;
|
|||
|
} else {
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlSessionSetup: Skip retry failed session setup since same DC discovered.\n" ));
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlSessionSetup: Session setup: cannot re-pick trusted DC\n" ));
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
switch(Status) {
|
|||
|
case STATUS_NO_TRUST_LSA_SECRET:
|
|||
|
|
|||
|
MsgStrings[0] = PreviouslyDiscoveredServer;
|
|||
|
MsgStrings[1] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[2] = ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
MsgStrings[3] = NULL; // RaiseNetlogonAlert
|
|||
|
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthNoTrustLsaSecret,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
3 );
|
|||
|
|
|||
|
RaiseNetlogonAlert( NELOG_NetlogonAuthNoTrustLsaSecret,
|
|||
|
MsgStrings,
|
|||
|
&ClientSession->CsAuthAlertCount);
|
|||
|
break;
|
|||
|
|
|||
|
case STATUS_NO_TRUST_SAM_ACCOUNT:
|
|||
|
|
|||
|
MsgStrings[0] = PreviouslyDiscoveredServer;
|
|||
|
MsgStrings[1] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[2] = ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
MsgStrings[3] = NULL; // RaiseNetlogonAlert
|
|||
|
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthNoTrustSamAccount,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
3 );
|
|||
|
|
|||
|
RaiseNetlogonAlert( NELOG_NetlogonAuthNoTrustSamAccount,
|
|||
|
MsgStrings,
|
|||
|
&ClientSession->CsAuthAlertCount);
|
|||
|
break;
|
|||
|
|
|||
|
case STATUS_ACCESS_DENIED:
|
|||
|
|
|||
|
if ( SignOrSealError ) {
|
|||
|
MsgStrings[0] = PreviouslyDiscoveredServer;
|
|||
|
MsgStrings[1] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[2] = NULL; // RaiseNetlogonAlert
|
|||
|
|
|||
|
NlpWriteEventlog (NELOG_NetlogonRequireSignOrSealError,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
MsgStrings,
|
|||
|
2 );
|
|||
|
|
|||
|
RaiseNetlogonAlert( NELOG_NetlogonRequireSignOrSealError,
|
|||
|
MsgStrings,
|
|||
|
&ClientSession->CsAuthAlertCount);
|
|||
|
} else {
|
|||
|
|
|||
|
MsgStrings[0] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[1] = PreviouslyDiscoveredServer;
|
|||
|
MsgStrings[2] = NULL; // RaiseNetlogonAlert
|
|||
|
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthDCFail,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
2 );
|
|||
|
|
|||
|
RaiseNetlogonAlert( NELOG_NetlogonAuthDCFail,
|
|||
|
MsgStrings,
|
|||
|
&ClientSession->CsAuthAlertCount);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case STATUS_NO_LOGON_SERVERS:
|
|||
|
default:
|
|||
|
|
|||
|
MsgStrings[0] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[1] = (LPWSTR) LongToPtr( Status );
|
|||
|
|
|||
|
// The order of checks is important
|
|||
|
if ( DomainDowngraded ) {
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthDomainDowngraded,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
|
|||
|
} else if ( GotNonDsDc ) {
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthNoUplevelDomainController,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
|
|||
|
} else {
|
|||
|
NlpWriteEventlog (NELOG_NetlogonAuthNoDomainController,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) &Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
|
|||
|
}
|
|||
|
|
|||
|
MsgStrings[0] = ClientSession->CsDebugDomainName;
|
|||
|
MsgStrings[1] = PreviouslyDiscoveredServer;
|
|||
|
MsgStrings[2] = NULL; // RaiseNetlogonAlert
|
|||
|
|
|||
|
RaiseNetlogonAlert( ALERT_NetlogonAuthDCFail,
|
|||
|
MsgStrings,
|
|||
|
&ClientSession->CsAuthAlertCount);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// ??: Is this how to handle failure for all account types.
|
|||
|
//
|
|||
|
|
|||
|
switch(Status) {
|
|||
|
|
|||
|
case STATUS_NO_TRUST_LSA_SECRET:
|
|||
|
case STATUS_NO_TRUST_SAM_ACCOUNT:
|
|||
|
case STATUS_ACCESS_DENIED:
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, Status );
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Mark the time we last tried to authenticate.
|
|||
|
//
|
|||
|
// We need to do this after NlSetStatusClientSession which zeros
|
|||
|
// CsLastAuthenticationTry.
|
|||
|
//
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
NlQuerySystemTime( &ClientSession->CsLastAuthenticationTry );
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlSessionSetup: Session setup %s\n",
|
|||
|
(NT_SUCCESS(ClientSession->CsConnectionStatus)) ? "Succeeded" : "Failed" ));
|
|||
|
|
|||
|
//
|
|||
|
// End the WMI trace of secure channel setup
|
|||
|
//
|
|||
|
|
|||
|
NlpTraceEvent( EVENT_TRACE_TYPE_END, NlpGuidSecureChannelSetup );
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NlTimeHasElapsedEx(
|
|||
|
IN PLARGE_INTEGER StartTime,
|
|||
|
IN PLARGE_INTEGER Period,
|
|||
|
OUT PULONG TimeInterval OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if "Timeout" milliseconds has has elapsed since StartTime.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
StartTime - Specifies an absolute time when the event started (100ns units).
|
|||
|
|
|||
|
Period - Specifies a relative time in 100ns units.
|
|||
|
|
|||
|
TimeInterval - If specified and time has elapsed, returns the amount of time
|
|||
|
(in milliseconds) passed since the timeout. If specified and time
|
|||
|
has not elapsed, returns the amount of time (in milliseconds) left until
|
|||
|
Period elapses.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE -- iff Period 100nano-seconds have elapsed since StartTime.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LARGE_INTEGER TimeNow;
|
|||
|
LARGE_INTEGER ElapsedTime;
|
|||
|
BOOLEAN Result = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// Compute the elapsed time since we last authenticated
|
|||
|
//
|
|||
|
|
|||
|
// NlpDumpTime( NL_MISC, "StartTime: ", *StartTime );
|
|||
|
|
|||
|
NlQuerySystemTime( &TimeNow );
|
|||
|
// NlpDumpTime( NL_MISC, "TimeNow: ", TimeNow );
|
|||
|
ElapsedTime.QuadPart = TimeNow.QuadPart - StartTime->QuadPart;
|
|||
|
// NlpDumpTime( NL_MISC, "ElapsedTime: ", ElapsedTime );
|
|||
|
// NlpDumpTime( NL_MISC, "Period: ", *Period );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the elapsed time is negative (totally bogus) or greater than the
|
|||
|
// maximum allowed, indicate that enough time has passed.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
if ( ElapsedTime.QuadPart < 0 ) {
|
|||
|
if ( ARGUMENT_PRESENT( TimeInterval )) {
|
|||
|
*TimeInterval = 0; // pretend it just elapsed
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if ( ElapsedTime.QuadPart > Period->QuadPart ) {
|
|||
|
Result = TRUE;
|
|||
|
} else {
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the caller want to know the amount of time left,
|
|||
|
// compute it.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( TimeInterval )) {
|
|||
|
|
|||
|
LARGE_INTEGER TimeRemaining;
|
|||
|
LARGE_INTEGER MillisecondsRemaining;
|
|||
|
|
|||
|
/*lint -e569 */ /* don't complain about 32-bit to 31-bit initialize */
|
|||
|
LARGE_INTEGER BaseGetTickMagicDivisor = { 0xe219652c, 0xd1b71758 };
|
|||
|
/*lint +e569 */ /* don't complain about 32-bit to 31-bit initialize */
|
|||
|
CCHAR BaseGetTickMagicShiftCount = 13;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Compute the Time remaining/passed on the timer.
|
|||
|
//
|
|||
|
if ( Result == FALSE ) {
|
|||
|
TimeRemaining.QuadPart = Period->QuadPart - ElapsedTime.QuadPart;
|
|||
|
} else {
|
|||
|
TimeRemaining.QuadPart = ElapsedTime.QuadPart - Period->QuadPart;
|
|||
|
}
|
|||
|
// NlpDumpTime( NL_MISC, "TimeRemaining: ", TimeRemaining );
|
|||
|
|
|||
|
//
|
|||
|
// Compute the number of milliseconds remaining/passed.
|
|||
|
//
|
|||
|
|
|||
|
MillisecondsRemaining = RtlExtendedMagicDivide(
|
|||
|
TimeRemaining,
|
|||
|
BaseGetTickMagicDivisor,
|
|||
|
BaseGetTickMagicShiftCount );
|
|||
|
|
|||
|
// NlpDumpTime( NL_MISC, "MillisecondsRemaining: ", MillisecondsRemaining );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the time is in the far distant future/past,
|
|||
|
// round it down.
|
|||
|
//
|
|||
|
|
|||
|
if ( MillisecondsRemaining.HighPart != 0 ||
|
|||
|
MillisecondsRemaining.LowPart > TIMER_MAX_PERIOD ) {
|
|||
|
|
|||
|
*TimeInterval = TIMER_MAX_PERIOD;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
*TimeInterval = MillisecondsRemaining.LowPart;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NlTimeToReauthenticate(
|
|||
|
IN PCLIENT_SESSION ClientSession
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if it is time to reauthenticate this Client Session.
|
|||
|
To reduce the number of re-authentication attempts, we try
|
|||
|
to re-authenticate only on demand and then only at most every 45
|
|||
|
seconds.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure used to define the session.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE -- iff it is time to re-authenticate
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN ReturnBoolean;
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
ReturnBoolean = NetpLogonTimeHasElapsed(
|
|||
|
ClientSession->CsLastAuthenticationTry,
|
|||
|
MAX_DC_AUTHENTICATION_WAIT );
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
return ReturnBoolean;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
NlCreateShare(
|
|||
|
LPWSTR SharePath,
|
|||
|
LPWSTR ShareName,
|
|||
|
BOOLEAN AllowAuthenticatedUsers
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Share the netlogon scripts directory.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SharePath - Path that the new share should be point to.
|
|||
|
|
|||
|
ShareName - Name of the share.
|
|||
|
|
|||
|
AllowAuthenticatedUsers - TRUE if AuthenticatedUsers should have
|
|||
|
Full Control on this share.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE: if successful
|
|||
|
FALSE: if error (NlExit was called)
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NET_API_STATUS NetStatus;
|
|||
|
SHARE_INFO_502 ShareInfo502;
|
|||
|
|
|||
|
WORD AnsiSize;
|
|||
|
CHAR AnsiRemark[NNLEN+1];
|
|||
|
TCHAR Remark[NNLEN+1];
|
|||
|
|
|||
|
ACE_DATA AceData[] = {
|
|||
|
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
|||
|
GENERIC_EXECUTE | GENERIC_READ, &WorldSid},
|
|||
|
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
|||
|
GENERIC_ALL, &AliasAdminsSid},
|
|||
|
// Must be the last ACE
|
|||
|
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
|||
|
GENERIC_ALL, &AuthenticatedUserSid}
|
|||
|
};
|
|||
|
ULONG AceCount = (sizeof(AceData)/sizeof(AceData[0]));
|
|||
|
|
|||
|
//
|
|||
|
// If Authenticated Users shouldn't be allowed full control,
|
|||
|
// remove the authenticated user ACE.
|
|||
|
//
|
|||
|
|
|||
|
if ( !AllowAuthenticatedUsers ) {
|
|||
|
AceCount --;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Build the structure describing the share.
|
|||
|
//
|
|||
|
|
|||
|
ShareInfo502.shi502_path = SharePath;
|
|||
|
ShareInfo502.shi502_security_descriptor = NULL;
|
|||
|
|
|||
|
NlPrint((NL_INIT, "'%ws' share is to '%ws'\n",
|
|||
|
ShareName,
|
|||
|
SharePath));
|
|||
|
|
|||
|
NetStatus = (NET_API_STATUS) DosGetMessage(
|
|||
|
NULL, // No insertion strings
|
|||
|
0, // No insertion strings
|
|||
|
AnsiRemark,
|
|||
|
sizeof(AnsiRemark),
|
|||
|
MTXT_LOGON_SRV_SHARE_REMARK,
|
|||
|
MESSAGE_FILENAME,
|
|||
|
&AnsiSize );
|
|||
|
|
|||
|
if ( NetStatus == NERR_Success ) {
|
|||
|
NetpCopyStrToTStr( Remark, AnsiRemark );
|
|||
|
ShareInfo502.shi502_remark = Remark;
|
|||
|
} else {
|
|||
|
ShareInfo502.shi502_remark = TEXT( "" );
|
|||
|
}
|
|||
|
|
|||
|
ShareInfo502.shi502_netname = ShareName;
|
|||
|
ShareInfo502.shi502_type = STYPE_DISKTREE;
|
|||
|
ShareInfo502.shi502_permissions = ACCESS_READ;
|
|||
|
ShareInfo502.shi502_max_uses = 0xffffffff;
|
|||
|
ShareInfo502.shi502_passwd = TEXT("");
|
|||
|
|
|||
|
//
|
|||
|
// Set the security descriptor on the share
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Create a security descriptor containing the DACL.
|
|||
|
//
|
|||
|
|
|||
|
Status = NetpCreateSecurityDescriptor(
|
|||
|
AceData,
|
|||
|
AceCount,
|
|||
|
NULL, // Default the owner Sid
|
|||
|
NULL, // Default the primary group
|
|||
|
&ShareInfo502.shi502_security_descriptor );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrint((NL_CRITICAL,
|
|||
|
"'%ws' share: Cannot create security descriptor 0x%lx\n",
|
|||
|
SharePath, Status ));
|
|||
|
|
|||
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|||
|
return NetStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Create the share.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NetShareAdd(NULL, 502, (LPBYTE) &ShareInfo502, NULL);
|
|||
|
|
|||
|
if (NetStatus == NERR_DuplicateShare) {
|
|||
|
|
|||
|
PSHARE_INFO_2 ShareInfo2 = NULL;
|
|||
|
|
|||
|
NlPrint((NL_INIT, "'%ws' share already exists. \n", ShareName));
|
|||
|
|
|||
|
//
|
|||
|
// check to see the shared path is same.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NetShareGetInfo( NULL,
|
|||
|
ShareName,
|
|||
|
2,
|
|||
|
(LPBYTE *) &ShareInfo2 );
|
|||
|
|
|||
|
if ( NetStatus == NERR_Success ) {
|
|||
|
|
|||
|
//
|
|||
|
// compare path names.
|
|||
|
//
|
|||
|
// ShareName is path canonicalized already.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
NlPrint((NL_INIT, "'%ws' share current path is %ws\n", ShareName, ShareInfo2->shi2_path));
|
|||
|
|
|||
|
if( NetpwPathCompare(
|
|||
|
SharePath,
|
|||
|
ShareInfo2->shi2_path, 0, 0 ) != 0 ) {
|
|||
|
|
|||
|
//
|
|||
|
// delete share.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NetShareDel( NULL, ShareName, 0);
|
|||
|
|
|||
|
if( NetStatus == NERR_Success ) {
|
|||
|
|
|||
|
//
|
|||
|
// Recreate share.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NetShareAdd(
|
|||
|
NULL,
|
|||
|
502,
|
|||
|
(LPBYTE) &ShareInfo502,
|
|||
|
NULL);
|
|||
|
|
|||
|
if( NetStatus == NERR_Success ) {
|
|||
|
|
|||
|
NlPrint((NL_INIT,
|
|||
|
"'%ws' share was recreated with new path %ws\n",
|
|||
|
ShareName, SharePath ));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( ShareInfo2 != NULL ) {
|
|||
|
NetpMemoryFree( ShareInfo2 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the security descriptor
|
|||
|
//
|
|||
|
|
|||
|
NetpMemoryFree( ShareInfo502.shi502_security_descriptor );
|
|||
|
|
|||
|
|
|||
|
if ( NetStatus != NERR_Success ) {
|
|||
|
|
|||
|
NlPrint((NL_CRITICAL,
|
|||
|
"'%ws' share: Error attempting to create-share: %ld\n",
|
|||
|
ShareName,
|
|||
|
NetStatus ));
|
|||
|
return NetStatus;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return NERR_Success;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlSamOpenNamedUser(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN LPCWSTR UserName,
|
|||
|
OUT SAMPR_HANDLE *UserHandle OPTIONAL,
|
|||
|
OUT PULONG UserId OPTIONAL,
|
|||
|
OUT PSAMPR_USER_INFO_BUFFER *UserAllInfo OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Utility routine to open a Sam user given the username.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Domain the user is in.
|
|||
|
|
|||
|
UserName - Name of user to open
|
|||
|
|
|||
|
UserHandle - Optionally returns a handle to the opened user.
|
|||
|
|
|||
|
UserId - Optionally returns the relative ID of the opened user.
|
|||
|
|
|||
|
UserAllInfo - Optionally returns ALL of the information about the
|
|||
|
named user. Free the returned information using
|
|||
|
SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_NO_SUCH_USER: if the account doesn't exist
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
UNICODE_STRING UserNameString;
|
|||
|
PSAMPR_USER_INFO_BUFFER LocalUserAllInfo = NULL;
|
|||
|
SID_AND_ATTRIBUTES_LIST ReverseMembership;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( UserHandle) ) {
|
|||
|
*UserHandle = NULL;
|
|||
|
}
|
|||
|
if ( ARGUMENT_PRESENT( UserAllInfo) ) {
|
|||
|
*UserAllInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the info about the user.
|
|||
|
//
|
|||
|
// Use SamIGetUserLogonInformation instead of SamrLookupNamesInDomain and
|
|||
|
// SamrOpen user. The former is more efficient (since it only does one
|
|||
|
// DirSearch and doesn't lock the global SAM lock) and more powerful
|
|||
|
// (since it returns UserAllInformation).
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString( &UserNameString, UserName );
|
|||
|
|
|||
|
Status = SamIGetUserLogonInformation(
|
|||
|
DomainInfo->DomSamAccountDomainHandle,
|
|||
|
SAM_NO_MEMBERSHIPS, // Don't need group memberships
|
|||
|
&UserNameString,
|
|||
|
&LocalUserAllInfo,
|
|||
|
&ReverseMembership,
|
|||
|
UserHandle );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
if ( Status == STATUS_NOT_FOUND ) {
|
|||
|
Status = STATUS_NO_SUCH_USER;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return information to the caller.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(UserId) ) {
|
|||
|
*UserId = LocalUserAllInfo->All.UserId;
|
|||
|
}
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( UserAllInfo) ) {
|
|||
|
*UserAllInfo = LocalUserAllInfo;
|
|||
|
LocalUserAllInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
|
|||
|
if ( LocalUserAllInfo != NULL ) {
|
|||
|
SamIFree_SAMPR_USER_INFO_BUFFER( LocalUserAllInfo, UserAllInformation );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlSamChangePasswordNamedUser(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN LPCWSTR UserName,
|
|||
|
IN PUNICODE_STRING ClearTextPassword OPTIONAL,
|
|||
|
IN PNT_OWF_PASSWORD OwfPassword OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Utility routine to set the OWF password on a user given the username.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Domain the user is in.
|
|||
|
|
|||
|
UserName - Name of user to open
|
|||
|
|
|||
|
ClearTextPassword - Clear text password to set on the account
|
|||
|
|
|||
|
OwfPassword - OWF password to set on the account
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
SAMPR_HANDLE UserHandle = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Open the user that represents this server.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSamOpenNamedUser( DomainInfo, UserName, &UserHandle, NULL, NULL );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If Clear text password isn't NULL, use it.
|
|||
|
// Otherwise use OWF password.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClearTextPassword != NULL ) {
|
|||
|
UNICODE_STRING UserNameString;
|
|||
|
RtlInitUnicodeString( &UserNameString, UserName );
|
|||
|
|
|||
|
Status = SamIChangePasswordForeignUser(
|
|||
|
&UserNameString,
|
|||
|
ClearTextPassword,
|
|||
|
NULL,
|
|||
|
0 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlSamChangePasswordNamedUser: Can't SamIChangePasswordForeignUser %lX\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use the NT OWF Password,
|
|||
|
//
|
|||
|
|
|||
|
} else if ( OwfPassword != NULL ) {
|
|||
|
SAMPR_USER_INFO_BUFFER UserInfo;
|
|||
|
|
|||
|
UserInfo.Internal1.PasswordExpired = FALSE;
|
|||
|
UserInfo.Internal1.LmPasswordPresent = FALSE;
|
|||
|
UserInfo.Internal1.NtPasswordPresent = TRUE;
|
|||
|
UserInfo.Internal1.EncryptedNtOwfPassword =
|
|||
|
*((PENCRYPTED_NT_OWF_PASSWORD)(OwfPassword));
|
|||
|
|
|||
|
Status = SamrSetInformationUser(
|
|||
|
UserHandle,
|
|||
|
UserInternal1Information,
|
|||
|
&UserInfo );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
NlPrint(( NL_CRITICAL,
|
|||
|
"NlSamChangePasswordNamedUser: Can't SamrSetInformationUser %lX\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Cleanup:
|
|||
|
if ( UserHandle != NULL ) {
|
|||
|
(VOID) SamrCloseHandle( &UserHandle );
|
|||
|
}
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlChangePassword(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
IN BOOLEAN ForcePasswordChange,
|
|||
|
OUT PULONG RetCallAgainPeriod OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Change this machine's password at the primary.
|
|||
|
Also update password locally if the call succeeded.
|
|||
|
|
|||
|
To determine if the password of "machine account"
|
|||
|
needs to be changed. If the password is older than
|
|||
|
7 days then it must be changed asap. We will defer
|
|||
|
changing the password if we know before hand that
|
|||
|
primary dc is down since our call will fail anyway.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure describing the session to change the password
|
|||
|
for. The specified structure must be referenced.
|
|||
|
|
|||
|
ForcePasswordChange - TRUE if the password should be changed even if
|
|||
|
the password hasn't expired yet.
|
|||
|
|
|||
|
RetCallAgainPeriod - Returns the amount of time (in milliseconds) that should elapse
|
|||
|
before the caller should call this routine again.
|
|||
|
0: After a period of time determined by the caller.
|
|||
|
MAILSLOT_WAIT_FOREVER: never
|
|||
|
other: After at least this amount of time.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status code
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NETLOGON_AUTHENTICATOR OurAuthenticator;
|
|||
|
NETLOGON_AUTHENTICATOR ReturnAuthenticator;
|
|||
|
|
|||
|
LM_OWF_PASSWORD OwfPassword;
|
|||
|
|
|||
|
LARGE_INTEGER CurrentPasswordTime;
|
|||
|
PUNICODE_STRING CurrentPassword = NULL;
|
|||
|
PUNICODE_STRING OldPassword = NULL;
|
|||
|
DWORD PasswordVersion;
|
|||
|
|
|||
|
WCHAR ClearTextPassword[LM20_PWLEN+1];
|
|||
|
UNICODE_STRING NewPassword;
|
|||
|
|
|||
|
BOOL PasswordChangedOnServer = FALSE;
|
|||
|
BOOL LsaSecretChanged = FALSE;
|
|||
|
BOOL DefaultCurrentPasswordBeingChanged = FALSE;
|
|||
|
BOOL DefaultOldPasswordBeingChanged = FALSE;
|
|||
|
|
|||
|
BOOLEAN AmWriter = FALSE;
|
|||
|
|
|||
|
ULONG CallAgainPeriod = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( ClientSession->CsReferenceCount > 0 );
|
|||
|
|
|||
|
//
|
|||
|
// If the password change was refused by the DC,
|
|||
|
// Don't ever try to change the password again (until the next reboot).
|
|||
|
//
|
|||
|
// This could have been written to try every MaximumPasswordAge. However,
|
|||
|
// that gets complex if you take into consideration the CS_UPDATE_PASSWORD
|
|||
|
// case where the time stamp on the LSA Secret doesn't get changed.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
if ( ClientSession->CsFlags & CS_PASSWORD_REFUSED ) {
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
CallAgainPeriod = MAILSLOT_WAIT_FOREVER;
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Become a writer of the ClientSession.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Can't become writer of client session.\n" ));
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
AmWriter = TRUE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the outgoing password and the time the password was last changed
|
|||
|
//
|
|||
|
|
|||
|
Status = NlGetOutgoingPassword( ClientSession,
|
|||
|
&CurrentPassword,
|
|||
|
&OldPassword,
|
|||
|
&PasswordVersion,
|
|||
|
&CurrentPasswordTime );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot NlGetOutgoingPassword %lX\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the (old or new) password is still the default password
|
|||
|
// (lower case computer name),
|
|||
|
// or the password is null (a convenient default for domain trust),
|
|||
|
// Flag that fact.
|
|||
|
//
|
|||
|
|
|||
|
if ( CurrentPassword == NULL ||
|
|||
|
CurrentPassword->Length == 0 ||
|
|||
|
RtlEqualComputerName( &ClientSession->CsDomainInfo->DomUnicodeComputerNameString,
|
|||
|
CurrentPassword ) ) {
|
|||
|
DefaultCurrentPasswordBeingChanged = TRUE;
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: New LsaSecret is default value.\n" ));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( OldPassword == NULL ||
|
|||
|
OldPassword->Length == 0 ||
|
|||
|
RtlEqualComputerName( &ClientSession->CsDomainInfo->DomUnicodeComputerNameString,
|
|||
|
OldPassword ) ) {
|
|||
|
DefaultOldPasswordBeingChanged = TRUE;
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Old LsaSecret is default value.\n" ));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the password has not yet expired,
|
|||
|
// and the password is not the default,
|
|||
|
// and the password change isn't forced,
|
|||
|
// just return.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
if ( (ClientSession->CsFlags & CS_UPDATE_PASSWORD) == 0 &&
|
|||
|
!NlTimeHasElapsedEx( &CurrentPasswordTime,
|
|||
|
&NlGlobalParameters.MaximumPasswordAge_100ns,
|
|||
|
&CallAgainPeriod ) &&
|
|||
|
!DefaultCurrentPasswordBeingChanged &&
|
|||
|
!DefaultOldPasswordBeingChanged &&
|
|||
|
!ForcePasswordChange ) {
|
|||
|
|
|||
|
//
|
|||
|
// Note that, since NlTimeHasElapsedEx returned FALSE,
|
|||
|
// CallAgainPeriod is the time left until the next
|
|||
|
// password change.
|
|||
|
//
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
CallAgainPeriod = 0; // Let the caller determine the frequency for retries.
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Doing it.\n" ));
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the session isn't authenticated,
|
|||
|
// do so now.
|
|||
|
//
|
|||
|
// We're careful to not force this authentication unless the password
|
|||
|
// needs to be changed.
|
|||
|
//
|
|||
|
// If this is the PDC changing its own password,
|
|||
|
// there's no need to authenticate.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsState != CS_AUTHENTICATED &&
|
|||
|
!( ClientSession->CsSecureChannelType == ServerSecureChannel &&
|
|||
|
ClientSession->CsDomainInfo->DomRole == RolePrimary ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we've tried to authenticate recently,
|
|||
|
// don't bother trying again.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlTimeToReauthenticate( ClientSession ) ) {
|
|||
|
Status = ClientSession->CsConnectionStatus;
|
|||
|
goto Cleanup;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try to set up the session.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSessionSetup( ClientSession );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Once we change the password in LsaSecret storage,
|
|||
|
// all future attempts to change the password should use the value
|
|||
|
// from LsaSecret storage. The secure channel is using the old
|
|||
|
// value of the password.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
if (ClientSession->CsFlags & CS_UPDATE_PASSWORD) {
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Password already updated in secret\n" ));
|
|||
|
|
|||
|
if ( CurrentPassword == NULL ) {
|
|||
|
RtlInitUnicodeString( &NewPassword, NULL );
|
|||
|
} else {
|
|||
|
NewPassword = *CurrentPassword;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle the case where LsaSecret storage has not yet been updated.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
ULONG i;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Build a new clear text password using:
|
|||
|
// Entirely random bits.
|
|||
|
// Srvmgr later uses this password as a zero terminated unicode string
|
|||
|
// so ensure there aren't any zero chars in the middle
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if ( !NlGenerateRandomBits( (LPBYTE)ClearTextPassword, sizeof(ClearTextPassword))) {
|
|||
|
NlPrint((NL_CRITICAL, "Can't NlGenerateRandomBits for clear password\n" ));
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0; i < sizeof(ClearTextPassword)/sizeof(WCHAR); i++) {
|
|||
|
if ( ClearTextPassword[i] == '\0') {
|
|||
|
ClearTextPassword[i] = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
ClearTextPassword[LM20_PWLEN] = L'\0';
|
|||
|
|
|||
|
RtlInitUnicodeString( &NewPassword, ClearTextPassword );
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// Set the new outgoing password locally.
|
|||
|
//
|
|||
|
// Set the OldValue to the perviously obtained CurrentValue.
|
|||
|
// Increment the password version number.
|
|||
|
//
|
|||
|
PasswordVersion++;
|
|||
|
Status = NlSetOutgoingPassword(
|
|||
|
ClientSession,
|
|||
|
&NewPassword,
|
|||
|
CurrentPassword,
|
|||
|
PasswordVersion,
|
|||
|
PasswordVersion-1 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot NlSetOutgoingPassword %lX\n",
|
|||
|
Status));
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Flag that we've updated the password in LsaSecret storage.
|
|||
|
//
|
|||
|
|
|||
|
LsaSecretChanged = TRUE;
|
|||
|
ClientSession->CsFlags |= CS_UPDATE_PASSWORD;
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Flag password changed in LsaSecret\n" ));
|
|||
|
|
|||
|
}
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Perform the initial encryption.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword( &NewPassword, &OwfPassword);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status )) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot RtlCalculateNtOwfPassword %lX\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a PDC, all we need to do is change the local account password
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsSecureChannelType == ServerSecureChannel &&
|
|||
|
ClientSession->CsDomainInfo->DomRole == RolePrimary ) {
|
|||
|
Status = NlSamChangePasswordNamedUser( ClientSession->CsDomainInfo,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
&NewPassword,
|
|||
|
&OwfPassword );
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
PasswordChangedOnServer = TRUE;
|
|||
|
} else {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot change password on PDC local user account 0x%lx\n",
|
|||
|
Status));
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Change the password on the PDC
|
|||
|
//
|
|||
|
|
|||
|
Status = NlChangePasswordHigher( ClientSession,
|
|||
|
ClientSession->CsAccountName,
|
|||
|
ClientSession->CsSecureChannelType,
|
|||
|
&OwfPassword,
|
|||
|
&NewPassword,
|
|||
|
&PasswordVersion );
|
|||
|
|
|||
|
if ( Status != STATUS_ACCESS_DENIED ) {
|
|||
|
PasswordChangedOnServer = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the server refused the change,
|
|||
|
// put the lsa secret back the way it was.
|
|||
|
// pretend the change was successful.
|
|||
|
//
|
|||
|
|
|||
|
if ( Status == STATUS_WRONG_PASSWORD ) {
|
|||
|
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: PDC refused to change password\n" ));
|
|||
|
//
|
|||
|
// If we changed the LSA secret,
|
|||
|
// put it back.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
if ( LsaSecretChanged ) {
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: undoing LSA secret change.\n" ));
|
|||
|
|
|||
|
PasswordVersion--;
|
|||
|
Status = NlSetOutgoingPassword(
|
|||
|
ClientSession,
|
|||
|
CurrentPassword,
|
|||
|
OldPassword,
|
|||
|
PasswordVersion,
|
|||
|
PasswordVersion > 0 ? PasswordVersion-1 : 0 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot undo NlSetOutgoingPassword %lX\n",
|
|||
|
Status));
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Undo what we've done above.
|
|||
|
//
|
|||
|
ClientSession->CsFlags &= ~CS_UPDATE_PASSWORD;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Prevent us from trying too frequently.
|
|||
|
//
|
|||
|
|
|||
|
ClientSession->CsFlags |= CS_PASSWORD_REFUSED;
|
|||
|
CallAgainPeriod = MAILSLOT_WAIT_FOREVER;
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
//
|
|||
|
// Avoid special cleanup below.
|
|||
|
//
|
|||
|
PasswordChangedOnServer = FALSE;
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Common exit
|
|||
|
//
|
|||
|
|
|||
|
Cleanup:
|
|||
|
|
|||
|
if ( PasswordChangedOnServer ) {
|
|||
|
|
|||
|
//
|
|||
|
// On success,
|
|||
|
// Indicate that the password has now been updated on the
|
|||
|
// PDC so the old password is no longer in use.
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS( Status ) ) {
|
|||
|
|
|||
|
LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
ClientSession->CsFlags &= ~CS_UPDATE_PASSWORD;
|
|||
|
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Flag password updated on PDC\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// If the default current password was changed,
|
|||
|
// avoid leaving the default password around as the old
|
|||
|
// password. Otherwise, a bogus DC could convince us to use
|
|||
|
// the bogus DC via the default password. Set both current
|
|||
|
// and old version numbers to the new value.
|
|||
|
//
|
|||
|
|
|||
|
if ( DefaultCurrentPasswordBeingChanged ) {
|
|||
|
NlPrintCs((NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlChangePassword: Setting LsaSecret old password to same as new password\n" ));
|
|||
|
|
|||
|
Status = NlSetOutgoingPassword(
|
|||
|
ClientSession,
|
|||
|
&NewPassword,
|
|||
|
&NewPassword,
|
|||
|
PasswordVersion,
|
|||
|
PasswordVersion );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status ) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePassword: Cannot LsarSetSecret to set old password %lX\n",
|
|||
|
Status));
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the password for our own future reference.
|
|||
|
//
|
|||
|
// CsNtOwfPassword is the most recent known good password
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( &ClientSession->CsNtOwfPassword, &OwfPassword, sizeof( OwfPassword ));
|
|||
|
UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
|
|||
|
|
|||
|
//
|
|||
|
// Indicate we don't need to call change the password again for awhile
|
|||
|
//
|
|||
|
|
|||
|
if ( NlGlobalParameters.MaximumPasswordAge > (TIMER_MAX_PERIOD/NL_MILLISECONDS_PER_DAY) ) {
|
|||
|
|
|||
|
CallAgainPeriod = TIMER_MAX_PERIOD;
|
|||
|
} else {
|
|||
|
CallAgainPeriod = NlGlobalParameters.MaximumPasswordAge * NL_MILLISECONDS_PER_DAY;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Notify the Admin that he'll have to manually set this server's
|
|||
|
// password on both this server and the PDC.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
LPWSTR MsgStrings[2];
|
|||
|
|
|||
|
//
|
|||
|
// Drop the secure channel
|
|||
|
//
|
|||
|
|
|||
|
NlSetStatusClientSession( ClientSession, Status );
|
|||
|
|
|||
|
//
|
|||
|
// write event log
|
|||
|
//
|
|||
|
|
|||
|
MsgStrings[0] = ClientSession->CsAccountName;
|
|||
|
MsgStrings[1] = (LPWSTR) LongToPtr( Status );
|
|||
|
|
|||
|
NlpWriteEventlog (
|
|||
|
NELOG_NetlogonPasswdSetFailed,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
(LPBYTE) & Status,
|
|||
|
sizeof(Status),
|
|||
|
MsgStrings,
|
|||
|
2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Clean up locally used resources.
|
|||
|
//
|
|||
|
|
|||
|
if ( CurrentPassword != NULL ) {
|
|||
|
LocalFree( CurrentPassword );
|
|||
|
}
|
|||
|
|
|||
|
if ( OldPassword != NULL ) {
|
|||
|
LocalFree( OldPassword );
|
|||
|
}
|
|||
|
|
|||
|
if ( AmWriter ) {
|
|||
|
NlResetWriterClientSession( ClientSession );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Tell the caller when he should call us again
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( RetCallAgainPeriod) ) {
|
|||
|
*RetCallAgainPeriod = CallAgainPeriod;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlRefreshClientSession(
|
|||
|
IN PCLIENT_SESSION ClientSession
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Refresh the client session info. The info that we intend
|
|||
|
to refresh is:
|
|||
|
|
|||
|
* Server name (the DC can be renamed in Whistler).
|
|||
|
* Discovery flags, in particular whether the server
|
|||
|
is still close.
|
|||
|
* The server IP address.
|
|||
|
|
|||
|
We will also refresh our site name (on workstation).
|
|||
|
|
|||
|
The caller must be a writer of the ClientSession.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure describing the session.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status code
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
NET_API_STATUS NetStatus = NO_ERROR;
|
|||
|
PNL_DC_CACHE_ENTRY NlDcCacheEntry = NULL;
|
|||
|
BOOLEAN DcRediscovered = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// If the client session is idle,
|
|||
|
// there is nothing to refresh
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsState == CS_IDLE ) {
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the server (DC) is NT4.0, there is no need for refresh.
|
|||
|
// (The only info that can potentially change for NT4.0 DC
|
|||
|
// is its IP address which is not worth refreshing)
|
|||
|
//
|
|||
|
|
|||
|
if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_HAS_DS) == 0 ) {
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If it's not yet time to refresh the info,
|
|||
|
// we don't need to do anything
|
|||
|
//
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
if ( !NetpLogonTimeHasElapsed(ClientSession->CsLastRefreshTime,
|
|||
|
MAX_DC_REFRESH_TIMEOUT) ) {
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the up to date server info
|
|||
|
//
|
|||
|
|
|||
|
Status = NlGetAnyDCName( ClientSession,
|
|||
|
FALSE, // Do not require IP
|
|||
|
FALSE, // Don't do with-account discovery
|
|||
|
&NlDcCacheEntry,
|
|||
|
&DcRediscovered );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use this opportunity to update our site on a workstation
|
|||
|
//
|
|||
|
|
|||
|
if ( NlGlobalMemberWorkstation ) {
|
|||
|
|
|||
|
//
|
|||
|
// Only Win2K or newer DCs undestand the site concept
|
|||
|
//
|
|||
|
if ( (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 ) {
|
|||
|
NlSetDynamicSiteName( NlDcCacheEntry->UnicodeClientSiteName );
|
|||
|
} else {
|
|||
|
NlPrint(( NL_SITE,
|
|||
|
"NlRefreshClientSession: NlGetAnyDCName returned NT4 DC\n" ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Cleanup:
|
|||
|
|
|||
|
if ( NlDcCacheEntry != NULL ) {
|
|||
|
NetpDcDerefCacheEntry( NlDcCacheEntry );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlEnsureSessionAuthenticated(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
IN DWORD DesiredFlags
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Ensure there is an authenticated session for the specified ClientSession.
|
|||
|
|
|||
|
If the authenticated DC does not have the characteristics specified by
|
|||
|
DesiredFlags, attempt to find a DC that does.
|
|||
|
|
|||
|
The caller must be a writer of the ClientSession.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure describing the session.
|
|||
|
|
|||
|
|
|||
|
DesiredFlags - characteristics that the authenticated DC should have.
|
|||
|
Can be one or more of the following:
|
|||
|
|
|||
|
CS_DISCOVERY_HAS_DS // Discovered DS has a DS
|
|||
|
CS_DISCOVERY_IS_CLOSE // Discovered DS is in a close site
|
|||
|
|
|||
|
It is the callers responsibility to ensure that the DC really DOES
|
|||
|
have those characteristics.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status code
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// First refresh the client session
|
|||
|
//
|
|||
|
|
|||
|
Status = NlRefreshClientSession( ClientSession );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlpEnsureSessionAuthenticated: Can't refresh the session: 0x%lx\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this secure channel is from a BDC to the PDC,
|
|||
|
// there is only ONE PDC so don't ask for special characteristics.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsSecureChannelType == ServerSecureChannel ) {
|
|||
|
DesiredFlags = 0;
|
|||
|
|
|||
|
//
|
|||
|
// If this secure channel isn't expected to have NT 5 DCs,
|
|||
|
// don't try to find one.
|
|||
|
//
|
|||
|
|
|||
|
} else if ((ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST) == 0 ) {
|
|||
|
DesiredFlags = 0;
|
|||
|
|
|||
|
//
|
|||
|
// If we don't have a close DC,
|
|||
|
// and it has been a long time since we've tried to find a close DC,
|
|||
|
// do it now.
|
|||
|
//
|
|||
|
|
|||
|
} else if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_IS_CLOSE) == 0 ) {
|
|||
|
BOOLEAN ReturnBoolean;
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
if ( NetpLogonTimeHasElapsed(
|
|||
|
ClientSession->CsLastDiscoveryTime,
|
|||
|
NlGlobalParameters.CloseSiteTimeout * 1000 ) ) {
|
|||
|
DesiredFlags |= CS_DISCOVERY_IS_CLOSE;
|
|||
|
}
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a DC has already been detected,
|
|||
|
// and the caller wants special characteristics,
|
|||
|
// try for them now.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsState != CS_IDLE &&
|
|||
|
DesiredFlags != 0 ) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the DC doesn't have the required characteristics,
|
|||
|
// try to find a new one now
|
|||
|
//
|
|||
|
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
if ( (ClientSession->CsDiscoveryFlags & DesiredFlags) != DesiredFlags ) {
|
|||
|
|
|||
|
//
|
|||
|
// Avoid discovery if we've done it recently.
|
|||
|
//
|
|||
|
// All discoveries prefer a DC that has all of the desired characteristics.
|
|||
|
// So if we didn't find one, don't try again.
|
|||
|
//
|
|||
|
|
|||
|
if ( NlTimeToRediscover(ClientSession, FALSE) ) { // we'll do discovery without account
|
|||
|
|
|||
|
NlPrintCs(( NL_SESSION_SETUP, ClientSession,
|
|||
|
"NlpEnsureSessionAuthenticated: Try to find a better DC for this operation. 0x%lx\n", DesiredFlags ));
|
|||
|
|
|||
|
//
|
|||
|
// Discovering a DC when the session is not idle tries to find a
|
|||
|
// "better" DC.
|
|||
|
//
|
|||
|
// Ignore failures.
|
|||
|
//
|
|||
|
// Call without the any locks locked to prevent doing network I/O
|
|||
|
// with the lock held.
|
|||
|
//
|
|||
|
// Don't ask for with-account discovery as it's too costly on the
|
|||
|
// server side. If the discovered server doesn't have our account,
|
|||
|
// the session setup logic will attempt with-account discovery.
|
|||
|
//
|
|||
|
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
Status = NlDiscoverDc ( ClientSession,
|
|||
|
DT_Synchronous,
|
|||
|
FALSE ,
|
|||
|
FALSE ); // without account
|
|||
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we haven't yet authenticated,
|
|||
|
// do so now.
|
|||
|
//
|
|||
|
|
|||
|
if ( ClientSession->CsState != CS_AUTHENTICATED ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we've tried to authenticate recently,
|
|||
|
// don't bother trying again.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlTimeToReauthenticate( ClientSession ) ) {
|
|||
|
Status = ClientSession->CsConnectionStatus;
|
|||
|
NlAssert( !NT_SUCCESS( Status ));
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
Status = STATUS_NO_LOGON_SERVERS;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try to set up the session.
|
|||
|
//
|
|||
|
|
|||
|
Status = NlSessionSetup( ClientSession );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
Cleanup:
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlChangePasswordHigher(
|
|||
|
IN PCLIENT_SESSION ClientSession,
|
|||
|
IN LPWSTR AccountName,
|
|||
|
IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
|
|||
|
IN PLM_OWF_PASSWORD NewOwfPassword OPTIONAL,
|
|||
|
IN PUNICODE_STRING NewClearPassword OPTIONAL,
|
|||
|
IN PDWORD ClearPasswordVersionNumber OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Pass the new password to the machine specified by the ClientSession.
|
|||
|
|
|||
|
The caller must be a writer of the ClientSession.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientSession - Structure describing the session to change the password
|
|||
|
for. The specified structure must be referenced.
|
|||
|
|
|||
|
AccountName - Name of the account whose password is being changed.
|
|||
|
|
|||
|
AccountType - Type of account whose password is being changed.
|
|||
|
|
|||
|
NewOwfPassword - Owf password to pass to ClientSession
|
|||
|
|
|||
|
NewClearPassword - Clear password to pass to client session
|
|||
|
|
|||
|
ClearPasswordVersionNumber - Version number of the clear password. Must
|
|||
|
be present if NewClearPassword is present.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status code
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NETLOGON_AUTHENTICATOR OurAuthenticator;
|
|||
|
NETLOGON_AUTHENTICATOR ReturnAuthenticator;
|
|||
|
SESSION_INFO SessionInfo;
|
|||
|
BOOLEAN FirstTry = TRUE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
NlAssert( ClientSession->CsReferenceCount > 0 );
|
|||
|
NlAssert( ClientSession->CsFlags & CS_WRITER );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the session isn't authenticated,
|
|||
|
// do so now.
|
|||
|
//
|
|||
|
// We're careful to not force this authentication unless the password
|
|||
|
// needs to be changed.
|
|||
|
//
|
|||
|
FirstTryFailed:
|
|||
|
Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
SessionInfo.SessionKey = ClientSession->CsSessionKey;
|
|||
|
SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Build the Authenticator for this request to the PDC.
|
|||
|
//
|
|||
|
|
|||
|
NlBuildAuthenticator(
|
|||
|
&ClientSession->CsAuthenticationSeed,
|
|||
|
&ClientSession->CsSessionKey,
|
|||
|
&OurAuthenticator);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the other side will accept a clear text password,
|
|||
|
// send it.
|
|||
|
//
|
|||
|
|
|||
|
if ( NewClearPassword != NULL &&
|
|||
|
(SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_PASSWORD_SET_2) != 0 ) {
|
|||
|
NL_TRUST_PASSWORD NlTrustPassword;
|
|||
|
NL_PASSWORD_VERSION PasswordVersion;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the new password to the end of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( ((LPBYTE)NlTrustPassword.Buffer) +
|
|||
|
NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
|
|||
|
NewClearPassword->Length,
|
|||
|
NewClearPassword->Buffer,
|
|||
|
NewClearPassword->Length );
|
|||
|
|
|||
|
NlTrustPassword.Length = NewClearPassword->Length;
|
|||
|
|
|||
|
//
|
|||
|
// For an interdomain trust account,
|
|||
|
// indicate that we pass the password version number by prefixing
|
|||
|
// a DWORD equal to PASSWORD_VERSION_NUMBER_PRESENT right before
|
|||
|
// the password in NewClearPassword->Buffer. An old server (RC0)
|
|||
|
// not supporting version numbers will simply ignore these bits.
|
|||
|
// A server supporting version numbers will examine these bits
|
|||
|
// and if they are equal to PASSWORD_VERSION_NUMBER_PRESENT then
|
|||
|
// that will be an indication that a version number is passed. An
|
|||
|
// old client not supporting version numbers will generate random
|
|||
|
// bits in place of PASSWORD_VERSION_NUMBER_PRESENT. It is highly
|
|||
|
// unlikely that an old client will generate random bits equal to
|
|||
|
// PASSWORD_VERSION_NUMBER_PRESENT. The
|
|||
|
// version number will be a DWORD preceeding the DWORD equal to
|
|||
|
// PASSWORD_VERSION_NUMBER_PRESENT. Another DWORD equal to 0 will
|
|||
|
// preceed the version number. Its purpose is to allow any future
|
|||
|
// additions to the buffer. The value of this DWORD different from
|
|||
|
// 0 will indicate without any uncertainty that some additional
|
|||
|
// info is passed preceding this DWORD. The 3 new DWORDs are packed
|
|||
|
// in a struct to avoid unalingment problems.
|
|||
|
//
|
|||
|
|
|||
|
if ( IsDomainSecureChannelType( AccountType ) ) {
|
|||
|
|
|||
|
NlAssert( ClearPasswordVersionNumber != NULL );
|
|||
|
NlAssert( NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
|
|||
|
NewClearPassword->Length -
|
|||
|
sizeof(PasswordVersion) > 0 );
|
|||
|
|
|||
|
PasswordVersion.ReservedField = 0;
|
|||
|
PasswordVersion.PasswordVersionNumber = *ClearPasswordVersionNumber;
|
|||
|
PasswordVersion.PasswordVersionPresent = PASSWORD_VERSION_NUMBER_PRESENT;
|
|||
|
|
|||
|
RtlCopyMemory( ((LPBYTE)NlTrustPassword.Buffer) +
|
|||
|
NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
|
|||
|
NewClearPassword->Length -
|
|||
|
sizeof(PasswordVersion),
|
|||
|
&PasswordVersion,
|
|||
|
sizeof(PasswordVersion) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill the rest of the buffer with random bytes
|
|||
|
//
|
|||
|
|
|||
|
if ( !NlGenerateRandomBits( (LPBYTE)NlTrustPassword.Buffer,
|
|||
|
(NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
|||
|
NewClearPassword->Length -
|
|||
|
sizeof(PasswordVersion) ) ) {
|
|||
|
NlPrint((NL_CRITICAL, "Can't NlGenerateRandomBits for clear password prefix\n" ));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the whole buffer.
|
|||
|
//
|
|||
|
|
|||
|
NlEncryptRC4( &NlTrustPassword,
|
|||
|
sizeof( NlTrustPassword ),
|
|||
|
&SessionInfo );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Change the password on the machine our connection is to.
|
|||
|
//
|
|||
|
|
|||
|
NL_API_START( Status, ClientSession, TRUE ) {
|
|||
|
|
|||
|
NlAssert( ClientSession->CsUncServerName != NULL );
|
|||
|
Status = I_NetServerPasswordSet2( ClientSession->CsUncServerName,
|
|||
|
AccountName,
|
|||
|
AccountType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&OurAuthenticator,
|
|||
|
&ReturnAuthenticator,
|
|||
|
&NlTrustPassword);
|
|||
|
|
|||
|
// NOTE: This call may drop the secure channel behind our back
|
|||
|
} NL_API_ELSE( Status, ClientSession, TRUE ) {
|
|||
|
} NL_API_END;
|
|||
|
|
|||
|
//
|
|||
|
// If the other side needs an OWF password,
|
|||
|
// send it.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrPassword;
|
|||
|
LM_OWF_PASSWORD LocalOwfPassword;
|
|||
|
|
|||
|
//
|
|||
|
// If the caller doesn't know the OWF password,
|
|||
|
// compute the owf.
|
|||
|
//
|
|||
|
|
|||
|
if ( NewOwfPassword == NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Perform the initial encryption.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCalculateNtOwfPassword( NewClearPassword, &LocalOwfPassword);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status )) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePasswordHigher: Cannot RtlCalculateNtOwfPassword %lX\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
NewOwfPassword = &LocalOwfPassword;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Encrypt the password again with the session key.
|
|||
|
// The PDC will decrypt it on the other side.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
|
|||
|
NewOwfPassword,
|
|||
|
(PNT_OWF_PASSWORD) &ClientSession->CsSessionKey,
|
|||
|
&SessKeyEncrPassword) ;
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status )) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePasswordHigher: Cannot RtlEncryptNtOwfPwdWithNtOwfPwd %lX\n",
|
|||
|
Status));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Change the password on the machine our connection is to.
|
|||
|
//
|
|||
|
|
|||
|
NL_API_START( Status, ClientSession, TRUE ) {
|
|||
|
|
|||
|
NlAssert( ClientSession->CsUncServerName != NULL );
|
|||
|
Status = I_NetServerPasswordSet( ClientSession->CsUncServerName,
|
|||
|
AccountName,
|
|||
|
AccountType,
|
|||
|
ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
|
|||
|
&OurAuthenticator,
|
|||
|
&ReturnAuthenticator,
|
|||
|
&SessKeyEncrPassword);
|
|||
|
|
|||
|
// NOTE: This call may drop the secure channel behind our back
|
|||
|
} NL_API_ELSE( Status, ClientSession, TRUE ) {
|
|||
|
} NL_API_END;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now verify primary's authenticator and update our seed
|
|||
|
//
|
|||
|
|
|||
|
if ( NlpDidDcFail( Status ) ||
|
|||
|
!NlUpdateSeed( &ClientSession->CsAuthenticationSeed,
|
|||
|
&ReturnAuthenticator.Credential,
|
|||
|
&ClientSession->CsSessionKey) ) {
|
|||
|
|
|||
|
NlPrintCs(( NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePasswordHigher: denying access after status: 0x%lx\n",
|
|||
|
Status ));
|
|||
|
|
|||
|
//
|
|||
|
// Preserve any status indicating a communication error.
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
Status = STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
NlSetStatusClientSession( ClientSession, Status );
|
|||
|
|
|||
|
//
|
|||
|
// Perhaps the netlogon service on the server has just restarted.
|
|||
|
// Try just once to set up a session to the server again.
|
|||
|
//
|
|||
|
if ( FirstTry ) {
|
|||
|
FirstTry = FALSE;
|
|||
|
goto FirstTryFailed;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Common exit
|
|||
|
//
|
|||
|
|
|||
|
Cleanup:
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
NlPrintCs((NL_CRITICAL, ClientSession,
|
|||
|
"NlChangePasswordHigher: %ws: failed %lX\n",
|
|||
|
AccountName,
|
|||
|
Status));
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NlGetUserPriv(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN ULONG GroupCount,
|
|||
|
IN PGROUP_MEMBERSHIP Groups,
|
|||
|
IN ULONG UserRelativeId,
|
|||
|
OUT LPDWORD Priv,
|
|||
|
OUT LPDWORD AuthFlags
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determines the Priv and AuthFlags for the specified user.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Hosted domain the user account is in.
|
|||
|
|
|||
|
GroupCount - Number of groups this user is a member of
|
|||
|
|
|||
|
Groups - Array of groups this user is a member of.
|
|||
|
|
|||
|
UserRelativeId - Relative ID of the user to query.
|
|||
|
|
|||
|
Priv - Returns the Lanman 2.0 Privilege level for the specified user.
|
|||
|
|
|||
|
AuthFlags - Returns the Lanman 2.0 Authflags for the specified user.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NET_API_STATUS NetStatus;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
ULONG GroupIndex;
|
|||
|
PSID *UserSids = NULL;
|
|||
|
ULONG UserSidCount = 0;
|
|||
|
SAMPR_PSID_ARRAY SamSidArray;
|
|||
|
SAMPR_ULONG_ARRAY Aliases;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization
|
|||
|
//
|
|||
|
|
|||
|
Aliases.Element = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer to point to the SIDs we're interested in
|
|||
|
// alias membership for.
|
|||
|
//
|
|||
|
|
|||
|
UserSids = (PSID *)
|
|||
|
NetpMemoryAllocate( (GroupCount+1) * sizeof(PSID) );
|
|||
|
|
|||
|
if ( UserSids == NULL ) {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the User's Sid to the Array of Sids.
|
|||
|
//
|
|||
|
|
|||
|
NetStatus = NetpDomainIdToSid( DomainInfo->DomAccountDomainId,
|
|||
|
UserRelativeId,
|
|||
|
&UserSids[0] );
|
|||
|
|
|||
|
if ( NetStatus != NERR_Success ) {
|
|||
|
Status = NetpApiStatusToNtStatus( NetStatus );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
UserSidCount ++;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Add each group the user is a member of to the array of Sids.
|
|||
|
//
|
|||
|
|
|||
|
for ( GroupIndex = 0; GroupIndex < GroupCount; GroupIndex ++ ){
|
|||
|
|
|||
|
NetStatus = NetpDomainIdToSid( DomainInfo->DomAccountDomainId,
|
|||
|
Groups[GroupIndex].RelativeId,
|
|||
|
&UserSids[GroupIndex+1] );
|
|||
|
|
|||
|
if ( NetStatus != NERR_Success ) {
|
|||
|
Status = NetpApiStatusToNtStatus( NetStatus );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
UserSidCount ++;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Find out which aliases in the builtin domain this user is a member of.
|
|||
|
//
|
|||
|
|
|||
|
SamSidArray.Count = UserSidCount;
|
|||
|
SamSidArray.Sids = (PSAMPR_SID_INFORMATION) UserSids;
|
|||
|
Status = SamrGetAliasMembership( DomainInfo->DomSamBuiltinDomainHandle,
|
|||
|
&SamSidArray,
|
|||
|
&Aliases );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
Aliases.Element = NULL;
|
|||
|
NlPrint((NL_CRITICAL,
|
|||
|
"NlGetUserPriv: SamGetAliasMembership returns %lX\n",
|
|||
|
Status ));
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the alias membership to priv and auth flags
|
|||
|
//
|
|||
|
|
|||
|
NetpAliasMemberToPriv(
|
|||
|
Aliases.Count,
|
|||
|
Aliases.Element,
|
|||
|
Priv,
|
|||
|
AuthFlags );
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Free Locally used resources.
|
|||
|
//
|
|||
|
Cleanup:
|
|||
|
if ( Aliases.Element != NULL ) {
|
|||
|
SamIFree_SAMPR_ULONG_ARRAY ( &Aliases );
|
|||
|
}
|
|||
|
|
|||
|
if ( UserSids != NULL ) {
|
|||
|
|
|||
|
for ( GroupIndex = 0; GroupIndex < UserSidCount; GroupIndex ++ ) {
|
|||
|
NetpMemoryFree( UserSids[GroupIndex] );
|
|||
|
}
|
|||
|
|
|||
|
NetpMemoryFree( UserSids );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*lint +e740 */ /* don't complain about unusual cast */
|