/**
 * WinPR: Windows Portable Runtime
 * NTLM Security Package (AV_PAIRs)
 *
 * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>

#include "ntlm.h"
#include "../sspi.h"

#include <winpr/crt.h>
#include <winpr/print.h>
#include <winpr/sysinfo.h>
#include <winpr/tchar.h>
#include <winpr/crypto.h>

#include "ntlm_compute.h"

#include "ntlm_av_pairs.h"

#include "../../log.h"
#define TAG WINPR_TAG("sspi.NTLM")

static const char* const AV_PAIR_STRINGS[] =
{
	"MsvAvEOL",
	"MsvAvNbComputerName",
	"MsvAvNbDomainName",
	"MsvAvDnsComputerName",
	"MsvAvDnsDomainName",
	"MsvAvDnsTreeName",
	"MsvAvFlags",
	"MsvAvTimestamp",
	"MsvAvRestrictions",
	"MsvAvTargetName",
	"MsvChannelBindings"
};

static NTLM_AV_PAIR* ntlm_av_pair_get_next_pointer(NTLM_AV_PAIR* pAvPair, size_t* pcbAvPair);

static void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16 id)
{
	Data_Write_UINT16(&pAvPair->AvId, id);
}

static void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len)
{
	Data_Write_UINT16(&pAvPair->AvLen, len);
}

static BOOL ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
{
	NTLM_AV_PAIR* pAvPair = pAvPairList;

	if (!pAvPair || (cbAvPairList < sizeof(NTLM_AV_PAIR)))
		return FALSE;

	ntlm_av_pair_set_id(pAvPair, MsvAvEOL);
	ntlm_av_pair_set_len(pAvPair, 0);
	return TRUE;
}

static INLINE UINT16 ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair)
{
	UINT16 AvId;

	if (!pAvPair)
		return MsvAvEOL;

	Data_Read_UINT16(&pAvPair->AvId, AvId);
	return AvId;
}

ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairListMaxLength)
{
	ULONG length;
	NTLM_AV_PAIR* pAvPair = pAvPairList;

	if (!pAvPair)
		return 0;

	while (ntlm_av_pair_get_id(pAvPair) != MsvAvEOL)
	{
		pAvPair = ntlm_av_pair_get_next_pointer(pAvPair, &cbAvPairListMaxLength);
	}

	length = (pAvPair - pAvPairList) + sizeof(NTLM_AV_PAIR);
	return length;
}

static INLINE SSIZE_T ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
{
	UINT16 AvLen;

	if (!pAvPair || (cbAvPair < sizeof(NTLM_AV_PAIR)))
		return -1;

	Data_Read_UINT16(&pAvPair->AvLen, AvLen);

	if (cbAvPair < sizeof(NTLM_AV_PAIR) + pAvPair->AvLen)
		return -1;

	return AvLen;
}

void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
{
	NTLM_AV_PAIR* pAvPair = pAvPairList;

	if (!pAvPair)
		return;

	WLog_INFO(TAG, "AV_PAIRs =");

	while (ntlm_av_pair_get_id(pAvPair) != MsvAvEOL)
	{
		WLog_INFO(TAG, "\t%s AvId: %"PRIu16" AvLen: %"PRIu16"",
		          AV_PAIR_STRINGS[ntlm_av_pair_get_id(pAvPair)],
		          ntlm_av_pair_get_id(pAvPair),
		          ntlm_av_pair_get_len(pAvPair, cbAvPairList));
		winpr_HexDump(TAG, WLOG_INFO, ntlm_av_pair_get_value_pointer(pAvPair, cbAvPairList),
		              ntlm_av_pair_get_len(pAvPair, cbAvPairList));
		pAvPair = ntlm_av_pair_get_next_pointer(pAvPair, &cbAvPairList);
	}
}

static ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength)
{
	/* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */
	return ((AvPairsCount + 1) * 4) + AvPairsValueLength;
}

PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair, size_t cbAvPairListMaxLength)
{
	if (cbAvPairListMaxLength < 2 * sizeof(NTLM_AV_PAIR))
		return NULL;

	return &((PBYTE) pAvPair)[sizeof(NTLM_AV_PAIR)];
}

static SSIZE_T ntlm_av_pair_get_next_offset(NTLM_AV_PAIR* pAvPair, size_t cbAvPairListMaxLength)
{
	return ntlm_av_pair_get_len(pAvPair, cbAvPairListMaxLength) + sizeof(NTLM_AV_PAIR);
}

NTLM_AV_PAIR* ntlm_av_pair_get_next_pointer(NTLM_AV_PAIR* pAvPair, size_t* pcbAvPair)
{
	SSIZE_T offset;

	if (!pAvPair || !pcbAvPair)
		return NULL;

	offset = ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair);

	if ((offset <= 0) || (offset > *pcbAvPair))
		return NULL;

	*pcbAvPair -= offset;
	return (NTLM_AV_PAIR*)((PBYTE) pAvPair + offset);
}

NTLM_AV_PAIR* ntlm_av_pair_get(void* pAvPairList,
                               size_t avPairListLength,
                               NTLM_AV_ID AvId,
                               size_t* pcbAvPairListRemainingLength)
{
	NTLM_AV_PAIR* pAvPair = pAvPairList;

	if (pcbAvPairListRemainingLength)
		*pcbAvPairListRemainingLength = 0;

	while (pAvPair)
	{
		if (ntlm_av_pair_get_id(pAvPair) == AvId)
		{
			if (pcbAvPairListRemainingLength)
				*pcbAvPairListRemainingLength = avPairListLength;

			return pAvPair;
		}

		if (ntlm_av_pair_get_id(pAvPair) == MsvAvEOL)
			return NULL;

		pAvPair = ntlm_av_pair_get_next_pointer(pAvPair, &avPairListLength);
	}

	return NULL;
}

static NTLM_AV_PAIR* ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairListLength,
                                      NTLM_AV_ID AvId, PBYTE Value,
                                      UINT16 AvLen)
{
	size_t cbAvPair;
	NTLM_AV_PAIR* pAvPair;
	pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairListLength, MsvAvEOL, &cbAvPair);

	if (!pAvPair)
		return NULL;

	assert(Value != NULL);
	ntlm_av_pair_set_id(pAvPair, AvId);
	ntlm_av_pair_set_len(pAvPair, AvLen);
	CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), Value, AvLen);
	return pAvPair;
}

static NTLM_AV_PAIR* ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairListLength,
        NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
{
	NTLM_AV_PAIR* pAvPairCopy;
	size_t cbAvPairCopy;
	pAvPairCopy = ntlm_av_pair_get(pAvPairList, cbAvPairListLength, MsvAvEOL, &cbAvPairCopy);

	if (!pAvPairCopy)
		return NULL;

	CopyMemory(&pAvPairCopy->AvId, &pAvPair->AvId, 2);
	CopyMemory(&pAvPairCopy->AvLen, &pAvPair->AvLen, 2);
	CopyMemory(ntlm_av_pair_get_value_pointer(pAvPairCopy, cbAvPairCopy),
	           ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair),
	           ntlm_av_pair_get_len(pAvPair, cbAvPair));
	return pAvPairCopy;
}

static int ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FORMAT type)
{
	char* name;
	int status;
	DWORD nSize = 0;
	CHAR* computerName;

	if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || GetLastError() != ERROR_MORE_DATA)
		return -1;

	computerName = calloc(nSize, sizeof(CHAR));

	if (!computerName)
		return -1;

	if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
	{
		free(computerName);
		return -1;
	}

	if (nSize > MAX_COMPUTERNAME_LENGTH)
		computerName[MAX_COMPUTERNAME_LENGTH] = '\0';

	name = computerName;

	if (!name)
		return -1;

	if (type == ComputerNameNetBIOS)
		CharUpperA(name);

	status = ConvertToUnicode(CP_UTF8, 0, name, -1, &pName->Buffer, 0);

	if (status <= 0)
	{
		free(name);
		return status;
	}

	pName->Length = (USHORT)((status - 1) * 2);
	pName->MaximumLength = pName->Length;
	free(name);
	return 1;
}

