static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
    PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
    ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr,
    PTimeStamp ptsTimeStamp)
{
	NEGOTIATE_CONTEXT* context = NULL;
	NEGOTIATE_CONTEXT init_context = { 0 };
	MechCred* creds;
	PCredHandle sub_cred = NULL;
	NegToken input_token = { NOSTATE, 0 };
	NegToken output_token = { NOSTATE, 0 };
	PSecBuffer input_buffer = NULL;
	PSecBuffer output_buffer = NULL;
	SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
	SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
	SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
	WinPrAsn1Decoder dec, dec2;
	WinPrAsn1_tagId tag;
	WinPrAsn1_OID oid = { 0 };
	const Mech* first_mech = NULL;

	if (!phCredential || !SecIsValidHandle(phCredential))
		return SEC_E_NO_CREDENTIALS;

	if (!pInput)
		return SEC_E_INVALID_TOKEN;

	context = sspi_SecureHandleGetLowerPointer(phContext);
	creds = sspi_SecureHandleGetLowerPointer(phCredential);
	input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
	if (pOutput)
		output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);

	if (!context)
	{
		init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
		if (!init_context.mech && !init_context.spnego)
			return SEC_E_INVALID_TOKEN;

		WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid));

		if (init_context.spnego)
		{
			/* Process spnego token */
			if (!negotiate_read_neg_token(input_buffer, &input_token))
				return SEC_E_INVALID_TOKEN;

			/* First token must be negoTokenInit and must contain a mechList */
			if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
				return SEC_E_INVALID_TOKEN;

			init_context.mechTypes.BufferType = SECBUFFER_DATA;
			init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;

			/* Prepare to read mechList */
			WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
			                         input_token.mechTypes.cbBuffer);

			if (!WinPrAsn1DecReadSequence(&dec, &dec2))
				return SEC_E_INVALID_TOKEN;
			dec = dec2;

			/* If an optimistic token was provided pass it into the first mech */
			if (input_token.mechToken.cbBuffer)
			{
				if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
					return SEC_E_INVALID_TOKEN;

				init_context.mech = negotiate_GetMechByOID(&oid);

				if (init_context.mech)
				{
					output_token.mechToken = *output_buffer;
					WLog_DBG(TAG, "Requested mechanism: %s",
					         negotiate_mech_name(init_context.mech->oid));
				}
			}
		}

		if (init_context.mech)
		{
			sub_cred = negotiate_FindCredential(creds, init_context.mech);

			status = init_context.mech->pkg->table->AcceptSecurityContext(
			    sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,
			    TargetDataRep, &init_context.sub_context,
			    init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
		}

		if (IsSecurityStatusError(status))
		{
			if (!init_context.spnego)
				return status;

			init_context.mic = TRUE;
			first_mech = init_context.mech;
			init_context.mech = NULL;
			output_token.mechToken.cbBuffer = 0;
		}

		while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
		{
			/* Read each mechanism */
			if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
				return SEC_E_INVALID_TOKEN;

			init_context.mech = negotiate_GetMechByOID(&oid);
			WLog_DBG(TAG, "Requested mechanism: %s", negotiate_mech_name(init_context.mech->oid));

			/* Microsoft may send two versions of the kerberos OID */
			if (init_context.mech == first_mech)
				init_context.mech = NULL;

			if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
				init_context.mech = NULL;
		}

		if (!init_context.mech)
			return SEC_E_INTERNAL_ERROR;

		context = negotiate_ContextNew(&init_context);
		if (!context)
		{
			if (!IsSecurityStatusError(status))
				init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
			return SEC_E_INSUFFICIENT_MEMORY;
		}

		sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
		sspi_SecureHandleSetLowerPointer(phNewContext, context);

		if (!init_context.spnego)
			return status;

		CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
		           input_token.mechTypes.cbBuffer);

		if (!context->mech->preferred)
		{
			output_token.negState = REQUEST_MIC;
			context->mic = TRUE;
		}
		else
		{
			output_token.negState = ACCEPT_INCOMPLETE;
		}

		if (status == SEC_E_OK)
			context->state = NEGOTIATE_STATE_FINAL;
		else
			context->state = NEGOTIATE_STATE_NEGORESP;

		output_token.supportedMech = oid;
		WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
	}
	else
	{
		sub_cred = negotiate_FindCredential(creds, context->mech);
		if (!sub_cred)
			return SEC_E_NO_CREDENTIALS;

		if (!context->spnego)
		{
			return context->mech->pkg->table->AcceptSecurityContext(
			    sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
			    &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
		}

		if (!negotiate_read_neg_token(input_buffer, &input_token))
			return SEC_E_INVALID_TOKEN;

		/* Process the mechanism token */
		if (input_token.mechToken.cbBuffer > 0)
		{
			if (context->state != NEGOTIATE_STATE_NEGORESP)
				return SEC_E_INVALID_TOKEN;

			/* Use the output buffer to store the optimistic token */
			CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));

			status = context->mech->pkg->table->AcceptSecurityContext(
			    sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
			    TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);

			if (IsSecurityStatusError(status))
				return status;

			if (status == SEC_E_OK)
				context->state = NEGOTIATE_STATE_FINAL;
		}
		else if (context->state == NEGOTIATE_STATE_NEGORESP)
			return SEC_E_INVALID_TOKEN;
	}

	if (context->state == NEGOTIATE_STATE_FINAL)
	{
		/* Check if initiator sent the last mech token without a mic and a mic was required */
		if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
			return SEC_E_INVALID_TOKEN;

		if (context->mic || input_token.mic.cbBuffer > 0)
		{
			status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
			if (status != SEC_E_OK)
				return status;
		}
		else
			output_token.negState = ACCEPT_COMPLETED;
	}

	if (input_token.negState == ACCEPT_COMPLETED)
	{
		output_buffer->cbBuffer = 0;
		return SEC_E_OK;
	}

	if (output_token.negState == ACCEPT_COMPLETED)
		status = SEC_E_OK;
	else
		status = SEC_I_CONTINUE_NEEDED;

	if (!negotiate_write_neg_token(output_buffer, &output_token))
		return SEC_E_INTERNAL_ERROR;

	return status;
}
static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
    PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
    ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr,
    PTimeStamp ptsTimeStamp)
{
	NEGOTIATE_CONTEXT* context = NULL;
	NEGOTIATE_CONTEXT init_context = { 0 };
	MechCred* creds;
	PCredHandle sub_cred = NULL;
	NegToken input_token = { NOSTATE, 0 };
	NegToken output_token = { NOSTATE, 0 };
	PSecBuffer input_buffer = NULL;
	PSecBuffer output_buffer = NULL;
	SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
	SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
	SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
	WinPrAsn1Decoder dec, dec2;
	WinPrAsn1_tagId tag;
	WinPrAsn1_OID oid = { 0 };
	const Mech* first_mech = NULL;

	if (!phCredential || !SecIsValidHandle(phCredential))
		return SEC_E_NO_CREDENTIALS;

	if (!pInput)
		return SEC_E_INVALID_TOKEN;

	context = sspi_SecureHandleGetLowerPointer(phContext);
	creds = sspi_SecureHandleGetLowerPointer(phCredential);
	input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
	if (pOutput)
		output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);

	if (!context)
	{
		init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
		if (!init_context.mech && !init_context.spnego)
			return SEC_E_INVALID_TOKEN;

		WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid));

		if (init_context.spnego)
		{
			 
			if (!negotiate_read_neg_token(input_buffer, &input_token))
				return SEC_E_INVALID_TOKEN;

			 
			if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
				return SEC_E_INVALID_TOKEN;

			init_context.mechTypes.BufferType = SECBUFFER_DATA;
			init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;

			 
			WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
			                         input_token.mechTypes.cbBuffer);

			if (!WinPrAsn1DecReadSequence(&dec, &dec2))
				return SEC_E_INVALID_TOKEN;
			dec = dec2;

			 
			if (input_token.mechToken.cbBuffer)
			{
				if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
					return SEC_E_INVALID_TOKEN;

				init_context.mech = negotiate_GetMechByOID(&oid);

				if (init_context.mech)
				{
					output_token.mechToken = *output_buffer;
					WLog_DBG(TAG, "Requested mechanism: %s",
					         negotiate_mech_name(init_context.mech->oid));
				}
			}
		}

		if (init_context.mech)
		{
			sub_cred = negotiate_FindCredential(creds, init_context.mech);

			status = init_context.mech->pkg->table->AcceptSecurityContext(
			    sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,
			    TargetDataRep, &init_context.sub_context,
			    init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
		}

		if (IsSecurityStatusError(status))
		{
			if (!init_context.spnego)
				return status;

			init_context.mic = TRUE;
			first_mech = init_context.mech;
			init_context.mech = NULL;
			output_token.mechToken.cbBuffer = 0;
		}

		while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
		{
			 
			if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
				return SEC_E_INVALID_TOKEN;

			init_context.mech = negotiate_GetMechByOID(&oid);
			WLog_DBG(TAG, "Requested mechanism: %s", negotiate_mech_name(init_context.mech->oid));

			 
			if (init_context.mech == first_mech)
				init_context.mech = NULL;

			if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
				init_context.mech = NULL;
		}

		if (!init_context.mech)
			return SEC_E_INTERNAL_ERROR;

		context = negotiate_ContextNew(&init_context);
		if (!context)
		{
			if (!IsSecurityStatusError(status))
				init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
			return SEC_E_INSUFFICIENT_MEMORY;
		}

		sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
		sspi_SecureHandleSetLowerPointer(phNewContext, context);

		if (!init_context.spnego)
			return status;

		CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
		           input_token.mechTypes.cbBuffer);

		if (!context->mech->preferred)
		{
			output_token.negState = REQUEST_MIC;
			context->mic = TRUE;
		}
		else
		{
			output_token.negState = ACCEPT_INCOMPLETE;
		}

		if (status == SEC_E_OK)
			context->state = NEGOTIATE_STATE_FINAL;
		else
			context->state = NEGOTIATE_STATE_NEGORESP;

		output_token.supportedMech = oid;
		WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
	}
	else
	{
		sub_cred = negotiate_FindCredential(creds, context->mech);
		if (!sub_cred)
			return SEC_E_NO_CREDENTIALS;

		if (!context->spnego)
		{
			return context->mech->pkg->table->AcceptSecurityContext(
			    sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
			    &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
		}

		if (!negotiate_read_neg_token(input_buffer, &input_token))
			return SEC_E_INVALID_TOKEN;

		 
		if (input_token.mechToken.cbBuffer > 0)
		{
			if (context->state != NEGOTIATE_STATE_NEGORESP)
				return SEC_E_INVALID_TOKEN;

			 
			CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));

			status = context->mech->pkg->table->AcceptSecurityContext(
			    sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
			    TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);

			if (IsSecurityStatusError(status))
				return status;

			if (status == SEC_E_OK)
				context->state = NEGOTIATE_STATE_FINAL;
		}
		else if (context->state == NEGOTIATE_STATE_NEGORESP)
			return SEC_E_INVALID_TOKEN;
	}

	if (context->state == NEGOTIATE_STATE_FINAL)
	{
		 
		if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
			return SEC_E_INVALID_TOKEN;

		if (context->mic || input_token.mic.cbBuffer > 0)
		{
			status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
			if (status != SEC_E_OK)
				return status;
		}
		else
			output_token.negState = ACCEPT_COMPLETED;
	}

	if (input_token.negState == ACCEPT_COMPLETED)
	{
		output_buffer->cbBuffer = 0;
		return SEC_E_OK;
	}

	if (output_token.negState == ACCEPT_COMPLETED)
		status = SEC_E_OK;
	else
		status = SEC_I_CONTINUE_NEEDED;

	if (!negotiate_write_neg_token(output_buffer, &output_token))
		return SEC_E_INTERNAL_ERROR;

	return status;
}