//+----------------------------------------------------------------------- // // 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 #include #include #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