static void ntlm_free_unicode_string(PUNICODE_STRING string)
{
	if (string)
	{
		if (string->Length > 0)
		{
			free(string->Buffer);
			string->Buffer = NULL;
			string->Length = 0;
			string->MaximumLength = 0;
		}
	}
}

/**
 * From http://www.ietf.org/proceedings/72/slides/sasl-2.pdf:
 *
 * tls-server-end-point:
 *
 * The hash of the TLS server's end entity certificate as it appears, octet for octet,
 * in the server's Certificate message (note that the Certificate message contains a
 * certificate_list, the first element of which is the server's end entity certificate.)
 * The hash function to be selected is as follows: if the certificate's signature hash
 * algorithm is either MD5 or SHA-1, then use SHA-256, otherwise use the certificate's
 * signature hash algorithm.
 */

/**
 * Channel Bindings sample usage:
 * https://raw.github.com/mozilla/mozilla-central/master/extensions/auth/nsAuthSSPI.cpp
 */

/*
typedef struct gss_channel_bindings_struct {
	OM_uint32 initiator_addrtype;
	gss_buffer_desc initiator_address;
	OM_uint32 acceptor_addrtype;
	gss_buffer_desc acceptor_address;
	gss_buffer_desc application_data;
} *gss_channel_bindings_t;
 */

static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num)
{
	BYTE be32[4];
	be32[0] = (num >> 0) & 0xFF;
	be32[1] = (num >> 8) & 0xFF;
	be32[2] = (num >> 16) & 0xFF;
	be32[3] = (num >> 24) & 0xFF;
	return winpr_Digest_Update(md5, be32, 4);
}

static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context)
{
	WINPR_DIGEST_CTX* md5;
	BYTE* ChannelBindingToken;
	UINT32 ChannelBindingTokenLength;
	SEC_CHANNEL_BINDINGS* ChannelBindings;
	ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH);
	ChannelBindings = context->Bindings.Bindings;

	if (!ChannelBindings)
		return;

	if (!(md5 = winpr_Digest_New()))
		return;

	if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
		goto out;

	ChannelBindingTokenLength = context->Bindings.BindingsLength - sizeof(SEC_CHANNEL_BINDINGS);
	ChannelBindingToken = &((BYTE*) ChannelBindings)[ChannelBindings->dwApplicationDataOffset];

	if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType))
		goto out;

	if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength))
		goto out;

	if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType))
		goto out;

	if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength))
		goto out;

	if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength))
		goto out;

	if (!winpr_Digest_Update(md5, (void*) ChannelBindingToken, ChannelBindingTokenLength))
		goto out;

	if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH))
		goto out;

out:
	winpr_Digest_Free(md5);
}

static void ntlm_compute_single_host_data(NTLM_CONTEXT* context)
{
	/**
	 * The Single_Host_Data structure allows a client to send machine-specific information
	 * within an authentication exchange to services on the same machine. The client can
	 * produce additional information to be processed in an implementation-specific way when
	 * the client and server are on the same host. If the server and client platforms are
	 * different or if they are on different hosts, then the information MUST be ignored.
	 * Any fields after the MachineID field MUST be ignored on receipt.
	 */
	Data_Write_UINT32(&context->SingleHostData.Size, 48);
	Data_Write_UINT32(&context->SingleHostData.Z4, 0);
	Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
	Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID);
	FillMemory(context->SingleHostData.MachineID, 32, 0xAA);
}

