/*++ Copyright (c) 1987-1991 Microsoft Corporation Module Name: logonapi.c Abstract: Remote Logon API routines. Author: Cliff Van Dyke (cliffv) 28-Jun-1991 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: Madana - Fixed several bugs. --*/ // // Common include files. // #include // Include files common to entire service // // Include files specific to this .c file // #include // Routines shared with NetUser Apis #include // ROUND_UP_COUNT ... #include // AE_* #include #include // Security Descriptor for APIs #include // NetpAccessCheck #include // offsetof() #include // NetpRpcStatusToApiStatus() #include // ROUND_UP_COUTN ... NET_API_STATUS NlEnsureClientIsNamedUser( IN LPWSTR UserName ) /*++ Routine Description: Ensure the client is the named user. Arguments: UserName - name of the user to check. Return Value: NT status code. --*/ { NET_API_STATUS NetStatus; RPC_STATUS RpcStatus; NTSTATUS Status; HANDLE TokenHandle = NULL; PTOKEN_USER TokenUserInfo = NULL; ULONG TokenUserInfoSize; ULONG UserId; PSID UserSid; SAMPR_HANDLE UserHandle = NULL; // // Get the relative ID of the specified user. // Status = NlSamOpenNamedUser( UserName, &UserHandle, &UserId ); if ( !NT_SUCCESS(Status) ) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: NlSamOpenNamedUser failed 0x%lx\n", UserName, Status )); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Impersonate the client while we check him out. // RpcStatus = RpcImpersonateClient( NULL ); if ( RpcStatus != RPC_S_OK ) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: RpcImpersonateClient failed 0x%lx\n", UserName, RpcStatus )); NetStatus = NetpRpcStatusToApiStatus( RpcStatus ); goto Cleanup; } // // Compare the username specified with that in // the impersonation token to ensure the caller isn't bogus. // // Do this by opening the token, // querying the token user info, // and ensuring the returned SID is for this user. // Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, (BOOLEAN) TRUE, // Use the logon service's security context // to open the token &TokenHandle ); if ( !NT_SUCCESS( Status )) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: NtOpenThreadToken failed 0x%lx\n", UserName, Status )); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Get the user's SID for the token. // Status = NtQueryInformationToken( TokenHandle, TokenUser, &TokenUserInfo, 0, &TokenUserInfoSize ); if ( Status != STATUS_BUFFER_TOO_SMALL ) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread failed 0x%lx\n", UserName, Status )); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } TokenUserInfo = NetpMemoryAllocate( TokenUserInfoSize ); if ( TokenUserInfo == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } Status = NtQueryInformationToken( TokenHandle, TokenUser, TokenUserInfo, TokenUserInfoSize, &TokenUserInfoSize ); if ( !NT_SUCCESS(Status) ) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread (again) failed 0x%lx\n", UserName, Status )); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } UserSid = TokenUserInfo->User.Sid; // // Ensure the last subauthority matches the UserId // if ( UserId != *RtlSubAuthoritySid( UserSid, (*RtlSubAuthorityCountSid(UserSid))-1 )){ NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: UserId mismatch 0x%lx\n", UserName, UserId )); NlpDumpSid( NL_CRITICAL, UserSid ); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // Convert the User's sid to a DomainId and ensure it is our domain Id. // (*RtlSubAuthorityCountSid(UserSid)) --; if ( !RtlEqualSid( (PSID) NlGlobalDBInfoArray[SAM_DB].DBId, UserSid ) ) { NlPrint(( NL_CRITICAL, "NlEnsureClientIsNamedUser: %ws: DomainId mismatch 0x%lx\n", UserName, UserId )); NlpDumpSid( NL_CRITICAL, UserSid ); NlpDumpSid( NL_CRITICAL, (PSID) NlGlobalDBInfoArray[SAM_DB].DBId ); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // Done // NetStatus = NERR_Success; Cleanup: // // Clean up locally used resources. // if ( TokenHandle != NULL ) { (VOID) NtClose( TokenHandle ); } if ( TokenUserInfo != NULL ) { NetpMemoryFree( TokenUserInfo ); } // // revert to system, so that we can close // the user handle properly. // (VOID) RpcRevertToSelf(); if ( UserHandle != NULL ) { SamrCloseHandle( &UserHandle ); } return NetStatus; } NET_API_STATUS NetrLogonUasLogon ( IN LPWSTR ServerName, IN LPWSTR UserName, IN LPWSTR Workstation, OUT PNETLOGON_VALIDATION_UAS_INFO *ValidationInformation ) /*++ Routine Description: Server side of I_NetLogonUasLogon. This function is called by the XACT server when processing a I_NetWkstaUserLogon XACT SMB. This feature allows a UAS client to logon to a SAM domain controller. Arguments: ServerName -- Server to perform this operation on. Must be NULL. UserName -- Account name of the user logging on. Workstation -- The workstation from which the user is logging on. ValidationInformation -- Returns the requested validation information. Return Value: NERR_SUCCESS if there was no error. Otherwise, the error code is returned. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; NETLOGON_INTERACTIVE_INFO LogonInteractive; PNETLOGON_VALIDATION_SAM_INFO SamInfo = NULL; PNETLOGON_VALIDATION_UAS_INFO usrlog1 = NULL; DWORD ValidationSize; LPWSTR EndOfVariableData; BOOLEAN Authoritative; BOOLEAN BadPasswordCountZeroed; LARGE_INTEGER TempTime; // // This API is not supported on workstations. // if ( NlGlobalRole == RoleMemberWorkstation ) { return ERROR_NOT_SUPPORTED; } // // This API can only be called locally. (By the XACT server). // if ( ServerName != NULL ) { return ERROR_INVALID_PARAMETER; } // // Initialization // *ValidationInformation = NULL; // // Perform access validation on the caller. // NetStatus = NetpAccessCheck( NlGlobalNetlogonSecurityDescriptor, // Security descriptor NETLOGON_UAS_LOGON_ACCESS, // Desired access &NlGlobalNetlogonInfoMapping ); // Generic mapping if ( NetStatus != NERR_Success) { NlPrint((NL_CRITICAL,"NetrLogonUasLogon of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " failed NetpAccessCheck\n", UserName, Workstation)); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // Ensure the client is actually the named user. // // The server has already validated the password. // The XACT server has already verified that the workstation name is // correct. // NetStatus = NlEnsureClientIsNamedUser( UserName ); if ( NetStatus != NERR_Success ) { NlPrint((NL_CRITICAL,"NetrLogonUasLogon of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " failed NlEnsureClientIsNamedUser\n", UserName, Workstation)); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // Validate the user against the local SAM database. // RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL ); LogonInteractive.Identity.ParameterControl = 0; RtlZeroMemory( &LogonInteractive.Identity.LogonId, sizeof(LogonInteractive.Identity.LogonId) ); RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName ); RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation ); Status = MsvSamValidate( NlGlobalDBInfoArray[SAM_DB].DBHandle, NlGlobalUasCompatibilityMode, NullSecureChannel, // Skip password check &NlGlobalUnicodeComputerNameString, &NlGlobalAccountDomainName, NlGlobalDBInfoArray[SAM_DB].DBId, NetlogonInteractiveInformation, &LogonInteractive, NetlogonValidationSamInfo, (PVOID *)&SamInfo, &Authoritative, &BadPasswordCountZeroed, MSVSAM_SPECIFIED ); if ( !NT_SUCCESS( Status )) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Allocate a return buffer // ValidationSize = sizeof( NETLOGON_VALIDATION_UAS_INFO ) + SamInfo->EffectiveName.Length + sizeof(WCHAR) + (wcslen( NlGlobalUncUnicodeComputerName ) +1) * sizeof(WCHAR) + NlGlobalAccountDomainName.Length + sizeof(WCHAR) + SamInfo->LogonScript.Length + sizeof(WCHAR); ValidationSize = ROUND_UP_COUNT( ValidationSize, ALIGN_WCHAR ); usrlog1 = MIDL_user_allocate( ValidationSize ); if ( usrlog1 == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Convert the SAM information to the right format for LM 2.0 // EndOfVariableData = (LPWSTR) (((PCHAR)usrlog1) + ValidationSize); if ( !NetpCopyStringToBuffer( SamInfo->EffectiveName.Buffer, SamInfo->EffectiveName.Length / sizeof(WCHAR), (LPBYTE) (usrlog1 + 1), &EndOfVariableData, &usrlog1->usrlog1_eff_name ) ) { NetStatus = NERR_InternalError ; goto Cleanup; } Status = NlGetUserPriv( SamInfo->GroupCount, (PGROUP_MEMBERSHIP) SamInfo->GroupIds, SamInfo->UserId, &usrlog1->usrlog1_priv, &usrlog1->usrlog1_auth_flags ); if ( !NT_SUCCESS( Status )) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } usrlog1->usrlog1_num_logons = 0; usrlog1->usrlog1_bad_pw_count = SamInfo->BadPasswordCount; OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogonTime, TempTime); if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_last_logon) ) { usrlog1->usrlog1_last_logon = 0; } OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogoffTime, TempTime); if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_last_logoff) ) { usrlog1->usrlog1_last_logoff = TIMEQ_FOREVER; } OLD_TO_NEW_LARGE_INTEGER( SamInfo->KickOffTime, TempTime); if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_logoff_time) ) { usrlog1->usrlog1_logoff_time = TIMEQ_FOREVER; } if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_kickoff_time) ) { usrlog1->usrlog1_kickoff_time = TIMEQ_FOREVER; } OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordLastSet, TempTime); usrlog1->usrlog1_password_age = NetpGetElapsedSeconds( &TempTime ); OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordCanChange, TempTime); if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_pw_can_change) ) { usrlog1->usrlog1_pw_can_change = TIMEQ_FOREVER; } OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordMustChange, TempTime); if ( !RtlTimeToSecondsSince1970( &TempTime, &usrlog1->usrlog1_pw_must_change) ) { usrlog1->usrlog1_pw_must_change = TIMEQ_FOREVER; } usrlog1->usrlog1_computer = NlGlobalUncUnicodeComputerName; if ( !NetpPackString( &usrlog1->usrlog1_computer, (LPBYTE) (usrlog1 + 1), &EndOfVariableData )) { NetStatus = NERR_InternalError ; goto Cleanup; } if ( !NetpCopyStringToBuffer( NlGlobalAccountDomainName.Buffer, NlGlobalAccountDomainName.Length / sizeof(WCHAR), (LPBYTE) (usrlog1 + 1), &EndOfVariableData, &usrlog1->usrlog1_domain ) ) { NetStatus = NERR_InternalError ; goto Cleanup; } if ( !NetpCopyStringToBuffer( SamInfo->LogonScript.Buffer, SamInfo->LogonScript.Length / sizeof(WCHAR), (LPBYTE) (usrlog1 + 1), &EndOfVariableData, &usrlog1->usrlog1_script_path ) ) { NetStatus = NERR_InternalError ; goto Cleanup; } NetStatus = NERR_Success; // // Done // Cleanup: // // Clean up locally used resources. // if ( SamInfo != NULL ) { MIDL_user_free( SamInfo ); } if ( NetStatus != NERR_Success ) { if ( usrlog1 != NULL ) { MIDL_user_free( usrlog1 ); usrlog1 = NULL; } } NlPrint((NL_LOGON,"NetrLogonUasLogon of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " returns %lu\n", UserName, Workstation, NetStatus )); *ValidationInformation = usrlog1; return(NetStatus); } NET_API_STATUS NetrLogonUasLogoff ( IN LPWSTR ServerName OPTIONAL, IN LPWSTR UserName, IN LPWSTR Workstation, OUT PNETLOGON_LOGOFF_UAS_INFO LogoffInformation ) /*++ Routine Description: This function is called by the XACT server when processing a I_NetWkstaUserLogoff XACT SMB. This feature allows a UAS client to logoff from a SAM domain controller. The request is authenticated, the entry is removed for this user from the logon session table maintained by the Netlogon service for NetLogonEnum, and logoff information is returned to the caller. The server portion of I_NetLogonUasLogoff (in the Netlogon service) compares the user name and workstation name specified in the LogonInformation with the user name and workstation name from the impersonation token. If they don't match, I_NetLogonUasLogoff fails indicating the access is denied. Group SECURITY_LOCAL is refused access to this function. Membership in SECURITY_LOCAL implies that this call was made locally and not through the XACT server. The Netlogon service cannot be sure that this function was called by the XACT server. Therefore, the Netlogon service will not simply delete the entry from the logon session table. Rather, the logon session table entry will be marked invisible outside of the Netlogon service (i.e., it will not be returned by NetLogonEnum) until a valid LOGON_WKSTINFO_RESPONSE is received for the entry. The Netlogon service will immediately interrogate the client (as described above for LOGON_WKSTINFO_RESPONSE) and temporarily increase the interrogation frequency to at least once a minute. The logon session table entry will reappear as soon as a function of interrogation if this isn't a true logoff request. Arguments: ServerName -- Reserved. Must be NULL. UserName -- Account name of the user logging off. Workstation -- The workstation from which the user is logging off. LogoffInformation -- Returns the requested logoff information. Return Value: The Net status code. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; NETLOGON_INTERACTIVE_INFO LogonInteractive; PNETLOGON_LOGOFF_UAS_INFO usrlog1 = NULL; // // This API is not supported on workstations. // if ( NlGlobalRole == RoleMemberWorkstation ) { return ERROR_NOT_SUPPORTED; } // // This API can only be called locally. (By the XACT server). // if ( ServerName != NULL ) { return ERROR_INVALID_PARAMETER; } // // Perform access validation on the caller. // NetStatus = NetpAccessCheck( NlGlobalNetlogonSecurityDescriptor, // Security descriptor NETLOGON_UAS_LOGOFF_ACCESS, // Desired access &NlGlobalNetlogonInfoMapping ); // Generic mapping if ( NetStatus != NERR_Success) { NlPrint((NL_CRITICAL,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " failed NetpAccessCheck\n", UserName, Workstation)); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // Ensure the client is actually the named user. // // The server has already validated the password. // The XACT server has already verified that the workstation name is // correct. // #ifdef notdef // Some clients (WFW 3.11) can call this over the null session NetStatus = NlEnsureClientIsNamedUser( UserName ); if ( NetStatus != NERR_Success ) { NlPrint((NL_CRITICAL,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " failed NlEnsureClientIsNamedUser\n", UserName, Workstation)); NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } #endif // notdef // // Build the LogonInformation to return // LogoffInformation->Duration = 0; LogoffInformation->LogonCount = 0; // // Update the LastLogoff time in the SAM database. // RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL ); LogonInteractive.Identity.ParameterControl = 0; RtlZeroMemory( &LogonInteractive.Identity.LogonId, sizeof(LogonInteractive.Identity.LogonId) ); RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName ); RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation ); Status = MsvSamLogoff( NlGlobalDBInfoArray[SAM_DB].DBHandle, NetlogonInteractiveInformation, &LogonInteractive ); if (!NT_SUCCESS(Status)) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Cleanup // Cleanup: // // Clean up locally used resources. // NlPrint((NL_LOGON,"NetrLogonUasLogoff of " FORMAT_LPWSTR " from " FORMAT_LPWSTR " returns %lu\n", UserName, Workstation, NetStatus)); return NetStatus; } VOID NlpDecryptLogonInformation ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN OUT LPBYTE LogonInformation, IN PSESSION_INFO SessionInfo ) /*++ Routine Description: This function decrypts the sensitive information in the LogonInformation structure. The decryption is done in place. Arguments: LogonLevel -- Specifies the level of information given in LogonInformation. LogonInformation -- Specifies the description for the user logging on. SessionInfo -- The session key to encrypt with and negotiate flags Return Value: None. --*/ { // // Only the interactive and service logon information is encrypted. // if ( LogonLevel == NetlogonInteractiveInformation || LogonLevel == NetlogonServiceInformation ) { PNETLOGON_INTERACTIVE_INFO LogonInteractive; LogonInteractive = (PNETLOGON_INTERACTIVE_INFO) LogonInformation; // // If both sides support RC4 encryption, // decrypt both the LM OWF and NT OWF passwords using RC4. // if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) { NlDecryptRC4( &LogonInteractive->LmOwfPassword, sizeof(LogonInteractive->LmOwfPassword), SessionInfo ); NlDecryptRC4( &LogonInteractive->NtOwfPassword, sizeof(LogonInteractive->NtOwfPassword), SessionInfo ); // // If the other side is running NT 1.0, // use the slower DES based encryption. // } else { NTSTATUS Status; ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword; ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword; // // Decrypt the LM_OWF password. // NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH ); NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey)); EncryptedLmOwfPassword = * ((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword); Status = RtlDecryptLmOwfPwdWithLmOwfPwd( &EncryptedLmOwfPassword, (PLM_OWF_PASSWORD) &SessionInfo->SessionKey, &LogonInteractive->LmOwfPassword ); NlAssert( NT_SUCCESS(Status) ); // // Decrypt the NT_OWF password. // NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH ); NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey)); EncryptedNtOwfPassword = * ((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword); Status = RtlDecryptNtOwfPwdWithNtOwfPwd( &EncryptedNtOwfPassword, (PNT_OWF_PASSWORD) &SessionInfo->SessionKey, &LogonInteractive->NtOwfPassword ); NlAssert( NT_SUCCESS(Status) ); } } return; } VOID NlpEncryptLogonInformation ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN OUT LPBYTE LogonInformation, IN PSESSION_INFO SessionInfo ) /*++ Routine Description: This function encrypts the sensitive information in the LogonInformation structure. The encryption is done in place. Arguments: LogonLevel -- Specifies the level of information given in LogonInformation. LogonInformation -- Specifies the description for the user logging on. SessionInfo -- The session key to encrypt with and negotiate flags Return Value: None. --*/ { NTSTATUS Status; // // Only the interactive and service logon information is encrypted. // if ( LogonLevel == NetlogonInteractiveInformation || LogonLevel == NetlogonServiceInformation ) { PNETLOGON_INTERACTIVE_INFO LogonInteractive; LogonInteractive = (PNETLOGON_INTERACTIVE_INFO) LogonInformation; // // If both sides support RC4 encryption, use it. // encrypt both the LM OWF and NT OWF passwords using RC4. // if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) { NlEncryptRC4( &LogonInteractive->LmOwfPassword, sizeof(LogonInteractive->LmOwfPassword), SessionInfo ); NlEncryptRC4( &LogonInteractive->NtOwfPassword, sizeof(LogonInteractive->NtOwfPassword), SessionInfo ); // // If the other side is running NT 1.0, // use the slower DES based encryption. // } else { ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword; ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword; // // Encrypt the LM_OWF password. // NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH ); NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey)); Status = RtlEncryptLmOwfPwdWithLmOwfPwd( &LogonInteractive->LmOwfPassword, (PLM_OWF_PASSWORD) &SessionInfo->SessionKey, &EncryptedLmOwfPassword ); NlAssert( NT_SUCCESS(Status) ); *((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword) = EncryptedLmOwfPassword; // // Encrypt the NT_OWF password. // NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH ); NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey)); Status = RtlEncryptNtOwfPwdWithNtOwfPwd( &LogonInteractive->NtOwfPassword, (PNT_OWF_PASSWORD) &SessionInfo->SessionKey, &EncryptedNtOwfPassword ); NlAssert( NT_SUCCESS(Status) ); *((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword) = EncryptedNtOwfPassword; } } return; } VOID NlpDecryptValidationInformation ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, IN OUT LPBYTE ValidationInformation, IN PSESSION_INFO SessionInfo ) /*++ Routine Description: This function decrypts the sensitive information in the ValidationInformation structure. The decryption is done in place. Arguments: LogonLevel -- Specifies the Logon level used to obtain ValidationInformation. ValidationLevel -- Specifies the level of information given in ValidationInformation. ValidationInformation -- Specifies the description for the user logging on. SessionInfo -- The session key to encrypt with and negotiated flags. Return Value: None. --*/ { PNETLOGON_VALIDATION_SAM_INFO ValidationInfo; // // Check the validation level. // if ( (ValidationLevel != NetlogonValidationSamInfo) && (ValidationLevel != NetlogonValidationSamInfo2) ) { return; } // // Only network logons contain information which is sensitive. // if ( LogonLevel != NetlogonNetworkInformation ) { return; } ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation; // // If we're suppossed to use RC4, // Decrypt both the NT and LM session keys using RC4. // if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) { NlDecryptRC4( &ValidationInfo->UserSessionKey, sizeof(ValidationInfo->UserSessionKey), SessionInfo ); NlDecryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY], SAMINFO_LM_SESSION_KEY_SIZE, SessionInfo ); // // If the other side is running NT1.0, // be compatible. // } else { NTSTATUS Status; CLEAR_BLOCK ClearBlock; DWORD i; LPBYTE DataBuffer = (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY]; // // Decrypt the LmSessionKey // NlAssert( CLEAR_BLOCK_LENGTH == CYPHER_BLOCK_LENGTH ); NlAssert( (SAMINFO_LM_SESSION_KEY_SIZE % CLEAR_BLOCK_LENGTH) == 0 ); // // Loop decrypting a block at a time // for (i=0; iSessionKey, &ClearBlock ); NlAssert( NT_SUCCESS( Status ) ); // // Copy the clear text back into the original buffer. // RtlCopyMemory( DataBuffer, &ClearBlock, CLEAR_BLOCK_LENGTH ); DataBuffer += CLEAR_BLOCK_LENGTH; } } return; } VOID NlpEncryptValidationInformation ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, IN OUT LPBYTE ValidationInformation, IN PSESSION_INFO SessionInfo ) /*++ Routine Description: This function encrypts the sensitive information in the ValidationInformation structure. The encryption is done in place. Arguments: LogonLevel -- Specifies the Logon level used to obtain ValidationInformation. ValidationLevel -- Specifies the level of information given in ValidationInformation. ValidationInformation -- Specifies the description for the user logging on. SessionInfo -- The session key to encrypt with and negotiated flags. Return Value: None. --*/ { PNETLOGON_VALIDATION_SAM_INFO ValidationInfo; // // Check the validation level. // if ( (ValidationLevel != NetlogonValidationSamInfo) && (ValidationLevel != NetlogonValidationSamInfo2) ) { return; } // // Only network logons contain information which is sensitive. // if ( LogonLevel != NetlogonNetworkInformation ) { return; } ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation; // // If we're suppossed to use RC4, // Encrypt both the NT and LM session keys using RC4. // if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) { NlEncryptRC4( &ValidationInfo->UserSessionKey, sizeof(ValidationInfo->UserSessionKey), SessionInfo ); NlEncryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY], SAMINFO_LM_SESSION_KEY_SIZE, SessionInfo ); // // If the other side is running NT1.0, // be compatible. // } else { NTSTATUS Status; CLEAR_BLOCK ClearBlock; DWORD i; LPBYTE DataBuffer = (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY]; // // Encrypt the LmSessionKey // // Loop decrypting a block at a time // for (i=0; iSessionKey, (PCYPHER_BLOCK)DataBuffer ); NlAssert( NT_SUCCESS( Status ) ); DataBuffer += CLEAR_BLOCK_LENGTH; } } return; } NTSTATUS NlpConvertSamInfoToSamInfo2 ( IN OUT LPBYTE * ValidationInformation ) /*++ Routine Description: This function converts a NETLOGON_VALIDATION_SAM_INFO from a NT1.0 server into a NETLOGON_VALIDATION_SAM_INFO2. This is necessary because it is not possible to tell RPC what kind of structure is being returned. Arguments: ValidationInformation -- Specifies the NETLOGON_VALIDATION_SAM_INFO to convert. logging on. SessionInfo -- The session key to encrypt with and negotiated flags. Return Value: STATUS_INSUFFICIENT_RESOURCES: not enough memory to allocate the new structure. --*/ { ULONG Length; PNETLOGON_VALIDATION_SAM_INFO SamInfo = (PNETLOGON_VALIDATION_SAM_INFO) *ValidationInformation; PNETLOGON_VALIDATION_SAM_INFO2 SamInfo2; PBYTE Where; // // Calculate the size of the new structure // Length = sizeof( NETLOGON_VALIDATION_SAM_INFO2 ) + SamInfo->GroupCount * sizeof(GROUP_MEMBERSHIP) + RtlLengthSid( SamInfo->LogonDomainId ); // // Round up now to take into account the round up in the // middle of marshalling // Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->LogonDomainName.Length + sizeof(WCHAR) + SamInfo->LogonServer.Length + sizeof(WCHAR) + SamInfo->EffectiveName.Length + sizeof(WCHAR) + SamInfo->FullName.Length + sizeof(WCHAR) + SamInfo->LogonScript.Length + sizeof(WCHAR) + SamInfo->ProfilePath.Length + sizeof(WCHAR) + SamInfo->HomeDirectory.Length + sizeof(WCHAR) + SamInfo->HomeDirectoryDrive.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT( Length, sizeof(WCHAR) ); SamInfo2 = (PNETLOGON_VALIDATION_SAM_INFO2) MIDL_user_allocate( Length ); if ( !SamInfo2 ) { *ValidationInformation = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // First copy the whole structure, since most parts are the same // RtlCopyMemory(SamInfo2,SamInfo,sizeof(NETLOGON_VALIDATION_SAM_INFO)); SamInfo2->SidCount = 0; SamInfo2->ExtraSids = NULL; // // Copy all the variable length data // Where = (PBYTE) (SamInfo2 + 1); RtlCopyMemory( Where, SamInfo->GroupIds, SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP) ); SamInfo2->GroupIds = (PGROUP_MEMBERSHIP) Where; Where += SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP ); RtlCopyMemory( Where, SamInfo->LogonDomainId, RtlLengthSid( SamInfo->LogonDomainId ) ); SamInfo2->LogonDomainId = (PSID) Where; Where += RtlLengthSid( SamInfo->LogonDomainId ); // // Copy the WCHAR-aligned data // Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo2->EffectiveName, &SamInfo->EffectiveName, &Where ); NlpPutString( &SamInfo2->FullName, &SamInfo->FullName, &Where ); NlpPutString( &SamInfo2->LogonScript, &SamInfo->LogonScript, &Where ); NlpPutString( &SamInfo2->ProfilePath, &SamInfo->ProfilePath, &Where ); NlpPutString( &SamInfo2->HomeDirectory, &SamInfo->HomeDirectory, &Where ); NlpPutString( &SamInfo2->HomeDirectoryDrive, &SamInfo->HomeDirectoryDrive, &Where ); NlpPutString( &SamInfo2->LogonServer, &SamInfo->LogonServer, &Where ); NlpPutString( &SamInfo2->LogonDomainName, &SamInfo->LogonDomainName, &Where ); MIDL_user_free(SamInfo); *ValidationInformation = (LPBYTE) SamInfo2; return STATUS_SUCCESS; } NTSTATUS NlpUserValidateHigher ( IN PCLIENT_SESSION ClientSession, IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN LPBYTE LogonInformation, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, OUT LPBYTE * ValidationInformation, OUT PBOOLEAN Authoritative ) /*++ Routine Description: This function sends a user validation request to a higher authority. Arguments: ClientSession -- Secure channel to send this request over. The Client Session should be referenced. LogonLevel -- Specifies the level of information given in LogonInformation. Has already been validated. LogonInformation -- Specifies the description for the user logging on. ValidationLevel -- Specifies the level of information returned in ValidationInformation. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2. ValidationInformation -- Returns the requested validation information. This buffer must be freed using MIDL_user_free. Authoritative -- Returns whether the status returned is an authoritative status which should be returned to the original caller. If not, this logon request may be tried again on another domain controller. This parameter is returned regardless of the status code. Return Value: STATUS_SUCCESS: if there was no error. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority. STATUS_NO_TRUST_LSA_SECRET: STATUS_TRUSTED_DOMAIN_FAILURE: STATUS_TRUSTED_RELATIONSHIP_FAILURE: can't authenticate with higer authority Otherwise, the error code is returned. --*/ { NTSTATUS Status; NETLOGON_AUTHENTICATOR OurAuthenticator; NETLOGON_AUTHENTICATOR ReturnAuthenticator; BOOLEAN FirstTry = TRUE; SESSION_INFO SessionInfo; NETLOGON_VALIDATION_INFO_CLASS RemoteValidationLevel; // // Mark us as a writer of the ClientSession // NlAssert( ClientSession->CsReferenceCount > 0 ); if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) { NlPrint((NL_CRITICAL, "NlpUserValidateHigher: Can't become writer of client session.\n" )); *Authoritative = TRUE; return STATUS_NO_LOGON_SERVERS; } // // If we don't currently have a session set up to the higher authority, // set one up. // FirstTryFailed: 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) ); NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); *Authoritative = TRUE; goto Cleanup; } // // Try to set up the session. // Status = NlSessionSetup( ClientSession ); if ( !NT_SUCCESS(Status) ) { switch(Status) { case STATUS_NO_TRUST_LSA_SECRET: case STATUS_NO_TRUST_SAM_ACCOUNT: case STATUS_ACCESS_DENIED: case STATUS_NO_LOGON_SERVERS: break; default: Status = STATUS_NO_LOGON_SERVERS; break; } NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); *Authoritative = TRUE; goto Cleanup; } } SessionInfo.SessionKey = ClientSession->CsSessionKey; SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags; // // If we are talking to a DC that doesn't support returning multiple // SIDs, make sure to only ask for NetlogonValidationSamInfo // if (!(SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_MULTIPLE_SIDS)) { RemoteValidationLevel = NetlogonValidationSamInfo; } else { RemoteValidationLevel = ValidationLevel; } // // Build the Authenticator for this request on the secure channel // NlBuildAuthenticator( &ClientSession->CsAuthenticationSeed, &ClientSession->CsSessionKey, &OurAuthenticator ); // // Make the request across the secure channel. // NlpEncryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo ); Status = NlStartApiClientSession( ClientSession, TRUE ); if ( NT_SUCCESS(Status) ) { Status = I_NetLogonSamLogon( ClientSession->CsUncServerName, NlGlobalUnicodeComputerName, &OurAuthenticator, &ReturnAuthenticator, LogonLevel, LogonInformation, RemoteValidationLevel, ValidationInformation, Authoritative ); NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); } // NOTE: This call may drop the secure channel behind our back (VOID) NlFinishApiClientSession( ClientSession, TRUE ); NlpDecryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo ); if ( NT_SUCCESS(Status) ) { NlAssert( *ValidationInformation != NULL ); } // // Verify authenticator of the server on the other side and update our seed. // // If the server denied access or the server's authenticator is wrong, // Force a re-authentication. // // #ifdef BAD_ALIGNMENT NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Seed = %lx %lx\n", ((DWORD *) (&ClientSession->CsAuthenticationSeed))[0], ((DWORD *) (&ClientSession->CsAuthenticationSeed))[1])); NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: SessionKey = %lx %lx\n", ((DWORD *) (&ClientSession->CsSessionKey))[0], ((DWORD *) (&ClientSession->CsSessionKey))[1])); NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Return Authenticator = %lx %lx\n", ((DWORD *) (&ReturnAuthenticator.Credential))[0], ((DWORD *) (&ReturnAuthenticator.Credential))[1])); #endif // BAD_ALIGNMENT if ( Status == STATUS_ACCESS_DENIED || !NlUpdateSeed( &ClientSession->CsAuthenticationSeed, &ReturnAuthenticator.Credential, &ClientSession->CsSessionKey) ) { 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; } *Authoritative = TRUE; NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); goto Cleanup; } // // Clean up after a successful call to higher authority. // if ( NT_SUCCESS(Status) ) { PNETLOGON_VALIDATION_SAM_INFO2 ValidationInfo; // // The server encrypted the validation information before sending it // over the wire. Decrypt it. // NlpDecryptValidationInformation ( LogonLevel, RemoteValidationLevel, *ValidationInformation, &SessionInfo ); // // If the returned data was a VALIDATION_SAM_INFO and the caller // wanted a VALIDATION_SAM_INFO2 convert it. // if ( RemoteValidationLevel != ValidationLevel) { NlAssert( ValidationLevel == NetlogonValidationSamInfo2 ); NlAssert( RemoteValidationLevel == NetlogonValidationSamInfo ); if (!NT_SUCCESS( NlpConvertSamInfoToSamInfo2( ValidationInformation ) ) ) { *ValidationInformation = NULL; *Authoritative = FALSE; Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } } // // Ensure the returned SID and domain name are correct. // ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO2) *ValidationInformation; // // If we validated on a trusted domain, // the higher authority must have returned his own domain name, // and must have returned his own domain sid. // if ( ClientSession->CsSecureChannelType == TrustedDomainSecureChannel ){ if ( !RtlEqualDomainName( &ValidationInfo->LogonDomainName, &ClientSession->CsDomainName ) || !RtlEqualSid( ValidationInfo->LogonDomainId, ClientSession->CsDomainId ) ) { Status = STATUS_DOMAIN_TRUST_INCONSISTENT; MIDL_user_free( *ValidationInformation ); *ValidationInformation = NULL; *Authoritative = TRUE; NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); } // // If we validated on our primary domain, // only verify the domain sid if the primary domain itself validated // the logon. // } else if ( ClientSession->CsSecureChannelType == WorkstationSecureChannel ){ if ( RtlEqualDomainName( &ValidationInfo->LogonDomainName, &ClientSession->CsDomainName ) && !RtlEqualSid( ValidationInfo->LogonDomainId, ClientSession->CsDomainId ) ) { Status = STATUS_DOMAIN_TRUST_INCONSISTENT; MIDL_user_free( *ValidationInformation ); *ValidationInformation = NULL; *Authoritative = TRUE; NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); } } } Cleanup: NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); // // We are no longer a writer of the client session. // NlResetWriterClientSession( ClientSession ); return Status; } NTSTATUS NlpUserLogoffHigher ( IN PCLIENT_SESSION ClientSession, IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN LPBYTE LogonInformation ) /*++ Routine Description: This function sends a user validation request to a higher authority. Arguments: ClientSession -- Secure channel to send this request over. The Client Session should be referenced. LogonLevel -- Specifies the level of information given in LogonInformation. Has already been validated. LogonInformation -- Specifies the description for the user logging on. Return Value: STATUS_SUCCESS: if there was no error. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority. STATUS_NO_TRUST_LSA_SECRET: STATUS_TRUSTED_DOMAIN_FAILURE: STATUS_TRUSTED_RELATIONSHIP_FAILURE: can't authenticate with higer authority Otherwise, the error code is returned. --*/ { NTSTATUS Status; NETLOGON_AUTHENTICATOR OurAuthenticator; NETLOGON_AUTHENTICATOR ReturnAuthenticator; BOOLEAN FirstTry = TRUE; // // Mark us as a writer of the ClientSession // NlAssert( ClientSession->CsReferenceCount > 0 ); if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) { NlPrint((NL_CRITICAL, "NlpUserLogoffHigher: Can't become writer of client session.\n" )); return STATUS_NO_LOGON_SERVERS; } // // If we don't currently have a session set up to the higher authority, // set one up. // FirstTryFailed: if ( ClientSession->CsState != CS_AUTHENTICATED ) { // // If we've tried to authenticate recently, // don't bother trying again. // if ( !NlTimeToReauthenticate( ClientSession ) ) { Status = ClientSession->CsConnectionStatus; goto Cleanup; } Status = NlSessionSetup( ClientSession ); if ( !NT_SUCCESS(Status) ) { switch(Status) { case STATUS_NO_TRUST_LSA_SECRET: case STATUS_NO_TRUST_SAM_ACCOUNT: case STATUS_ACCESS_DENIED: case STATUS_NO_LOGON_SERVERS: break; default: Status = STATUS_NO_LOGON_SERVERS; break; } goto Cleanup; } } // // Build the Authenticator for this request on the secure channel // NlBuildAuthenticator( &ClientSession->CsAuthenticationSeed, &ClientSession->CsSessionKey, &OurAuthenticator ); // // Make the request across the secure channel. // Status = NlStartApiClientSession( ClientSession, TRUE ); if ( NT_SUCCESS(Status) ) { Status = I_NetLogonSamLogoff( ClientSession->CsUncServerName, NlGlobalUnicodeComputerName, &OurAuthenticator, &ReturnAuthenticator, LogonLevel, LogonInformation ); } // NOTE: This call may drop the secure channel behind our back (VOID) NlFinishApiClientSession( ClientSession, TRUE ); // // Verify authenticator of the server on the other side and update our seed. // // If the server denied access or the server's authenticator is wrong, // Force a re-authentication. // // if ( Status == STATUS_ACCESS_DENIED || !NlUpdateSeed( &ClientSession->CsAuthenticationSeed, &ReturnAuthenticator.Credential, &ClientSession->CsSessionKey) ) { Status = STATUS_ACCESS_DENIED; NlSetStatusClientSession( ClientSession, Status ); // // Perhaps the netlogon service in the server has just restarted. // Try just once to set up a session to the server again. // if ( FirstTry ) { FirstTry = FALSE; goto FirstTryFailed; } goto Cleanup; } Cleanup: // // We are no longer a writer of the client session. // NlResetWriterClientSession( ClientSession ); return Status; } NTSTATUS NlpUserValidateOnPdc ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN LPBYTE LogonInformation, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, OUT LPBYTE * ValidationInformation, OUT PBOOLEAN Authoritative ) /*++ Routine Description: This function sends a user validation request to the PDC in this same domain. Currently, this is called from a BDC after getting a password mismatch. The theory is that the password might be right on the PDC but it merely hasn't replicated yet. Arguments: LogonLevel -- Specifies the level of information given in LogonInformation. Has already been validated. LogonInformation -- Specifies the description for the user logging on. ValidationLevel -- Specifies the level of information returned in ValidationInformation. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2. ValidationInformation -- Returns the requested validation information. This buffer must be freed using MIDL_user_free. Authoritative -- Returns whether the status returned is an authoritative status which should be returned to the original caller. If not, this logon request may be tried again on another domain controller. This parameter is returned regardless of the status code. Return Value: STATUS_SUCCESS: if there was no error. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority. STATUS_NO_TRUST_LSA_SECRET: STATUS_TRUSTED_DOMAIN_FAILURE: STATUS_TRUSTED_RELATIONSHIP_FAILURE: can't authenticate with higer authority Otherwise, the error code is returned. --*/ { NTSTATUS Status; // // If this isn't a BDC, // There's nothing to do here. // if ( NlGlobalRole != RoleBackup ) { return STATUS_INVALID_DOMAIN_ROLE; } // // The normal pass-thru authentication logic handles this quite nicely. // Status = NlpUserValidateHigher( NlGlobalClientSession, LogonLevel, LogonInformation, ValidationLevel, ValidationInformation, Authoritative ); #if DBG if ( NT_SUCCESS(Status) ) { IF_DEBUG( LOGON ) { PNETLOGON_LOGON_IDENTITY_INFO LogonInfo; LPWSTR LogonType; LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) &((PNETLOGON_LEVEL)LogonInformation)->LogonInteractive; if ( LogonLevel == NetlogonInteractiveInformation ) { LogonType = L"Interactive"; } else if ( LogonLevel == NetlogonNetworkInformation ) { LogonType = L"Network"; } else if ( LogonLevel == NetlogonServiceInformation ) { LogonType = L"Service"; } else { LogonType = L"[Unknown]"; } NlPrint((NL_LOGON, "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ " "from %wZ successfully handled on PDC.\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation )); } } #endif // DBG return Status; } VOID NlpZeroBadPasswordCountOnPdc ( IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN LPBYTE LogonInformation ) /*++ Routine Description: This function zeros the BadPasswordCount field for the specified user on the PDC. Arguments: LogonLevel -- Specifies the level of information given in LogonInformation. Has already been validated. LogonInformation -- Specifies the description for the user logging on. Return Value: None. --*/ { NTSTATUS Status; BOOLEAN Authoritative; LPBYTE ValidationInformation = NULL; // // We only call this function on a BDC and if the BDC has just zeroed // the BadPasswordCount because of successful logon. Therefore, // we can zero the BadPasswordCount on the PDC by doing the logon over // again on the PDC. // Status = NlpUserValidateOnPdc ( LogonLevel, LogonInformation, NetlogonValidationSamInfo, &ValidationInformation, &Authoritative ); if ( NT_SUCCESS(Status) ) { MIDL_user_free( ValidationInformation ); } } NTSTATUS NlpUserValidate ( IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType, IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN LPBYTE LogonInformation, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, OUT LPBYTE * ValidationInformation, OUT PBOOLEAN Authoritative ) /*++ Routine Description: This function processes an interactive or network logon. It is a worker routine for I_NetSamLogon. I_NetSamLogon handles the details of validating the caller. This function handles the details of whether to validate locally or pass the request on. MsvValidateSam does the actual local validation. session table only in the domain defining the specified user's account. This service is also used to process a re-logon request. Arguments: SecureChannelType -- Type of secure channel this request was made over. LogonLevel -- Specifies the level of information given in LogonInformation. Has already been validated. LogonInformation -- Specifies the description for the user logging on. ValidationLevel -- Specifies the level of information returned in ValidationInformation. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2. ValidationInformation -- Returns the requested validation information. This buffer must be freed using MIDL_user_free. Authoritative -- Returns whether the status returned is an authoritative status which should be returned to the original caller. If not, this logon request may be tried again on another domain controller. This parameter is returned regardless of the status code. Return Value: STATUS_SUCCESS: if there was no error. Otherwise, the error code is returned. --*/ { NTSTATUS Status; NTSTATUS DefaultStatus = STATUS_NO_SUCH_USER; PNETLOGON_LOGON_IDENTITY_INFO LogonInfo; PCLIENT_SESSION ClientSession; DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST; BOOLEAN BadPasswordCountZeroed; BOOLEAN LogonToLocalDomain; // // Initialization // LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation; *Authoritative = FALSE; LogonToLocalDomain = RtlEqualDomainName( &LogonInfo->LogonDomainName, &NlGlobalAccountDomainName ); // // Check to see if the account is in the local SAM database. // // The Theory: // If a particular database is absolutely requested, // we only try the account in the requested database. // // In the event that an account exists in multiple places in the hierarchy, // we want to find the version of the account that is closest to the // logged on machine (i.e., workstation first, primary domain, then // trusted domain.). So we always try to local database before going // to a higher authority. // // Finally, handle the case that this call is from a BDC in our own domain // just checking to see if the PDC (us) has a better copy of the account // than it does. // if ( LogonInfo->LogonDomainName.Length == 0 || LogonToLocalDomain || SecureChannelType == ServerSecureChannel ) { // // Indicate we've already tried the specified account and // we won't need to try it again locally. // AccountsToTry &= ~MSVSAM_SPECIFIED; Status = MsvSamValidate( NlGlobalDBInfoArray[SAM_DB].DBHandle, NlGlobalUasCompatibilityMode, SecureChannelType, &NlGlobalUnicodeComputerNameString, &NlGlobalAccountDomainName, NlGlobalDBInfoArray[SAM_DB].DBId, LogonLevel, LogonInformation, ValidationLevel, (PVOID *)ValidationInformation, Authoritative, &BadPasswordCountZeroed, MSVSAM_SPECIFIED ); // // If this is a BDC and we zeroed the BadPasswordCount field, // allow the PDC to do the same thing. // if ( BadPasswordCountZeroed ) { NlpZeroBadPasswordCountOnPdc ( LogonLevel, LogonInformation ); } // // If the request is explicitly for this domain, // The STATUS_NO_SUCH_USER answer is authoritative. // if ( LogonToLocalDomain && Status == STATUS_NO_SUCH_USER ) { *Authoritative = TRUE; } // // If this is one of our BDCs calling, // return with whatever answer we got locally. // if ( SecureChannelType == ServerSecureChannel ) { DefaultStatus = Status; goto Cleanup; } // // If the local SAM database authoritatively handled the logon attempt, // just return. // if ( *Authoritative ) { DefaultStatus = Status; // // If the problem is just that the password is wrong, // try again on the PDC where the password may already be changed. // if ( BAD_PASSWORD(Status) ) { BOOLEAN TempAuthoritative; Status = NlpUserValidateOnPdc ( LogonLevel, LogonInformation, ValidationLevel, ValidationInformation, &TempAuthoritative ); // Ignore failures from the PDC if ( NT_SUCCESS(Status) ) { DefaultStatus = Status; *Authoritative = TempAuthoritative; } } goto Cleanup; } DefaultStatus = Status; } // // If the request in not for this domain, // or the domain name isn't specified (and we haven't found the account yet) // send the request to a higher authority. // if ( LogonInfo->LogonDomainName.Length == 0 || !LogonToLocalDomain ) { // // If this machine is a workstation, // send the request to the Primary Domain. // if ( NlGlobalRole == RoleMemberWorkstation ) { Status = NlpUserValidateHigher( NlGlobalClientSession, LogonLevel, LogonInformation, ValidationLevel, ValidationInformation, Authoritative ); NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); // // return more appropriate error // if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) || (Status == STATUS_ACCESS_DENIED) ) { Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE; } // // If the primary domain authoritatively handled the logon attempt, // just return. // if ( *Authoritative ) { // // If we didn't actually talk to the primary domain, // check locally if the domain requested is a trusted domain. // (This list is only a cache so we had to try to contact the // primary domain.) if ( Status == STATUS_NO_LOGON_SERVERS ) { // // If the domain specified is trusted, // then return the status to the caller. // otherwise just press on. if ( NlIsDomainTrusted ( &LogonInfo->LogonDomainName ) ) { DefaultStatus = Status; goto Cleanup; } else { // // Set the return codes to look as though the primary // determine this is an untrusted domain. // *Authoritative = FALSE; Status = STATUS_NO_SUCH_USER; } } else { DefaultStatus = Status; goto Cleanup; } } if ( Status != STATUS_NO_SUCH_USER ) { DefaultStatus = Status; } // // The machine is a Domain Controller. // // If this request was passed to us as a trusted domain request, // There is no higher authority to pass the request to. // } else if ( SecureChannelType == TrustedDomainSecureChannel ) { // DefaultStatus = STATUS_NO_SUCH_USER; // // This machine is a Domain Controller. // // This request is either a pass-thru request by a workstation in // our domain, or this request came directly from the MSV // authentication package. // // In either case, pass the request to the trusted domain. // } else { // // If this is the LanMan 2.0 case, // Try to find the domain name by asking all the trusted // domains if they define the account // if ( LogonInfo->LogonDomainName.Length == 0 ) { LPWSTR UserName; UserName = NlStringToLpwstr( &LogonInfo->UserName ); if ( UserName == NULL ) { *Authoritative = FALSE; DefaultStatus = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } ClientSession = NlPickDomainWithAccount( UserName, USER_NORMAL_ACCOUNT ); NetpMemoryFree( UserName ); // // It the domain is explicitly given, // simply find the client session for that domain. // } else { ClientSession = NlFindNamedClientSession( &LogonInfo->LogonDomainName ); } // // If a trusted domain was determined, // pass the logon request to the trusted domain. // if ( ClientSession != NULL ) { Status = NlpUserValidateHigher( ClientSession, LogonLevel, LogonInformation, ValidationLevel, ValidationInformation, Authoritative ); NlUnrefClientSession( ClientSession ); NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); // // return more appropriate error // if( (Status == STATUS_NO_TRUST_LSA_SECRET) || (Status == STATUS_NO_TRUST_SAM_ACCOUNT) || (Status == STATUS_ACCESS_DENIED) ) { Status = STATUS_TRUSTED_DOMAIN_FAILURE; } // // Since the request is explicitly for a trusted domain, // The STATUS_NO_SUCH_USER answer is authoritative. // if ( Status == STATUS_NO_SUCH_USER ) { *Authoritative = TRUE; } // // If the trusted domain authoritatively handled the // logon attempt, just return. // if ( *Authoritative ) { DefaultStatus = Status; goto Cleanup; } DefaultStatus = Status; } } } // // We have no authoritative answer from a higher authority and // DefaultStatus is the higher authority's response. // NlAssert( ! *Authoritative ); Cleanup: NlAssert( !NT_SUCCESS(DefaultStatus) || DefaultStatus == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(DefaultStatus) || *ValidationInformation != NULL ); // // If this is a network logon and this call in non-passthru, // Try one last time to log on. // if ( LogonLevel == NetlogonNetworkInformation && SecureChannelType == MsvApSecureChannel ) { // // If the only reason we can't log the user on is that he has // no user account, logging him on as guest is OK. // // There are actaully two cases here: // * If the response is Authoritative, then the specified domain // is trusted but the user has no account in the domain. // // * If the response in non-authoritative, then the specified domain // is an untrusted domain. // // In either case, then right thing to do is to try the guest account. // if ( DefaultStatus != STATUS_NO_SUCH_USER ) { AccountsToTry &= ~MSVSAM_GUEST; } // // If this is not an authoritative response, // then the domain specified isn't a trusted domain. // try the specified account name too. // // The specified account name will probably be a remote account // with the same username and password. // if ( *Authoritative ) { AccountsToTry &= ~MSVSAM_SPECIFIED; } // // Validate against the Local Sam database. // if ( AccountsToTry != 0 ) { BOOLEAN TempAuthoritative; Status = MsvSamValidate( NlGlobalDBInfoArray[SAM_DB].DBHandle, NlGlobalUasCompatibilityMode, SecureChannelType, &NlGlobalUnicodeComputerNameString, &NlGlobalAccountDomainName, NlGlobalDBInfoArray[SAM_DB].DBId, LogonLevel, LogonInformation, ValidationLevel, (PVOID *)ValidationInformation, &TempAuthoritative, &BadPasswordCountZeroed, AccountsToTry ); NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL ); // // If this is a BDC and we zeroed the BadPasswordCount field, // allow the PDC to do the same thing. // if ( BadPasswordCountZeroed ) { NlpZeroBadPasswordCountOnPdc ( LogonLevel, LogonInformation ); } // // If the local SAM database authoritatively handled the // logon attempt, // just return. // if ( TempAuthoritative ) { DefaultStatus = Status; *Authoritative = TRUE; // // If the problem is just that the password is wrong, // try again on the PDC where the password may already be // changed. // if ( BAD_PASSWORD(Status) ) { Status = NlpUserValidateOnPdc ( LogonLevel, LogonInformation, ValidationLevel, ValidationInformation, &TempAuthoritative ); // Ignore failures from the PDC if ( NT_SUCCESS(Status) ) { DefaultStatus = Status; *Authoritative = TempAuthoritative; } } // // Here we must choose between the non-authoritative status in // DefaultStatus and the non-authoritative status from the local // SAM lookup. Use the one from the higher authority unless it // isn't interesting. // } else { if ( DefaultStatus == STATUS_NO_SUCH_USER ) { DefaultStatus = Status; } } } } return DefaultStatus; } NTSTATUS NetrLogonSamLogon ( IN LPWSTR LogonServer OPTIONAL, IN LPWSTR ComputerName OPTIONAL, IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL, OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL, IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN PNETLOGON_LEVEL LogonInformation, IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, OUT PNETLOGON_VALIDATION ValidationInformation, OUT PBOOLEAN Authoritative ) /*++ Routine Description: This function is called by an NT client to process an interactive or network logon. This function passes a domain name, user name and credentials to the Netlogon service and returns information needed to build a token. It is called in three instances: * It is called by the LSA's MSV1_0 authentication package for any NT system that has LanMan installed. The MSV1_0 authentication package calls SAM directly if LanMan is not installed. In this case, this function is a local function and requires the caller to have SE_TCB privilege. The local Netlogon service will either handle this request directly (validating the request with the local SAM database) or will forward this request to the appropriate domain controller as documented in sections 2.4 and 2.5. * It is called by a Netlogon service on a workstation to a DC in the Primary Domain of the workstation as documented in section 2.4. In this case, this function uses a secure channel set up between the two Netlogon services. * It is called by a Netlogon service on a DC to a DC in a trusted domain as documented in section 2.5. In this case, this function uses a secure channel set up between the two Netlogon services. The Netlogon service validates the specified credentials. If they are valid, adds an entry for this LogonId, UserName, and Workstation into the logon session table. The entry is added to the logon session table only in the domain defining the specified user's account. This service is also used to process a re-logon request. Arguments: LogonServer -- Supplies the name of the logon server to process this logon request. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. ComputerName -- Name of the machine making the call. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. Authenticator -- supplied by the client. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. ReturnAuthenticator -- Receives an authenticator returned by the server. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. LogonLevel -- Specifies the level of information given in LogonInformation. LogonInformation -- Specifies the description for the user logging on. ValidationLevel -- Specifies the level of information returned in ValidationInformation. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2 ValidationInformation -- Returns the requested validation information. This buffer must be freed using MIDL_user_free. Authoritative -- Returns whether the status returned is an authoritative status which should be returned to the original caller. If not, this logon request may be tried again on another domain controller. This parameter is returned regardless of the status code. Return Value: STATUS_SUCCESS: if there was no error. STATUS_NO_LOGON_SERVERS -- no domain controller in the requested domain is currently available to validate the logon request. STATUS_NO_TRUST_LSA_SECRET -- there is no secret account in the local LSA database to establish a secure channel to a DC. STATUS_TRUSTED_DOMAIN_FAILURE -- the secure channel setup between the domain controllers of the trust domains to pass-through validate the logon request failed. STATUS_TRUSTED_RELATIONSHIP_FAILURE -- the secure channel setup between the workstation and the DC failed. STATUS_INVALID_INFO_CLASS -- Either LogonLevel or ValidationLevel is invalid. STATUS_INVALID_PARAMETER -- Another Parameter is invalid. STATUS_ACCESS_DENIED -- The caller does not have access to call this API. STATUS_NO_SUCH_USER -- Indicates that the user specified in LogonInformation does not exist. This status should not be returned to the originally caller. It should be mapped to STATUS_LOGON_FAILURE. STATUS_WRONG_PASSWORD -- Indicates that the password information in LogonInformation was incorrect. This status should not be returned to the originally caller. It should be mapped to STATUS_LOGON_FAILURE. STATUS_INVALID_LOGON_HOURES -- The user is not authorized to logon at this time. STATUS_INVALID_WORKSTATION -- The user is not authorized to logon from the specified workstation. STATUS_PASSWORD_EXPIRED -- The password for the user has expired. STATUS_ACCOUNT_DISABLED -- The user's account has been disabled. . . . --*/ { NTSTATUS Status; PNETLOGON_LOGON_IDENTITY_INFO LogonInfo; PSERVER_SESSION ServerSession; NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType; SESSION_INFO SessionInfo; #if DBG LPWSTR LogonType; #endif // DBG // // Check the LogonLevel // *Authoritative = TRUE; ValidationInformation->ValidationSam = NULL; SessionInfo.NegotiatedFlags = NETLOGON_SUPPORTS_MASK; switch ( LogonLevel ) { case NetlogonInteractiveInformation: case NetlogonNetworkInformation: case NetlogonServiceInformation: break; default: *Authoritative = TRUE; return STATUS_INVALID_INFO_CLASS; } LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation->LogonInteractive; #if DBG if ( LogonLevel == NetlogonInteractiveInformation ) { LogonType = L"Interactive"; } else if ( LogonLevel == NetlogonNetworkInformation ) { LogonType = L"Network"; } else if ( LogonLevel == NetlogonServiceInformation ) { LogonType = L"Service"; } else { LogonType = L"[Unknown]"; } IF_DEBUG( LOGON ) { if ( ComputerName != NULL ) { NlPrint((NL_LOGON, "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ " "from %wZ (via " FORMAT_LPWSTR ") Entered\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation, ComputerName )); } else { NlPrint((NL_LOGON, "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ " "from %wZ Entered\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation )); } } #endif // DBG // // Check the ValidationLevel // switch (ValidationLevel) { case NetlogonValidationSamInfo: case NetlogonValidationSamInfo2: break; default: *Authoritative = TRUE; return STATUS_INVALID_INFO_CLASS; } // // If MSV is calling when the netlogon service isn't running, // tell it so. // EnterCriticalSection( &NlGlobalMsvCritSect ); if ( !NlGlobalMsvEnabled ) { LeaveCriticalSection( &NlGlobalMsvCritSect ); return STATUS_NETLOGON_NOT_STARTED; } NlGlobalMsvThreadCount ++; LeaveCriticalSection( &NlGlobalMsvCritSect ); // // If we're being called from the MSV Authentication Package, // require SE_TCB privilege. // if ( LogonServer == NULL && ComputerName == NULL && Authenticator == NULL && ReturnAuthenticator == NULL ) { // // ?? Do as I said // SecureChannelType = MsvApSecureChannel; // // If we're being called from another Netlogon Server, // Verify the secure channel information. // } else { // // This API is not supported on workstations. // if ( NlGlobalRole == RoleMemberWorkstation ) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; } // // Arguments are no longer optional. // if ( LogonServer == NULL || ComputerName == NULL || Authenticator == NULL || ReturnAuthenticator == NULL ) { *Authoritative = TRUE; Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Check the LogonServer name. // Status = NlVerifyWorkstation( LogonServer ); if ( !NT_SUCCESS( Status ) ) { *Authoritative = FALSE; goto Cleanup; } // // Find the server session entry for this session. // LOCK_SERVER_SESSION_TABLE(); ServerSession = NlFindNamedServerSession( ComputerName ); if (ServerSession == NULL) { UNLOCK_SERVER_SESSION_TABLE(); *Authoritative = FALSE; Status = STATUS_ACCESS_DENIED; goto Cleanup; } // // now verify the Authenticator and update seed if OK // Status = NlCheckAuthenticator( ServerSession, Authenticator, ReturnAuthenticator); if ( !NT_SUCCESS(Status) ) { UNLOCK_SERVER_SESSION_TABLE(); *Authoritative = FALSE; goto Cleanup; } SecureChannelType = ServerSession->SsSecureChannelType; SessionInfo.SessionKey = ServerSession->SsSessionKey; SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags; UNLOCK_SERVER_SESSION_TABLE(); // // Decrypt the password information // NlpDecryptLogonInformation ( LogonLevel, (LPBYTE) LogonInfo, &SessionInfo ); } // // If the logon service is paused then don't process this logon // request any further. // if ( (NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED) || ( NlGlobalFirstTimeFullSync == TRUE ) ) { // // Don't reject logons originating inside this // machine. Such requests aren't really pass-thru requests. // // Don't reject logons from a BDC in our own domain. These logons // support account lockout and authentication of users whose password // has been updated on the PDC but not the BDC. Such pass-thru // requests can only be handled by the PDC of the domain. // if ( SecureChannelType != MsvApSecureChannel && SecureChannelType != ServerSecureChannel ) { // // Return STATUS_ACCESS_DENIED to convince the caller to drop the // secure channel to this logon server and reconnect to some other // logon server. // *Authoritative = FALSE; Status = STATUS_ACCESS_DENIED; goto Cleanup; } } // // Validate the Request. // Status = NlpUserValidate( SecureChannelType, LogonLevel, (LPBYTE) LogonInfo, ValidationLevel, (LPBYTE *)&ValidationInformation->ValidationSam, Authoritative ); if ( !NT_SUCCESS(Status) ) { // // If this is an NT 3.1 client, // map NT 3.5 status codes to their NT 3.1 equivalents. // // The NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT bit is really the wrong bit // to be using, but all NT3.5 clients have it set and all NT3.1 clients // don't, so it'll work for our purposes. // if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT) == 0 ) { switch ( Status ) { case STATUS_PASSWORD_MUST_CHANGE: Status = STATUS_PASSWORD_EXPIRED; break; case STATUS_ACCOUNT_LOCKED_OUT: Status = STATUS_ACCOUNT_DISABLED; break; } } goto Cleanup; } NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS ); NlAssert( !NT_SUCCESS(Status) || ValidationInformation->ValidationSam != NULL ); // // If the validation information is being returned to a client on another // machine, encrypt it before sending it over the wire. // if ( SecureChannelType != MsvApSecureChannel ) { NlpEncryptValidationInformation ( LogonLevel, ValidationLevel, *((LPBYTE *) ValidationInformation), &SessionInfo ); } Status = STATUS_SUCCESS; // // Cleanup up before returning. // Cleanup: if ( !NT_SUCCESS(Status) ) { if (ValidationInformation->ValidationSam != NULL) { MIDL_user_free( ValidationInformation->ValidationSam ); ValidationInformation->ValidationSam = NULL; } } #if DBG IF_DEBUG( LOGON ) { if ( ComputerName != NULL ) { NlPrint((NL_LOGON, "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ " "from %wZ (via " FORMAT_LPWSTR ") Returns 0x%lX\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation, ComputerName, Status )); } else { NlPrint((NL_LOGON, "SamLogon: " FORMAT_LPWSTR " logon of %wZ\\%wZ from %wZ Returns 0x%lX\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation, Status )); } } #endif // DBG // // Indicate that the MSV thread has left netlogon.dll // EnterCriticalSection( &NlGlobalMsvCritSect ); NlGlobalMsvThreadCount --; if ( NlGlobalMsvThreadCount == 0 && !NlGlobalMsvEnabled ) { if ( !SetEvent( NlGlobalMsvTerminateEvent ) ) { NlPrint((NL_CRITICAL, "Cannot set MSV termination event: %lu\n", GetLastError() )); } } LeaveCriticalSection( &NlGlobalMsvCritSect ); return Status; } NTSTATUS NetrLogonSamLogoff ( IN LPWSTR LogonServer OPTIONAL, IN LPWSTR ComputerName OPTIONAL, IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL, OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL, IN NETLOGON_LOGON_INFO_CLASS LogonLevel, IN PNETLOGON_LEVEL LogonInformation ) /*++ Routine Description: This function is called by an NT client to process an interactive logoff. It is not called for the network logoff case since the Netlogon service does not maintain any context for network logons. This function does the following. It authenticates the request. It updates the logon statistics in the SAM database on whichever machine or domain defines this user account. It updates the logon session table in the primary domain of the machine making the request. And it returns logoff information to the caller. This function is called in same scenarios that I_NetLogonSamLogon is called: * It is called by the LSA's MSV1_0 authentication package to support LsaApLogonTerminated. In this case, this function is a local function and requires the caller to have SE_TCB privilege. The local Netlogon service will either handle this request directly (if LogonDomainName indicates this request was validated locally) or will forward this request to the appropriate domain controller as documented in sections 2.4 and 2.5. * It is called by a Netlogon service on a workstation to a DC in the Primary Domain of the workstation as documented in section 2.4. In this case, this function uses a secure channel set up between the two Netlogon services. * It is called by a Netlogon service on a DC to a DC in a trusted domain as documented in section 2.5. In this case, this function uses a secure channel set up between the two Netlogon services. When this function is a remote function, it is sent to the DC over a NULL session. Arguments: LogonServer -- Supplies the name of the logon server which logged this user on. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. ComputerName -- Name of the machine making the call. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. Authenticator -- supplied by the client. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. ReturnAuthenticator -- Receives an authenticator returned by the server. This field should be null to indicate this is a call from the MSV1_0 authentication package to the local Netlogon service. LogonLevel -- Specifies the level of information given in LogonInformation. LogonInformation -- Specifies the logon domain name, logon Id, user name and workstation name of the user logging off. Return Value: --*/ { NTSTATUS Status; PNETLOGON_LOGON_IDENTITY_INFO LogonInfo; PSERVER_SESSION ServerSession; NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType; PCLIENT_SESSION ClientSession; #if DBG LPWSTR LogonType; #endif // DBG // // Check the LogonLevel // if ( LogonLevel != NetlogonInteractiveInformation ) { return STATUS_INVALID_INFO_CLASS; } LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation->LogonInteractive; #if DBG if ( LogonLevel == NetlogonInteractiveInformation ) { LogonType = L"Interactive"; } else { LogonType = L"[Unknown]"; } NlPrint((NL_LOGON, "NetrLogonSamLogoff: " FORMAT_LPWSTR " logoff of %wZ\\%wZ from %wZ Entered\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation )); #endif // DBG // // Sanity check the username and domain name. // if ( LogonInfo->UserName.Length == 0 || LogonInfo->LogonDomainName.Length == 0 ) { return STATUS_INVALID_PARAMETER; } // // If MSV is calling when the netlogon service isn't running, // tell it so. // EnterCriticalSection( &NlGlobalMsvCritSect ); if ( !NlGlobalMsvEnabled ) { LeaveCriticalSection( &NlGlobalMsvCritSect ); return STATUS_NETLOGON_NOT_STARTED; } NlGlobalMsvThreadCount ++; LeaveCriticalSection( &NlGlobalMsvCritSect ); // // If we've been called from the local msv1_0, // special case the secure channel type. // if ( LogonServer == NULL && ComputerName == NULL && Authenticator == NULL && ReturnAuthenticator == NULL ) { SecureChannelType = MsvApSecureChannel; // // If we're being called from another Netlogon Server, // Verify the secure channel information. // } else { // // This API is not supported on workstations. // if ( NlGlobalRole == RoleMemberWorkstation ) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; } // // Arguments are no longer optional. // if ( LogonServer == NULL || ComputerName == NULL || Authenticator == NULL || ReturnAuthenticator == NULL ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Check the LogonServer name. // Status = NlVerifyWorkstation( LogonServer ); if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } // // Find the server session entry for this secure channel. // LOCK_SERVER_SESSION_TABLE(); ServerSession = NlFindNamedServerSession( ComputerName ); if (ServerSession == NULL) { UNLOCK_SERVER_SESSION_TABLE(); Status = STATUS_ACCESS_DENIED; goto Cleanup; } // // Now verify the Authenticator and update seed if OK // Status = NlCheckAuthenticator( ServerSession, Authenticator, ReturnAuthenticator); if ( !NT_SUCCESS(Status) ) { UNLOCK_SERVER_SESSION_TABLE(); goto Cleanup; } SecureChannelType = ServerSession->SsSecureChannelType; UNLOCK_SERVER_SESSION_TABLE(); } // // If this is the domain that logged this user on, // update the logon statistics. // if ( RtlEqualDomainName( &LogonInfo->LogonDomainName, &NlGlobalAccountDomainName) ) { Status = MsvSamLogoff( NlGlobalDBInfoArray[SAM_DB].DBHandle, LogonLevel, LogonInfo ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } // // If this is not the domain that logged this user on, // pass the request to a higher authority. // } else { // // If this machine is a workstation, // send the request to the Primary Domain. // if ( NlGlobalRole == RoleMemberWorkstation ) { Status = NlpUserLogoffHigher( NlGlobalClientSession, LogonLevel, (LPBYTE) LogonInfo ); // // return more appropriate error // if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) || (Status == STATUS_ACCESS_DENIED) ) { Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE; } goto Cleanup; // // The machine is a Domain Controller. // // If this request was passed to us as a trusted domain request, // There is no higher authority to pass the request to. // } else if ( SecureChannelType == TrustedDomainSecureChannel ) { Status = STATUS_NO_SUCH_DOMAIN; goto Cleanup; // // This machine is a Domain Controller. // // This request is either a pass-thru request by a workstation in // our domain, or this request came directly from the MSV // authentication package. // // In either case, pass the request to the trusted domain. // } else { // // Send the request to the appropriate Trusted Domain. // // Find the ClientSession structure for the domain. // ClientSession = NlFindNamedClientSession( &LogonInfo->LogonDomainName ); if ( ClientSession == NULL ) { Status = STATUS_NO_SUCH_DOMAIN; goto Cleanup; } Status = NlpUserLogoffHigher( ClientSession, LogonLevel, (LPBYTE) LogonInfo ); NlUnrefClientSession( ClientSession ); // // return more appropriate error // if( (Status == STATUS_NO_TRUST_LSA_SECRET) || (Status == STATUS_NO_TRUST_SAM_ACCOUNT) || (Status == STATUS_ACCESS_DENIED) ) { Status = STATUS_TRUSTED_DOMAIN_FAILURE; } } } Cleanup: // // If the request failed, be carefull to not leak authentication // information. // if ( Status == STATUS_ACCESS_DENIED ) { if ( ReturnAuthenticator != NULL ) { RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) ); } } #if DBG NlPrint((NL_LOGON, "NetrLogonSamLogoff: " FORMAT_LPWSTR " logoff of %wZ\\%wZ from %wZ returns %lX\n", LogonType, &LogonInfo->LogonDomainName, &LogonInfo->UserName, &LogonInfo->Workstation, Status )); #endif // DBG // // Indicate that the MSV thread has left netlogon.dll // EnterCriticalSection( &NlGlobalMsvCritSect ); NlGlobalMsvThreadCount --; if ( NlGlobalMsvThreadCount == 0 && !NlGlobalMsvEnabled ) { if ( !SetEvent( NlGlobalMsvTerminateEvent ) ) { NlPrint((NL_CRITICAL, "Cannot set MSV termination event: %lu\n", GetLastError() )); } } LeaveCriticalSection( &NlGlobalMsvCritSect ); return Status; } NET_API_STATUS NetrGetDCName ( IN LPWSTR ServerName OPTIONAL, IN LPWSTR DomainName OPTIONAL, OUT LPWSTR *Buffer ) /*++ Routine Description: Get the name of the primary domain controller for a domain. Arguments: ServerName - name of remote server (null for local) DomainName - name of domain (null for primary) Buffer - Returns a pointer to an allcated buffer containing the servername of the PDC of the domain. The server name is prefixed by \\. The buffer should be deallocated using NetApiBufferFree. Return Value: NERR_Success - Success. Buffer contains PDC name prefixed by \\. NERR_DCNotFound No DC found for this domain. ERROR_INVALID_NAME Badly formed domain name --*/ { NET_API_STATUS NetStatus; UNREFERENCED_PARAMETER( ServerName ); // // This API is not supported on workstations. // if ( NlGlobalRole == RoleMemberWorkstation ) { return ERROR_NOT_SUPPORTED; } // // Simply call the API which handles the local case specially. // NetStatus = NetGetDCName( NULL, DomainName, (LPBYTE *)Buffer ); return NetStatus; }