static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(
    PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
    ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr,
    PTimeStamp ptsExpity)
{
#ifdef WITH_GSSAPI
	KRB_CREDENTIALS* credentials;
	KRB_CONTEXT* context;
	KRB_CONTEXT new_context = { 0 };
	PSecBuffer input_buffer;
	PSecBuffer output_buffer = NULL;
	WinPrAsn1_OID oid = { 0 };
	uint16_t tok_id;
	krb5_data input_token = { 0 };
	krb5_data output_token = { 0 };
	SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
	krb5_error_code rv = 0;
	krb5_flags ap_flags = 0;
	krb5_authenticator* authenticator;
	char* target = NULL;
	char* sname = NULL;
	char* realm = NULL;
	krb5_kt_cursor cur;
	krb5_keytab_entry entry = { 0 };
	krb5_principal principal = NULL;
	krb5_creds creds = { 0 };

	context = sspi_SecureHandleGetLowerPointer(phContext);
	credentials = sspi_SecureHandleGetLowerPointer(phCredential);

	if (pInput)
		input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
	if (pOutput)
		output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);

	if (!input_buffer)
		return SEC_E_INVALID_TOKEN;

	if (!sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token))
		return SEC_E_INVALID_TOKEN;

	if (!context)
	{
		context = &new_context;

		if ((rv = krb5_init_context(&context->ctx)))
			return SEC_E_INTERNAL_ERROR;

		if (sspi_gss_oid_compare(&oid, &kerberos_u2u_OID))
		{
			context->u2u = TRUE;
			context->state = KERBEROS_STATE_TGT_REQ;
		}
		else if (sspi_gss_oid_compare(&oid, &kerberos_OID))
			context->state = KERBEROS_STATE_AP_REQ;
		else
			goto bad_token;
	}
	else
	{
		if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) ||
		    (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID)))
			goto bad_token;
	}

	if (context->state == KERBEROS_STATE_TGT_REQ && tok_id == TOK_ID_TGT_REQ)
	{
		if (!kerberos_rd_tgt_token(&input_token, &target, NULL))
			goto bad_token;

		if (target)
		{
			if (*target != 0 && *target != '@')
				sname = target;
			realm = strchr(target, '@');
			if (realm)
				realm++;
		}

		if ((rv = krb5_parse_name_flags(context->ctx, sname ? sname : "",
		                                KRB5_PRINCIPAL_PARSE_NO_REALM, &principal)))
			goto cleanup;

		if (realm)
			if ((rv = krb5_set_principal_realm(context->ctx, principal, realm)))
				goto cleanup;

		if ((rv = krb5_kt_start_seq_get(context->ctx, credentials->keytab, &cur)))
			goto cleanup;

		while ((rv = krb5_kt_next_entry(context->ctx, credentials->keytab, &entry, &cur)) == 0)
		{
			if ((!sname ||
			     krb5_principal_compare_any_realm(context->ctx, principal, entry.principal)) &&
			    (!realm || krb5_realm_compare(context->ctx, principal, entry.principal)))
				break;
			krb5_free_keytab_entry_contents(context->ctx, &entry);
		}

		krb5_kt_end_seq_get(context->ctx, credentials->keytab, &cur);

		if (!entry.principal)
			goto cleanup;

		/* Get the TGT */
		if ((rv = krb5_get_init_creds_keytab(context->ctx, &creds, entry.principal,
		                                     credentials->keytab, 0, NULL, NULL)))
			goto cleanup;

		if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REP, NULL, NULL, &creds.ticket))
			goto cleanup;

		if ((rv = krb5_auth_con_init(context->ctx, &context->auth_ctx)))
			goto cleanup;

		if ((rv = krb5_auth_con_setuseruserkey(context->ctx, context->auth_ctx, &creds.keyblock)))
			goto cleanup;

		context->state = KERBEROS_STATE_AP_REQ;
	}
	else if (context->state == KERBEROS_STATE_AP_REQ && tok_id == TOK_ID_AP_REQ)
	{
		if ((rv = krb5_rd_req(context->ctx, &context->auth_ctx, &input_token, NULL,
		                      credentials->keytab, &ap_flags, NULL)))
			goto cleanup;

		krb5_auth_con_setflags(context->ctx, context->auth_ctx,
		                       KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY);

		/* Retrieve and validate the checksum */
		if ((rv = krb5_auth_con_getauthenticator(context->ctx, context->auth_ctx, &authenticator)))
			goto cleanup;
		if (!authenticator->checksum || authenticator->checksum->checksum_type != GSS_CHECKSUM_TYPE)
			goto bad_token;
		Data_Read_UINT32((authenticator->checksum->contents + 20), context->flags);

		if (ap_flags & AP_OPTS_MUTUAL_REQUIRED && context->flags & SSPI_GSS_C_MUTUAL_FLAG)
		{
			if (!output_buffer)
				goto bad_token;
			if ((rv = krb5_mk_rep(context->ctx, context->auth_ctx, &output_token)))
				goto cleanup;
			if (!sspi_gss_wrap_token(output_buffer,
			                         context->u2u ? &kerberos_u2u_OID : &kerberos_OID,
			                         TOK_ID_AP_REP, &output_token))
				goto cleanup;
		}
		else
		{
			output_buffer->cbBuffer = 0;
		}

		*pfContextAttr = context->flags & 0x1F;
		if (context->flags & SSPI_GSS_C_INTEG_FLAG)
			*pfContextAttr |= ASC_RET_INTEGRITY;

		if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG)
		{
			krb5_auth_con_getlocalseqnumber(context->ctx, context->auth_ctx,
			                                (INT32*)&context->local_seq);
			krb5_auth_con_getremoteseqnumber(context->ctx, context->auth_ctx,
			                                 (INT32*)&context->remote_seq);
		}

		krb5_auth_con_getkey_k(context->ctx, context->auth_ctx, &context->session_key);
		krb5_auth_con_getrecvsubkey_k(context->ctx, context->auth_ctx, &context->initiator_key);
		krb5_auth_con_getsendsubkey_k(context->ctx, context->auth_ctx, &context->acceptor_key);

		context->state = KERBEROS_STATE_FINAL;
	}
	else
		goto bad_token;

	/* On first call allocate new context */
	if (new_context.ctx)
	{
		context = kerberos_ContextNew();
		if (!context)
		{
			status = SEC_E_INSUFFICIENT_MEMORY;
			goto cleanup;
		}
		CopyMemory(context, &new_context, sizeof(KRB_CONTEXT));
		ZeroMemory(&new_context, sizeof(KRB_CONTEXT));
		context->acceptor = TRUE;
	}

	if (context->state == KERBEROS_STATE_FINAL)
		status = SEC_E_OK;
	else
		status = SEC_I_CONTINUE_NEEDED;

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