int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context)
{
	int rc = -1;
	int length;
	ULONG AvPairsCount;
	ULONG AvPairsLength;
	NTLM_AV_PAIR* pAvPairList;
	size_t cbAvPairList;
	UNICODE_STRING NbDomainName = { 0 };
	UNICODE_STRING NbComputerName = { 0 };
	UNICODE_STRING DnsDomainName = { 0 };
	UNICODE_STRING DnsComputerName = { 0 };

	if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0)
		goto fail;

	NbComputerName.Buffer = NULL;

	if (ntlm_get_target_computer_name(&NbComputerName, ComputerNameNetBIOS) < 0)
		goto fail;

	DnsDomainName.Buffer = NULL;

	if (ntlm_get_target_computer_name(&DnsDomainName, ComputerNameDnsDomain) < 0)
		goto fail;

	DnsComputerName.Buffer = NULL;

	if (ntlm_get_target_computer_name(&DnsComputerName, ComputerNameDnsHostname) < 0)
		goto fail;

	AvPairsCount = 5;
	AvPairsLength = NbDomainName.Length + NbComputerName.Length +
	                DnsDomainName.Length + DnsComputerName.Length + 8;
	length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);

	if (!sspi_SecBufferAlloc(&context->ChallengeTargetInfo, length))
		goto fail;

	pAvPairList = (NTLM_AV_PAIR*) context->ChallengeTargetInfo.pvBuffer;
	cbAvPairList = context->ChallengeTargetInfo.cbBuffer;

	if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList))
		goto fail;

	if (ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName, (PBYTE) NbDomainName.Buffer,
	                     NbDomainName.Length) == NULL)
		goto fail;

	if (ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName, (PBYTE) NbComputerName.Buffer,
	                     NbComputerName.Length) == NULL)
		goto fail;

	if (ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName, (PBYTE) DnsDomainName.Buffer,
	                     DnsDomainName.Length) == NULL)
		goto fail;

	if (ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName,
	                     (PBYTE) DnsComputerName.Buffer,
	                     DnsComputerName.Length) == NULL)
		goto fail;

	if (ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp,
	                     sizeof(context->Timestamp)) == NULL)
		goto fail;

	rc = 1;
fail:
	ntlm_free_unicode_string(&NbDomainName);
	ntlm_free_unicode_string(&NbComputerName);
	ntlm_free_unicode_string(&DnsDomainName);
	ntlm_free_unicode_string(&DnsComputerName);
	return rc;
}

