//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: kpasswd.cxx // // Contents: Functions for the kpasswd protocol // // History: 30-Sep-97 MikeSw Created // //---------------------------------------------------------------------------- #include "kdcsvr.hxx" #include "kpasswd.h" #include "kdctrace.h" extern "C" { #include } #define FILENO FILENO_KPASSWD //+------------------------------------------------------------------------- // // Function: KdcbMarshallApReply // // Synopsis: Takes a reply and reply body and encrypts and marshalls them // into a return message // // Effects: Allocates output buffer // // Arguments: Reply - The outer reply to marshall // ReplyBody - The reply body to marshall // EncryptionType - Encryption algorithm to use // SessionKey - Session key to encrypt reply // PackedReply - Recives marshalled reply buffer // PackedReplySize - Receives size in bytes of marshalled reply // // Requires: // // Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES // // Notes: // // //-------------------------------------------------------------------------- KERBERR KdcMarshallApReply( IN PKERB_AP_REPLY Reply, IN PKERB_ENCRYPTED_AP_REPLY ReplyBody, IN ULONG EncryptionType, IN PKERB_ENCRYPTION_KEY SessionKey, OUT PUCHAR * PackedReply, OUT PULONG PackedReplySize ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG PackedApReplySize; PUCHAR PackedApReply = NULL; ULONG EncryptionOverhead; ULONG BlockSize; ULONG ReplySize; KerbErr = KerbPackApReplyBody( ReplyBody, &PackedApReplySize, &PackedApReply ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Now encrypt the response // KerbErr = KerbAllocateEncryptionBufferWrapper( EncryptionType, PackedApReplySize, &Reply->encrypted_part.cipher_text.length, &Reply->encrypted_part.cipher_text.value ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbEncryptDataEx( &Reply->encrypted_part, PackedApReplySize, PackedApReply, EncryptionType, KERB_AP_REP_SALT, SessionKey ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"Failed to encrypt AP Reply.%d\n", KerbErr)); goto Cleanup; } // // Now pack the reply into the output buffer // KerbErr = KerbPackApReply( Reply, PackedReplySize, PackedReply ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } Cleanup: if (Reply->encrypted_part.cipher_text.value != NULL) { MIDL_user_free(Reply->encrypted_part.cipher_text.value); Reply->encrypted_part.cipher_text.value = NULL; } if (PackedApReply != NULL) { MIDL_user_free(PackedApReply); } return(KerbErr); } //+------------------------------------------------------------------------- // // Function: KdcBuildKpasswdResponse // // Synopsis: builds the response to a kpasswd request // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- KERBERR KdcBuildKpasswdResponse( IN OPTIONAL PKERB_ENCRYPTED_TICKET EncryptedTicket, IN OPTIONAL PKERB_AUTHENTICATOR Authenticator, IN OPTIONAL PKERB_ENCRYPTION_KEY SessionKey, IN OPTIONAL PSOCKADDR ServerAddress, IN NTSTATUS ChangeResult, IN KERBERR ProtocolResult, IN PKERB_EXT_ERROR pExtendedError, OUT PKERB_MESSAGE_BUFFER Response ) { KERB_PRIV_MESSAGE PrivMessage = {0}; KERB_ENCRYPTED_PRIV PrivBody = {0}; USHORT ReplySize = 0; PBYTE PackedApReply = NULL; ULONG PackedApReplySize = 0; BYTE ResultData[2] = {0}; PBYTE PackedPrivBody = NULL; ULONG PackedPrivBodySize = 0; ULONG EncryptionOverhead; ULONG BlockSize; KERBERR KerbErr = KRB_ERR_GENERIC; PBYTE ReplyData = NULL; ULONG ReplyDataSize = 0; PSOCKET_ADDRESS IpAddresses = NULL; ULONG AddressCount = 0; BOOLEAN ReturningError = TRUE; if (!NT_SUCCESS(ChangeResult)) { switch(ChangeResult) { case STATUS_PASSWORD_RESTRICTION: SET_SHORT(ResultData,KERB_KPASSWD_POLICY); ProtocolResult = KDC_ERR_POLICY; break; case STATUS_ACCOUNT_RESTRICTION: SET_SHORT(ResultData, KERB_KPASSWD_AUTHENTICATION); ProtocolResult = KDC_ERR_CLIENT_REVOKED; break; case STATUS_INVALID_PARAMETER: SET_SHORT(ResultData, KERB_KPASSWD_MALFORMED); ProtocolResult = KRB_ERR_GENERIC; break; case STATUS_ACCESS_DENIED: SET_SHORT(ResultData, KERB_KPASSWD_AUTHORIZATION); ProtocolResult = KRB_ERR_GENERIC; break; default: SET_SHORT(ResultData, KERB_KPASSWD_ERROR); ProtocolResult = KRB_ERR_GENERIC; break; } } else if (!KERB_SUCCESS(ProtocolResult)) { switch(ProtocolResult) { case KRB_ERR_GENERIC: // // BUG 453652: how does this distinguish between random hard errors // and malformed data? // SET_SHORT(ResultData, KERB_KPASSWD_MALFORMED); break; default: // // The other errors come from the call to verify the // AP request // SET_SHORT(ResultData, KERB_KPASSWD_AUTHENTICATION); break; } } // // Now build the AP reply, if possible. // if (ARGUMENT_PRESENT(EncryptedTicket)) { NTSTATUS Status = STATUS_SUCCESS; KERB_AP_REPLY Reply = {0}; KERB_ENCRYPTED_AP_REPLY ReplyBody = {0}; Reply.version = KERBEROS_VERSION; Reply.message_type = KRB_AP_REP; ReplyBody.client_time = Authenticator->client_time; ReplyBody.client_usec = Authenticator->client_usec; KerbErr = KdcMarshallApReply( &Reply, &ReplyBody, EncryptedTicket->key.keytype, &EncryptedTicket->key, &PackedApReply, &PackedApReplySize ); if (!KERB_SUCCESS(KerbErr)) { goto BuildError; } PrivBody.sender_address.addr_type = KERB_ADDRTYPE_INET; if (ARGUMENT_PRESENT(ServerAddress)) { PrivBody.sender_address.address.length = 4; PrivBody.sender_address.address.value = (PUCHAR) &((PSOCKADDR_IN)ServerAddress)->sin_addr.S_un.S_addr; } else { PrivBody.sender_address.address.length = 0; PrivBody.sender_address.address.value = NULL; } PrivBody.user_data.length = sizeof(ResultData); PrivBody.user_data.value = ResultData; KerbErr = KerbPackData( &PrivBody, KERB_ENCRYPTED_PRIV_PDU, &PackedPrivBodySize, &PackedPrivBody ); if (!KERB_SUCCESS(KerbErr)) { goto BuildError; } // // Now encrypt it with the session key // KerbErr = KerbAllocateEncryptionBufferWrapper( SessionKey->keytype, PackedPrivBodySize, &PrivMessage.encrypted_part.cipher_text.length, &PrivMessage.encrypted_part.cipher_text.value ); if (!KERB_SUCCESS(KerbErr)) { goto BuildError; } KerbErr = KerbEncryptDataEx( &PrivMessage.encrypted_part, PackedPrivBodySize, PackedPrivBody, SessionKey->keytype, KERB_PRIV_SALT, SessionKey ); if (!KERB_SUCCESS(KerbErr)) { goto BuildError; } PrivMessage.version = KERBEROS_VERSION; PrivMessage.message_type = KRB_PRIV; // // Now pack the KERB_PRIV // KerbErr = KerbPackData( &PrivMessage, KERB_PRIV_MESSAGE_PDU, &ReplyDataSize, &ReplyData ); if (!KERB_SUCCESS(KerbErr)) { goto BuildError; } ReturningError = FALSE; } BuildError: // // If we have an error of one of the three error codes, build the // appropriate result code & error code for the KERB_ERROR // if (!KERB_SUCCESS(KerbErr) || (ReplyData == NULL)) { UNICODE_STRING TempString; RtlInitUnicodeString( &TempString, KERB_KPASSWD_NAME ); KerbErr = KerbBuildErrorMessageEx( ProtocolResult, pExtendedError, // note: probably won't get used SecData.KdcDnsRealmName(), SecData.KpasswdInternalName(), NULL, // no client realm, ResultData, sizeof(ResultData), &ReplyDataSize, &ReplyData ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } // // Now, if we have an AP reply, build a kpasswd response. Otherwise // return the kerb_error message // if (ReturningError) { Response->Buffer = ReplyData; ReplyData = NULL; Response->BufferSize = ReplyDataSize; goto Cleanup; } else { USHORT TempShort; PKERB_KPASSWD_REP Reply = NULL; if ((FIELD_OFFSET(KERB_KPASSWD_REP,Data) + PackedApReplySize + ReplyDataSize) > SHRT_MAX) { D_DebugLog((DEB_ERROR,"Kpasswd reply too long!\n")); KerbErr = KRB_ERR_FIELD_TOOLONG; goto Cleanup; } ReplySize = (USHORT) (FIELD_OFFSET(KERB_KPASSWD_REP,Data) + PackedApReplySize + ReplyDataSize); Reply = (PKERB_KPASSWD_REP) MIDL_user_allocate(ReplySize); if (Reply == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } SET_SHORT(Reply->MessageLength,ReplySize); SET_SHORT(Reply->Version, KERB_KPASSWD_VERSION); SET_SHORT(Reply->ApRepLength, (USHORT) PackedApReplySize); RtlCopyMemory( Reply->Data, PackedApReply, PackedApReplySize ); RtlCopyMemory( Reply->Data + PackedApReplySize, ReplyData, ReplyDataSize ); Response->Buffer = (PBYTE) Reply; Response->BufferSize = ReplySize; } Cleanup: if (IpAddresses != NULL) { I_NetLogonFree(IpAddresses); } if (PackedApReply != NULL) { MIDL_user_free(PackedApReply); } if (PackedPrivBody !=NULL) { MIDL_user_free(PackedPrivBody); } if (ReplyData != NULL) { MIDL_user_free(ReplyData); } return(KerbErr); } //+------------------------------------------------------------------------- // // Function: KdcChangePassword // // Synopsis: receives a kerb-change-password buffer and attempts the // password change // // Effects: // // Arguments: Context - ATQ context, used for requesting more data if the // buffer isn't complete // ClientAddress - Address of client, from the socket // ServerAddress - address client used to contact this server. // InputMessage - Receives data sent by client // OutputMessage - Contains data to be sent to client & freed // using KdcFreeEncodedData // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- extern "C" KERBERR KdcChangePassword( IN OPTIONAL PVOID Context, IN OPTIONAL PSOCKADDR ClientAddress, IN OPTIONAL PSOCKADDR ServerAddress, IN PKERB_MESSAGE_BUFFER InputMessage, OUT PKERB_MESSAGE_BUFFER OutputMessage ) { PKERB_KPASSWD_REQ Request = NULL; PKERB_KPASSWD_REP Reply = NULL; NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr = KDC_ERR_NONE; KERB_EXT_ERROR ExtendedError = {0,0}; PKERB_EXT_ERROR pExtendedError = &ExtendedError; USHORT ProtocolVersion; USHORT MessageLength; USHORT ApReqLength; ULONG PrivLength; PKERB_AUTHENTICATOR Authenticator = NULL; PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL; KERB_ENCRYPTION_KEY SessionKey = {0}; KERB_ENCRYPTION_KEY ServerKey = {0}; KDC_TICKET_INFO ServerTicketInfo = {0}; KDC_TICKET_INFO ClientTicketInfo = {0}; UNICODE_STRING Password = {0}; ANSI_STRING AnsiPassword = {0}; PKERB_PRIV_MESSAGE PrivMessage = NULL; PKERB_ENCRYPTED_PRIV PrivBody = NULL; BOOLEAN UseSubKey = FALSE; ULONG TicketFlags; BOOLEAN DoPasswordSet = FALSE; PKERB_CHANGE_PASSWORD_DATA ChangeData = NULL; PKERB_INTERNAL_NAME ClientName = NULL; UNICODE_STRING ClientRealm = {0}; UNICODE_STRING ReferralRealm = {0}; BOOLEAN ClientReferral = FALSE; HANDLE TokenHandle = NULL; PSID UserSid = NULL; ULONG UserRid = 0; SAM_CLIENT_INFO SamClientInfoBuffer; PSAM_CLIENT_INFO SamClientInfo = NULL; KDC_CHANGEPASS_INFO ChangePassTraceInfo; if( KdcEventTraceFlag ) // Event Trace: KerbChangePasswordStart {No Data} { ChangePassTraceInfo.EventTrace.Guid = KdcChangePassGuid; ChangePassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; ChangePassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; ChangePassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER); TraceEvent( KdcTraceLoggerHandle, (PEVENT_TRACE_HEADER)&ChangePassTraceInfo ); } Status = EnterApiCall(); if (!NT_SUCCESS(Status)) { return(Status); } if (InputMessage->BufferSize < sizeof(KERB_KPASSWD_REQ)) { D_DebugLog((DEB_ERROR,"Bad message size to KdcChangePassword: %d\n",InputMessage->BufferSize)); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KDC_ERR_NO_RESPONSE; goto NoMsgResponse; } Request = (PKERB_KPASSWD_REQ) InputMessage->Buffer; // // Verify the length & protocol version // GET_SHORT(MessageLength, Request->MessageLength); GET_SHORT(ProtocolVersion, Request->Version); if (ProtocolVersion == KERB_KPASSWD_SET_VERSION) { // // This is the advanced protocol // DoPasswordSet = TRUE; } else if (ProtocolVersion != KERB_KPASSWD_VERSION) { D_DebugLog((DEB_ERROR,"Bad version passed to KdcChangePassword: %d\n", ProtocolVersion )); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KDC_ERR_NO_RESPONSE; goto NoMsgResponse; } if ((ULONG)MessageLength != InputMessage->BufferSize) { D_DebugLog((DEB_ERROR,"Bad length passed to KdcChangePassword: %d instead of %d\n", MessageLength, InputMessage->BufferSize )); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KDC_ERR_NO_RESPONSE; goto NoMsgResponse; } // // Unpack the AP request // GET_SHORT(ApReqLength, Request->ApReqLength); if (FIELD_OFFSET(KERB_KPASSWD_REQ, ApReqLength) + ApReqLength > MessageLength) { D_DebugLog((DEB_ERROR,"ApReqLength in kpasswd request bad: %d\n",ApReqLength)); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KDC_ERR_NO_RESPONSE; goto NoMsgResponse; } // // Verify the AP request // KerbErr = KdcVerifyKdcRequest( Request->Data, ApReqLength, ClientAddress, FALSE, // this is not a kdc request NULL, &Authenticator, &EncryptedTicket, &SessionKey, &ServerKey, &ServerTicketInfo, &UseSubKey, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { if (KerbErr == KDC_ERR_NO_RESPONSE) { goto NoMsgResponse; } DebugLog((DEB_WARN,"Failed to unpack AP req in kpasswd request: 0x%x\n", KerbErr )); goto Cleanup; } // // The spec says the client has to ask for a sub key. // if (!UseSubKey) { D_DebugLog((DEB_ERROR,"The client of kpasswd did not ask for a sub key.\n")); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } // // The name of the principal must be correct. // // WAS BUG: check by RID // if (ServerTicketInfo.UserId != DOMAIN_USER_RID_KRBTGT) { D_DebugLog((DEB_ERROR,"Wrong principal for kpasswd: %wZ\n", &ServerTicketInfo.AccountName )); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KRB_AP_ERR_NOT_US; goto Cleanup; } // // Now try to unpack the KERB_PRIV // PrivLength = MessageLength - (ApReqLength + FIELD_OFFSET(KERB_KPASSWD_REQ, Data)); KerbErr = KerbUnpackData( Request->Data + ApReqLength, PrivLength, KERB_PRIV_MESSAGE_PDU, (PVOID *) &PrivMessage ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"Failed to decode priv message in kpasswd req: 0x%x\n",KerbErr)); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); goto Cleanup; } // // Now decrypt the KERB_PRIV message // if (PrivMessage->version != KERBEROS_VERSION) { D_DebugLog((DEB_ERROR,"Bad version in kpasswd priv message: %d\n", PrivMessage->version )); KerbErr = KRB_AP_ERR_BADVERSION; goto Cleanup; } if (PrivMessage->message_type != KRB_PRIV) { D_DebugLog((DEB_ERROR,"Bad message type in kpasswd priv message: %d\n", PrivMessage->message_type )); KerbErr = KRB_ERR_GENERIC; 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)) { D_DebugLog((DEB_ERROR,"Failed to decrypt priv message from kpasswd: 0x%x\n", 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)) { D_DebugLog((DEB_ERROR,"Failed to unpack priv body from kpasswd: 0x%x\n", KerbErr)); goto Cleanup; } // // Verify the client's address // if (ARGUMENT_PRESENT(ClientAddress)) { KERB_HOST_ADDRESSES Addresses; // // Build a host_addresses structure because the caller sent a single // address. // Addresses.next = NULL; Addresses.value.address_type = PrivBody->sender_address.addr_type; Addresses.value.address.value = PrivBody->sender_address.address.value; Addresses.value.address.length = PrivBody->sender_address.address.length; KerbErr = KdcVerifyClientAddress( ClientAddress, &Addresses ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"Client sent kpasswd request with wrong address\n")); goto Cleanup; } } // // Now, we should have a password // if (DoPasswordSet) { // // Unpack the chaneg password data in the priv body. // KerbErr = KerbUnpackData( PrivBody->user_data.value, PrivBody->user_data.length, KERB_CHANGE_PASSWORD_DATA_PDU, (PVOID *) &ChangeData ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"Failed to unpack password change data from kpasswd: 0x%x\n", KerbErr)); goto Cleanup; } if (ChangeData->new_password.length > SHRT_MAX / 2) { D_DebugLog((DEB_ERROR,"Password length too long: %d\n", ChangeData->new_password.length )); KerbErr = KRB_ERR_FIELD_TOOLONG; goto Cleanup; } AnsiPassword.Length = (USHORT)ChangeData->new_password.length; AnsiPassword.MaximumLength = AnsiPassword.Length; AnsiPassword.Buffer = (PCHAR) ChangeData->new_password.value; KerbErr = KerbStringToUnicodeString( &Password, &AnsiPassword ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // if the target name and realm aren't present, this is a // change password // if ((ChangeData->bit_mask & (target_name_present | target_realm_present)) != (target_name_present | target_realm_present)) { DoPasswordSet = FALSE; } else { // // Get the names from the change data // KerbErr = KerbConvertPrincipalNameToKdcName( &ClientName, &ChangeData->target_name ); if (!NT_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &ChangeData->target_realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } } if (!DoPasswordSet) { // // The spec says the ticket must be an initial ticket for a // change. // TicketFlags = KerbConvertFlagsToUlong(&EncryptedTicket->flags); if ((TicketFlags & KERB_TICKET_FLAGS_initial) == 0) { D_DebugLog((DEB_ERROR,"Ticket to kpasswd was not initial\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } // // NOTE: verify other kerb-priv fields here // Current ID doesn't require other kerb priv fields, so // this is a no-op. If things change, however, we'll need // to add code here. // // // If we didn't get a password from the change password data, // get it directly from the data // if (Password.Buffer == NULL) { // // The password has to fit in a unicode string, so it can't be longer // than half a ushort. // if (PrivBody->user_data.length > SHRT_MAX / 2) { D_DebugLog((DEB_ERROR,"Password length too long: %d\n", PrivBody->user_data.length )); KerbErr = KRB_ERR_FIELD_TOOLONG; goto Cleanup; } AnsiPassword.Length = (USHORT)PrivBody->user_data.length; AnsiPassword.MaximumLength = AnsiPassword.Length; AnsiPassword.Buffer = (PCHAR) PrivBody->user_data.value; KerbErr = KerbStringToUnicodeString( &Password, &AnsiPassword ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } KerbErr = KerbConvertPrincipalNameToKdcName( &ClientName, &EncryptedTicket->client_name ); if (!NT_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &EncryptedTicket->client_realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } // // Get the client ticket info so we can do a set pass // KerbErr = KdcNormalize( ClientName, NULL, &ClientRealm, KDC_NAME_CLIENT, &ClientReferral, &ReferralRealm, &ClientTicketInfo, pExtendedError, NULL, // no UserHandle 0L, // no fields to fetch 0L, // no extended fields NULL, // no fields to fetch NULL // no GroupMembership ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to normalize name ")); KerbPrintKdcName(DEB_ERROR,ClientName); goto Cleanup; } { LUID LogonId = {0}; UNICODE_STRING UClientName = {0}; UNICODE_STRING UClientDomain = {0}; Status = KerbCreateTokenFromTicket( EncryptedTicket, Authenticator, 0, // no flags &ServerKey, SecData.KdcDnsRealmName(), &SessionKey, &LogonId, &UserSid, &TokenHandle, &UClientName, &UClientDomain ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create token from ticket: 0x%x\n",Status)); FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } // // Setup the client info // if ( (ClientAddress == NULL) || (ClientAddress->sa_family == AF_INET) ) { // Set to local address (known to be 4 bytes) or IP address RtlZeroMemory(&SamClientInfoBuffer, sizeof(SamClientInfoBuffer)); SamClientInfoBuffer.Type = SamClientIpAddr; SamClientInfoBuffer.Data.IpAddr = *((ULONG*)GET_CLIENT_ADDRESS(ClientAddress)); SamClientInfo = &SamClientInfoBuffer; } // // Free all the memory returned // KerbFree(UClientName.Buffer); KerbFree(UClientDomain.Buffer); KerbFree(UserSid); // // Store the password on the user's account // // // We shouldn't enforce password policy restrictions if we do a password SET // if (!DoPasswordSet) { Status = SamIChangePasswordForeignUser2( SamClientInfo, &ClientTicketInfo.AccountName, &Password, TokenHandle, USER_CHANGE_PASSWORD ); } else { Status = SamISetPasswordForeignUser2( SamClientInfo, &ClientTicketInfo.AccountName, &Password, TokenHandle ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to change password for user %wZ: 0x%x\n", &ClientTicketInfo.AccountName, Status )); FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__); KerbErr = KDC_ERR_POLICY; goto Cleanup; } } Cleanup: KerbErr = KdcBuildKpasswdResponse( EncryptedTicket, Authenticator, &SessionKey, ServerAddress, Status, KerbErr, pExtendedError, OutputMessage ); NoMsgResponse: if( KdcEventTraceFlag ) // Event Trace: KdcChangePasswordEnd {KerbErr, ExtErr, Klininfo, (ClientRealm), (AccountName)} { INSERT_ULONG_INTO_MOF( KerbErr, ChangePassTraceInfo.MofData, 0 ); INSERT_ULONG_INTO_MOF( ExtendedError.status, ChangePassTraceInfo.MofData, 1 ); INSERT_ULONG_INTO_MOF( ExtendedError.klininfo, ChangePassTraceInfo.MofData, 2 ); // Protect against uninitialized UNICODE_STRINGs WCHAR UnicodeNullChar = 0; UNICODE_STRING UnicodeEmptyString = {sizeof(WCHAR),sizeof(WCHAR),&UnicodeNullChar}; PUNICODE_STRING pClientRealmTraceString = &ClientRealm; PUNICODE_STRING pAccountNameTraceString = &ClientTicketInfo.AccountName; if( ClientRealm.Buffer == NULL || ClientRealm.Length <= 0 ) pClientRealmTraceString = &UnicodeEmptyString; if( ClientTicketInfo.AccountName.Buffer == NULL || ClientTicketInfo.AccountName.Length <= 0 ) pAccountNameTraceString = &UnicodeEmptyString; // INSERT_UNICODE_STRING_INTO_MOF( *pClientRealmTraceString, ChangePassTraceInfo.MofData, 3 ); INSERT_UNICODE_STRING_INTO_MOF( *pAccountNameTraceString, ChangePassTraceInfo.MofData, 5 ); ChangePassTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 7*sizeof(MOF_FIELD); ChangePassTraceInfo.EventTrace.Guid = KdcChangePassGuid; ChangePassTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; ChangePassTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR; TraceEvent( KdcTraceLoggerHandle, (PEVENT_TRACE_HEADER)&ChangePassTraceInfo ); } KerbFreeKey( &SessionKey ); KerbFreeKey( &ServerKey ); KerbFreeTicket(EncryptedTicket); FreeTicketInfo(&ServerTicketInfo); FreeTicketInfo(&ClientTicketInfo); KerbFreeData(KERB_PRIV_MESSAGE_PDU, PrivMessage); KerbFreeData(KERB_ENCRYPTED_PRIV_PDU, PrivBody); KerbFreeData(KERB_CHANGE_PASSWORD_DATA_PDU, ChangeData); KerbFreeString(&Password); KerbFreeAuthenticator(Authenticator); KerbFreeKdcName(&ClientName); KerbFreeString(&ClientRealm); KerbFreeString(&ReferralRealm); if (TokenHandle != NULL) { NtClose(TokenHandle); } LeaveApiCall(); return(KerbErr); }