cleanup:

	if (rv)
		kerberos_log_msg(context->ctx, rv);

	if (output_token.data)
		krb5_free_data_contents(context->ctx, &output_token);
	if (entry.principal)
		krb5_free_keytab_entry_contents(context->ctx, &entry);

	if (new_context.auth_ctx)
		krb5_auth_con_free(context->ctx, new_context.auth_ctx);
	if (new_context.ctx)
		krb5_free_context(new_context.ctx);

	return status;

bad_token:
	status = SEC_E_INVALID_TOKEN;
	goto cleanup;
#else
	return SEC_E_UNSUPPORTED_FUNCTION;
#endif /* WITH_GSSAPI */
}
static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(
    PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
    ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr,
    PTimeStamp ptsExpity)
{
#ifdef WITH_GSSAPI
	KRB_CREDENTIALS* credentials;
	KRB_CONTEXT* context;
	KRB_CONTEXT new_context = { 0 };
	PSecBuffer input_buffer;
	PSecBuffer output_buffer = NULL;
	WinPrAsn1_OID oid = { 0 };
	uint16_t tok_id;
	krb5_data input_token = { 0 };
	krb5_data output_token = { 0 };
	SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
	krb5_error_code rv = 0;
	krb5_flags ap_flags = 0;
	krb5_authenticator* authenticator;
	char* target = NULL;
	char* sname = NULL;
	char* realm = NULL;
	krb5_kt_cursor cur;
	krb5_keytab_entry entry = { 0 };
	krb5_principal principal = NULL;
	krb5_creds creds = { 0 };

	context = sspi_SecureHandleGetLowerPointer(phContext);
	credentials = sspi_SecureHandleGetLowerPointer(phCredential);

	if (pInput)
		input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
	if (pOutput)
		output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);

	if (!input_buffer)
		return SEC_E_INVALID_TOKEN;

	if (!sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token))
		return SEC_E_INVALID_TOKEN;

	if (!context)
	{
		context = &new_context;

		if ((rv = krb5_init_context(&context->ctx)))
			return SEC_E_INTERNAL_ERROR;

		if (sspi_gss_oid_compare(&oid, &kerberos_u2u_OID))
		{
			context->u2u = TRUE;
			context->state = KERBEROS_STATE_TGT_REQ;
		}
		else if (sspi_gss_oid_compare(&oid, &kerberos_OID))
			context->state = KERBEROS_STATE_AP_REQ;
		else
			goto bad_token;
	}
	else
	{
		if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) ||
		    (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID)))
			goto bad_token;
	}

	if (context->state == KERBEROS_STATE_TGT_REQ && tok_id == TOK_ID_TGT_REQ)
	{
		if (!kerberos_rd_tgt_token(&input_token, &target, NULL))
			goto bad_token;

		if (target)
		{
			if (*target != 0 && *target != '@')
				sname = target;
			realm = strchr(target, '@');
			if (realm)
				realm++;
		}

		if ((rv = krb5_parse_name_flags(context->ctx, sname ? sname : "",
		                                KRB5_PRINCIPAL_PARSE_NO_REALM, &principal)))
			goto cleanup;

		if (realm)
			if ((rv = krb5_set_principal_realm(context->ctx, principal, realm)))
				goto cleanup;

		if ((rv = krb5_kt_start_seq_get(context->ctx, credentials->keytab, &cur)))
			goto cleanup;

		while ((rv = krb5_kt_next_entry(context->ctx, credentials->keytab, &entry, &cur)) == 0)
		{
			if ((!sname ||
			     krb5_principal_compare_any_realm(context->ctx, principal, entry.principal)) &&
			    (!realm || krb5_realm_compare(context->ctx, principal, entry.principal)))
				break;
			krb5_free_keytab_entry_contents(context->ctx, &entry);
		}

		krb5_kt_end_seq_get(context->ctx, credentials->keytab, &cur);

		if (!entry.principal)
			goto cleanup;

		 
		if ((rv = krb5_get_init_creds_keytab(context->ctx, &creds, entry.principal,
		                                     credentials->keytab, 0, NULL, NULL)))
			goto cleanup;

		if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REP, NULL, NULL, &creds.ticket))
			goto cleanup;

		if ((rv = krb5_auth_con_init(context->ctx, &context->auth_ctx)))
			goto cleanup;

		if ((rv = krb5_auth_con_setuseruserkey(context->ctx, context->auth_ctx, &creds.keyblock)))
			goto cleanup;

		context->state = KERBEROS_STATE_AP_REQ;
	}
	else if (context->state == KERBEROS_STATE_AP_REQ && tok_id == TOK_ID_AP_REQ)
	{
		if ((rv = krb5_rd_req(context->ctx, &context->auth_ctx, &input_token, NULL,
		                      credentials->keytab, &ap_flags, NULL)))
			goto cleanup;

		krb5_auth_con_setflags(context->ctx, context->auth_ctx,
		                       KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY);

		 
		if ((rv = krb5_auth_con_getauthenticator(context->ctx, context->auth_ctx, &authenticator)))
			goto cleanup;
		if (!authenticator->checksum || authenticator->checksum->checksum_type != GSS_CHECKSUM_TYPE)
			goto bad_token;
		Data_Read_UINT32((authenticator->checksum->contents + 20), context->flags);

		if (ap_flags & AP_OPTS_MUTUAL_REQUIRED && context->flags & SSPI_GSS_C_MUTUAL_FLAG)
		{
			if (!output_buffer)
				goto bad_token;
			if ((rv = krb5_mk_rep(context->ctx, context->auth_ctx, &output_token)))
				goto cleanup;
			if (!sspi_gss_wrap_token(output_buffer,
			                         context->u2u ? &kerberos_u2u_OID : &kerberos_OID,
			                         TOK_ID_AP_REP, &output_token))
				goto cleanup;
		}
		else
		{
			output_buffer->cbBuffer = 0;
		}

		*pfContextAttr = context->flags & 0x1F;
		if (context->flags & SSPI_GSS_C_INTEG_FLAG)
			*pfContextAttr |= ASC_RET_INTEGRITY;

		if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG)
		{
			krb5_auth_con_getlocalseqnumber(context->ctx, context->auth_ctx,
			                                (INT32*)&context->local_seq);
			krb5_auth_con_getremoteseqnumber(context->ctx, context->auth_ctx,
			                                 (INT32*)&context->remote_seq);
		}

		krb5_auth_con_getkey_k(context->ctx, context->auth_ctx, &context->session_key);
		krb5_auth_con_getrecvsubkey_k(context->ctx, context->auth_ctx, &context->initiator_key);
		krb5_auth_con_getsendsubkey_k(context->ctx, context->auth_ctx, &context->acceptor_key);

		context->state = KERBEROS_STATE_FINAL;
	}
	else
		goto bad_token;

	 
	if (new_context.ctx)
	{
		context = kerberos_ContextNew();
		if (!context)
		{
			status = SEC_E_INSUFFICIENT_MEMORY;
			goto cleanup;
		}
		CopyMemory(context, &new_context, sizeof(KRB_CONTEXT));
		ZeroMemory(&new_context, sizeof(KRB_CONTEXT));
		context->acceptor = TRUE;
	}

	if (context->state == KERBEROS_STATE_FINAL)
		status = SEC_E_OK;
	else
		status = SEC_I_CONTINUE_NEEDED;

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

cleanup:

	if (rv)
		kerberos_log_msg(context->ctx, rv);

	if (output_token.data)
		krb5_free_data_contents(context->ctx, &output_token);
	if (entry.principal)
		krb5_free_keytab_entry_contents(context->ctx, &entry);

	if (new_context.auth_ctx)
		krb5_auth_con_free(context->ctx, new_context.auth_ctx);
	if (new_context.ctx)
		krb5_free_context(new_context.ctx);

	return status;

bad_token:
	status = SEC_E_INVALID_TOKEN;
	goto cleanup;
#else
	return SEC_E_UNSUPPORTED_FUNCTION;
#endif  
}