windows-nt/Source/XPSP1/NT/ds/security/protocols/kerberos/server/kpasswd.cxx

1100 lines
31 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// 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 <nlrepl.h>
}
#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);
}