int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context)
{
	ULONG size;
	ULONG AvPairsCount;
	ULONG AvPairsValueLength;
	NTLM_AV_PAIR* AvTimestamp;
	NTLM_AV_PAIR* AvNbDomainName;
	NTLM_AV_PAIR* AvNbComputerName;
	NTLM_AV_PAIR* AvDnsDomainName;
	NTLM_AV_PAIR* AvDnsComputerName;
	NTLM_AV_PAIR* AvDnsTreeName;
	NTLM_AV_PAIR* ChallengeTargetInfo;
	NTLM_AV_PAIR* AuthenticateTargetInfo;
	size_t cbAvTimestamp;
	size_t cbAvNbDomainName;
	size_t cbAvNbComputerName;
	size_t cbAvDnsDomainName;
	size_t cbAvDnsComputerName;
	size_t cbAvDnsTreeName;
	size_t cbChallengeTargetInfo;
	size_t cbAuthenticateTargetInfo;
	AvPairsCount = 1;
	AvPairsValueLength = 0;
	ChallengeTargetInfo = (NTLM_AV_PAIR*) context->ChallengeTargetInfo.pvBuffer;
	cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer;
	AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName,
	                                  &cbAvNbDomainName);
	AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbComputerName,
	                                    &cbAvNbComputerName);
	AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsDomainName,
	                                   &cbAvDnsDomainName);
	AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
	                                     MsvAvDnsComputerName,
	                                     &cbAvDnsComputerName);
	AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName,
	                                 &cbAvDnsTreeName);
	AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp,
	                               &cbAvTimestamp);

	if (AvNbDomainName)
	{
		AvPairsCount++; /* MsvAvNbDomainName */
		AvPairsValueLength += ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName);
	}

	if (AvNbComputerName)
	{
		AvPairsCount++; /* MsvAvNbComputerName */
		AvPairsValueLength += ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName);
	}

	if (AvDnsDomainName)
	{
		AvPairsCount++; /* MsvAvDnsDomainName */
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName);
	}

	if (AvDnsComputerName)
	{
		AvPairsCount++; /* MsvAvDnsComputerName */
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName);
	}

	if (AvDnsTreeName)
	{
		AvPairsCount++; /* MsvAvDnsTreeName */
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName);
	}

	AvPairsCount++; /* MsvAvTimestamp */
	AvPairsValueLength += 8;

	if (context->UseMIC)
	{
		AvPairsCount++; /* MsvAvFlags */
		AvPairsValueLength += 4;
	}

	if (context->SendSingleHostData)
	{
		AvPairsCount++; /* MsvAvSingleHost */
		ntlm_compute_single_host_data(context);
		AvPairsValueLength += context->SingleHostData.Size;
	}

	/**
	 * Extended Protection for Authentication:
	 * http://blogs.technet.com/b/srd/archive/2009/12/08/extended-protection-for-authentication.aspx
	 */

	if (!context->SuppressExtendedProtection)
	{
		/**
		 * SEC_CHANNEL_BINDINGS structure
		 * http://msdn.microsoft.com/en-us/library/windows/desktop/dd919963/
		 */
		AvPairsCount++; /* MsvChannelBindings */
		AvPairsValueLength += 16;
		ntlm_compute_channel_bindings(context);

		if (context->ServicePrincipalName.Length > 0)
		{
			AvPairsCount++; /* MsvAvTargetName */
			AvPairsValueLength += context->ServicePrincipalName.Length;
		}
	}

	size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);

	if (context->NTLMv2)
		size += 8; /* unknown 8-byte padding */

	if (!sspi_SecBufferAlloc(&context->AuthenticateTargetInfo, size))
		return -1;

	AuthenticateTargetInfo = (NTLM_AV_PAIR*) context->AuthenticateTargetInfo.pvBuffer;
	cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer;

	if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo))
		return -1;

	if (AvNbDomainName)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName,
		                          cbAvNbDomainName) == NULL)
			return -1;
	}

	if (AvNbComputerName)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
		                          AvNbComputerName, cbAvNbComputerName) == NULL)
			return -1;
	}

	if (AvDnsDomainName)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
		                          AvDnsDomainName, cbAvDnsDomainName) == NULL)
			return -1;
	}

	if (AvDnsComputerName)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
		                          AvDnsComputerName, cbAvDnsComputerName) == NULL)
			return -1;
	}

	if (AvDnsTreeName)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName,
		                          cbAvDnsTreeName) == NULL)
			return -1;
	}

	if (AvTimestamp)
	{
		if (ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp,
		                          cbAvTimestamp) == NULL)
			return -1;
	}

	if (context->UseMIC)
	{
		UINT32 flags;
		Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK);

		if (ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags, (PBYTE) &flags,
		                     4) == NULL)
			return -1;
	}

	if (context->SendSingleHostData)
	{
		if (ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost,
		                     (PBYTE) &context->SingleHostData, context->SingleHostData.Size) == NULL)
			return -1;
	}

	if (!context->SuppressExtendedProtection)
	{
		if (ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvChannelBindings,
		                     context->ChannelBindingsHash, 16) == NULL)
			return -1;

		if (context->ServicePrincipalName.Length > 0)
		{
			if (ntlm_av_pair_add(AuthenticateTargetInfo,
			                     cbAuthenticateTargetInfo,
			                     MsvAvTargetName,
			                     (PBYTE) context->ServicePrincipalName.Buffer,
			                     context->ServicePrincipalName.Length) == NULL)
				return -1;
		}
	}

	if (context->NTLMv2)
	{
		NTLM_AV_PAIR* AvEOL;
		AvEOL = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, NULL);

		if (!AvEOL)
			return -1;

		ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR));
	}

	return 1;
}
