|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: kerbpass.cxx
//
// Contents: Code for changing the Kerberos password on a KDC
//
//
// History: 17-October-1998 MikeSw Created
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#include <kerbpass.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[] = TEXT(__FILE__); #endif
#define FILENO FILENO_KERBPASS
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbUpdateLogonSessionPasswords
//
// Synopsis: If the caller of this API is changing the password
// of its own account, update the passwords.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbUpdateLogonSessionPasswords( IN PKERB_LOGON_SESSION TempLogonSession, IN PUNICODE_STRING NewPassword ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; PKERB_LOGON_SESSION LogonSession = NULL; BOOLEAN LockHeld = FALSE;
//
// Get the logon session for the caller so we can compare the name of
// the account of the changed password to the name of the account of the
// caller.
//
Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; }
LogonSession = KerbReferenceLogonSession( &ClientInfo.LogonId, FALSE // don't remove
);
if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Now compare the names
//
DsysAssert( !LockHeld ); KerbWriteLockLogonSessions( LogonSession ); KerbReadLockLogonSessions( TempLogonSession ); LockHeld = TRUE;
if (RtlEqualUnicodeString( &LogonSession->PrimaryCredentials.UserName, &TempLogonSession->PrimaryCredentials.UserName, TRUE) && // case insensitive
RtlEqualUnicodeString( &LogonSession->PrimaryCredentials.DomainName, &TempLogonSession->PrimaryCredentials.DomainName, TRUE)) // case insensitive
{ Status = KerbChangeCredentialsPassword( &LogonSession->PrimaryCredentials, NewPassword, NULL, // no etype info
UserAccount, PRIMARY_CRED_CLEAR_PASSWORD );
if (NT_SUCCESS(Status)) { SECPKG_PRIMARY_CRED PrimaryCredentials = {0};
PrimaryCredentials.LogonId = ClientInfo.LogonId; PrimaryCredentials.Password = *NewPassword; PrimaryCredentials.Flags = PRIMARY_CRED_UPDATE | PRIMARY_CRED_CLEAR_PASSWORD;
//
// Update all the other packages
//
KerbUnlockLogonSessions(TempLogonSession); KerbUnlockLogonSessions(LogonSession); LockHeld = FALSE;
(VOID) LsaFunctions->UpdateCredentials( &PrimaryCredentials, NULL // no supplemental credentials
); } } Cleanup: if (LockHeld) { KerbUnlockLogonSessions(TempLogonSession); KerbUnlockLogonSessions(LogonSession); }
return(Status); }
#endif // WIN32_CHICAGO
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbGetKpasswdTicket
//
// Synopsis: Gets a ticket for the kpasswd/changepw service in the
// realm of the logon session.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetKpasswdTicket( IN PKERB_LOGON_SESSION LogonSession, OUT PKERB_TICKET_CACHE_ENTRY * KpasswdTicket, OUT PUNICODE_STRING ClientRealm, OUT PKERB_INTERNAL_NAME * ClientName ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_INTERNAL_NAME KpasswdName = NULL; UNICODE_STRING CorrectRealm = {0}; ULONG RetryCount = KERB_CLIENT_REFERRAL_MAX; BOOLEAN MitLogon;
RtlInitUnicodeString( ClientRealm, NULL );
//
// Build the service name for the ticket
//
Status = KerbBuildKpasswdName( &KpasswdName ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// We don't know exactly what realm to change the password on. If the
// client presesnted a UPN, we may need to chase that down first.
// This is similar code to KerbGetTicketGrantingTicket.
//
//
// We start off assuming that the domain name is the domain name
// supplied by the client.
//
KerbReadLockLogonSessions( LogonSession );
Status = KerbGetClientNameAndRealm( &LogonSession->LogonId, &LogonSession->PrimaryCredentials, FALSE, NULL, &MitLogon, FALSE, // default to wksta realm for UPN
ClientName, ClientRealm );
KerbUnlockLogonSessions( LogonSession );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
GetTicketRestart:
//
// Try to get the ticket now.
//
Status = KerbGetAuthenticationTicket( LogonSession, NULL, // credential
NULL, TRUE, KpasswdName, ClientRealm, *ClientName, KERB_GET_AUTH_TICKET_NO_CANONICALIZE, // no name canonicalization
0, // no cache flags
KpasswdTicket, NULL, // no credential key,
&CorrectRealm );
//
// If it failed but gave us another realm to try, go there
//
if (!NT_SUCCESS(Status) && (CorrectRealm.Length != 0)) { if (--RetryCount != 0) { KerbFreeString(ClientRealm); *ClientRealm = CorrectRealm; CorrectRealm.Buffer = NULL;
goto GetTicketRestart; } } Cleanup: KerbFreeKdcName( &KpasswdName ); KerbFreeString(&CorrectRealm);
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbBuildKerbPriv
//
// Synopsis: Builds a kerb-priv message with none of the optional
// fields.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildKerbPriv( IN PBYTE Data, IN ULONG DataSize, IN PKERB_ENCRYPTION_KEY Key, IN OPTIONAL PULONG Nonce, OUT PKERB_MESSAGE_BUFFER PrivMessage ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status = STATUS_SUCCESS; KERB_PRIV_MESSAGE Priv = {0}; KERB_ENCRYPTED_PRIV PrivBody = {0}; KERB_MESSAGE_BUFFER PackedBody = {0}; PKERB_HOST_ADDRESSES Addresses = NULL; PKERB_HOST_ADDRESSES OurAddress = NULL;
Status = KerbBuildHostAddresses( TRUE, TRUE, &Addresses ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Look for the first IP address in the list
//
OurAddress = Addresses; while (OurAddress != NULL) { if (OurAddress->value.address_type == KERB_ADDRTYPE_INET) { break; } OurAddress = OurAddress->next; }
if (OurAddress == NULL) { DebugLog((DEB_ERROR,"No IP addresses. %ws, line %d\n",THIS_FILE, __LINE__)); Status = STATUS_NO_IP_ADDRESSES; goto Cleanup; }
//
// Get the client address
//
PrivBody.user_data.length = (int) DataSize; PrivBody.user_data.value = Data;
PrivBody.sender_address.addr_type = OurAddress->value.address_type; PrivBody.sender_address.address.length = OurAddress->value.address.length; PrivBody.sender_address.address.value = OurAddress->value.address.value;
if (ARGUMENT_PRESENT(Nonce)) { PrivBody.KERB_ENCRYPTED_PRIV_sequence_number = (int) *Nonce; PrivBody.bit_mask |= KERB_ENCRYPTED_PRIV_sequence_number_present; }
//
// Now pack the priv_body
//
KerbErr = KerbPackData( &PrivBody, KERB_ENCRYPTED_PRIV_PDU, &PackedBody.BufferSize, &PackedBody.Buffer ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Now encrypt the body
//
KerbErr = KerbAllocateEncryptionBufferWrapper( Key->keytype, PackedBody.BufferSize, &Priv.encrypted_part.cipher_text.length, &Priv.encrypted_part.cipher_text.value ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
KerbErr = KerbEncryptDataEx( &Priv.encrypted_part, PackedBody.BufferSize, PackedBody.Buffer, KERB_NO_KEY_VERSION, KERB_PRIV_SALT, Key );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Finally, pack the outer priv message.
//
Priv.version = KERBEROS_VERSION; Priv.message_type = KRB_PRIV;
KerbErr = KerbPackData( &Priv, KERB_PRIV_MESSAGE_PDU, &PrivMessage->BufferSize, &PrivMessage->Buffer ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
Cleanup: KerbFreeHostAddresses(Addresses); if (Priv.encrypted_part.cipher_text.value != NULL) { MIDL_user_free(Priv.encrypted_part.cipher_text.value); } if (PackedBody.Buffer != NULL) { MIDL_user_free(PackedBody.Buffer); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbBuildKpasswdRequest
//
// Synopsis: Builds a kpasswd request - build the AP REQ, KERB_PRIV,
// and then combines them in the request.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildKpasswdRequest( IN PKERB_TICKET_CACHE_ENTRY KpasswdTicket, IN PUNICODE_STRING ClientRealm, IN PUNICODE_STRING NewPassword, OUT PKERB_MESSAGE_BUFFER RequestMessage, OUT PKERB_ENCRYPTION_KEY SessionKey, OUT PULONG Nonce ) { NTSTATUS Status = STATUS_SUCCESS; KERB_MESSAGE_BUFFER ApRequest = {0}; KERB_MESSAGE_BUFFER PrivMessage = {0}; PKERB_KPASSWD_REQ KpasswdRequest; KERBERR KerbErr = KDC_ERR_NONE; STRING AnsiPassword = {0};
RtlZeroMemory( SessionKey, sizeof(KERB_ENCRYPTION_KEY) );
*Nonce = KerbAllocateNonce();
//
// Make a sub-session key for the AP request and for encrypting
// the KERB_PRIV message.
//
KerbErr = KerbMakeKey( KpasswdTicket->SessionKey.keytype, SessionKey );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Build the AP request first
//
KerbReadLockTicketCache();
KerbErr = KerbCreateApRequest( KpasswdTicket->ClientName, &KpasswdTicket->ClientDomainName, &KpasswdTicket->SessionKey, SessionKey, *Nonce, NULL, // authenticatortime on AP request - mutual auth???
&KpasswdTicket->Ticket, 0, // no ap options
NULL, // no checksum
&KpasswdTicket->TimeSkew, FALSE, // not a KDC request
&ApRequest.BufferSize, &ApRequest.Buffer );
KerbUnlockTicketCache();
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to create AP request for kpasswd: 0x%x, %ws line %d\n", KerbErr, THIS_FILE, __LINE__ ));
Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// convert the password to UTF-8
//
KerbErr = KerbUnicodeStringToKerbString( &AnsiPassword, NewPassword ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Build the kerb_priv message
//
Status = KerbBuildKerbPriv( (PUCHAR) AnsiPassword.Buffer, AnsiPassword.Length, SessionKey, Nonce, &PrivMessage ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to build Kerb-priv: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Now build the request itself.
//
RequestMessage->BufferSize = PrivMessage.BufferSize + ApRequest.BufferSize + FIELD_OFFSET(KERB_KPASSWD_REQ,Data); RequestMessage->Buffer = (PBYTE) MIDL_user_allocate(RequestMessage->BufferSize); if (RequestMessage->Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
KpasswdRequest = (PKERB_KPASSWD_REQ) RequestMessage->Buffer; SET_SHORT(KpasswdRequest->MessageLength, (USHORT) RequestMessage->BufferSize); SET_SHORT(KpasswdRequest->Version, KERB_KPASSWD_VERSION); SET_SHORT(KpasswdRequest->ApReqLength, (USHORT) ApRequest.BufferSize);
RtlCopyMemory( KpasswdRequest->Data, ApRequest.Buffer, ApRequest.BufferSize ); RtlCopyMemory( (PBYTE) KpasswdRequest->Data + ApRequest.BufferSize, PrivMessage.Buffer, PrivMessage.BufferSize );
Cleanup: if (PrivMessage.Buffer != NULL) { MIDL_user_free(PrivMessage.Buffer); } if (ApRequest.Buffer != NULL) { MIDL_user_free(ApRequest.Buffer); } RtlEraseUnicodeString((PUNICODE_STRING) &AnsiPassword); KerbFreeString((PUNICODE_STRING) &AnsiPassword);
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbBuildSetPasswordRequest
//
// Synopsis: Builds a kpasswd request to set a password - build the
// AP REQ, KERB_PRIV,
// and then combines them in the request.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildSetPasswordRequest( IN PKERB_TICKET_CACHE_ENTRY KpasswdTicket, IN PKERB_INTERNAL_NAME ClientName, IN PUNICODE_STRING ClientRealm, IN PUNICODE_STRING NewPassword, OUT PKERB_MESSAGE_BUFFER RequestMessage, OUT PKERB_ENCRYPTION_KEY SessionKey, OUT PULONG Nonce ) { NTSTATUS Status = STATUS_SUCCESS; KERB_MESSAGE_BUFFER ApRequest = {0}; KERB_MESSAGE_BUFFER PrivMessage = {0}; KERB_MESSAGE_BUFFER EncodedData = {0}; PKERB_KPASSWD_REQ KpasswdRequest; KERBERR KerbErr = KDC_ERR_NONE; STRING AnsiPassword = {0}; KERB_CHANGE_PASSWORD_DATA ChangeData = {0};
RtlZeroMemory( SessionKey, sizeof(KERB_ENCRYPTION_KEY) );
*Nonce = KerbAllocateNonce();
//
// Build the encoded data
//
//
// convert the password to UTF-8
//
KerbErr = KerbUnicodeStringToKerbString( &AnsiPassword, NewPassword );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
ChangeData.new_password.value = (PUCHAR) AnsiPassword.Buffer; ChangeData.new_password.length = AnsiPassword.Length;
//
// Convert the names
//
KerbErr = KerbConvertUnicodeStringToRealm( &ChangeData.target_realm, ClientRealm );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
KerbErr = KerbConvertKdcNameToPrincipalName( &ChangeData.target_name, ClientName ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
ChangeData.bit_mask = target_name_present | target_realm_present;
//
// Asn.1 encode the data for sending
//
KerbErr = KerbPackData( &ChangeData, KERB_CHANGE_PASSWORD_DATA_PDU, &EncodedData.BufferSize, &EncodedData.Buffer ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to pack kerb change password data: 0x%xx, file %ws, line %d\n", KerbErr, THIS_FILE, __LINE__ ));
Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Make a sub-session key for the AP request and for encrypting
// the KERB_PRIV message.
//
KerbErr = KerbMakeKey( KpasswdTicket->SessionKey.keytype, SessionKey ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Build the AP request first
//
KerbReadLockTicketCache();
KerbErr = KerbCreateApRequest( KpasswdTicket->ClientName, &KpasswdTicket->ClientDomainName, &KpasswdTicket->SessionKey, SessionKey, *Nonce, NULL, // authenticatortime on AP request - mutual auth???
&KpasswdTicket->Ticket, 0, // no ap options
NULL, // no checksum
&KpasswdTicket->TimeSkew, FALSE, // not a KDC request
&ApRequest.BufferSize, &ApRequest.Buffer );
KerbUnlockTicketCache();
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to create AP request for kpasswd: 0x%x, %ws line %d\n", KerbErr, THIS_FILE, __LINE__ ));
Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Build the kerb_priv message
//
Status = KerbBuildKerbPriv( EncodedData.Buffer, EncodedData.BufferSize, SessionKey, Nonce, &PrivMessage ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to build Kerb-priv: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Now build the request itself.
//
RequestMessage->BufferSize = PrivMessage.BufferSize + ApRequest.BufferSize + FIELD_OFFSET(KERB_KPASSWD_REQ,Data); RequestMessage->Buffer = (PBYTE) MIDL_user_allocate(RequestMessage->BufferSize); if (RequestMessage->Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
KpasswdRequest = (PKERB_KPASSWD_REQ) RequestMessage->Buffer; SET_SHORT(KpasswdRequest->MessageLength, (USHORT) RequestMessage->BufferSize);
//
// Use the special version for setting passwords
//
SET_SHORT(KpasswdRequest->Version, KERB_KPASSWD_SET_VERSION); SET_SHORT(KpasswdRequest->ApReqLength, (USHORT) ApRequest.BufferSize);
RtlCopyMemory( KpasswdRequest->Data, ApRequest.Buffer, ApRequest.BufferSize ); RtlCopyMemory( (PBYTE) KpasswdRequest->Data + ApRequest.BufferSize, PrivMessage.Buffer, PrivMessage.BufferSize );
Cleanup: if (PrivMessage.Buffer != NULL) { MIDL_user_free(PrivMessage.Buffer); } if (ApRequest.Buffer != NULL) { MIDL_user_free(ApRequest.Buffer); } if (EncodedData.Buffer != NULL) { MIDL_user_free(EncodedData.Buffer); } RtlEraseUnicodeString((PUNICODE_STRING) &AnsiPassword); KerbFreeString((PUNICODE_STRING) &AnsiPassword);
KerbFreeRealm(&ChangeData.target_realm); KerbFreePrincipalName( &ChangeData.target_name );
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbVerifyPrivMessage
//
// Synopsis: Verifies that a priv message is correct and returns the
// user data from the message.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbVerifyPrivMessage( IN PKERB_PRIV_MESSAGE PrivMessage, IN PKERB_ENCRYPTION_KEY SessionKey, OUT PKERB_MESSAGE_BUFFER PrivData ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status = STATUS_SUCCESS; PKERB_ENCRYPTED_PRIV PrivBody = NULL;
//
// Now decrypt the KERB_PRIV message
//
if (PrivMessage->version != KERBEROS_VERSION) { DebugLog((DEB_ERROR,"Bad version in kpasswd priv message: %d, %ws, line %d\n", PrivMessage->version, THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (PrivMessage->message_type != KRB_PRIV) { DebugLog((DEB_ERROR,"Bad message type in kpasswd priv message: %d, %ws, line %d\n", PrivMessage->message_type, THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
KerbErr = KerbDecryptDataEx( &PrivMessage->encrypted_part, SessionKey, KERB_PRIV_SALT, (PULONG) &PrivMessage->encrypted_part.cipher_text.length, PrivMessage->encrypted_part.cipher_text.value ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to decrypt priv message from kpasswd: 0x%x, %ws, line %d\n", KerbErr, THIS_FILE, __LINE__)); Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Now decode the kerb priv body
//
KerbErr = KerbUnpackData( PrivMessage->encrypted_part.cipher_text.value, (ULONG) PrivMessage->encrypted_part.cipher_text.length, KERB_ENCRYPTED_PRIV_PDU, (PVOID *) &PrivBody ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unpack priv body from kpasswd: 0x%x, %ws, line %d\n", KerbErr, THIS_FILE, __LINE__)); Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// There is nothing in the body we want to verify (although other clients
// verify the sender's address).
//
if (PrivBody->user_data.length != 0) { PrivData->BufferSize = PrivBody->user_data.length; PrivData->Buffer = (PBYTE) MIDL_user_allocate(PrivData->BufferSize); if (PrivData->Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( PrivData->Buffer, PrivBody->user_data.value, PrivBody->user_data.length ); } Cleanup:
if (PrivBody != NULL) { KerbFreeData( KERB_ENCRYPTED_PRIV_PDU, PrivBody ); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbHandleKpasswdReply
//
// Synopsis: Unpacks the reply from the kpasswd service and converts
// the error to an NT status code
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbHandleKpasswdReply( IN PKERB_TICKET_CACHE_ENTRY KpasswdTicket, IN PKERB_ENCRYPTION_KEY SessionKey, IN PKERB_MESSAGE_BUFFER ReplyMessage ) { NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr = KDC_ERR_NONE; PKERB_KPASSWD_REP KpasswdReply; PKERB_ERROR ErrorMessage = NULL; PKERB_AP_REPLY ApReply = NULL; PKERB_ENCRYPTED_AP_REPLY ApReplyBody = NULL; PKERB_PRIV_MESSAGE PrivMessage = NULL; KERB_MESSAGE_BUFFER PrivData = {0}; USHORT ResultCode = 0;
//
// First check to see if this is a reply
//
if (ReplyMessage->BufferSize > sizeof(KERB_KPASSWD_REP)) { USHORT Version; USHORT Length; KpasswdReply = (PKERB_KPASSWD_REP) ReplyMessage->Buffer; GET_SHORT(Version, KpasswdReply->Version ); GET_SHORT(Length, KpasswdReply->MessageLength);
//
// Verify these values are correct
//
if ((Version != KERB_KPASSWD_VERSION) || (Length != (USHORT) ReplyMessage->BufferSize)) { //
// It must be a kerb_error message, so unpack it.
//
KerbErr = KerbUnpackKerbError( ReplyMessage->Buffer, ReplyMessage->BufferSize, &ErrorMessage ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } } else { USHORT ApRepLength; ULONG PrivLength;
//
// It is a well formed kpasswd reply, so unpack that
//
GET_SHORT(ApRepLength, KpasswdReply->ApRepLength); if (ApRepLength > ReplyMessage->BufferSize - FIELD_OFFSET(KERB_KPASSWD_REP,Data)) { DebugLog((DEB_ERROR,"ApReq length in kpasswd rep is wrong: %d vs %d, %ws, line %d\n", ApRepLength, FIELD_OFFSET(KERB_KPASSWD_REP,Data), THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup;
}
//
// Now unpack the AP reply
//
Status = KerbUnpackApReply( KpasswdReply->Data, ApRepLength, &ApReply ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Now try to unpack the remainder as KERB_PRIV. If that fails,
// try it as a KERB_ERROR
//
PrivLength = ReplyMessage->BufferSize - (ApRepLength + FIELD_OFFSET(KERB_KPASSWD_REP,Data));
KerbErr = KerbUnpackData( (PBYTE) KpasswdReply->Data + ApRepLength, PrivLength, KERB_PRIV_MESSAGE_PDU, (PVOID *) &PrivMessage );
//
// If that didn't work, try it as a kerb error message
//
if (!KERB_SUCCESS(KerbErr)) { KerbErr = KerbUnpackKerbError( (PBYTE) KpasswdReply->Data + ApRepLength, PrivLength, &ErrorMessage ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unpack data from kpasswd rep: 0x%x, %ws line %d\n", KerbErr, THIS_FILE, __LINE__ ));
Status = KerbMapKerbError(KerbErr); goto Cleanup; } }
} }
//
// If we have an AP reply, verify it
//
if (ApReply != NULL) { KerbReadLockTicketCache();
KerbErr = KerbDecryptDataEx( &ApReply->encrypted_part, &KpasswdTicket->SessionKey, KERB_AP_REP_SALT, (PULONG) &ApReply->encrypted_part.cipher_text.length, ApReply->encrypted_part.cipher_text.value ); KerbUnlockTicketCache();
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Failed to decrypt AP reply: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__)); if (KerbErr == KRB_ERR_GENERIC) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { Status = STATUS_LOGON_FAILURE; } goto Cleanup; }
//
// Decode the contents now
//
if (!KERB_SUCCESS(KerbUnpackApReplyBody( ApReply->encrypted_part.cipher_text.value, ApReply->encrypted_part.cipher_text.length, &ApReplyBody))) { DebugLog((DEB_ERROR, "Failed to unpack AP reply body. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } }
//
// If we got a priv-message, verify it
//
if (PrivMessage != NULL) { Status = KerbVerifyPrivMessage( PrivMessage, SessionKey, &PrivData ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to verify priv message while changing password: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
if (PrivData.BufferSize >= sizeof(USHORT)) { GET_SHORT(ResultCode, PrivData.Buffer); } } else { //
// Process the error message
//
if (ErrorMessage == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
// TBD: Extended errors, client side
KerbReportKerbError( NULL, NULL, NULL, NULL, KLIN(FILENO, __LINE__), ErrorMessage, ErrorMessage->error_code, NULL, FALSE );
Status = KerbMapKerbError(ErrorMessage->error_code);
if ((ErrorMessage->bit_mask & error_data_present) != 0) { if (ErrorMessage->error_data.length >= sizeof(USHORT)) { GET_SHORT(ResultCode, ErrorMessage->error_data.value); } }
}
//
// Convert the result code & status into a real status
//
if (NT_SUCCESS(Status) || (Status == STATUS_INSUFFICIENT_RESOURCES)) { switch(ResultCode) { case KERB_KPASSWD_SUCCESS: Status = STATUS_SUCCESS; break; case KERB_KPASSWD_MALFORMED: Status = STATUS_INVALID_PARAMETER; break; case KERB_KPASSWD_ERROR: Status = STATUS_UNSUCCESSFUL; break; case KERB_KPASSWD_AUTHENTICATION: Status = STATUS_MUTUAL_AUTHENTICATION_FAILED; break; case KERB_KPASSWD_POLICY: Status = STATUS_PASSWORD_RESTRICTION; break; case KERB_KPASSWD_AUTHORIZATION: Status = STATUS_ACCESS_DENIED; break; default: Status = STATUS_UNSUCCESSFUL;
} }
Cleanup:
if (ErrorMessage != NULL) { KerbFreeKerbError(ErrorMessage); } if (ApReplyBody != NULL) { KerbFreeApReplyBody(ApReplyBody); } if (ApReply != NULL) { KerbFreeApReply(ApReply); }
if (PrivData.Buffer != NULL) { MIDL_user_free(PrivData.Buffer); }
if (PrivMessage != NULL) { KerbFreeData( KERB_PRIV_MESSAGE_PDU, PrivMessage ); }
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbChangePassword
//
// Synopsis: Uses the kerberos change password protocol to change
// a password. It is called through the LsaCallAuthenticationPackage
// interface
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbChangePassword( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferSize, OUT PNTSTATUS ProtocolStatus ) { PKERB_CHANGEPASSWORD_REQUEST ChangePasswordRequest = NULL; NTSTATUS Status = STATUS_SUCCESS; PSECURITY_SEED_AND_LENGTH SeedAndLength; UCHAR Seed; LUID DummyLogonId = {0}; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_TICKET_CACHE_ENTRY KpasswdTicket = NULL; PKERB_INTERNAL_NAME ClientName = NULL; UNICODE_STRING ValidatedAccountName; UNICODE_STRING ValidatedDomainName; LPWSTR ValidatedOldPasswordBuffer; LPWSTR ValidatedNewPasswordBuffer; UNICODE_STRING RealmName = {0}; KERB_MESSAGE_BUFFER KpasswdRequest = {0}; KERB_MESSAGE_BUFFER KpasswdReply = {0}; KERB_ENCRYPTION_KEY SessionKey = {0}; ULONG Nonce = 0; BOOLEAN PasswordBufferValidated = FALSE; BOOLEAN CalledPDC = FALSE; ULONG StructureSize = sizeof(KERB_CHANGEPASSWORD_REQUEST);
KERB_CHANGEPASS_INFO ChangePassTraceInfo; if( KerbEventTraceFlag ) // Event Trace: KerbChangePasswordStart {No Data}
{ ChangePassTraceInfo.EventTrace.Guid = KerbChangePassGuid; ChangePassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; ChangePassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; ChangePassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER);
TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&ChangePassTraceInfo ); }
*ReturnBufferSize = 0; *ProtocolReturnBuffer = NULL; *ProtocolStatus = STATUS_PENDING;
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if(!LsaFunctions->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { StructureSize = sizeof(KERB_CHANGEPASSWORD_REQUEST_WOW64); }
#endif // _WIN64
//
// Sanity checks.
//
if ( SubmitBufferSize < StructureSize ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
ChangePasswordRequest = (PKERB_CHANGEPASSWORD_REQUEST) ProtocolSubmitBuffer;
ASSERT( ChangePasswordRequest->MessageType == KerbChangePasswordMessage );
#if _WIN64
KERB_CHANGEPASSWORD_REQUEST LocalChangePasswordRequest;
//
// Thunk 32-bit pointers if this is a WOW caller
//
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { PKERB_CHANGEPASSWORD_REQUEST_WOW64 ChangePasswordRequestWOW = (PKERB_CHANGEPASSWORD_REQUEST_WOW64) ChangePasswordRequest;
LocalChangePasswordRequest.MessageType = ChangePasswordRequest->MessageType; LocalChangePasswordRequest.Impersonating = ChangePasswordRequest->Impersonating;
UNICODE_STRING_FROM_WOW_STRING(&LocalChangePasswordRequest.DomainName, &ChangePasswordRequestWOW->DomainName);
UNICODE_STRING_FROM_WOW_STRING(&LocalChangePasswordRequest.AccountName, &ChangePasswordRequestWOW->AccountName);
UNICODE_STRING_FROM_WOW_STRING(&LocalChangePasswordRequest.OldPassword, &ChangePasswordRequestWOW->OldPassword);
UNICODE_STRING_FROM_WOW_STRING(&LocalChangePasswordRequest.NewPassword, &ChangePasswordRequestWOW->NewPassword);
ChangePasswordRequest = &LocalChangePasswordRequest; }
#endif // _WIN64
RELOCATE_ONE( &ChangePasswordRequest->DomainName ); RELOCATE_ONE( &ChangePasswordRequest->AccountName ); RELOCATE_ONE_ENCODED( &ChangePasswordRequest->OldPassword ); RELOCATE_ONE_ENCODED( &ChangePasswordRequest->NewPassword );
//
// save away copies of validated buffers to check later.
//
RtlCopyMemory( &ValidatedDomainName, &ChangePasswordRequest->DomainName, sizeof(ValidatedDomainName) ); RtlCopyMemory( &ValidatedAccountName, &ChangePasswordRequest->AccountName, sizeof(ValidatedAccountName) );
ValidatedOldPasswordBuffer = ChangePasswordRequest->OldPassword.Buffer; ValidatedNewPasswordBuffer = ChangePasswordRequest->NewPassword.Buffer;
SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangePasswordRequest->OldPassword.Length; Seed = SeedAndLength->Seed; SeedAndLength->Seed = 0;
//
// Check to see if the OldPassword will run over the buffer for the New
// Password
//
if ((ChangePasswordRequest->OldPassword.Buffer + (ChangePasswordRequest->OldPassword.Length/sizeof(WCHAR)) )> ChangePasswordRequest->NewPassword.Buffer) { Status = STATUS_ILL_FORMED_PASSWORD; goto Cleanup; }
if (Seed != 0) {
__try { RtlRunDecodeUnicodeString( Seed, &ChangePasswordRequest->OldPassword );
} __except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ILL_FORMED_PASSWORD; goto Cleanup; } }
SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangePasswordRequest->NewPassword.Length; Seed = SeedAndLength->Seed; SeedAndLength->Seed = 0;
if (Seed != 0) {
__try { RtlRunDecodeUnicodeString( Seed, &ChangePasswordRequest->NewPassword );
} __except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ILL_FORMED_PASSWORD; goto Cleanup; } }
//
// sanity check that we didn't whack over buffers.
//
if( !RtlCompareMemory( &ValidatedDomainName, &ChangePasswordRequest->DomainName, sizeof(ValidatedDomainName) ) || !RtlCompareMemory( &ValidatedAccountName, &ChangePasswordRequest->AccountName, sizeof(ValidatedAccountName) ) || (ValidatedOldPasswordBuffer != ChangePasswordRequest->OldPassword.Buffer) || (ValidatedNewPasswordBuffer != ChangePasswordRequest->NewPassword.Buffer) ) {
Status= STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Validate IN params, to not exceed KERB_MAX_UNICODE_STRING, as we add a NULL
// to UNICODE buffers when we're duping strings.
//
if (ChangePasswordRequest->OldPassword.Length > KERB_MAX_UNICODE_STRING || ChangePasswordRequest->NewPassword.Length > KERB_MAX_UNICODE_STRING || ChangePasswordRequest->AccountName.Length > KERB_MAX_UNICODE_STRING || ChangePasswordRequest->DomainName.Length > KERB_MAX_UNICODE_STRING) { Status = STATUS_NAME_TOO_LONG; goto Cleanup; }
PasswordBufferValidated = TRUE;
//
// The protocol requires a ticket to the kadmin/changepw service. We
// need to create a logon session to use the KerbGetAuthenticationTicket
// routine.
//
Status = NtAllocateLocallyUniqueId( &DummyLogonId ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
Status = KerbCreateLogonSession( &DummyLogonId, &ChangePasswordRequest->AccountName, &ChangePasswordRequest->DomainName, &ChangePasswordRequest->OldPassword, NULL, // no old password
PRIMARY_CRED_CLEAR_PASSWORD, 0, FALSE, &LogonSession ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Now get a ticket for the kpasswd service
//
Status = KerbGetKpasswdTicket( LogonSession, &KpasswdTicket, &RealmName, &ClientName );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbBuildKpasswdRequest( KpasswdTicket, &RealmName, &ChangePasswordRequest->NewPassword, &KpasswdRequest, &SessionKey, &Nonce );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to build kpasswd request: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Call the KDC
//
Status = KerbMakeSocketCall( &RealmName, NULL, // no account name
FALSE, // don't call PDC
FALSE, // don't use TCP
TRUE, &KpasswdRequest, &KpasswdReply, NULL, // no optional binding cache info
0, // no additonal flags
&CalledPDC );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to call kpasswd service: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; }
//
// Unpack the reply and return the error from it.
//
Status = KerbHandleKpasswdReply( KpasswdTicket, &SessionKey, &KpasswdReply );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Change password reply failed: 0x%x, %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; }
//
// Update the password in the logon session, if need be.
//
Status = KerbUpdateLogonSessionPasswords( LogonSession, &ChangePasswordRequest->NewPassword );
if (!NT_SUCCESS(Status)) { //
// In some cases, we may not know about the caller's logon session
// (*e.g. anonymous caller). In that case, its normal not to know
// the logon session - don't fail.
//
if (Status == STATUS_NO_SUCH_LOGON_SESSION) { Status = STATUS_SUCCESS; } else { D_DebugLog((DEB_ERROR, "KerbUPdateLogonSEssionPasswords failed %x\n", Status)); }
goto Cleanup; } else { //
// Update credential manager password
//
KerbNotifyCredentialManager( LogonSession, ChangePasswordRequest, ClientName, &RealmName ); }
Cleanup: if( KerbEventTraceFlag ) // Event Trace: KerbChangePasswordEnd {Status, AccountName, DomainName}
{ INSERT_ULONG_INTO_MOF( Status, ChangePassTraceInfo.MofData, 0 ); ChangePassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 1*sizeof(MOF_FIELD);
if( ChangePasswordRequest != NULL ) { INSERT_UNICODE_STRING_INTO_MOF( ChangePasswordRequest->AccountName, ChangePassTraceInfo.MofData, 1 ); INSERT_UNICODE_STRING_INTO_MOF( ChangePasswordRequest->DomainName, ChangePassTraceInfo.MofData, 3 ); ChangePassTraceInfo.EventTrace.Size += 4*sizeof(MOF_FIELD); }
ChangePassTraceInfo.EventTrace.Guid = KerbChangePassGuid; ChangePassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; ChangePassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER) &ChangePassTraceInfo ); }
KerbFreeString(&RealmName);
KerbFreeKdcName(&ClientName);
if (KpasswdTicket != NULL) { KerbDereferenceTicketCacheEntry( KpasswdTicket ); }
if (LogonSession != NULL) { KerbReferenceLogonSessionByPointer( LogonSession, TRUE // Pull from list
); KerbDereferenceLogonSession( LogonSession ); KerbDereferenceLogonSession( LogonSession ); }
//
// Don't let the password stay in the page file.
//
if ( PasswordBufferValidated ) { RtlEraseUnicodeString( &ChangePasswordRequest->OldPassword ); RtlEraseUnicodeString( &ChangePasswordRequest->NewPassword ); }
if (KpasswdRequest.Buffer != NULL) { MIDL_user_free(KpasswdRequest.Buffer); }
if (KpasswdReply.Buffer != NULL) { MIDL_user_free(KpasswdReply.Buffer); }
if (SessionKey.keyvalue.value != NULL) { MIDL_user_free(SessionKey.keyvalue.value); }
*ProtocolStatus = Status; return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbSetPasswordEx
//
// Synopsis: Uses the kerberos set password protocol to set an account
// password. It uses the identity of the caller to authenticate
// the request. It is called through the
// LsaCallAuthenticationPackage interface
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbSetPassword( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferSize, OUT PNTSTATUS ProtocolStatus ) { PKERB_SETPASSWORD_EX_REQUEST SetPasswordRequest = NULL; NTSTATUS Status = STATUS_SUCCESS; PSECURITY_SEED_AND_LENGTH SeedAndLength; UCHAR Seed; LUID DummyLogonId = {0}; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDENTIAL Credential = NULL; PKERB_TICKET_CACHE_ENTRY KpasswdTicket = NULL; KERB_PRIMARY_CREDENTIAL PrimaryCreds = {0}; KERB_MESSAGE_BUFFER KpasswdRequest = {0}; KERB_MESSAGE_BUFFER KpasswdReply = {0}; KERB_ENCRYPTION_KEY SessionKey = {0}; ULONG Nonce = 0; BOOLEAN PasswordBufferValidated = FALSE; BOOLEAN CalledPDC = FALSE; BOOLEAN SuppliedCreds = FALSE; SECPKG_CLIENT_INFO ClientInfo; PKERB_INTERNAL_NAME KpasswdName = NULL; PKERB_INTERNAL_NAME ClientName = NULL; PKERB_BINDING_CACHE_ENTRY OptionalBindingHandle = NULL; UNICODE_STRING ClientRealm = {0}; PLUID LogonId; ULONG StructureSize = sizeof(KERB_SETPASSWORD_REQUEST); ULONG StructureSizeEx = sizeof(KERB_SETPASSWORD_EX_REQUEST);
KERB_SETPASS_INFO SetPassTraceInfo; if( KerbEventTraceFlag ) // Event Trace: KerbSetPasswordStart {No Data}
{ SetPassTraceInfo.EventTrace.Guid = KerbSetPassGuid; SetPassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; SetPassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; SetPassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER); TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER)&SetPassTraceInfo ); }
*ReturnBufferSize = 0; *ProtocolReturnBuffer = NULL; *ProtocolStatus = STATUS_PENDING;
SetPasswordRequest = (PKERB_SETPASSWORD_EX_REQUEST) ProtocolSubmitBuffer;
ASSERT( (SetPasswordRequest->MessageType == KerbSetPasswordExMessage || SetPasswordRequest->MessageType == KerbSetPasswordMessage) );
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if (!LsaFunctions->GetCallInfo(&CallInfo)) { goto Cleanup; }
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { //
// These levels are not supported for WOW
//
Status = STATUS_NOT_SUPPORTED; goto Cleanup; }
#endif // _WIN64
//
// Sanity checks.
//
if (SubmitBufferSize < StructureSize) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (SetPasswordRequest->MessageType == KerbSetPasswordExMessage) { if (SubmitBufferSize < StructureSizeEx) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
NULL_RELOCATE_ONE( &SetPasswordRequest->KdcAddress ); NULL_RELOCATE_ONE( &SetPasswordRequest->ClientName ); NULL_RELOCATE_ONE( &SetPasswordRequest->ClientRealm ); }
//
// Note: although the struct members may be different, the type
// remains the same between EX and normal version of SETPASSWORD_REQUEST
// structure.
//
RELOCATE_ONE( &SetPasswordRequest->AccountRealm ); RELOCATE_ONE( &SetPasswordRequest->AccountName ); RELOCATE_ONE_ENCODED( &SetPasswordRequest->Password );
SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &SetPasswordRequest->Password.Length; Seed = SeedAndLength->Seed; SeedAndLength->Seed = 0;
if (Seed != 0) {
__try { RtlRunDecodeUnicodeString( Seed, &SetPasswordRequest->Password );
} __except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ILL_FORMED_PASSWORD; goto Cleanup; } }
if (SetPasswordRequest->AccountName.Length > KERB_MAX_UNICODE_STRING || SetPasswordRequest->AccountRealm.Length > KERB_MAX_UNICODE_STRING || SetPasswordRequest->Password.Length > KERB_MAX_UNICODE_STRING) { Status = STATUS_NAME_TOO_LONG; goto Cleanup; }
if (SetPasswordRequest->MessageType == KerbSetPasswordExMessage) { if(SetPasswordRequest->ClientRealm.Length > KERB_MAX_UNICODE_STRING || SetPasswordRequest->ClientName.Length > KERB_MAX_UNICODE_STRING || SetPasswordRequest->KdcAddress.Length > KERB_MAX_UNICODE_STRING ) { Status = STATUS_NAME_TOO_LONG; goto Cleanup; } }
PasswordBufferValidated = TRUE;
Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( (SetPasswordRequest->Flags & KERB_SETPASS_USE_LOGONID) != 0) { //
// Verify the caller has TCB privilege if they want access to someone
// elses ticket cache.
//
if (!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; }
LogonId = &SetPasswordRequest->LogonId; } else if ( (SetPasswordRequest->Flags & KERB_SETPASS_USE_CREDHANDLE) != 0) { //
// Get the associated credential
//
Status = KerbReferenceCredential( SetPasswordRequest->CredentialsHandle.dwUpper, KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL, FALSE, &Credential);
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Failed to locate credential: 0x%x\n",Status)); goto Cleanup; }
//
// Get the logon id from the credentials so we can locate the
// logon session.
//
SuppliedCreds = TRUE; DummyLogonId = Credential->LogonId; LogonId = &DummyLogonId; } else { LogonId = &ClientInfo.LogonId; }
//
// The protocol requires a ticket to the kadmin/changepw service. We
// need to get the caller's logon session to use to get this
// ticket.
//
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if (LogonSession == NULL) { DebugLog((DEB_ERROR,"Can't locate caller's logon session\n")); Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Now get a ticket for the kpasswd service
//
Status = KerbBuildKpasswdName( &KpasswdName ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbGetServiceTicket( LogonSession, Credential, // no credential
NULL, KpasswdName, &SetPasswordRequest->AccountRealm, NULL, TRUE, // don't do name canonicalization
0, // no ticket options
0, // no encryption type
NULL, // no error message
NULL, // no authorizatoin data,
NULL, // no tgt reply
&KpasswdTicket, NULL // don't return logon guid
);
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Parse the names to get the real kerberos names
//
PrimaryCreds.UserName = SetPasswordRequest->AccountName; PrimaryCreds.DomainName = SetPasswordRequest->AccountRealm;
Status = KerbGetClientNameAndRealm( LogonId, &PrimaryCreds, SuppliedCreds, NULL, NULL, FALSE, // default to wksta realm for UPN
&ClientName, &ClientRealm );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to get client name and realm: 0x%x file %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Build the set password request
//
Status = KerbBuildSetPasswordRequest( KpasswdTicket, ClientName, &ClientRealm, &SetPasswordRequest->Password, &KpasswdRequest, &SessionKey, &Nonce );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to build kpasswd request: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Here we may possibly need to set the target KDC
// This KDC is not gaurenteed to succeeed, and retry logic
// will occur on a failed SetPwd request..
//
if (SetPasswordRequest->MessageType == KerbSetPasswordExMessage && SetPasswordRequest->KdcAddress.Buffer != NULL) { OptionalBindingHandle = (PKERB_BINDING_CACHE_ENTRY) KerbAllocate(sizeof(KERB_BINDING_CACHE_ENTRY));
if (NULL == OptionalBindingHandle) { Status = STATUS_INSUFFICIENT_RESOURCES; }
OptionalBindingHandle->AddressType = SetPasswordRequest->KdcAddressType;
RtlCopyMemory( &(OptionalBindingHandle->KdcAddress), &(SetPasswordRequest->KdcAddress), sizeof(UNICODE_STRING) );
RtlCopyMemory( &(OptionalBindingHandle->RealmName), &(SetPasswordRequest->AccountRealm), sizeof(UNICODE_STRING) ); }
//
// Call the KDC
//
Status = KerbMakeSocketCall( &ClientRealm, NULL, // no account name
FALSE, // don't call PDC
FALSE, // don't use TCP
TRUE, &KpasswdRequest, &KpasswdReply, OptionalBindingHandle, 0, // no additional flags
&CalledPDC );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to call kpasswd service: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; }
//
// Unpack the reply and return the error from it.
//
Status = KerbHandleKpasswdReply( KpasswdTicket, &SessionKey, &KpasswdReply );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Change password reply failed: 0x%x, %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; }
Cleanup:
if( KerbEventTraceFlag ) // Event Trace: KerbSetPasswordEnd {Status, AccountName, AccountRealm, (ClientName), (ClientRealm), (KdcAddress)}
{ INSERT_ULONG_INTO_MOF( Status, SetPassTraceInfo.MofData, 0 ); SetPassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 1*sizeof(MOF_FIELD);
if( SetPasswordRequest != NULL ) { INSERT_UNICODE_STRING_INTO_MOF(SetPasswordRequest->AccountName, SetPassTraceInfo.MofData, 1); INSERT_UNICODE_STRING_INTO_MOF(SetPasswordRequest->AccountRealm, SetPassTraceInfo.MofData, 3);
SetPassTraceInfo.EventTrace.Size += 4 * sizeof(MOF_FIELD);
if (SetPasswordRequest->MessageType == KerbSetPasswordExMessage) { INSERT_UNICODE_STRING_INTO_MOF(SetPasswordRequest->ClientName, SetPassTraceInfo.MofData, 5); INSERT_UNICODE_STRING_INTO_MOF(SetPasswordRequest->ClientRealm, SetPassTraceInfo.MofData, 7); INSERT_UNICODE_STRING_INTO_MOF(SetPasswordRequest->KdcAddress, SetPassTraceInfo.MofData, 9);
SetPassTraceInfo.EventTrace.Size += 6 * sizeof(MOF_FIELD); } }
SetPassTraceInfo.EventTrace.Guid = KerbSetPassGuid; SetPassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; SetPassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
TraceEvent( KerbTraceLoggerHandle, (PEVENT_TRACE_HEADER) &SetPassTraceInfo ); }
KerbFreeKdcName( &KpasswdName ); KerbFreeKdcName( &ClientName ); KerbFreeString( &ClientRealm ); KerbFreeKey( &SessionKey );
if (KpasswdTicket != NULL) { KerbDereferenceTicketCacheEntry( KpasswdTicket ); }
if (Credential != NULL) { KerbDereferenceCredential(Credential); }
if (NULL != OptionalBindingHandle) { KerbFree(OptionalBindingHandle); }
if (LogonSession != NULL) { KerbDereferenceLogonSession( LogonSession ); }
//
// Don't let the password stay in the page file.
//
if ( PasswordBufferValidated ) { RtlEraseUnicodeString( &SetPasswordRequest->Password ); }
if (KpasswdRequest.Buffer != NULL) { MIDL_user_free(KpasswdRequest.Buffer); }
if (KpasswdReply.Buffer != NULL) { MIDL_user_free(KpasswdReply.Buffer); }
*ProtocolStatus = Status; return(STATUS_SUCCESS); }
#endif // WIN32_CHICAGO
|